web-dev-qa-db-ja.com

オブジェクトを初期化するためにコンストラクターでシリアル通信を実行する

特定のエンコーディングなど、構造化された方法でユニット情報を取得するメソッドを備えたユニット情報のコレクションを表すクラスUnitInfoがあります。このユニット情報は、ユニットから読み取る必要のある情報で構成されています。シリアルポート通信経由。クラスのコンストラクターには、ユニットから照会できないシリアル番号と、ユニットに情報を照会するために使用するインスタンスシリアル通信インターフェイスが与えられます。

public class UnitInfo {
    public int SerialNumber { get; }
    public int InternalMemorySize { get; }
    // And more properties...

    public UnitInfo(ISerialCom serialCom, int serialNumber) {
        SerialNumber = serialNumber;
        InternalMemorySize = serialCom.GetInternalMemorySize();
        // More serial communication to set more properties
    }

    // Get methods to retrieve all the unit information in a specific encoding
    // i.e a collection of bytes.
}

私のブラウジングSOとGoogleから、コンストラクタで作業を行うことは通常は問題ないことを理解していますが、シリアル、GPIBなどを介して通信を実行する例はまだ見つかりません。コンストラクタ。クラスはそれ自体を初期化するためにインターフェイスの実装に依存しているため、これは依存性注入の原則に違反している可能性があると考えています。すべてのユニット情報をクラスに渡す方がよいでしょうか。

1
pavuxun

このデザインは完全に素晴らしいです。外部ポートと通信していますが、おそらくコンピュータに直接接続されているハードウェアであるため、通信は迅速に行われます。

C#で作業しているので、ユーザーがプラグを(文字通り)プルした結果としてのコンストラクターの例外は問題になりません。共通言語ランタイムがクリーンアップを担当するためですが、ISerialComオブジェクトは、接続を管理する必要がある場合にIDisposableを実装します(ただし、これはこのコンストラクターの責任ではありません)。

この重要な情報を提供するオブジェクトを受け入れることの良いところは、このオブジェクトの構築が非常に防弾になることです。シリアル番号の代わりに誤って内部メモリサイズを渡すことはできません。

1
Greg Burghardt

コンストラクターで作業を行う

これは、最終的には意見の問題であるものの1つです。あなたの現在受け入れられている答えはone意見ですが、それは私が共有するものではありません、そして 私はそれだけではありません

コラボレーターの作成/初期化、他のサービスとの通信、独自の状態を設定するロジックなどのコンストラクターで作業し、テストに必要な継ぎ目を削除し、強制します不要な動作を継承するサブクラス/モック。コンストラクターでの作業が多すぎると、テストでのインスタンス化や共同作業者の変更が妨げられます。

(エンファシスマイン)

しかし、「フィールドの割り当て以上のもの」を警告サインと見なしているMisko Hevery(異なる意見...)にも100%同意することはできません。 nullチェックやintが特定の範囲内にあると主張するなどの「短くて甘い」検証は許容されます。

これについての私のお気に入りの比喩:歯科医の仕事は、歯科医院を建設することではありません

実用的なアドバイス

それらのUnitInfoFactorysの作成を担当するUnitInfoを作成することはそれほど難しくないはずです。 UnitInfoが非常に小さい場合は、静的ファクトリメソッドも問題ありません。

instantlyUnitInfoの実装を何らかの方法で取得する代わりに、2つのintでISerialComを作成できます(例で省略したコードを無視します)もちろん)。ユニットテストを行う場合、これはゴールドです。モックフレームワークを使用しても、そのモックを作成するのは追加の作業であるためです。ただし、単体テストを行っていない場合でも、テスト可能になるように記述されたコードは、他のコードよりもクリーンになる傾向があります。

DI原則への違反なし

ただし、ここでのこの部分は意見の問題ではありません。

また、クラスはそれ自体を初期化するためのインターフェイスの実装に依存しているため、これは依存性注入の原則に違反している可能性があると考えています。

サンプルコードに関する限り、GetInternalMemorySize()に対して常にゼロを返すISerialComのモック実装を提供できますが、それでも機能します。そのため、クラスは「インターフェイスの一部の実装に依存」するのではなく、インターフェイス自体にのみ依存します。

誤解は、クラスが実際に機能するためにまだ(y)実装が必要であるという事実にあると思いますが、それはDIの原則の目的ではありません。 David Arnoがコメントですでに述べたように、コンストラクターでnewを使用すると問題が発生します。

これについて私が見つけた最高のニーモニックは「new isglue」です:コードでnewを使用するたびに、そのコードは結び付けられますその特定の実装まで。 newコンストラクター内を繰り返し使用すると、特定の実装のチェーンが作成されます。また、クラスを構築せずにインスタンスを「持つ」ことはできないため、そのチェーンを分離することはできません。

しかし、あなたはそれをしていないようですので、それは問題ありません。

3
R. Schmitz