オブジェクトを同じメソッドに2回渡すか、結合されたインターフェイスで統合しますか?


15

デジタルボードと通信した後にデータファイルを作成する方法があります。

CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)

ここではboardFileAccessboardMeasurerの同じインスタンスですBoardその実装するオブジェクトの両方IFileAccessIMeasurerIMeasurerこの場合、ボード上の1つのピンをアクティブに設定して簡単な測定を行う単一の方法に使用されます。この測定からのデータは、を使用してボードにローカルに保存されIFileAccessます。Board別のプロジェクトにあります。

私はCreateDataFile、簡単な測定を行ってからデータを保存することで1つのことを行うという結論に達しました。同じ方法で両方を行うことは、このコードを使用して測定を行ってファイルに書き込む必要がある他の人にとってより直感的です個別のメソッド呼び出しとして。

私には、同じオブジェクトをメソッドに2回渡すのは厄介に思えます。IDataFileCreator拡張IFileAccessし、必要なメソッドを呼び出すだけのインスタンスをIMeasurer含む実装を持つローカルインターフェイスを作成することを検討しました。同じボードオブジェクトが常に測定とファイル書き込みに使用されることを考えると、同じオブジェクトをメソッドに2回渡すのは悪い習慣ですか?もしそうなら、ローカルインターフェイスと実装を使用して適切なソリューションですか?BoardBoard


2
使用している名前からコードの意図を明確にすることは不可能に近いです。CreateDataFileという名前のメソッドに渡されるIDataFileCreatorという名前のインターフェイスは気が遠くなるでしょう。データを保持する責任をめぐって競合していますか?とにかくCreateDataFileのメソッドはどのクラスですか?測定はデータの永続化とは関係がないため、多くのことが明らかです。あなたの質問は、あなたがあなたのコードで抱えている最大の問題に関するものではありません。
マーティンマート

ファイルアクセスオブジェクトと測定オブジェクトが2つの異なるオブジェクトになる可能性は考えられますか?はいと言うでしょう。今すぐ変更する場合は、ネットワーク全体の測定をサポートするバージョン2 に戻す必要があります。
user253751

2
しかし、別の質問があります-そもそもデータファイルアクセスと測定オブジェクトが同じなのはなぜですか?
user253751

回答:


40

いいえ、これはまったく問題ありません。これは単に、現在のアプリケーションに関して APIが過剰に設計されていることを意味します

しかし、それはデータソースと測定者が異なるユースケースが決してないことを証明しません。APIのポイントは、すべてが使用されるわけではなく、アプリケーションプログラマに可能性を提供することです。ネットの理解度が低下するようにAPIを複雑にしない限り、APIユーザーができることを人為的に制限しないでください。


7

これはまったく問題ないという@KilianFothの回答に同意します。

それでも、必要に応じて、両方のインターフェイスを実装する単一のオブジェクトを取るメソッドを作成できます。

public object CreateDataFile<T_BoardInterface>(
             T_BoardInterface boardInterface
    )
    where T_BoardInterface : IFileAccess, IMeasurer
{
    return CreateDataFile(
                boardInterface
            ,   boardInterface
        );
}

引数が異なるオブジェクトである必要があるという一般的な理由はありません。メソッドが異なる引数を必要とする場合、それはその契約が明確にするべき特別な要件です。


4

私はCreateDataFile、簡単な測定を行ってからデータを保存することで1つのことを行うという結論に達しました。同じ方法で両方を行うことは、このコードを使用して測定を行ってファイルに書き込む必要がある他の人にとってより直感的です個別のメソッド呼び出しとして。

これは実際あなたの問題だと思います。メソッドは1つのことを実行していません異なるデバイスへのI / Oを伴う2つの異なる操作を実行しています。どちらも他のオブジェクトにオフロードしています。

  • 測定値を取得する
  • その結果をどこかのファイルに保存します

これらは2つの異なるI / O操作です。特に、最初のものはファイルシステムを変更しません。

実際、暗黙の中間ステップがあることに注意する必要があります。

  • 測定値を取得する
  • 測定値を既知の形式にシリアル化します
  • シリアル化された測定値をファイルに保存します

APIは、これらのそれぞれを何らかの形で個別に提供する必要があります。発信者がどこにでも保存せずに測定を行いたくないことをどのように知っていますか?他のソースから測定値を取得することを望まないことをどのように確認しますか?デバイス以外の場所に保存したくないことをどのように確認しますか?操作を分離する正当な理由があります。で裸の最小、各個々のピースがあるべきである利用可能なすべての呼び出し元へ。私のユースケースがそれを要求しない場合、測定値をファイルに書き込むことを強制されるべきではありません。

例として、このように操作を分離できます。

IMeasurer 測定値を取得する方法があります:

public interface IMeasurer
{
    IMeasurement Measure(int someInput);
}

あなたの測定タイプは、stringまたはのような単純なものかもしれませんdecimal。インターフェースやクラスが必要だと主張しているわけではありませんが、ここでは例をより一般的にしています。

IFileAccess ファイルを保存する方法がいくつかあります:

interface IFileAccess
{
    void SaveFile(string fileContents);
}

次に、測定値をシリアル化する方法が必要です。それを測定を表すクラスまたはインターフェースに組み込むか、ユーティリティメソッドを用意します。

interface IMeasurement
{
    // As part of the type
    string Serialize();
}

// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
    return ...
}

このシリアル化操作がまだ分離されているかどうかは明らかではありません。

このような分離により、APIが向上します。I / Oを実行することについて先入観を持たせるのではなく、発信者が必要なものとタイミングを決定できるようにします。呼び出し側は、あなたがそれが有用であると思うかどうかにかかわらず、有効な操作を実行するためのコントロールを持っているべきです

操作ごとに個別の実装を作成すると、CreateDataFileメソッドは次の短縮形になります。

fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));

特に、これらすべてを行った後は、メソッドはほとんど価値を追加しません。上記のコード行は、呼び出し元が直接使用することは難しくなく、メソッドはせいぜい利便性のためだけです。これはオプションである必要があります。そして、それはAPIが振る舞う正しい方法です。


すべての関連する部分が考慮され、メソッドが単なる便利であることを確認したら、質問を言い換える必要があります。

発信者の最も一般的なユースケースは何ですか?

全体のポイントが、同じボードからの測定と同じボードへの書き込みの典型的なユースケースをわずかに便利にすることである場合、Boardクラスで直接利用できるようにすることは完全に理にかなっています。

public class Board : IMeasurer, IFileAccess
{
    // Interface methods...

    /// <summary>
    /// Convenience method to measure and immediate record measurement in
    /// default location.
    /// </summary>
    public void ReadAndSaveMeasurement()
    {
        this.SaveFile(SerializeMeasurement(this.Measure()));
    }
}

これで利便性が改善されない場合は、この方法をまったく気にしません。


これは便利な方法であるため、もう1つ質問があります。

IFileAccessインターフェイスは測定タイプとシリアル化の方法を知っている必要がありますか?その場合、次のメソッドを追加できますIFileAccess

interface IFileAccess
{
    void SaveFile(string fileContents);
    void SaveMeasurement(IMeasurement m);
}

今、発信者はこれを行うだけです:

fileAccess.SaveFile(measurer.Measure());

これは、質問で考えた便利な方法と同じくらい短く、おそらくより明確です。


2

消費するクライアントは、単一のアイテムで十分な場合、アイテムのペアを処理する必要はありません。あなたの場合、の呼び出しまで、彼らはほとんどしませんCreateDataFile

提案する潜在的な解決策は、組み合わせた派生インターフェイスを作成することです。ただし、このアプローチでは、両方のインターフェイスを実装する単一のオブジェクトが必要です。これはかなり制約的であり、特定の実装に基本的にカスタマイズされるという点で、おそらく漏れやすい抽象化です。誰かが別々のオブジェクトに2つのインターフェイスを実装したい場合、それがどれほど複雑かを考えてみてください。他のオブジェクトに転送するには、インターフェイスのいずれかですべてのメソッドをプロキシする必要があります。(FWIW、別のオプションは、1つのオブジェクトが派生インターフェースを介して2つのインターフェースを実装する必要があるのではなく、インターフェースを単にマージすることです。)

ただし、実装に対する制約/指示が少ない別のアプローチは、インコンポジションIFileAccessとペアIMeasurerになり、一方が他方にバインドされて参照されるようにすることです。(これは、ペアリングを表すようになったため、そのうちの1つの抽象化をいくらか高めます。)次にCreateDataFile、たとえばIFileAccess、参照の1つだけを取り、必要に応じてもう1つを取得できます。両方のインターフェイスを実装するオブジェクトとしての現在の実装は、単にreturn this;構成参照用であり、ここではIMeasurerin のゲッターですIFileAccess

開発のある時点でペアリングが偽であることが判明した場合、つまり、同じファイルアクセスで異なる測定者が使用されることがある場合、この同じペアリングを行うことができますが、代わりにより高いレベルで、追加のインターフェイスが導入されます派生インターフェイスではなく、派生ではなく構成を介してファイルアクセスと測定者をペアにした2つのゲッターを持つインターフェイスです。消費側のクライアントは、ペアリングが保持されている限り、関心を持つ1つのアイテムと、必要に応じて(新しいペアリングを構成するために)処理する個々のオブジェクトを持ちます。


別の注意として、私は誰がを所有しているかを尋ねるかもしれませんがCreateDataFile、質問はこの第三者が誰であるかということです。を呼び出すいくつかの消費クライアントが既にありCreateDataFile、所有オブジェクト/クラスCreateDataFile、およびIFileAccessIMeasurer。コンテキストをより大きく表示すると、代替の組織が表示される場合があります。コンテキストが不完全であるため、ここで行うのは困難です。


0

CreateDataFileあまりにも多くのことをやっている人もいます。Boardファイルにアクセスすることは、ボードの他の部分とは別の関心事のように思えるので、代わりにやりすぎだと提案するかもしれません。

ただし、これが間違いではないと仮定した場合、大きな問題は、この場合のように、クライアントがインターフェイスを定義する必要があることCreateDataFileです。

インターフェイスの棲み分け原理は、クライアントがより多くのインターフェイスのそれは必要なものよりも依存する必要はありませんと述べています。この他の答えからフレーズを借りると、これは「インターフェースはクライアントが必要とするものによって定義される」と言い換えることができます。

これで、他の回答が示唆するように、このクライアント固有のインターフェイスを構成することが可能にIFileAccessなりIMeasurerましたが、最終的に、このクライアントはそれに合わせたインターフェイスを持つ必要があります。


@Downvoter-これについては間違っていますか、改善できますか?
Xtros
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.