私たちのコードベースは、私と同じように古くて新しいプログラマーであり、すぐにそれを行うことを学びますそれが行われている方法均一性のために。どこかから始めなければならないと思ったので、データホルダークラスを次のようにリファクタリングしました。
final
にしました(私は "final
is good"を公理的に使用しています)。結局のところ、setterはコンストラクターでのみ使用されていたため、副作用はありませんでした。コンストラクター(そもそもリファクタリングを促すもの)が約3行のコードにまたがっているため、Builderクラスが必要でした。 たくさんパラメータがあります。
幸運なことに、私のチームメイトが別のモジュールで作業していて、フローのさまざまなポイントで必要な値が利用可能になったため、たまたまセッターが必要でした。したがって、コードは次のようになります。
public void foo(Bar bar){
//do stuff
bar.setA(stuff);
//do more stuff
bar.setB(moreStuff);
}
セッターを取り除くことでフィールドを不変のままにできるため(以前は不変性について怒りを聞いたことがある)、またビルダーでオブジェクトの作成をトランザクションに対応できるため、彼は代わりにビルダーを使用するべきだと私は主張しました。次の疑似コードをスケッチしました。
public void foo(Bar bar){
try{
bar.setA(a);
//enter exception-throwing stuff
bar.setB(b);
}catch(){}
}
その例外が発生した場合、bar
に破損したデータが含まれます。これはビルダーで回避されたはずです。
public Bar foo(){
Builder builder=new Builder();
try{
builder.setA(a);
//dangerous stuff;
builder.setB(b);
//more dangerous stuff
builder.setC(c);
return builder.build();
}catch(){}
return null;
}
私のチームメイトは、問題の例外が発生することは決してないことを反省しました。これは、コードのその特定の領域には十分に公平ですが、ツリーのフォレストが欠落していると思います。
妥協案は、古いソリューションに戻すことでした。つまり、パラメーターなしでコンストラクターを使用し、必要に応じてセッターですべてを設定しました。理論的根拠は、このソリューションがKISS=原理に従っていることです。
私はこの会社を初めて利用し(6か月未満)、この会社を失ったことを十分に認識しています。私が持っている質問は:
でも本当に
どこかから始めなければならないと思って、データホルダークラスをリファクタリングすることを自分で考えました
ここで問題が発生しました。コードベースにアーキテクチャ上の大きな変更を加えたため、予想とは大きく異なります。あなたは同僚を驚かせた。サプライズはパーティーが関係している場合にのみ有効です。サプライズの最小化の原則は黄金です(たとえパーティーが関係している場合でも、きれいなパンツを着用することを私は知っています!)
この変更に価値があると考えた場合、変更を示す疑似コードをスケッチして同僚(または少なくとも建築家に最も近い役割を持つ人)に提示し、何をする前に彼らが何を考えたかを確認する方がはるかに良いでしょう。現状のままで、コードベース全体を自分の都合に合わせて操作できると考え、悪党になりました。チームプレーヤーではなく、チームのプレーヤー!
私はあなたに少し厳しいかもしれないが、私は反対の場所にいた:いくつかのコードをチェックして、「WTFがここで発生したのか?!」と思って、新しい人(ほとんど常に新しい人)が変更することに決めた彼が「正しい方法」であるべきだと思っていることに基づいて、物事の負荷。もう1つの大きな問題であるSCMの履歴もあるネジ。
幸運なことに、私のチームメイトが別のモジュールで作業していて、フローのさまざまなポイントで必要な値が利用可能になったため、たまたまセッターが必要でした。
説明したように、コードがrequireセッターになる原因がわかりません。私はおそらくユースケースについて十分に理解していませんが、オブジェクトをインスタンス化する前にすべてのパラメーターがわかるまで待ってはどうですか?
別の方法として、状況にセッターが必要な場合:オブジェクトの準備ができた後でフィールドを変更しようとすると、セッターにコードをフリーズさせる方法を提供して、アサーションエラー(プログラマーエラー)をスローします。
セッターを削除し、finalを使用することは、それを行う「適切な」方法であり、不変性を強制することだと思いますが、本当にあなたが時間を費やすべきものですか?
古い=悪い、新しい=良い、私のやり方、あなたのやり方...この種の議論は建設的ではありません。
現在、オブジェクトの使用方法はいくつかの暗黙のルールに従います。これは、コードの未記述仕様の一部であり、コードの他の部分を悪用するリスクはそれほどないとチームは考えるかもしれません。ここで行うことは、ビルダーとコンパイル時のチェックを使用してルールをより明確にすることです。
何らかの方法で、コードのリファクタリングは Broken Window 理論に関連付けられています。コードを確認できるように、問題を修正する(または、後の「品質向上」ミーティングのためにメモする)ことが適切だと感じます。常に快適に作業でき、安全で清潔です。しかし、あなたとあなたのチームは、何が「十分」であるかについて同じ考えを持っていません。誠意をもって貢献し、コードを改善する機会とみなすものは、おそらく既存のチームとは異なって見えます。
ちなみに、チームは必ずしも慣性、悪い習慣、教育の欠如に苦しんでいると想定すべきではありません...あなたができる最悪のことは、知識のイメージを投影することですallコーディング方法を示すために誰がそこにいるか。たとえば、不変性は何かnewではありません。あなたの仲間はおそらくそれについて読んだでしょうが、この主題がブログでpopularになっているからですコードの変更に時間を費やすように説得するには十分ではありません。
結局のところ、既存のコードベースを改善する方法は無限にありますが、それには時間とお金がかかります。私はあなたのコードを見て、それを「古い」と呼んで、ちょっと気になることを見つけることができました。例:正式な仕様がない、十分なアサートがない、おそらく十分なコメントやテストがない、十分なコードカバレッジがない、アルゴリズムが最適でない、メモリ使用量が多すぎる、各ケースで「最良の」可能な設計パターンを使用していない、などad nauseam。しかし、私が提起するほとんどの点について、あなたは思うでしょう:それで大丈夫です、私のコードは機能します。
多分あなたのチームは、あなたが提案することはリターンを減少させるポイントを過ぎていると考えています。 実際のインスタンスの例を示したために(例外を除いて)バグのインスタンスを見つけたとしても、おそらく一度だけ修正し、コードをリファクタリングしたくないでしょう。
したがって、問題は、限られたであることを考えると、時間とエネルギーを費やす方法についてです。ソフトウェアには継続的に変更が加えられますが、一般的には、良い議論を提供できるようになるまで、アイデアは負のスコアから始まります。 「Nice to have、one day ...」バスケットで終わるので、利益について正しいとしても、それだけでは十分な議論ではありません。
あなたがあなたの議論を提示するとき、あなたはいくつかの「正気」チェックをしなければなりません:あなたはアイデアまたはあなた自身を売ろうとしていますか?他の誰かがこれをチームに提示した場合はどうなりますか?彼らの推論にいくつかの欠陥を見つけますか?一般的なベストプラクティスを適用しようとしているか、コードの詳細を考慮に入れましたか?
そうすれば、チームの過去の「間違い」を修正しようとしているかのように見えないはずです。それはあなたが自分の考えではないが、合理的にフィードバックを取ることができることを示すのに役立ちます。これを実行すればするほど、より多くの提案がフォローされる機会になります。
チームでビルダーパターンの使用を促進するにはどうすればよいですか?
あなたはしません。
コンストラクターに3行のパラメーターがある場合、それは大きすぎて複雑すぎます。それを修正するデザインパターンはありません。
データが異なるタイミングで利用可能になるクラスがある場合、それは2つの異なるオブジェクトであるか、またはコンシューマーがコンポーネントを集約してthenがオブジェクトを構築する必要があります。そのためにビルダーは必要ありません。
あなたは他の人とうまく働くよりも正しいことに集中しているようです。さらに悪いことに、あなたは自分がやっていることは権力闘争であることを認識していますが、自分の経験と権威に挑戦している人たちに物事がどのように感じられるかを考えることはできません。
それを逆にします。
他の人との協力に焦点を当てます。提案やアイデアとして考えを組み立てます。彼らにあなたの考えを彼らに考えさせるために質問をする前に彼らに彼らの考えを通して話してもらいましょう。直接的な対立を避け、外向的に進んで、グループの方法(今のところ)で物事を進めることは、グループを変えるために困難な戦いを戦うよりも生産的であることを受け入れます。 (物事を取り戻すが。)あなたと一緒に働くことを喜びにします。
これを一貫して行うと、競合が減り、他の人がより多くのアイデアを採用できるようになります。私たちは皆、完璧な論理的議論が他の人を驚かせ、その日を勝ち取るという幻想に苦しんでいます。そして、それがそのように機能しない場合は、それはshouldだと思います。
しかし、それは現実と直接対立しています。ほとんどの引数は、最初の論理的なポイントが作成される前に失われます。論理的な人々の間でさえ、心理学は理性よりも優先されます。人々を納得させることは、適切に聞こえるように心理的な障壁を乗り越えることであり、完全な論理を持つことではありません。
「古い方法」の代わりにビルダーを使用するための別の議論はありますか?
「新しいハンマーを持っていると、すべてが釘のように見えます。」 -これは私があなたの質問を読んだときに私が思ったものです。
私があなたの同僚だったら、「なぜコンストラクターでオブジェクト全体を初期化しないのですか?」それが結局のところ機能です。ビルダー(またはその他のデザイン)パターンを使用する理由
私が提案する変更は本当に価値がありますか?
その小さなコードスニペットから区別することは困難です。多分それは-あなたとあなたの同僚はそれを評価する必要があります。
何か新しいことを試みることを主張するときに、そのような議論をより適切に提示するためのヒントはありますか?
はい、議論する前に、そのトピックについてもっと学びましょう。ディスカッションに入る前に、適切な議論と理論的根拠を身につけてください。
私は自分のスキルのために採用された状況にありました。私がコードを見るようになったとき、まあ..それは恐ろしいほどでした。その後、それをクリーンアップしようとすると、そこにずっといる人は、変更を常に抑制します。それはあなたが説明しているもののように聞こえます。それは私がその地位を辞めることで終わりました。そして今、私はより良い実践を持つ会社でより良く進化することができます。
チームの他のメンバーと一緒にいるのはもう長くないので、あなたはチームの他のメンバーと一緒に役割を果たすべきではないと思います。チームメンバーが作業中のプロジェクトで不適切な慣行を使用しているのを見た場合は、これまでと同様にimoを指摘する必要があります。そうでない場合、あなたは非常に貴重なリソースではありません。あなたが物事を何度も指摘しているにもかかわらず、だれも聞いていない場合は、経営陣に交換または変更の仕事を依頼してください。あなたがそこに悪い習慣に適応することを許可しないことが非常に重要です。
なぜ不変オブジェクトを使用するのですか?あなたが何を扱っているか知っているからです。
たとえば、セッターを介してホストを変更できるように渡されるdb接続があるとします。その場合、それがどの接続であるかがわかりません。不変であるなら、あなたは知っています。
ただし、永続化から取得して新しいデータで再永続化できるデータの構造があるとすると、この構造は不変ではありません。同じエンティティですが、値は変わる可能性があります。代わりに、引数として不変の 値オブジェクト を使用してください。
ただし、問題の焦点を当てているのは、コンストラクターの依存関係を取り除くためのビルダーパターンです。私はこれに大きな脂肪[〜#〜] no [〜#〜]と言います。インスタンスは明らかに複雑になっています。それを隠そうとしないでください、修正してください!
クラスによっては、セッターの削除が正しい場合があります。ビルダーパターンは問題を解決せず、単にそれを隠すだけです。 (見えなくてもカーペットの下に汚れが残っています)
他のすべての答えは非常に便利ですが(私のものよりも便利です)、まだ言及していないビルダーの特性があります。オブジェクトの作成をプロセスに変えることにより、複雑な論理制約を強制できます。
たとえば、特定のフィールドを一緒に設定したり、特定の順序で設定したりできます。これらの決定に応じて、ターゲットオブジェクトの異なる実装を返すこともできます。
// business objects
interface CharRange {
public char getCharacter();
public int getFrom();
public int getTo();
}
class MultiCharRange implements CharRange {
final char ch;
final int from;
final int to;
public char getCharacter() { return ch; }
public int getFrom() { return from; }
public int getTo() { return to; }
}
class SingleCharRange implements CharRange {
final char ch;
final int position;
public char getCharacter() { return ch; }
public int getFrom() { return position; }
public int getTo() { return position; }
}
// builders
class Builder1 {
public Builder2 character(char ch) {
return new Builder2(ch);
}
}
class Builder2 {
final char ch;
int from;
int to;
public Builder2 range(int from, int to) {
if (from > to) throw new IllegalArgumentException("from > to");
this.from = from;
this.to = to;
return this;
}
public Builder2 zeroStartRange(int to) {
this.from = 0;
this.to = to;
return this;
}
public CharRange build() {
if (from == to)
return new SingleCharRange(ch,from);
else
return new MultiCharRange(ch,from,to);
}
}
これはあまり意味のないおかしな例ですが、3つのプロパティすべてが示されています。常に文字が最初に設定され、範囲の制限は常に一緒に設定および検証され、ビルド時に実装を選択します。
これがどのように読みやすく信頼性の高いユーザーコードにつながるかは簡単にわかりますが、ご覧のとおり、多くのビルダーコード(そしてスニペットを短くするためにコンストラクターを省略している)にも欠点があります。したがって、次のような質問をして、トレードオフを計算しようとします。
あなたの同僚は、そのトレードオフが有益ではないと単に決めました。理由を率直に、そして正直に、できれば抽象的な原則に頼らずに、この解決策またはその解決策の実際的な意味合いに集中して話し合う必要があります。
このクラスは実際には複数のクラスであり、同じファイル/クラス定義にまとめられているようです。 DTOの場合、一度にインスタンス化して不変にすることができるはずです。 1つのトランザクションですべてのフィールド/プロパティを使用しない場合、ほぼ確実に単一責任の原則に違反しています。それをより小さく、まとまりのある、不変のDTOクラスに分割することが役立つかもしれません。まだ読んでいない場合は クリーンコード を読んで、問題を明確に説明できるようにしてください。
あなたが文字通り暴徒プログラミングでない限り、あなたは委員会によってこのレベルでコードを設計することはできないと思います(それはあなたがその種のチームに属しているようには聞こえません)。最善の判断で、間違ったと判断した場合は顎に当ててください。
ただし、コードの潜在的な問題をそのまま明確に示すことができれば、解決策が必要であることについても議論できます。あなたのものが受け入れられていない場合。その後、人々は自由に代替案を提供できます。
" 強い意見、弱々しい "は良い原則です-知っていることに基づいて自信を持って進みますが、それに反する新しい情報を見つけたら、控えめに立ち下がり、正しい解決策についての議論。唯一の間違った答えは悪化させるためにそれを残すことです。