プログラミング規則よりも一貫性を優先すべきですか?


14

クラスを設計するとき、動作の一貫性は一般的なプログラミングの実践よりも優先されるべきですか?特定の例を挙げます:

一般的な規則は次のとおりです。クラスがオブジェクトを所有している場合(たとえば、オブジェクトを作成した場合)、それが完了したら、それをクリーンアップする責任があります。具体的な例は、.NETで、クラスがIDisposableオブジェクトを所有している場合、そのオブジェクトが寿命の終わりに破棄されるようにすることです。そして、もしあなたがそれを所有していないなら、触らないでください。

StreamWriter.NET のクラスを見ると、ドキュメンテーションで、基になるストリームが閉じられている/破棄されているときに閉じていることがわかります。これはStreamWriter、ライターが基礎となるファイルストリームを作成し、それを閉じる必要があるときにファイル名を渡すことによってインスタンス化される場合に必要です。ただし、ライターが閉じる外部ストリームを渡すこともできます。

これにより私は何度も悩まされました(閉じないラッパーを作成できることはわかっていますが、それはポイントではありません)が、Microsoftは、ストリームがどこから来たとしても常にストリームを閉じる方が一貫しているという決定を下したようです。

クラスの1つでこのようなパターンに出くわすと、通常、コンストラクターを介して注入さownsFooBarれた場合はfalseに設定され、FooBarそれ以外の場合はtrueに設定されるフラグを作成します。このようにして、呼び出し元がインスタンスを明示的に渡すときに、それをクリーンアップする責任が呼び出し元に渡されます。

今、私はおそらくベストプラクティスよりも一貫性を優先すべきかどうか疑問に思っています(または私のベストプラクティスはそれほど良くないかもしれません)?それに対する/反対の議論はありますか?

明確化のために編集

「一貫性」とは、つまり、オブジェクトを作成または明示的に所有権を譲渡した場合にのみオブジェクトの所有権を取得する「ベストプラクティス」に対して、常に所有権を取得する(およびストリームを閉じる)クラスの一貫した動作です。

厄介な例:

いくつかのデータを作成および処理するなど、ストリームを受け入れて何らかの処理を行う2つのクラス(サードパーティライブラリから)があるとします。

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

今、私はこれを次のように使いたい:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

これはCannot access a closed stream、データプロセッサの例外で失敗します。それは少し構築されていますが、ポイントを説明する必要があります。それを修正するにはさまざまな方法がありますが、それでも私はするべきではない何かを回避すると感じています。


StreamWriterの図解された動作が苦痛を引き起こす理由について詳しく説明していただけますか?他の場所で同じストリームを消費しますか?どうして?
ジョブ

@Job:私は私の質問を明らかにした
ChrisWue

回答:


9

私もこの手法を使用し、「ハンドオフ」と呼び、パターンのステータスに値すると考えています。オブジェクトAが構築時間パラメーターとして使い捨てオブジェクトBを受け入れる場合、「handoff」(デフォルトはfalse)と呼ばれるブール値も受け入れます。trueの場合、Aの破棄はBの破棄にカスケードされます。

私は他の人の不幸な選択を増殖させることに賛成していないので、Microsoftの悪い慣行を確立された慣習として決して受け入れません。 。


注:このパターンの正式な定義をブログの投稿で書いた:michael.gr:The "Handoff" Pattern
マイクナキス

handOffパターンのさらなる拡張として、そのようなプロパティがコンストラクターで設定されているが、それを変更する別のメソッドが存在する場合、その別のメソッドもhandOffフラグを受け入れる必要があります。handOff現在保持されているアイテムに指定された場合、それは破棄され、新しいアイテムが受け入れられ、内部handOffフラグが更新されて新しいアイテムのステータスが反映されます。元の参照が「ハンドオフ」された場合、元の呼び出し元が保持していたすべての参照を破棄する必要があるため、メソッドは古い参照と新しい参照の同等性をチェックする必要はありません。
supercat

@supercat私は同意しますが、最後の文に問題があります:ハンドオフが現在保持されているアイテムに対して真であるが、新しく提供されたアイテムが現在保持されているアイテムを参照して等しい場合、現在保持されているアイテムを破棄する実際に、新しく提供されたアイテムを破棄しています。同じアイテムを再供給することは違法だと思います。
マイクナキス14

通常、オブジェクトが不変で、実際にリソースを保持せず、廃棄されるかどうかを気にしない限り、同じアイテムを再供給することは違法です。たとえばDispose、システムブラシの要求が無視された場合、 "color.CreateBrush`メソッドは、余暇に新しいブラシを作成するか(廃棄が必要)、システムブラシを返す(廃棄を無視します)。それは処分を気にしている場合、オブジェクトの再利用が、それがない場合は、再利用を許可し、それを配置することである。
supercat

3

私はあなたが正しいと思う、正直に言うと。マイクロソフトがStreamWriterクラスを台無しにしたのは、具体的には、あなたが説明した理由のためだと思います。

ただし、フレームワークがストリームを処理するため、人々が自分のStreamを破棄しようとしないコードを多く見てきました。


2

私は一貫して行きます。ほとんどの人に言ったように、オブジェクトが子オブジェクトを作成した場合、それはそれを所有し、破壊します。外部から参照が与えられた場合、ある時点で「外部」も同じオブジェクトを保持しており、所有すべきではありません。所有権を外部からオブジェクトに転送する場合は、Attach / Detachメソッド、または所有権が転送されることを明確にするブール値を受け入れるコンストラクターを使用します。

完全に答えるためにすべてを入力したので、ほとんどの一般的なデザインパターンは必ずしもStreamWriter / StreamReaderクラスと同じカテゴリに分類されるとは限らないと思います。私は、MSがStreamWriter / Readerに自動的に所有権を引き継ぐことを選択した理由は、この特定の例では、共有所有権を持つことはあまり意味がないためだと常に考えてきました。2つの異なるオブジェクトを同じストリームから同時に読み書きすることはできません。何らかの決定論的なタイムシェアを書くことができると確信していますが、それはアンチパターンの1つの地獄です。ストリームを共有することは意味をなさないため、常に引き継いでみましょう。しかし、私はこの振る舞いがすべてのクラスの全面的な一般的な慣習になったことはないと思います。

Streamsは、渡されるオブジェクトが設計の観点から共有できないため、追加のフラグがなければ、リーダー/ライターが所有権を引き継ぐ一貫したパターンであると考えることができます。


私は自分の質問を明確にしました-一貫して、私は常に所有権を取る行動を意味しました。
ChrisWue

2

注意してください。「一貫性」と考えるものは、ランダムな習慣の集まりにすぎないかもしれません。

「一貫性のためのユーザーインターフェイスの調整」、SIGCHI Bulletin 20(1989)、63-65。申し訳ありませんが、リンクはありません。古い記事です。「... 15人の専門家による2日間のワークショップでは、一貫性の定義を作成できませんでした。」はい、それは「インターフェース」に関するものですが、しばらくの間「一貫性」について考えると、それも定義できないことに気付くでしょう。


専門家が異なるサークルから集まった場合、あなたは正しいですが、コードを開発するとき、私のチームがすでに慣れている既存のパターン(またはあなたがするなら習慣)に従うのは簡単だとわかりました。たとえば、先日、私たちの仲間の1人が非同期操作について問い合わせていましたが、Win32 Overlapped I / Oと同じように動作することを彼に伝えたとき、30秒でインターフェイス全体を理解しました...この場合は両方とも私と彼は同じサーバーバックエンドのWin32バックグラウンドから来ました。一貫性ftw!:)
DXM

@ブルース私はあなたのポイントを得る-私は一貫して私が意味したことは明らかだったと思ったが、明らかにそうではなかった。
ChrisWue
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.