2013-02-12 9 views
9

私は以下のようなコードを持っています。クラスレベル変数でusingステートメントを使用することは悪い習慣ですか?

class MyController 
{ 
    [ThreadStatic] private DbInterface db; 

    public void ImportAllData() 
    { 
     using (db = new DbInterface()) 
     { 
      var records = PullData(); 
      PushData(records); 
     } 
    } 

    private DbRecord[] PullData() 
    { 
     return db.GetFromTableA(); 
    } 

    private void PushData(DbRecord[] records) 
    { 
     db.InsertIntoTableB(records); 
    } 
} 

代替方法は、維持するのがずっと面倒です。

class MyController 
{ 
    public void ImportAllData() 
    { 
     using (var db = new DbInterface()) 
     { 
      var records = PullData(db); 
      PushData(records, db); 
     } 
    } 

    private DbRecord[] PullData(DbInterface db) 
    { 
     return db.GetFromTableA(); 
    } 

    private void PushData(DbRecord[] records, DbInterface db) 
    { 
     db.InsertIntoTableB(records); 
    } 
} 

私の知る限り見ることができるように、私の最初の実装:

  • は(DbInterfaceは、スレッドセーフであると仮定した場合)スレッドセーフで、
  • db変数に触れるから他のプロセスを防ぎ、かつ
  • dbは、例外時にも常に廃棄されます。

usingステートメントをクラススコープの変数に使用することは悪い習慣ですか?私は何かを逃したか?

+0

拡張子を使用していると見なされますか?あるいはこれをたくさんすることを計画していますか? –

+3

興味深いことに、Winフォームのペイントコードを実行した場合、OnPaintおよびPaintイベントは、少なくともrefをuseブロックの外に渡すことに関して、これと非常によく似ています。あなたに割り当てられたグラフィカルコンテキストが渡され、その管理/廃棄が呼び出し元によって処理されるため、実際の描画メソッドで自分自身を処理しません。 – tcarvin

+0

@DanSaltmer、私は他のコントローラで 'DbInterface'クラスの使用を予定しています。私はコントローラがすべての 'DbInterface'メソッドにアクセスできるようにしたいと思いますが、私はコントローラがそれぞれのプライベートメソッドについて知ることは望ましくありません。私の理解では、拡張メソッドはすべての 'DbInterface'インスタンスに適用されます。それとも私はあなたを誤解したことがありますか? –

答えて

10

個人的には、私はあなたの第2の選択肢を好みます。

最初のデザインの問題点は、不要なカップリングをデザインに効果的に追加することにあります。 PullDataPushDataメソッドは単独では使用できません。には、ImportAllData、またはdb変数をセットアップして適切にクリーンアップするその他のメソッドを最初に呼び出す必要があります。が必要です。

第2の選択肢は、少し多くのコードではありますが、それぞれの方法ごとに非常に明確になります。各メソッドは、外部DbInterfaceインスタンスが渡される必要があることを認識しています。これが将来悪用される可能性はほとんどまたはまったくありません。

8

最初の変更ではdbusingブロックで管理されている範囲外に表示されます。それは意図しない副作用の可能性を開く。たとえば、別の方法ではdbを使用したり、処理したりすることもできます。あなたや後のメンテナーがdbの暗黙の契約を忘れた場合や、コードの入力ミスを忘れてしまった場合に起こります。

私は最初の変種を使用しません。ここ

4

代替である:この場合

sealed class MyImporter 
{ 
    private readonly DbInterface db; 

    public MyImporter(DbInterface db) 
    { 
     this.db = db; 
    } 

    public void ImportAllData() 
    { 
     var records = PullData(); 
     PushData(records); 
    } 

    private DbRecord[] PullData() 
    { 
     return db.GetFromTableA(); 
    } 

    private void PushData(DbRecord[] records) 
    { 
     db.InsertIntoTableB(records); 
    } 
} 

、基準に保持するクラスの責任の明確な部分です。また、処分の責任をユーザーに押し上げています。このより明示的な構成は、「コントローラ」に追加の機能を追加する誘惑を軽減します。これは、最初のアプローチが長期的に悪くなる可能性がある方法です。基本的に、インポート機能を別のクラスにリファクタリングして、共有フィールドのアクセスがもはや問題にならないようにしました。

+1

+1:コンストラクタに依存性注入を追加することもできます。public MyImporter(DbInterface _db) { db = _db; } – horgh

+0

@ ConstantinVasilcovは、それは実際にはもっときれいだと同意した。それは実際にはクラス内に廃棄をする必要性を排除します。 –

+0

これは、 'db'を処分する責任を呼び出し側に移します。良いことか悪いことかというと、クラスの責任の変更です。呼び出し元が 'db 'のインスタンスを作成する方法を知らない場合、このパターンは機能しません。 –

0

これはそのための構造です。規約では、プロパティへのアクセスが軽量であり、ユーザーが変数にアクセスしていると思ったときに、作成ディスペンス・サイクルをトリガする必要はないと命令しているため、プロパティにusingを入れることには注意が必要です。

ただし、コードの構造については以下のコメントが重要です。インポートを一度やりたい場合は、ユーザーが知っておく必要があるセットアップ手順になります。異なるアクセスパターンを想定することができれば、依存関係注入設定では、ユーザーは接続の作成と廃棄を制御できます。

関連する問題