正統なギャング・オブ・フォーのリストに、誤用、誤解、または使いすぎによく見られる設計パターンはありますか (非常に議論されているシングルトンを除く)。言い換えれば、使用する前によく考えるようアドバイスする設計パターンはありますか? (なぜ?)
15 に答える
工場のパターン...
私は以前、システム内のすべてのものが新しいインスタンスを生成するためMyObject
の同等のものを持っていたプロジェクトに飛び込みました。MyObjectFactory
抽象化や拡張クラスの概念はありませんでした...単純な古い ClassX と ClassXFactory だけです。
その理由は誰にも説明できませんでした...
シングルトン パターン .. グローバルな状態は、テスト時に問題を引き起こすことがよくあります
シングルトンに依存するコードは、その依存関係を簡単にモックできないため、テストがますます難しくなります。
唯一のもの (前述の Singleton と犯罪のパートナーである Factory を除く) は GoF ではなく、オブジェクトのネイティブ プロパティに適用されるとセッターとゲッターになります。
メンバー変数に適用されるセッターとゲッターは、パブリック メンバー変数と機能的に同じです。セッターのないゲッターは、パブリック最終メンバー変数に似ていますが、その時点で、パブリック最終メンバー変数を使用するだけでなく、害はありません...
唯一の違いは、呼び出しをインターセプトしてオーバーライドすることが「できる」ことですが、そうする人はめったにいません。多くの場合、手続き型プログラマーがオブジェクト指向プログラミングを回避するための松葉杖として使用されます (これがアンチパターンである本当の理由です)。
セッターおよび/またはゲッターを使用すると、内部メンバー構造を外部の世界に公開し続けます (たとえば、int を long に変更する必要がある場合は、他のクラスをリファクタリングする必要があります)。オブジェクト内にあるはずの一部のコードが、代わりに外部に配置されています。
私が考えることができるいくつかの例外があります:
オブジェクトの構築を簡素化するために使用されるセッター。オブジェクトを作成し、後で他の値を設定する必要がある場合があります。安全のために、これらの値は不変でなければなりません (set を 2 回呼び出すことはできません)。
含まれているオブジェクトにアクセスするために使用されるゲッター。含まれているオブジェクトは通常、それ自体の完全性を保証できるため、それらを共有することは素晴らしいことです。この場合、セッターは一般的に悪いです。特定の状態のオブジェクトを鼻のすぐ下でスワップアウトしたくないので、自分の整合性を保証することがはるかに難しくなります。
画面コンポーネントに使用される Java Beans: ええ、これらの「プロパティ ボール」を実装するためのより良い方法がわかりません。リフレクションはこのコンポーネントに役立ちます。パターンは役に立ちます。ちょっとハックですが、機能します。
DAO/DTO Bean オブジェクト。正直なところ、これらはパターンのあいまいな使い方だと思いますが、パターンです。コードの代わりにメタデータを介してプロパティを操作することは、反射的でなければならないため、本来よりもはるかに困難になります。Bean のプロパティは常に何らかの外部ソース (データベース形式、データ転送形式、コンポーネント プロパティなど) に結び付けられているのに、なぜ各部分を定義する作業を重複させるのでしょうか?
編集: kyoryu のコメントから盗まれて、投稿に持ち込まれました。言語にアクセサを追加することは、悪い OO 設計パターンを成文化するだけであるという概念を誰もが理解しているわけではないため、必要です。
短縮版 -
if (account1.balance > 1000)
{
account1.balance = account1.balance - 1000;
account2.balance = account2.balance + 1000;
}; = BAD CODE.
account2.deposit(account1.withdraw(1000)); = GOOD CODE.
2 番目のものはアクセサを必要としません... – kyoryu (bill k によって少し変更されました。これは、彼のコメントよりも少し余裕があるためです)。
2 つ目は、転送を行う可能性のあるすべての場所でコード全体を複製するのではなく、アカウント内のテストとその他の数学を移動します。
要点をさらに詳しく説明すると、「GOOD CODE」スタイルでは、.withdraw の出力が、トランザクション全体に関する情報 (成功、送信元と送信先、ログ機能など) を含む Transaction オブジェクトになる可能性があることは明らかです。「BAD CODE」スタイルでコードを書く人にとって、これはどのように発生したのでしょうか?
また、そのようなオブジェクトを使用するために BAD CODE をどのようにリファクタリングしますか? それはただの混乱です。
工場のパターンとも言えます。Eoinと同様の経験。私の場合、プロジェクトには大量のファクトリがありました。これは、オブジェクト A をローカル実装に使用し、オブジェクト B をリモート実装に使用した可能性があり、ファクトリを介して抽象化されたためです (これは賢明なことです)。
しかし、「リモート」実装は、必要とされたり、実装されたり、将来予見されたりすることはありませんでした...また、スキルの低いエンジニアが、クッキーカッターとして他の多くのものにパターンを採用し始めました...
実際、私が最もよく目にするのは、適切なパターンが使用されていないことです。典型的なシナリオ:私:「モジュールAには、オブジェクトのセットをループしてデータベース操作Xを実行するコードがすでに含まれていますが、なぜそのコードを再利用しなかったのですか?」コーダー:「まあ、でも私はそれらのオブジェクトに対して操作Yをしなければなりませんでした。」私:「リファクタリングを使用して、コマンドパターンを使用してXまたはYを適切に実行するのはどうですか?」
私はかつて、Subject-Observerパターンの使用法が手に負えなくなるのを見ました。これは、データベースを使用してプロセス間で実装され、サブジェクトを永続的に保存します。サブジェクトの更新数とオブザーバーの数が非常に多いため、データベースの負荷が非常に大きくなり、システム全体の予期しない速度低下が発生しました。
実際、KISS (シンプルに、バカに短くシンプルに保つ) ソリューションだけで十分です。
設計パターンはシステムを柔軟にするのに優れていますが、実装がより複雑になるという代償があります。これは、柔軟性が実用的な利点を提供する場合にのみ、価値のあるトレードオフになります。
Mediator パターンは、あらゆる種類のロジックが 1 つの巨大なクラスにまとめられた場合、間違いなく悪用される可能性があります。
全て。
ここで私を誤解しないでください。私はそれらが素晴らしいベースであり、よく理解されていると非常に役立つと思います. 設計スキルをいつ、どのようにコードに適用するかをよく理解するには、多くの労力が必要です。実際、それはクリーンなコードを作成するスキルの全体的なケースです。
それは使用しないことではなく、まさに「使用する前によく考えてください」という質問であなたが言ったことです。あなたが何をしているのか、そしてその理由をよく理解してください。そうでない場合は、多くの本を読む以外に、それについて指導できる人に相談してください。すべてのパターンを知っているが、それらのどれか/なぜそれらのどれかを明確に説明できない人、特に問題のパターンに注意してください。
「すべてのパターンが悪い」という答えをいくつか見た後、別のコメントを追加したかっただけです。
あなたが適度に挑戦的な問題に取り組んでいる半分まともなプログラマーであるならば、ほとんどすべてのパターンは、問題に対する「明白な」解決策として、ある時点であなたに彼ら自身を提示するべきでした。
デザインパターンの本の唯一の本当のポイントは、私たちがより良いコミュニケーションをとれるように、私たち全員が毎日行うことに名前を付けることでした。
あなたが新しいプログラマーなら、それらを読んでおくと非常に役立つと思います。そうすれば、必要な日に自分で理解する必要がなく、すでにツールボックスにありますが、一般的には、これらのうち、Gang of One(anyOne!)が把握できます。
あなたがまだ知らなかったこれらのどれかがあれば、あなたはおそらくそれを必要としなかったでしょう。
パターンに名前を付けて少し形式化すると非常に便利ですが、私はこの本についてまったく不満を言っていません。ここでほとんどすべてのパターンが必要になることがない場合は、非常にハードなコードに取り組んでいないだけです(または、コードを何度も複製します。これは私が基本的な罪だと考えています)。
私が目にする大きな問題は、シングルトンのデストラクタをいつ、どのように呼び出すかについて十分な注意と注意が払われていないシングルトン パターンです。
このようなユビキタスなパターンの場合、シングルトンがいつ終了する必要があるかを決定する適切なプロセスについての議論はほとんどありません。
ちょうど私の0.02。
乾杯、
ロブ
オブザーバーパターンは、イベントがあるため、C#ではかなり役に立ちません。
リポジトリ パターン
ほとんどの人は、Eric Evans による Domain Driven Design の本を読んだ直後に、このパターンを使い始めます。
データ アクセス オブジェクトのように構築されたリポジトリを見たことがある人はどれくらいいるでしょうか。
まず、言語に「依存」します。一部の言語の一部の構造では、特定の設計パターンの必要性が軽減されます。
第 2 に、デザイン パターンの概念のテンプレートの一部には、最初から「適用性」と「結果」のセクションが含まれています。これらは自己責任で無視してください。パターンを「知る」ということは、選択した言語でそれをコーディングする方法を知っているということだけではありません。それは、それをいつ使用するか、およびそれを使用するとどのような欠点が生じる可能性があるかを知ることも意味します。
必要な場合にのみパターンを使用してください。未来を予測することはできません。したがって、デザインを柔軟にするためにパターンを入れることはできますが、製品が別の方向に進み、パターンがまさにユーザーが望むものを実装するのを妨げるものになったらどうなるでしょうか?
最初はできるだけシンプルなデザインにしてください。デザインをどのように変更する必要があるかについて詳しく知っている場合は、以前ではなく適切なパターンを使用してください。
この質問に対する率直な答えはありません。ほとんどが主観的であり、アプリケーションの要件によって異なります。
ほとんどの人がSingleton_patternは悪いと言っていますが、すべてのユーザーとプロジェクトにとって悪いわけではありません。私のプロジェクト要件では、それは目的を果たします。クライアントとアプリケーション間のセッション管理を処理するには、1 つの ConnectionManager が必要であり、Singleton は見事に仕事をします。
適切なドキュメントを含むサードパーティの jar を提供しました。jar には継承階層が含まれています。ここで、各子に操作を追加する必要があります。ソースコードがないので、それを行うことはできません。Visitor_patternを使用することでメリットが得られるようになりました。ただし、ソース コードをお持ちの場合は、
Visitor
パターンをまったく使用しない場合があります。親と各子に新しい操作を簡単に追加できます。Visitor
ソース コードがないからといって、パターンを誤用しているわけではありません。さまざまなオブジェクト間の通信を制限したい。オブジェクト通信用のMediator_patternを実装していきます。システムへのクライアントの露出を制限して、複雑さを隠したいと考えています。Facade_patternを先に進めます。それは、私がこれらのパターンを誤用しているという意味ではありません。この同じ例は、 Proxy_patternなどの他のパターンに拡張できます。