回避する設計パターン[クローズド]


105

多くの人が同意しているようです。シングルトンパターンには多くの欠点があり、パターンを完全に回避することを提案する人もいます。ここで素晴らしい議論があります。シングルトンパターンに関するコメントは、その質問に向けてください。

私の質問:他のデザインパターンはありますか?



@casperOneなぜ質問を閉じたのですか?質問は正当でした。
bleepzter

回答:


149

パターンは複雑です

すべての設計パターンは注意して使用する必要があります。私の意見では、パターンをすぐに実装するのではなく、正当な理由がある場合はパターンリファクタリングする必要があります。パターンの使用に関する一般的な問題は、パターンが複雑になることです。パターンを使いすぎると、特定のアプリケーションやシステムをさらに開発して維持するのが面倒になります。

ほとんどの場合、簡単な解決策があり、特定のパターンを適用する必要はありません。大まかな目安としては、コードの一部が頻繁に置き換えられる、または頻繁に変更される必要がある場合は常にパターンを使用し、パターンを使用するときに複雑なコードの警告に対応できるようにしてください。

コードの変更をサポートするための実用的なニーズがある場合は、単純な目標を設定し、パターンを採用することを忘れないでください

パターンに関する原則

パターンが明らかに過度に設計された複雑なソリューションにつながる可能性がある場合、パターンを使用することは意味がないように思えるかもしれません。ただし、プログラマにとっては、ほとんどのパターンの基礎となる設計手法と原則について読む方がはるかに興味深いです。実際、「デザインパターン」に関する私の好きな本の 1つは、問題のパターンに適用できる原則を繰り返し説明することで、これを強調しています。それらは、関連性の点でパターンよりも有用であるほど十分に単純です。コードのモジュールを作成できる限り、いくつかの原則は、Liskov Substitution Principleなどのオブジェクト指向プログラミング(OOP)以上を網羅するのに十分一般的です。

多数の設計原則がありますが、GoFブックの最初の章で説明されているものは、最初から非常に役立ちます。

  • 「実装」ではなく「インターフェース」にプログラムします。(ギャングオブフォー1995:18)
  • 「クラス継承」より「オブジェクト構成」を優先します。(ギャングオブフォー1995:20)

それらをしばらくの間あなたに沈めてみましょう。GoFが作成されたとき、インターフェイスは抽象化(スーパークラスも意味する)を意味し、JavaまたはC#の型としてのインターフェイスと混同しないように注意してください。第二の原則は、今日でも悲しいことに一般的である、継承の過剰使用の観察から来ています

そこから、ロバートセシルマーティン(別名、ボクルおじさん)によって知られるようになったSOLIDの原則について読むことができます。スコットハンセルマンは、これらの原則に関するポッドキャストでボブおじさんにインタビューしました。

  • Sイングル責任原則
  • Oペンの閉じた原理
  • L置換原則iskov
  • I棲み分け原理nterface
  • Dの ependency反転原理

これらの原則は、仲間と読んで話し合う良い出発点です。原則が相互に絡み合ったり、懸念の分離依存性注入などの他のプロセスと相互に作用していることに気付くかもしれません。しばらくTDDを行った後、これらの原則が実際に実際に適用される場合もあります。これは、分離された繰り返し可能な単体テストを作成するためにある程度従う必要があるためです。


7
+1非常に良い答え。今日、すべての(初心者)プログラマは、自分のデザインパターンを知っているか、少なくともそれらが存在することを知っているようです。しかし、非常に多くの人が、コードの複雑さを管理する単一の責任などの絶対に不可欠な原則のいくつかを聞いたことはありません。
eljenso 2009年

21

デザインパターンの作者自身が最も心配したのは「ビジター」パターンでした。

これは「必要な悪」ですが、頻繁に使用されすぎており、その必要性から設計の根本的な欠陥が明らかになることがよくあります。

「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を使用する前に検討する価値があります。それは後であなたに多くの苦痛を保存するかもしれません。


2
訪問者についていえば、アンクルボブはそれを使用して「すべての時間」butunclebob.com/ArticleS.UncleBob.IuseVisitor
Spoike

3
@Paul Hollingsworthデザインパターンの作者が心配していると言っている場所への参照を提供できますか(そして、なぜ心配しているのですか)。
m3th0dman 2013年

16

シングルトン-シングルトンXを使用するクラスは、依存関係があり、テストのために見たり分離したりすることが困難です。

便利で理解しやすいため、よく使用されますが、テストが非常に複雑になる場合があります。

シングルトンは病理学的嘘つきを参照してください。


1
また、Mockオブジェクトを注入するための単一のポイントを提供できるので、テストを単純化できます。それはすべてバランスを正しく取ることにかかっています。
マーティンブラウン

1
@Martin:もちろん、テスト用にシンゲルトンを変更することは可能ですが(標準のシンゲルトン実装を使用しない場合)、コンストラクタにテスト実装を渡すよりも簡単です。
09年

14

テンプレートメソッドのパターンは一般的に非常に危険なパターンだと思います。

  • 多くの場合、「誤った理由」のために継承階層を使い果たします。
  • 基本クラスは、あらゆる種類の無関係なコードで散らかされる傾向があります。
  • 多くの場合、開発プロセスのかなり早い段階で設計をロックダウンする必要があります。(多くの場合、時期尚早のロックダウン)
  • 後の段階でこれを変更することは、ますます難しくなります。

2
テンプレートメソッドを使用するときはいつでも、ストラテジーを使用する方が良いでしょう。TemplateMethodの問題は、基本クラスと派生クラスの間に再入可能性があることです。
ポールホリングスワース

5
@Paul:テンプレートメソッドは、適切に使用した場合、つまり、変化する部分が、変化しない部分について多くを知る必要がある場合に最適です。私の見解では、基本コードがカスタムコードのみを呼び出す場合に戦略を使用し、カスタムコードが基本コードについて本質的に知る必要がある場合にテンプレートメソッドを使用する必要があります。
dsimcha '30年

ええ、dsimcha、同意します...クラス設計者がこれを認識している限り。
ポールホリングスワース2010

9

デザインパターン(DP)を避けてはいけないと思います。また、アーキテクチャを計画するときに、自分でDPを使わせてはいけないと思います。計画から自然に生まれた場合にのみ、DPを使用する必要があります。

最初から特定のDPを使用することを定義した場合、将来の設計決定の多くはその選択によって影響を受けるため、選択したDPがニーズに適しているとは限りません。

また、DPを不変のエンティティとして扱い、パターンをニーズに適合させる必要があります。

要約すると、私たちはDPを避けるべきではないと思います。DPがすでに私たちのアーキテクチャで形を成しているとき、それらを採用するべきです。


7

Active Recordは、ビジネスロジックと永続化コードの混合を促進する使いすぎのパターンだと思います。これは、モデル層からストレージ実装を隠すという非常に優れた仕事をせず、モデルをデータベースに結び付けます。テーブルデータゲートウェイ、行データゲートウェイ、データマッパーなど、多くの代替手段(PoEAAで説明)があり、これらは多くの場合、より優れたソリューションを提供し、ストレージへのより良い抽象化を提供するのに役立ちます。また、モデルをデータベースに保存する必要ありません。それらをXMLとして保存したり、Webサービスを使用してアクセスしたりするにはどうすればよいですか?モデルのストレージメカニズムを変更するのは簡単ですか?

とはいえ、Active Recordは必ずしも悪いわけではなく、他のオプションではやり過ぎるような単純なアプリケーションに最適です。


1
ちょっと本当ですが、実装にある程度依存しています。
マイクウッドハウス

6

それは簡単です... あなたにははっきりないデザインパターンや、快適に感じられないデザインパターンを避けてください

名前を付けるには...

たとえば、次のような実用的でないパターンがあります。

  • Interpreter
  • Flyweight

把握しにくいものもあります。例:

  • Abstract Factory -作成されたオブジェクトのファミリーを持つ完全な抽象的なファクトリーパターンは、それがそうであるようにそれほど簡単ではありません
  • Bridge -抽象化と実装がサブツリーに分割されている場合、抽象化しすぎる可能性がありますが、場合によっては非常に使いやすいパターンです
  • Visitor -二重発送メカニズムの理解は本当に必須です

ひどくシンプル見えるパターンもいくつかありますが、その原理や実装に関連するさまざまな理由により、あまり明確ではありません

  • Singleton -実際には完全に悪いパターンではなく、過度に使用されすぎている(多くの場合、適切ではない)
  • Observer -優れたパターン...コードの読み取りとデバッグをはるかに困難にします
  • Prototype -コンパイラチェックのダイナミズムをチェックします(これは良いか悪いか...依存します)
  • Chain of responsibility -強制的または人工的に設計に押し込まれることが多すぎる

それらの「非実用的なもの」については、どこかに通常よりエレガントな解決策があるので、それらを使用する前に本当に考える必要があります。

「把握しに​​くい」ものについては...適切な場所で使用され、適切に実装されている場合は非常に役立ちますが、不適切に使用されていると悪夢になります。

さて、次は...


Flyweightパターンは、リソース(多くの場合、イメージ)を複数回使用する場合はいつでも必要です。それはパターンではなく、解決策です。
センギス・カンデミール

5

私はこれであまり殴られないことを望みます。Christer Ericssonは、リアルタイムの衝突検出ブログで、設計パターンのトピックについて2つの記事(1つ2つ)を書いています。彼の口調はどちらかといえば過激で、おそらく少し挑発的ですが、男は彼のことを知っているので、狂人のからかいとしてそれを無視するつもりはありません。


興味深い読み物。リンクをありがとう!
ボンベ、

3
Moronsは悪いコードを生成します。パターンのあるモロンは、パターンを見たことのないモロンよりも悪いコードを生成しますか?彼らはそうは思わない。賢い人にとって、パターンはよく知られた語彙を提供し、アイデアの交換を容易にします。解決策:パターンを学び、賢いプログラマーのみを扱う。
Martin Brown、

私はそれが悪化したコードを生成するために、真のバカのために実際に可能だとは思わない-彼らが使用しているツールに関係なく
1800 INFORMATION

1
彼の大学テストでの彼の例は、問題の領域を軽視し、1つの週末に数時間以上それを研究したくない人が問題を解決しようとすると正しくない答えを出すことを証明するだけだと思います。
scriptocalypse

5

一部の人は、サービスロケータは反パターンであると言います。


また、サービスロケーターが必要になる場合があることにも注意してください。たとえば、オブジェクトのインスタンス化を適切に制御できない場合(C#の非定数パラメーターを持つ属性など)。ただし、サービスロケータWITH ctorインジェクションを使用することもできます。
Sinaesthetic 2016

2

オブザーバーパターンには多くの答えが必要であると思います。非常に一般的なケースで機能しますが、システムが複雑になると悪夢になり、OnBefore()、OnAfter()通知が必要になり、再同期を回避するために非同期タスクをポストすることがよくあります。入り込み。より優れたソリューションは、計算中にすべてのオブジェクトアクセス(読み取りバリアを使用)を計測し、依存関係グラフにエッジを自動的に作成する自動依存関係分析システムを開発することです。


4
私は「A」という言葉までの回答のすべてを理解しました
1800情報

あなたが話しているこの自動依存関係分析を展開またはリンクする必要があるかもしれません。また、.NETでは、オブザーバーパターンの代わりにデリゲート/イベントが使用されます。
スポーク09年

3
@スポーク:デリゲート/イベントはオブザーバーパターンの実装です
orip

1
Observerに対する私の個人的な恨みは、ガベージコレクションされた言語でメモリリークが発生する可能性があることです。オブジェクトを使い終わったら、オブジェクトがクリーンアップされないことを覚えておく必要があります。
マーティンブラウン

@orip:はい、デリゲート/イベントを使用するのはそのためです。;)
スポーク

2

Spoikeの投稿の補足であるRefactoring to Patternsは良い読み物です。


私は実際にインターネット上の本のカタログにリンクしています。:)
スポーク

ああ!気にせずにホバリングしました。実際、質問を終えた直後にこの本が思い浮かび、あなたの答えを見ました。投稿を止められなかった。:)
Adeel Ansari、

0

イテレーターは回避すべきもう1つのGoFパターンであり、少なくとも代替手段がない場合にのみ使用します。

代替案は次のとおりです。

  1. for-eachループ。この構造はほとんどの主流言語に存在し、ほとんどの場合、反復子を回避するために使用できます。

  2. LINQまたはjQueryのセレクター。コンテナのすべてのオブジェクトを処理する必要がないため、for-eachが適切でない場合に使用する必要があります。イテレータとは異なり、セレクタでは、処理するオブジェクトの種類を1か所に明示できます。


私はセレクターに同意します。Foreachはイテレーターであり、ほとんどのOO言語は、foreachを可能にするために実装する反復可能なインターフェースを提供します。
Neil Aitken

一部の言語では、for-each構文はイテレーターを介して実装されますが、その概念は実際にはより高レベルであり、セレクターに近いものです。for-each開発者は、コンテナのすべての要素を処理する必要があることを明示的に宣言します。
Volodymyr Frolov

イテレータは素晴らしいパターンです。アンチパターンは、イテレータなしで IEnumerable / IEnumerator 実装することになります。LINQはyieldイテレータによって可能になったと思います。エリックホワイトは、C#3.0でこれについていくつかの素晴らしい議論があります:blogs.msdn.com/b/ericwhite/archive/2006/10/04/…。また、イテレータを使用したコルーチンに関するJeremy Liknessのディスカッションも確認してください:wintellect.com/CS/blogs/jlikness/archive/2010/03/23/…

@Ryan Riley、イテレータは低レベルのオブジェクトであるため、高レベルの設計とコードでは避けてください。イテレータおよびさまざまな種類のセレクタの実装の詳細は、ここでは重要ではありません。セレクターは、イテレーターとは異なり、プログラマーが処理したいものを明示的に表現できるため、高水準になります。
Volodymyr Frolov、2010

Fwiw、代替のLINQのようなF#構文は `List.map(fun x-> x.Value)xs`で、これはリストの内包とほぼ同じ長さです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.