同僚とコーディングスタイルの競合が発生しました。我々は両方のインターフェースから始め、言う:インターフェイスの実装とインターフェイスの提供
interface IMessageList {
void AddMessage(IMessage message);
void RemoveMessage(IMessage message);
}
私はインターフェイスは、私はそれをクラスを実装していますで使用する方法:
// Method 1
class SomeClass : IMessageList {
private IList<IMessage> messages = new List<IMessage>();
public AddMessage(IMessage message) {
messages.Add(message);
}
public RemoveMessage(IMessage message) {
messages.Remove(message);
}
}
私の同僚が何をするかは、しかし、これです:
// Mehod 2
interface IMessageListProvider {
IMessageList MessageList { get; }
}
class MessageList : IMessageList {
// An implementation similar to the above
}
class SomeClass : IMessageListProvider {
private IMessageList messageList = new MessageList();
public MessageList { get { return messageList; } }
}
彼は、Microsoftのコードからこの方法をプログラムするためのアイデアを得たと言いますが、私の研究では、私は、これは「プラグ可能なシングルトンのMicrosoft固有の形式である「プロバイダ」のパターンのように聞こえることがわかりました"しかし、私の同僚は実際にそれを実際に使っていることはありません。これは彼が一般的にインターフェイスで動作する方法です。また、適切なコンテキストでもプロバイダパターンに批判的な人々が見つかりました:http://blog.ploeh.dk/2011/04/27/ProviderIsNotAPattern.aspx、http://www.colourcoding.net/blog/archive/2009/07/29/microsofts-provider-pattern-a-bad-idea-carried-out-to-perfection.aspx。第1の方法と同様
::だから、ここでは、プロバイダパターン自体のより具体的な問題を脇に設定するには、私は上記のコードだけを見ると、賛否両論ある
長所:
- 1つの契約(
IMessageList
)のみを定義して使用し、IMessageList
の使用は他の抽象に依存しません。 - 一つが容易派生型で
IMessageList
メンバをオーバーライドしbase.AddMessage
介してベースの実装にアクセスすることができるので、それは拡張可能である、等
短所:
- インターフェースの多くを実装するクラスこの方法は多くの方法で終了し、名前の競合はより簡単に発生します。
- 多くのクラスがインターフェイスメソッドを実装して転送している場合、インターフェイスにメンバーが存在する数だけフォワーダが存在します。
SomeClass
のIMessageList
関連コンポーネントと他のコンポーネントの間にカプセル化はありません。長所:第2の方法として
- 多くのクラスが
IMessageListProvider
を実装し、転送している場合には、唯一の方法は、それぞれでオーバーライドする必要があります。 IMessageList
の実装は、より緩やかな結合を維持してSomeClass
のままになります。
短所:この方法は、単にそれをカプセル化していない、複雑さを隠しているプロバイダの多くを実装
- 。
- は、私たちが定義した実質的な契約ですが、コードはほとんど
IMessageListProvider
を通過しなければならないので、IMessageList
だけを使用することはほとんどありません。私たちの抽象化は別の抽象化に依存しています。 IMessageList
とIMessageListProvider
を同時に実装することはできません。なぜなら、 'IMessageList'メソッドにアクセスするには2つの方法があり、1つは間違っている可能性があるからです。これは、定義している型に制限を設けていますが、これらの両方のインタフェースを実装してもコンパイラエラーは発生しません。base.MessageList
を呼び出すと、私たちにオブジェクトが与えられるだけなので、IMessageList
の動作を簡単に上書きすることはできません。代わりに、基本クラスが使用しているオブジェクトのサブクラスを返すようにオーバーライドする必要があります。
私の方法では、IMessageList
のメンバーを上書きすると、誰もがIMessageList
を使用するために起こることがわかります。彼の方法では、IMessageListProvider
を使用するクラスの抽象ファクトリを配置する必要があります。そのため、派生型を送信して、必要なものを実装することができます。しかし、これは単純な多型に戻るための長い道のりです。インタフェースが必要となる。また、オーバーライドが非常に難しいので、IMessageList
の実装を1つだけ使用することに私たちを固定するようです。しかし、具体的な実装が1つしかない場合は、なぜインターフェイスの集まりですか?私たちは契約(IMessageList
)から始めましたが、今では別の抽象化(IMessageListProvider
)に依存しています。これは基本的に1つのタイプ(MessageList
)私たちの元来の抽象化したコンセンサス。
要するに、私は彼の方法から悪い匂いがするが、彼は2つの間の選択は単なるスタイルの違いであると断言している。私はこれについて他の意見を見つけることができるかどうかを調べるためにたくさんの調査をしましたが、パターン固有のプロバイダのものを思いついただけなので、どれくらいうまく適用できるかはわかりません。だから私はコミュニティがインターフェースを実際にインターフェースを実装するのと比べてプロパティゲッターとして提供するこの一般的なスタイルについて何を考えているのか不思議です。
// Compromise
class SomeClass : IMessageList {
private struct MessageList : IMessageList {
private IList<IMessage> messages = new List<IMessage>();
public AddMessage(IMessage message) {
messages.Add(message);
}
public RemoveMessage(IMessage message) {
messages.Remove(message);
}
}
private readonly messageList = new MessageList();
public AddMessage(IMessage message) {
messageList.Add(message);
}
public RemoveMessage(IMessage message) {
messageList.Remove(message);
}
}
これは、しかし、上記のようにインターフェースを破壊することなく、彼はSomeClass
とIMessageList
の実装間望んでカプセル化を実現:私の同僚への妥協案として
MessageList
は、他のクラスがそれを使用できるように内部または公的にすることができます。しかし、
MessageList
は常に便利な形で転送されるべきであり、実際には返されないという私の信念です。その結果、実際にはインタフェースの実装となり、抽象化を妨害します。
あなたの答えをありがとう、ありがとう!ありがとう!でも私はちょうど "tl; dr"を私の質問にしたいと思っています。 – user875215