シングルトン パターンには多くの欠点があり、パターンを完全に避けることを提案する人さえいることに、多くの人が同意しているようです。ここに素晴らしい議論があります。Singleton パターンに関するコメントは、その質問に送ってください。
私の質問: 避けるべき、または細心の注意を払って使用する必要がある他の設計パターンはありますか?
シングルトン パターンには多くの欠点があり、パターンを完全に避けることを提案する人さえいることに、多くの人が同意しているようです。ここに素晴らしい議論があります。Singleton パターンに関するコメントは、その質問に送ってください。
私の質問: 避けるべき、または細心の注意を払って使用する必要がある他の設計パターンはありますか?
すべての設計パターンは注意して使用する必要があります。私の意見では、パターンをすぐに実装するのではなく、正当な理由がある場合はパターンに向けてリファクタリングする必要があります。パターンを使用する際の一般的な問題は、パターンが複雑になることです。パターンを使いすぎると、特定のアプリケーションやシステムをさらに開発して維持するのが面倒になります。
ほとんどの場合、単純な解決策があり、特定のパターンを適用する必要はありません。経験則としては、コードの一部が置き換えられる傾向にある場合や頻繁に変更する必要がある場合はいつでもパターンを使用し、パターンを使用するときに複雑なコードの警告を受け入れる準備をすることです。
コードの変更を実際にサポートする必要がある場合は、単純化を目標とし、パターンを採用する必要があることを忘れないでください。
過度に設計された複雑なソリューションにつながる可能性があることが明らかな場合、パターンを使用するのは無意味に思えるかもしれません。しかし、プログラマーにとっては、ほとんどのパターンの基礎となる設計手法と原則を読む方がはるかに興味深いものです。実際、「デザイン パターン」に関する私のお気に入りの本の 1 つは、問題のパターンにどの原則が適用できるかを繰り返し説明することで、これを強調しています。これらは、関連性の点でパターンよりも十分に単純です。一部の原則は、コードのモジュールを構築できる限り、Liskov Substitution Principleなど、オブジェクト指向プログラミング (OOP) を超えるものを網羅するのに十分一般的です。
多数の設計原則がありますが、GoF ブックの最初の章で説明されているものは、最初に使用するのに非常に役立ちます。
しばらくの間、それらをあなたに沈ませてください。GoF が記述されたとき、インターフェースは抽象化されたもの (スーパー クラスも意味する) を意味することに注意してください。Java や C# の型としてのインターフェースと混同しないでください。2 番目の原則は、悲しいことに今日でも一般的な継承の乱用が観察されていることに由来します。
そこから、Robert Cecil Martin (aka. Uncle Bob)によって知られるようになったSOLID 原則を読むことができます。Scott Hanselman は、これらの原則についてポッドキャストでボブおじさんにインタビューしました。
これらの原則は、読んで仲間と話し合うための良い出発点です。原則が相互に絡み合っており、関心の分離や依存性注入などの他のプロセスと絡み合っていることに気付くかもしれません。しばらくの間TDDを行った後、これらの原則が実際に自然に得られることに気付くかもしれません。分離された反復可能な単体テストを作成するには、ある程度従う必要があるからです。
デザインパターンの作者自身が最も心配していたのは「ビジター」パターンでした。
これは「必要な悪」ですが、使いすぎることが多く、その必要性から、設計のより根本的な欠陥が明らかになることがよくあります。
「Visitor」パターンの別名は「Multi-dispatch」です。これは、Visitorパターンは、単一タイプのディスパッチOO言語を使用して、2つのタイプに基づいて使用するコードを選択する場合に使用されるものであるためです。 (またはそれ以上)異なるオブジェクト。
古典的な例は、2つの形状の間に交差があるというものですが、見過ごされがちなさらに単純なケースがあります。それは、2つの異種オブジェクトの同等性を比較することです。
とにかく、多くの場合、次のような結果になります。
interface IShape
{
double intersectWith(Triangle t);
double intersectWith(Rectangle r);
double intersectWith(Circle c);
}
これに伴う問題は、「IShape」のすべての実装を結合していることです。階層に新しい形状を追加する場合は常に、他のすべての「形状」実装も変更する必要があることを意味します。
時々、これは正しい最小限のデザインです-しかし、それをよく考えてください。あなたのデザインは本当にあなたが2つのタイプで派遣する必要があることを義務付けていますか?マルチメソッドの組み合わせ爆発のそれぞれを喜んで書きますか?
多くの場合、別の概念を導入することで、実際に記述しなければならない組み合わせの数を減らすことができます。
interface IShape
{
Area getArea();
}
class Area
{
public double intersectWith(Area otherArea);
...
}
もちろん、状況によって異なりますが、これらのさまざまなケースをすべて処理するためのコードを実際に作成する必要がある場合もありますが、思い切ってVisitorを使用する前に、一時停止して考えてみる価値があります。それは後であなたに多くの苦痛を救うかもしれません。
シングルトン - シングルトン X を使用するクラスには依存関係があり、それを確認してテストするために分離するのは困難です。
これらは便利で理解しやすいため、非常に頻繁に使用されますが、テストが非常に複雑になる可能性があります。
Singletons are Pathological Liarsを参照してください。
Template Method パターンは一般的に非常に危険なパターンだと思います。
デザインパターン(DP)を避けるべきではないと思います。また、アーキテクチャを計画するときにDPを使用するように強制するべきではないと思います。DPは、計画から自然に出現した場合にのみ使用する必要があります。
最初から特定のDPを使用することを定義した場合、将来の設計上の決定の多くはその選択に影響され、選択したDPがニーズに適しているという保証はありません。
また、DPを不変のエンティティとして扱うこともすべきではありません。パターンをニーズに適合させる必要があります。
したがって、要約すると、DPを回避する必要はないと思います。DPがすでにアーキテクチャで具体化されている場合は、DPを採用する必要があります。
Active Record は、ビジネス ロジックと永続化コードを混在させることを奨励する使い古されたパターンだと思います。ストレージの実装をモデル層から隠したり、モデルをデータベースに結び付けたりするのはうまくいきません。Table Data Gateway、Row Data Gateway、Data Mapper などの多くの代替手段 (PoEAA で説明) があり、これらは多くの場合、より優れたソリューションを提供し、確実にストレージへのより優れた抽象化を提供するのに役立ちます。また、モデルをデータベースに保存する必要はありません。それらを XML として保存したり、Web サービスを使用してそれらにアクセスしたりするのはどうですか? モデルのストレージ メカニズムを変更するのはどれくらい簡単ですか?
とは言っても、Active Record は必ずしも悪いわけではなく、他のオプションが過剰な単純なアプリケーションに最適です。
それは簡単です...あなたにとって明確でないデザインパターンやあなたが快適に感じないデザインパターンは避けてください。
いくつか名前を付けるには...
たとえば、次のような実用的でないパターンがいくつかあります。
Interpreter
Flyweight
たとえば、次のように、把握するのが難しいものもあります。
Abstract Factory
-作成されたオブジェクトのファミリを含む完全な抽象ファクトリパターンは、見た目ほど簡単ではありませんBridge
-抽象化と実装がサブツリーに分割されている場合、抽象化しすぎる可能性がありますが、場合によっては非常に使いやすいパターンですVisitor
-ダブルディスパッチメカニズムの理解は本当に必須です非常に単純に見えるパターンもありますが、その原理や実装に関連するさまざまな理由により、それほど明確な選択ではありません。
Singleton
-完全に悪いパターンではなく、使いすぎているだけです(多くの場合、適切ではありません)Observer
-素晴らしいパターン...コードの読み取りとデバッグがはるかに困難になるだけですPrototype
-コンパイラチェックのダイナミズムを交換します(これは良い場合も悪い場合もあります...依存します)Chain of responsibility
-あまりにも頻繁に強制的/人為的にデザインに押し込まれたそれらの「非実用的なもの」については、通常どこかにもっとエレガントな解決策があるので、それらを使用する前に本当に考える必要があります。
「把握しにくい」ものについては...適切な場所で使用され、適切に実装されている場合、それらは本当に大きな助けになります...しかし、不適切に使用された場合、それらは悪夢です。
さて、次は...
サービスロケーターはアンチパターンだと言う人もいます。
これであまり殴られないことを願っています。Christer Ericsson は、彼のリアルタイム衝突検出ブログで、設計パターンのトピックに関する 2 つの記事 (1、2)を書きました。彼の口調はかなり荒々しく、おそらく少し挑発的ですが、彼は自分のことを知っているので、私はそれを狂人の暴言として却下するつもりはありません.
Spoike の投稿を補完するRefactoring to Patternsは、よく読んでください。
オブザーバー パターンには解決すべきことがたくさんあると思います。それは非常に一般的なケースで機能しますが、システムがより複雑になると、OnBefore()、OnAfter() 通知が必要になり、再回避のために非同期タスクをポストすることが頻繁に必要になり、悪夢になります。入場。はるかに優れた解決策は、計算中にすべてのオブジェクト アクセス (読み取りバリアを使用) を計測し、依存関係グラフにエッジを自動的に作成する、自動依存関係分析のシステムを開発することです。
Iterator は、避けるべきもう 1 つの GoF パターンです。少なくとも、代替手段がない場合にのみ使用してください。
代替手段は次のとおりです。
for-each ループ。この構造は、ほとんどの主流言語に存在し、ほとんどの場合、反復子を回避するために使用できます。
LINQ または jQuery 風のセレクター。コンテナーのすべてのオブジェクトを処理する必要がないため、for-each が適切でない場合に使用する必要があります。イテレーターとは異なり、セレクターを使用すると、処理するオブジェクトの種類を 1 か所で明示できます。