「type」属性を持つエンティティがあるとします。 20以上のタイプが考えられます。
次に、タイプをA-> Bから変更できるようにする実装を求められます。これは、唯一の使用例です。
それで、有効なタイプである限り、タイプの任意の変更を許可する何かを実装する必要がありますか?または、要件ごとにA-> Bからの変更のみを許可し、B-> AまたはA-> Cなどの他のタイプの変更を拒否する必要がありますか?
両方から長所と短所を見ることができます。一般的な解決策は、将来同様の要件が発生した場合の作業が少なくなることを意味しますが、失敗する可能性が高くなります(ただし、この時点で呼び出し元を100%制御しますが)ポイント)。
特定のソリューションではエラーが発生しにくくなりますが、同様の要件が発生した場合、将来さらに作業が必要になります。
優れた開発者は変更を予測してシステムを設計し、将来の拡張が容易になるようにすべきであると聞いていますが、これは一般的なソリューションのように思えますか?
編集:
私のそれほど具体的でない例に詳細を追加します:この場合の「ジェネリック」ソリューションは、「ジェネリック」ソリューションよりも少ないタイプの作業で済みます。新しいタイプのみを検証する必要があります。
私の経験則:
もちろん、これはガイドラインであり、厳格な規則ではありません。本当の答えは、ケースバイケースで最善の判断をすることです。
特定のソリューション[...]同様の要件が必要な場合は、将来さらに作業が必要になります
私はこの議論を数十回聞いたことがありますが、私の経験からすると、これは間違いであることがよくわかります。現在または後で一般化すると、2番目の同様の要件が発生した場合、作業は全体でほぼ同じになります。したがって、一般化に追加の労力を費やすことは絶対に意味がありません。
(明らかに、これは、より一般的なソリューションが特定のソリューションよりも複雑でなく、必要な作業が少ない場合には当てはまりませんが、私の経験では、これらはまれなケースです。このようなシナリオは後で質問に編集されました。私の答えの1つは)です。
「2番目の類似のケース」が表示されたら、一般化について考え始める時間です。この2番目の要件は、正しいことを一般化したかどうかを検証できるシナリオを提供するため、一般化する正しくはるかに簡単になります。 1つのケースについて一般化しようとすると、暗闇の中で撮影されます。一般化する必要のない特定のものを過度に一般化し、他の必要な部分を見落とす可能性が高いです。そして、2番目のケースが発生し、間違ったことを一般化したことに気付いた場合、これを修正するためにはるかに多くの作業を行う必要があります。
ですから、「念のため」のことをやる誘惑を遅らせることをお勧めします。このアプローチでは、3回、4回、またはそれ以上の一般化の機会を逃し、同様に見える(重複している)コードの山を維持する必要がある場合にのみ、より多くの作業とメンテナンスが必要になります。
TL; DR:解決しようとしている内容によって異なります。
C#のFuncとActionがいかに素晴らしいかについて話していた間、私はこれについて私のおじいちゃんと同様の会話をしました。私のGrampsは非常に古いタイマープログラマーであり、ソフトウェアが全領域を占めるコンピューターで実行されて以来、ソースコードの周りにありました。
彼は人生で数回技術を変えました。彼はC、COBOL、Pascal、BASIC、Fortran、Smalltalk、Javaでコードを記述し、最終的にC#を趣味として始めました。私は彼と一緒に膝の上に座ってプログラミングする方法を学びましたが、 IBMのSideKickの青いエディターでコードの最初の行を切り分けて、私は20歳になるまでに、外で遊ぶよりもコーディングに多くの時間を費やしていました。
それらは私の記憶の一部ですので、それらを語り直すときに私が正確に実用的ではない場合は失礼します。私はそれらの瞬間がいくらか好きです。
それは彼が私に言ったことです:
「問題を一般化するために行くべきか、それとも特定の範囲で解決するべきか、とあなたは尋ねるのか。まあ、それは...質問だ。」
おばあちゃんは顔にメガネの位置を固定しながら、少しの間それについて考えるために一時停止しました。彼は古いサウンドシステムでディープパープルのLPを聴きながら、コンピューターでマッチ3ゲームをプレイしていました。
「まあ、それはあなたが解決しようとしている問題に依存するだろう」と彼は私に言った。 「すべてのデザインの選択肢に対して単一の神聖なソリューションが存在すると信じるのは魅力的ですが、それはありません。ソフトウェアアーキテクチャはチーズのようなものです。」
「……チーズ、おじいさん?」
「お気に入りのことをどう思っていても、臭いと思っている人が常にいます」。
私は一瞬混乱して瞬きましたが、何か言うことができる前に、おじいさんは続きました。
「あなたが車を作っているとき、どのように部品の材料を選ぶのですか?」
「私は……それは関係するコストとその部品が何をすべきかによると思う」
「それは部品が解決しようとしている問題に依存します。鋼で作られたタイヤや革で作られたフロントガラスを作ることはありません。あなたが手元にある問題を最もよく解決する材料を選びます。今、何が一般的な解決策ですか、それとも特定の問題ですか?どの問題にどのユースケースに対応しますか?一度だけ使用されるコードに最大限の柔軟性を与えるには、完全な機能的アプローチを採用する必要がありますか?非常に特殊で壊れやすいコードを作成する必要がありますシステムの一部であり、多くの用途があり、場合によっては多くの変更が加えられますか?このような設計の選択は、自動車の一部として選択する材料や、小さな家を建てるために選択するレゴブロックの形状のようなものです。最高のレゴブロックは何ですか?」
高齢のプログラマーは、続行する前に自分のテーブルにある小さなレゴ列車モデルに手を伸ばしました。
「何がについてわかっている場合にのみ、そのブリックが必要であると答えることができます。特定のソリューションが一般的なソリューションよりも優れている場合、どのように知ることができますか?解決しようとしている問題がわからない場合は、どちらか、またはその逆ですか?理解できない選択を過ぎて見ることはできません。」
"..引用しただけですかThe Matrix?"
"何?"
「何も、続けて」
「まあ、National Invoice Systemに何かを構築しようとしているとしましょう。地獄のAPIとその3万行のXMLファイルが内部からどのように見えるか知っています。そのファイルを作成するための「一般的な」ソリューションはどのように見えるでしょうかファイルはオプションのパラメータでいっぱいで、特定の事業部門のみが使用する必要のあるケースでいっぱいです。ほとんどの場合、それらを無視しても安全です。一般的な請求システムを作成する必要がないのは、靴を販売します。靴を販売するためのシステムを作成し、それを最高の靴販売請求システムにします。次に、より広範なアプリケーションで、あらゆるタイプのクライアントの請求システムを作成する必要がある場合は、たとえば、独立した一般的な販売システムとして転売される-今では、ガス、食品、またはアルコールにのみ使用されるオプションを実装することが興味深いですNowこれらは可能な使用例です。いくつかの架空の使用しないでくださいの場合、そしてあなたはインプしたくないlement 使用しないケース。 使用しないでくださいは使用しないでくださいの弟です。 "
Grampsはレゴトレインを元の場所に戻し、マッチ3ゲームに戻りました。
「そのため、特定の問題の一般的な解決策または特定の解決策を選択できるようにするには、まずその問題が何であるかを理解する必要があります。それ以外の場合は、推測しているだけであり、推測はプログラマではなくマネージャの仕事です。 ITのすべて、状況によって異なります。」
だから、あなたはそれを持っています。 "場合によります"。ソフトウェア設計について考えるとき、これはおそらく最も強力な2ワード式です。
主に、そのような変更が発生する可能性があるかどうかを予測することを試みる必要があります。
そうでない場合は、通常は今すぐ単純なソリューションに移行し、後で拡張することをお勧めします。その場合、何が必要かをより明確に把握できる可能性があります。
あなたの質問の本文の性質を考えると、私がそれを正しく理解していると仮定すると、これは実際には、一般的なソリューションと特定のソリューションについての質問ではなく、中央システム機能の設計の質問と見なします。
また、中央システムの機能と機能に関しては、最も信頼できるものはそこにないものです。ミニマリズムの側面で誤りを犯すことは、システムを操作することを非常に困難にしたため、多くの依存関係がある、望ましくない長い問題のある機能を削除するよりも、一般的に機能を中央で簡単に追加する方が簡単であることを考慮すると、価値があります新しい機能ごとに無限の設計上の問題を提起する必要があるよりも。
実際、これが将来頻繁に必要になるかどうかについての強い期待が欠けているので、これを可能であればAからBへの型の置き換えと見なすのは避け、代わりにAの状態を変換する方法としてそれを追求するだけです。たとえば、実際に別の「タイプ」に変更することなく、Aの一部のフィールドを設定してモーフィングし、ユーザーにBのように見えるようにします。構成を使用してAストアBをプライベートに作成し、Aの状態のときにBで関数を呼び出すことができます。可能な場合は実装を簡略化するためにBを模倣する必要があることを示すように設定されています。それは非常にシンプルで低侵襲のソリューションでなければなりません。
とにかく、他の多くのことを繰り返して、この場合は一般的な解決策を回避する側で誤解することをお勧めしますが、これは中央システムに非常に大胆な機能を追加することを考慮していると想定しているので、特に今のところ、それを除外することを誤るように提案してください。
この特定の問題に一般的な答えを出すのは難しいです;-)
より一般的であるほど、将来の変更のためにより多くの時間が得られます。たとえば、この理由により、多くのゲームプログラムは、ゲーム内のキャラクターとオブジェクトの非常に複雑で堅固な型システムを構築する代わりに、 エンティティコンポーネントパターン を使用します。
一方、一般的なものを作成するには、非常に具体的なものよりもはるかに高い設計への事前の時間と労力の投資が必要です。これは、過剰設計のリスクを負い、将来の潜在的な要件で失われるリスクさえも負っています。
画期的な結果をもたらす自然な一般化があるかどうかを確認することは常に価値があります。しかし、結局のところ、それは、現在費やすことができる努力と将来必要になるかもしれない努力との間のバランスの問題です。
ハイブリッド。これはどちらか一方の質問である必要はありません。現在必要な特定の変換のみを実装しながら、一般的な型変換用のAPIを設計できます。 (サポートされていない変換で誰かが一般APIを呼び出した場合、「サポートされていない」エラーステータスで失敗することを確認してください。)
テスト。 A-> B変換の場合、1つ(または少数)のテストを作成する必要があります。一般的なx-> y変換の場合、テストのマトリックス全体を記述しなければならない場合があります。すべての変換が単一の汎用実装を共有していても、これはかなり多くの作業です。
一方、考えられるすべての変換をテストする一般的な方法があることが判明した場合、作業はそれほど多くなく、私はより早く一般的なソリューションに進む傾向があるかもしれません。
カップリング。 AからBへのコンバーターは、AとBの実装の詳細を知る必要がある場合があります(密結合)。 AとBがまだ進化している場合、これは私がコンバーター(およびそのテスト)を再検討し続けなければならない可能性があることを意味しますが、少なくともAとBに限定されます。
すべての型の詳細にアクセスする必要がある汎用ソリューションを使用した場合、CとDが進化しても、汎用コンバーター(および一連のテスト)を微調整し続ける必要がある場合があります。 まだ誰もCまたはDに変換する必要はありませんが。
一般的な変換と特定の変換の両方を、型の詳細に疎結合された方法でのみ実装できる場合は、これについて心配する必要はありません。それらの1つが疎結合の方法で実行でき、もう1つが密結合を必要とする場合、それはゲートからの疎結合アプローチの強力な議論です。
ソリューションは可能な限り一般的であるか、それともできるだけ具体的である必要がありますか?
それは答えられる質問ではありません。
合理的に取得できる最善の方法は、特定のソリューションを作成するための一般的または具体的な方法を決定するいくつかのヒューリスティックです。以下のプロセスのような処理を行うと、通常は1次近似が正しい(または十分に良い)です。そうでない場合、その理由はドメイン固有のものである可能性が高く、ここで詳しく説明することはできません。
一次近似:Daniel Pryden、Doc Brown、et alによって説明されている3つの通常のYAGNIルール。
これは、ドメインや他の変数に依存しないできないことができるので、これは一般的に役立つヒューリスティックです。
したがって、最初の推定は、最も具体的なことを行うことです。
2次近似:ソリューションドメインに関する専門知識に基づいて、
この場合の「一般的な」ソリューションは、「特定の」ソリューションよりも少ない作業で済みます
したがって、mightは不要なworkを回避することを推奨するとYAGNIを再解釈します。不要な一般性を回避するのではなく。したがって、最初の推定を変更して、代わりにeasiestを実行する場合があります。
ただし、ソリューションドメインの知識により、最も簡単なソリューションで多くのバグが発生する可能性が高い、または適切にテストするのが困難である、またはその他の問題が発生する可能性があることが示されている場合、コーディングが容易であることが必ずしも変更を行う十分な理由ではありません。元の選択。
3次近似:問題領域の知識は、最も簡単な解決策が実際に正しいことを示唆していますか?それとも、多くの遷移が無意味または間違っていることを許可していますか?
簡単だが一般的な解決策に問題があるように見える場合、またはこれらのリスクを判断する能力に自信がない場合は、追加の作業を行い、最初の推測を維持する方がおそらく良いでしょう。
4次近似:顧客の行動に関する知識、またはこの機能が他の人とどのように関連しているか、プロジェクト管理の優先順位、または...厳密でない技術的な考慮事項が現在の作業上の決定を変更していますか?
これは単純な答えで答えるのは簡単な質問ではありません。多くの回答から、3のルールまたはそれに類似したルールに基づいて構築されたヒューリスティックが提供されています。そのような経験則を超えることは難しい。
本当に本当に質問に答えるためには、あなたの仕事がA-> Bを変更するものを実装しない可能性が最も高いと考える必要があります。あなたが請負業者であれば、それが要件かもしれませんが、従業員であれば、会社のために多くの小さなタスクを実行するために雇われました。 A-> Bの変更は、これらのタスクの1つにすぎません。あなたの会社は、たとえそれが要求に記載されていなくても、将来の変更がどれだけうまく行われるかについても気にかけます。 「TheBestImplementation(tm)」を見つけるには、実際に要求されていることの全体像を見て、それを使用して、A-> Bを変更するために与えられた小さな要求を解釈する必要があります。
大学を卒業したばかりの低レベルのエントリープログラマーである場合は、指示されたとおりのことを行うことをお勧めします。 15年の経験を持つソフトウェアアーキテクトとして採用された場合、一般的に全体像について考えることをお勧めします。すべての実際の仕事は、「狭い仕事を正確に行う」と「全体像を考える」の間のどこかに入るでしょう。人と十分に話をし、彼らのために十分な仕事をするなら、あなたはあなたの仕事がそのスペクトルのどこに適合するかを感じるでしょう。
あなたの質問が文脈に基づく決定的な答えを持っているいくつかの具体的な例をあげることができます。安全性が重要なソフトウェアを作成している場合を考えてみましょう。つまり、テストチームが立ち上がって、製品が期待どおりに機能することを確認します。これらのテストチームの一部は、コードを通じて可能なすべてのパスをテストする必要があります。彼らと話をすると、動作を一般化すると、それらのすべての余分なパスをテストする必要があるため、テストコストに30,000ドルが追加されることがあります。その場合、作業が7または8回重複する必要がある場合でも、一般化された機能を追加しないでください。会社のお金を節約し、要求が述べたとおりに行います。
一方、会社が作成したデータベースプログラムのデータに顧客がアクセスできるようにするAPIを作成していることを考慮してください。顧客は変更を許可するように要求しますA-> B。通常、APIには黄金の手錠という側面があります。APIに機能を追加すると、(次のメジャーバージョン番号まで)その機能を削除することは通常ありません。顧客の多くは、次のメジャーバージョン番号へのアップグレードの費用を惜しむことはないかもしれません。そのため、長期間にわたって選択したソリューションに悩まされる可能性があります。この場合、私は最初から一般的なソリューションを作成することを推奨しています非常に。 1回限りの動作でいっぱいの悪いAPIを開発したくありません。
私が少し前に作成したガイドラインは、「架空の要件の場合は架空のコードのみを記述する」です。つまり、追加の要件が予想される場合は、それらを実装する方法について少し考え、現在のコードがそのようにブロックされないように構造化する必要があります。
しかし、今これらのために実際のコードを書かないでください-あなたが何をするかについて少し考えてください。そうしないと、通常、物事が不必要に複雑になり、後で実際の要件が予想とは異なるものになると、おそらく迷惑になります。
あなたの例の4つ:convertメソッドのすべての使用法を制御下にある場合は、とりあえずそれをconvertAToBと呼び、IDEただし、変換メソッドがパブリックAPIの一部である場合、これはまったく異なる可能性があります。その場合、名前を変更することが難しいため、特定化すると、後で一般化がブロックされます。
うーん...答えを得るためのコンテキストはあまりありません...以前の答えを繰り返します。
ある意味で、あなたは自分の経験に頼らなければなりません。あなたのものではない場合は、ドメイン内のより上級の誰か。受け入れ基準が何を述べているかについて、あなたは疑問を抱くかもしれません。それが「ユーザーがタイプを "A"から "B"に変更できる必要がある」か「ユーザーがタイプを現在の値から許可されている代替値に変更できるはずである」のようなものである場合。
多くの場合、受け入れ基準は解釈の対象となりますが、優れたQAスタッフは、必要な解釈を最小限に抑えて、目前のタスクに適した基準を作成できます。
「A」から「C」または他のオプションへの変更を許可せず、「A」から「B」への変更のみを許可するドメイン制限はありますか?それとも、「前向きな考え」ではない、狭義の要件なのでしょうか。
一般的なケースの方が難しい場合は、作業を開始する前に尋ねますが、あなたのケースでは、将来的に他の「タイプ」変更リクエストが来ることを「予測」できるとしたら、私は次のように誘惑されます。一般的なケースで再利用可能なものを記述し、b)今のところA-> Bのみを許可する条件付きでラップします。
自動テストで現在のケースを確認するのに十分なほど簡単で、異なるユースケースが発生した場合に、後で他のオプションを開くのに十分簡単です。
優れた開発者は変更を予測してシステムを設計し、将来の拡張が容易になるようにすべきだと聞いています。
原則として、はい。しかし、これはnotが必然的に一般的なソリューションにつながることを意味します。
ソフトウェア開発には、将来の変化を予測する必要がある2つのタイプのトピックがあります。
最初のケースは、凝集/結合、依存性注入などを監視することで解決されます。 2番目のケースは、より抽象的なレベルにあります。たとえば、大規模なアプリケーションでは、コードの大きなモノロシックなブロブではなく、サービス指向アーキテクチャを選択します。
あなたのケースでは、特定の問題に対する特定の解決策を求めています。これは、将来に予測可能な影響を与えることはありません。この場合、YAGNIとDRYは、次の目的で使用するのに適したモットーです。
他の最新のプラクティス(安全なリファクタリングを可能にする優れたテストカバレッジなど)と組み合わせると、必要に応じて成長する、迅速に記述された無駄のない平均的なコードが得られます。
一般的な解決策のように聞こえるのはどれですか?
いいえ、必要なときに簡単に楽しくリファクタリングできるプログラミング環境、言語、ツールが必要なようです。一般的なソリューションはそれを提供しません。アプリケーションを実際のドメインから切り離します。
たとえば、Ruby on Rails;アプリケーションレベルでは、一般的な作業以外の作業にすべての焦点を当てています。)Rails =ライブラリ自体は明らかにほぼ100%汎用ですが、ドメインコード(質問について)は、その面で最小限の不正操作を行う必要があります。
問題について考える別の方法は、何が理にかなっているのかを考えることです。
たとえば、私が開発しているアプリケーションには、ほぼ同じことをするセクションがありましたが、許可ルールに一貫性がありませんでした。それらを異なるようにする理由はなかったので、そのセクションをリファクタリングしたとき、私はそれらすべてに同じ方法で権限を与えるようにしました。により、コード全体がより小さくシンプルになり、インターフェイスの一貫性が向上しました。
管理者が他の人に機能へのアクセスを許可することを決定したとき、フラグを変更するだけでそれを行うことができました。
特定の型変換を行うのは当然のことです。追加の型変換を行うことも意味がありますか?
一般的なソリューションの方が実装が速い場合は、特定のケースも簡単です。許可されている唯一の型変換であることを確認してください。
アプリケーションが非常に規制された領域(医療または金融アプリケーション)にある場合、より多くの人を設計に関与させるようにしてください。