同様に最適化されていない設計を繰り返し繰り返すことをどのように回避しますか?


10

おそらく多くの人と同じように、設計の問題が頭痛の種になっていることがよくあります。たとえば、問題に直感的に適合し、望ましい利点があるデザインパターン/アプローチがあります。多くの場合、何らかの回避策がないとパターン/アプローチを実装するのが困難になる警告があり、パターン/アプローチの利点が無効になります。多くのパターン/アプローチを繰り返し処理してしまう可能性があります。実際には、簡単な解決策がない実際の状況では、ほぼすべてのパターンに非常に大きな注意事項があるためです。


例:

最近遭遇した実際のものに大まかに基づいた架空の例を紹介します。継承階層が過去のコードのスケーラビリティを妨げていたため、継承ではなくコンポジションを使用したいとしましょう。私はコードをリファクタリングするかもしれませんが、スーパークラス/ベースクラスがサブクラスの機能を単に呼び出す必要があるにもかかわらず、それを回避しようとするいくつかのコンテキストがあることがわかりました。

次善のアプローチは、半分のデリゲート/オブザーバーパターンと半分の構成パターンを実装して、スーパークラスが動作を委任できるようにするか、サブクラスがスーパークラスのイベントを監視できるようにすることです。その場合、クラスを拡張する方法が不明確であるため、クラスの拡張性と保守性が低下します。また、既存のリスナー/デリゲートを拡張するのも難しいです。また、スーパークラスを拡張する方法を確認するために実装を知る必要があるため、情報は十分に隠されていません(コメントを非常に広範囲に使用しない限り)。

したがって、この後は、オブザーバーまたはデリゲートを完全に使用して、アプローチを過度に混同することに伴う欠点を回避することを選択する場合があります。ただし、これには独自の問題があります。たとえば、事実上すべての行動にオブザーバー/デリゲートが必要になるまで、行動の量を増やすためにオブザーバーまたはデリゲートが必要になることに気づく場合があります。1つのオプションは、すべての動作に対して1つの大きなリスナー/デリゲートを持つことですが、実装するクラスは多くの空のメソッドなどで終了します。

それから私は別のアプローチを試すかもしれませんが、それと同じくらい多くの問題があります。それから次のもの、そして次のものなど。


この反復プロセスは、各アプローチが他のアプローチと同じくらい多くの問題を抱えているように見え、一種の設計決定麻痺につながる場合、非常に難しくなります。また、使用する設計パターンやアプローチに関係なく、コードが同じように問題を抱えてしまうことを受け入れるのも困難です。このような状況になった場合、問題自体を再検討する必要があるということですか。この状況に遭遇したとき、他の人は何をしますか?

編集: 私が明確にしたい質問のいくつかの解釈があるようです:

  • OOPは実際にはOOPに固有のものではないことが判明したため、OOPについて質問から完全に除外しました。さらに、OOPについて渡した私のコメントの一部を誤解するのは簡単です。
  • 反復的なアプローチでさまざまなパターンを試す必要があると主張したり、パターンが機能しなくなった場合は破棄したりする必要があると主張する人もいます。これは私が最初に参照するつもりだったプロセスです。これは例からは明らかだと思いましたが、もっと明確にすることができたので、そのために質問を編集しました。

3
「オブジェクト指向の哲学を購読」しないでください。それはツールであり、人生の主要な決定ではありません。役立つときに使用し、役に立たないときは使用しないでください。
user253751

特定のパターンについて考えが行き詰まっている場合は、Cで適度に複雑なプロジェクトを作成してみてください。これらのパターンがなくてもできることを体験すると興味深いでしょう。
user253751

2
ああCにはパターンがあります。それらは非常に異なるパターンです。:)
candied_orange 2018

私は編集でこれらの誤解のほとんどを片付けました
ジョナサン

これらの問題が発生するのはよくあることです。それらが頻繁に発生することは当てはまりません。最も可能性が高いのは、問題に対して間違ったプログラミングパラダイムを使用しているか、設計が間違った場所にパラダイムが混在していることです。
2018年

回答:


8

そのような難しい決断をしたとき、私はたいてい3つの質問をします。

  1. 利用可能なすべてのソリューションの長所と短所は何ですか?

  2. 解決策はありますか、まだ検討していませんか?

  3. 最も重要なこと:
    私の要件は正確には何ですか?紙の要件ではなく、本当の基本的な要件ですか?
    どういうわけか問題を再定式化/その要件を微調整して、シンプルで簡単な解決策を可能にすることができますか?
    データをいくつかの異なる方法で表現して、そのような単純で直接的なソリューションを可能にすることはできますか?

    これは、知覚された問題の基本についての質問です。あなたが実際に間違った問題を解決しようとしていたことが判明するかもしれません。問題には、一般的な場合よりもはるかに簡単な解決策を可能にする特定の要件がある場合があります。問題の定式化に質問してください!

続行する前に、3つすべての質問について一生懸命考えることが非常に重要だと思います。散歩をしたり、オフィスのペースを調整したりして、これらの質問への回答を検討するために必要なことは何でもしてください。ただし、これらの質問への回答に不適切な時間がかかることはありません。適切な時間は、15分から1週間程度の範囲であり、すでに見つけたソリューションの問題とその全体への影響に依存します。

このアプローチの価値は、驚くほど良い解決策が見つかることがあることです。エレガントなソリューション。これらの3つの質問に答えるために費やした時間に見合う価値のあるソリューション。そして、すぐに次の反復に入るだけでは、これらの解決策は見つかりません。

もちろん、良い解決策がない場合もあります。その場合、あなたは質問1への答えに行き詰まっており、善は単に最悪です。この場合、時間をかけてこれらの質問に答えることの価値は、失敗するはずの反復を回避できる可能性があることです。適切な時間内にコーディングに戻るようにしてください。


私はこれを答えとしてマークするかもしれませんが、これは私にとって最も理にかなっており、単に問題自体を再評価することについてはコンセンサスがあるようです。
ジョナサン

また、それを段階に分解する方法が好きなので、状況を評価するためのある種の緩い手順があります。
ジョナサン

OP、コードソリューションの仕組みに行き詰まっているので、ええと、「この答えにマークを付けることもできます」。私たちが書いた最高のコードは、要件、派生した要件、クラス図、クラスの相互作用などを非常に注意深く分析した後のものです。 「設計に多くの時間を費やしている」、「急いでコードを取得する!」、「それはクラスが多すぎる!」など。客観的には、それがプロジェクト全体で最高のコードでした。
レーダーボブ2018年

13

まず最初に、パターンは有用な抽象概念であり、オブジェクト指向の設計はもちろん、すべてが設計のすべてではありません。

次に、最新のオブジェクト指向は、すべてがオブジェクトであるとは限らないことを知るのに十分スマートです。時々、プレーンな古い関数を使用したり、いくつかの命令スタイルのスクリプトを使用したりすると、特定の問題に対してより良い解決策が得られます。

さて、物事の肉に:

これは、各アプローチが他のアプローチと同じくらい多くの問題を抱えているように見える場合、非常に難しくなります。

どうして?同様のオプションがたくさんあると、あなたの決断はもっと簡単になるはずです!「間違った」オプションを選択しても、多くを失うことはありません。そして、実際には、コードは修正されていません。何かを試して、それが良いかどうかを確認してください。繰り返す

また、使用するデザインパターンやアプローチに関係なく、コードが非常に問題の多いものになることを受け入れるのも困難です。

タフなナッツ。難しい問題-実際の数学的に難しい問題はただ難しいです。おそらく難しい。彼らにとって文字通り良い解決策はありません。そして、結局のところ、簡単な問題はあまり価値がありません。

しかし、注意してください。特定の方法で問題を見て立ち往生しているため、または手元の問題に対して不自然な方法で責任を削減しているために、良い選択肢がないことに不満を感じる人を頻繁に目にしました。「良い選択肢がない」というのは、あなたのアプローチに根本的に何か問題があるというにおいがします。

これにより、「クリーン」なコードがオプションでなくなる場合があるため、モチベーションが失われます。

完璧は善の敵です。何かを動作させ、それをリファクタリングします。

この問題を最小限に抑えるのに役立つ設計方法はありますか?このような状況で何をすべきかについて認められた実践はありますか?

すでに述べたように、反復的な開発は一般にこの問題を最小限に抑えます。何かが機能するようになったら、問題をよりよく理解して、問題を解決するためのより良い立場に置くことができます。見て、評価する実際のコードがあり、正しくないように見える抽象的なデザインではない。


私は編集でいくつかの誤解を解消したと言うべきだと思っただけです。私は通常、「機能させる」とリファクタリングのアプローチを行います。ただし、多くの場合、これは私の経験で多くの技術的負債につながります。たとえば、私が物事を機能させるだけで、その上に私自身または他の人が構築する場合、それらの物事のいくつかには避けられない依存関係があり、そのため大量のリファクタリングが必要になる場合があります。もちろん、これを常に事前に知ることはできません。しかし、アプローチに欠陥があることをすでに知っている場合は、それを機能させてから、コードを作成する前に繰り返しリファクタリングする必要がありますか?
ジョナサン

@ジョナサン-すべてのデザインは一連のトレードオフです。はい、設計に欠陥があり、それを改善する明確な方法がある場合は、すぐに繰り返す必要があります。明らかな改善が見られない場合は、やめましょう。
Telastyn 2018

「彼らは、当面の問題に不自然な方法で責任を削減しました。良い選択肢は、あなたのアプローチに根本的に何か問題があるという匂いになることはありません。」これに賭けます。OPで説明されている問題に決して遭遇しない開発者もいれば、かなり頻繁にそうする開発者もいます。多くの場合、元の開発者によって提案されたときの解決策は、問題をフレーム化する方法で設計にバイアスをかけているため、簡単にはわかりません。場合によっては、非常に明白な解決策を見つける唯一の方法が、設計の要件からさかのぼることです。
2018年

8

あなたが説明している状況は、ボトムアップアプローチのように見えます。あなたは葉を取り、それを修正しようとし、それが別のブランチにも接続されているブランチに接続されていることを確認します。

タイヤから車を作るようなものです。

必要なのは、一歩下がって全体像を見ることです。その葉は全体的なデザインにどのように座っていますか?それはまだ最新で正しいですか?

ゼロから設計および実装する場合、モジュールはどのように見えますか?現在の実装は、その「理想」からどれだけ離れているか。

こうすることで、何に取り組むべきかについての全体像がわかります。(または、作業量が多すぎると判断した場合、問題は何ですか)。


4

あなたの例は、レガシーコードの大きな部分で通常発生する状況、および「大きすぎる」リファクタリングを作成しようとしている状況について説明しています。

この状況について私があなたに与えることができる最良のアドバイスは次のとおりです。

  • 1つの「ビッグバン」主な目標を達成しようとしないでください

  • 小さなステップでコードを改善する方法を学びましょう!

もちろん、書くのは書くより簡単なので、実際にこれを実現する方法は?まあ、あなたは練習と経験を必要とします、それはケースに非常に依存します、そして、すべてのケースにふさわしい「これまたはそれをしてください」と言う厳格な規則はありません。しかし、架空の例を使用してみましょう。「組成より継承」は全か無かの設計目標ではなく、いくつかの小さなステップで達成するのに最適な候補です。

「継承より継承」がケースに適したツールであることに気付いたとしましょう。これが賢明な目標であることを示す兆候がいくつかあるはずです。そうでなければ、これを選択しなかったでしょう。したがって、スーパークラスにはサブクラスから「呼び出される」だけの機能がたくさんあるとしましょう。そのため、この機能は、そのスーパークラスに留まらない候補です。

サブクラスからスーパークラスをすぐに削除できないことに気付いた場合は、まず、スーパークラスを上記の機能をカプセル化する小さなコンポーネントにリファクタリングすることから始めます。ぶら下がりが最も少ない果物から始めて、いくつかの単純なコンポーネントを最初に抽出します。これにより、スーパークラスの複雑さが緩和されます。スーパークラスが小さいほど、追加のリファクタリングが容易になります。これらのコンポーネントは、サブクラスとスーパークラスから使用します。

運が良ければ、スーパークラスの残りのコードはこのプロセス全体で非常に単純になるため、それ以上問題なくサブクラスからスーパークラスを削除できます。あるいは、継承なしで再利用したいコンポーネントに十分なコードをすでに抽出しているので、スーパークラスを維持することはもはや問題ではないことに気付くでしょう。

どこから始めればよいかわからない場合は、リファクタリングが簡単であるかどうかわからないため、スクラッチリファクタリングを行うのが最善の方法になる場合があります。

もちろん、実際の状況はさらに複雑になる可能性があります。だから、学び、経験を集め、忍耐強く、これを正しく行うには何年もかかります。私がここで推薦できる本が2冊あり、多分あなたはそれらが役に立つと思うかもしれません:

  • Fowlerによるリファクタリング非常に小さなリファクタリングの完全なカタログについて説明します。

  • Feathersによるレガシーコードの効果的な操作:不適切に設計されたコードの大きなチャンクを処理し、小さなステップでテストしやすくする方法について優れたアドバイスを提供します


1
これも非常に役立ちます。小さなステップワイズまたはスクラッチリファクタリングは、他の作業と一緒に、あまり妨げることなく徐々に完了することができるものになるため、良いアイデアです。複数の回答を承認できればと思います!
ジョナサン

1

時には、最良の2つの設計原則はKISS *とYAGNI **です。「Hello world!」を印刷するだけのプログラムに、既知のすべてのデザインパターンを詰め込む必要を感じないでください。

 * Keep It Simple, Stupid
 ** You Ain't Gonna Need It

質問の更新後に編集します(そして、Pieter Bの発言をある程度反映します):

時々、それを実装しようとするときにあらゆる種類の醜さをもたらす特定の設計に導く早い段階で、アーキテクチャの決定をすることがあります。残念ながら、その時点での「適切な」解決策は、一歩下がって、その位置にたどり着いた方法を理解することです。まだ回答が表示されない場合は、表示されるまで後戻りしてください。

しかし、それを行う作業が不均衡である場合、問題に対する最も醜い回避策を思いつくことは実用的な決定が必要です。


私は編集でこれのいくつかを片付けましたが、一般に私は利用できる簡単な解決策がない正確にそれらの問題に言及しています。
ジョナサン

ええと、はい、一歩下がって問題を再評価することについて合意が生まれているようです。これは良い考えです。
ジョナサン

1

私がこの状況にあるとき、私が最初にすることは停止です。私は別の問題に切り替えて、しばらくそれについて作業します。たぶん1時間、多分1日、多分もっと。それは必ずしも選択肢ではありませんが、私の潜在意識は、私の意識的な脳がより生産的な何かをする間、物事に取り組みます。結局、新鮮な目で戻ってやり直します。

私がするもう1つのことは、私より賢い人に尋ねることです。これは、Stack Exchangeで質問したり、トピックに関するWeb上の記事を読んだり、この分野での経験が豊富な同僚に質問したりするという形をとることがあります。多くの場合、私が正しいアプローチだと思う方法は、私がやろうとしていることに対して完全に間違っていることが判明しています。問題の一部の特徴を誤解しましたが、実際には、パターンとは一致しません。その場合、他の誰かに「これはもっと似ている…」と言わせるのが大きな助けになります。

上記に関連するのは、告白による設計またはデバッグです。あなたは同僚のところに行き、「私が抱えている問題について説明し、次に私が持っている解決策を説明します。それぞれのアプローチで問題を指摘し、他のアプローチを提案します。 」私が説明しているように、他の人が話す前にしばしば、他の人と同じように見えた1つの経路は、当初思っていたよりもはるかに良いか悪いことに気づき始めます。結果として生じる会話は、その認識を強化するか、または私が考えていなかった新しいことを指摘するかもしれません。

TL; DR:休憩をとって、無理に押し込まないで、助けを求めてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.