適切な設計で簡単にするには大きすぎる変更は何ですか?


11

これはかなり曖昧な質問ですが、適切な設計について読んだときに満足のいく方法で答えられたとは思っていませんでした。

一般に、オブジェクト指向プログラミング、抽象化、因数分解などについて学ぶとき、設計の聖杯-そして、あなたが問題の開発技術を使用していると常に主張する理由-は、プログラムを「変更しやすくする」ことです、「維持可能」、「柔軟」、またはそのような生産的な響きの概念を表現するために使用される同義語のいずれか。ivarをプライベートとしてマークし、コードを多くの小さな自己完結型のメソッドに分割し、インターフェイスを一般的な状態に保つことで、プログラムを完全に簡単かつ優雅に変更できるようになります。

比較的小さな変更の場合、これは私にとってはうまくいきました。クラスがパフォーマンスを向上させるために使用する内部データ構造の変更は大きな困難ではありませんでした。また、テキスト入力システムの再設計やゲームプレイ要素のグラフィックのオーバーホールなど、APIに依存しないユーザーインターフェイスエンドの変更もありません。

これらの変更はすべて、本質的に自己完結型のようです。それらのいずれも、外部コードに関する限り、変更されるプログラムのコンポーネントの動作または設計への変更を伴いません。手続き型であろうと大規模な機能であろうと小規模であろうとOOスタイルであろうと、これらはあなたが適度に良いデザインしか持っていなくても簡単に変更できます。

しかし、変更が大きくて毛深いものになったとき、つまりAPIに変更が加えられたときはいつでも、私の貴重な「パターン」はどれも助けになりません。大きな変化は大きなままで、影響を受けるコードは影響を受けたままで、何時間もバグを生み出す作業が私の前にあります。

だから、私の質問はこれです。適切な設計がどの程度大きな変更を促進できると主張していますか?私には未知の、または実装に失敗した、さらにスティッキーなサウンドの修正を本当に単純にする、またはその約束(非常に多くの異なるパラダイムによって行われたと聞いた)は単に素晴らしいアイデアである、いくつかのさらなる設計技術がありますか?ソフトウェア開発の不変の真実から完全に切り離されていますか?ツールベルトに追加できる「変更ツール」はありますか?

具体的には、私がフォーラムに導かれた問題はこれです:私は解釈されたプログラミング言語(Dで実装されていますが、それは関係ありません)の実装に取り​​組んでおり、クロージャの引数は現在の位置ではなく、キーワードベース。これには、匿名関数を呼び出す既存のすべてのコードを変更する必要がありますが、幸いなことに、私は言語の開発の初期段階(2000行未満)であるため、かなり小さいですが、後の段階でこの決定を行った場合は非常に大きくなります。そのような状況では、設計の適切な先見によって、この変更を簡単にすることができたのでしょうか、それとも特定の(ほとんどの)変更が本質的に広範囲に及ぶのでしょうか?これが何らかの形で自分自身の設計スキルの失敗であるかどうかに興味があります-もしそうなら、私は

明確にするために、私はOOPや一般的に使用されている他のパターンのいずれにも決して懐疑的ではありません。ただし、私にとっては、それらの長所は、コードベースの保守ではなく、元の記述にあります。継承を使用すると、繰り返しパターンを適切に抽象化できます。ポリモーフィズムを使用すると、機械が理解する効果(switchステートメントのブランチ)ではなく、人間が理解する機能(クラス)でコードを分離できます。非常に快適な「ボトムアップ」スタイルで記述します。しかし、私は彼らの柔軟性の主張に懐疑的です。


あなたの言語の変化がパーサー以外にどのように影響するかはわかりません。これを明確にできますか?
スカーフリッジ

Smalltalkのような言語では、それが治療の単純な問題になるので、あなたは、パーサを変更するのみ必要があるだろうというのは本当だfoo metarg1: bar metarg2: bazとしてfoo.metarg1_metarg2_(bar, baz)。しかし、私の言語は、ランタイムが、影響も大きく変化する、辞書ベースのパラメータに、すなわち、リストベースのパラメータ、作っていないため、私は今の中に入ることはありません私の言語の特定のプロパティに、実際に、パーサを。順序付けられていないキーワードと位置引数の間に明確なマッピングがないため、ランタイムが主な問題です。
存在-forall

回答:


13

移行パスを設計する必要があるほど変更が大きい場合があります。開始点と終了点が適切に設計されていても、しばしば冷たいターキーを切り替えることはできません。他の点では優れた設計者の多くは、優れた移行パスを設計できません。

だから私は、すべてのプログラマーが24時間年中無休の実稼働環境向けにソフトウェアを作成する必要があると考えています。堅牢な移行コードを書くことを学ぶように動機づける損失は、1分あたりの年間給与の順に見積もられています。

大規模な変更に対して人々が自然に行うことは、古い方法を切り取り、新しい方法を適用し、数日かけて膨大なコンパイルエラーを修正し、コードが最終的に再びコンパイルされるまでテストを開始できるようにすることです。コードは2日間テストできなかったため、突然、混乱させる多数のバグが混乱してしまいます。

位置引数からキーワードへの変更などの大規模な設計変更の場合、適切な移行パスは、位置サポートを削除せずにキーワード引数のサポートを最初に追加することです。次に、キーワード引数を使用するように呼び出しコードを系統的に変更します。これは以前の方法と似ていますが、変更されていない呼び出しコードが引き続き機能するため、今回はテストを行うことができます。テスト間の変更は小さいため、バグは以前の方が簡単に修正できます。その後、すべての呼び出しコードが変更されたら、位置サポートを削除するのは簡単です。

これが、公開されたAPIが古いメソッドを削除するのではなく、古いメソッドを非推奨にする理由です。コードを呼び出すためのシームレスな移行パスを提供します。しばらくの間両方をサポートするのはもっと手間がかかるように思えるかもしれませんが、テストで時間を補います。

したがって、元々の言い回しとしての質問に答えるために、適切な設計で簡単にするには大きすぎる変更はほとんどありません。変更が大きすぎると思われる場合は、コードを移行するための一時的な中間段階を設計する必要があります。


新しいケースと古いケースをサポートするための+1。
エリックReppen

9

Big Ball of Mudのエッセイを読むことをお勧めします。

基本的に、開発が進むにつれてデザインが劣化する傾向があり、複雑さを抑えるために作業を捧げなければならないという点です。複雑さ自体は、解決しようとしている問題に固有であるため、排除することはできず、封じ込めるだけです。

これは、アジャイル開発の主要な原則につながります。最も関連性の高い2つは、まだ実装する予定のない機能のためにデザインを準備しないように「必要ない」、とにかくデザインを正しく行うことはまずない、そして「容赦なくリファクタリング」するように指示することです。プロジェクト全体でコードの健全性を維持します。

答えは、デザインが実際よりも強力であることを示すために特別に設計された人為的な例以外の変更を容易に行えるデザインはないようです。

サイドノートでは、オブジェクト指向プログラミングについて懐疑的である必要があります。これは多くの状況で優れたツールですが、制限があることに注意する必要があります。特に継承の誤用は、信じられないほど複雑なコードにつながる可能性があります。もちろん、他の設計手法についても同様に懐疑的でなければなりません。それぞれに用途があり、それぞれに弱点があります。特効薬はありません。


1
+1:斬新な先行設計はめったに成功しません。成功したデザインは、実績のあるデザインを再現したものか、繰り返し洗練されたものです。
ケビンクライン

1
1あなたがすべき唯一の客観的な批判を通じて、我々は見つけるために私たちの分析能力を適用するか、すべてのプログラミングの概念の懐疑的権利ソリューションだけではなく、ソリューションを
ジミー・ホッファ

4

一般的には、「コードの断片」(関数、メソッド、オブジェクトなど)と「コードの断片間のインターフェース」(API、関数宣言、その他、動作を含む)があります。

他のコードが依存するインターフェイスを変更せずにコードの一部を変更できる場合、変更は簡単になります(OOPを使用するかどうかは関係ありません)。

他のコードが依存するインターフェイスを変更せずにコードの一部を変更できない場合、変更はより難しくなります(OOPを使用するかどうかは関係ありません)。

OOPの主な利点は、パブリック "インターフェイス"が明確にマークされ(たとえば、オブジェクトのパブリックメソッド)、他のコードが内部インターフェイス(たとえば、オブジェクトのプライベートメソッド)を使用できないことです。パブリックインターフェイスには明確なマークが付けられているため、人々はそれらのパブリックインターフェイスをより慎重に設計する傾向があります(より困難な変更を避けるのに役立ちます)。内部インターフェイスは他のコードで使用できないため、それらに依存する他のコードを心配せずに変更できます。


OOPのもう1つの利点は、その上で動作するロジックでデータをカプセル化することで、パブリックインターフェイスが小さくなることです(うまくいけば)。
マイケルボルグワード

2

大きな要件/スコープの変更の手間からあなたを救うことができる設計方法論はありません。後日考えられる可能性のあるすべての変更を想像して説明することは不可能です。

優れた設計役立つのは、すべての部品がどのように適合し、提案された変更によって何が影響を受けるかを理解するのに役立ちます。
さらに、適切な設計により、提案された変更のほとんどによって影響を受ける部品を制限できます。

つまり、優れたデザインによってすべての変更が容易になりますが、優れたデザインでは大きな変更を小さな変更に変えることはできません。(一方で、悪い/ないデザインは、小さな変更を大きな変更に変えることができます。)


1

設計は、開発プロジェクトを白紙なしのゼロから機能する信頼できる最終製品(少なくとも1つの設計)に移行することを目的としているので、それが可能なプロジェクトの最大の変更であるため、変更が大きすぎて処理できません。

どちらかと言えば逆です。いくつかの変更は小さすぎて、「デザイン」や派手な思考に煩わされません。たとえば、単純なバグ修正。

設計パターンは、よく考えて、同じ種類のオブジェクトを大量に作成するなど、一般的な状況に対する適切な設計ソリューションを見つけるのに役立つことを忘れないでください。完全なパターンのリストはありません。あなたと私たち全員は、一般的ではなく、鳩の穴に合わない設計上の問題を抱えています。何らかの公式パターンに従ってソフトウェア開発プロセスのあらゆる小さな動きを試みることは、物事を行うための過度に宗教的な方法であり、維持できない混乱につながります。

ソフトウェアでのすべての作業は、当然ながらバグが発生します。フットボール選手は負傷します。ミュージシャンは、楽器に応じて角質または唇のけいれんを起こします。(ギターを弾いているときに唇のけいれんが起こると、間違っています。)ソフトウェア開発者はバグを受け取ります。産卵率を減らす方法を見つけるのが大好きですが、決してゼロになることはありません。


3
開始位置もマイナスにすることができます。レガシーの力を過小評価しないでください:)
マグロ

ゼロからプロジェクトを設計するのは簡単な部分です。しかし、新しいプロジェクトをゼロから開始することもできますが、それはめったにありませんが、最初の数日間は何もないところからビルドすることしか楽しめません。最初の設計決定が間違っていたことが判明し、物事はそこから下り坂になり始めます。ゼロからの設計は、実際にはまったく無関係です。
Jan Hudec

1

抜本的な変更のコストは、多くの場合、コードベースのサイズに比例します。したがって、コードベースのサイズを縮小することは、適切な設計の目標の1つです。

あなたの例では、適切な設計によりすでに仕事が簡単になっています。20000行または200000行のコードベースではなく、2000行のコードベース全体に変更を加える必要があります。

適切な設計により、抜本的な変更が削減されますが、それらを排除しないでください。

言語分析ツールから適切なサポートがあれば、大幅な変更は非常に簡単に自動化できます。ブランケットリファクタリングの変更は、検索と置換のスタイルでコードベース全体に適用できます(ただし、言語規則を適切に尊重します)。プログラマは、検索ヒットごとにyes / noの判断をするだけです。


+1自動化の提案。私は実際にクロージャーを呼び出す多くのライブラリ関数にそれを使用すると思います-どの関数がブロックを取るかを検出し、値を渡す引数名に追加のシンボルパラメータを持つようにそれらを変更するだけです。
存在-forall
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.