クリーンなコードとハイブリッドオブジェクトおよび機能羨望


10

そのため、最近、コードにいくつかの主要なリファクタリングを行いました。私がしようとした主なことの1つは、クラスをデータオブジェクトとワーカーオブジェクトに分割することでした。これは、とりわけ、Clean Codeの次のセクションに触発されました。

ハイブリッド

この混乱は、半分のオブジェクトと半分のデータ構造である不幸なハイブリッドデータ構造につながることがあります。それらには重要な機能を果たす関数があり、パブリック変数またはパブリックアクセサーとミューテーターのいずれかがあり、すべての目的と目的のためにプライベート変数をパブリックにして、他の外部関数がそれらの変数を手続き型プログラムが使用する方法で使用するように誘惑しますデータ構造。

そのようなハイブリッドは、新しい関数を追加することを難しくしますが、新しいデータ構造を追加することも難しくします。彼らは両方の世界で最悪です。それらを作成しないでください。それらは、作者が関数や型からの保護を必要としているかどうか、またはさらに悪いことに無知である混乱した設計を示しています。

最近、私はワーカーオブジェクトの1つ(たまたま、Visitorパターンを実装する)のコードを見て、これを確認しました。

@Override
public void visit(MarketTrade trade) {
    this.data.handleTrade(trade);
    updateRun(trade);
}

private void updateRun(MarketTrade newTrade) {
    if(this.data.getLastAggressor() != newTrade.getAggressor()) {
        this.data.setRunLength(0);
        this.data.setLastAggressor(newTrade.getAggressor());
    }
    this.data.setRunLength(this.data.getRunLength() + newTrade.getLots());
}

私はすぐに「!このロジックがであるべき機能の羨望自分自身に言ったData-特に、クラスhandleTradeメソッド。handleTradeupdateRunする必要があり、常に一緒に起こります」。しかし、「データクラスは単なるpublicデータ構造です。それを始めれば、ハイブリッドオブジェクトになるでしょう!」

何が良いのか、そしてその理由は?どちらを行うかをどのように決定しますか?


2
なぜ「データ」はデータ構造でなければならないのですか。それは明確な行動を持っています。したがって、すべてのゲッターとセッターをオフにして、オブジェクトが内部状態を操作できないようにします。
Cormac Mulhall 2014年

回答:


9

あなたが引用したテキストは良いアドバイスを持っていますが、構造体のようなものを意味すると仮定して、「データ構造」を「レコード」に置き換えます。レコードは、データの単なる集約です。それらは変更可能である可能性があります(したがって、関数型プログラミングの考え方ではステートフルです)が、内部状態はなく、保護する必要のある不変条件はありません。使用を容易にする操作をレコードに追加することは完全に有効です。

たとえば、3Dベクトルはダムレコードであると主張できます。ただし、addこれにより、のようなメソッドを追加でき、ベクトルの追加が簡単になります。動作を追加しても、(それほど馬鹿ではない)レコードはハイブリッドになりません。

オブジェクトのパブリックインターフェイスがカプセル化を解除できるようになると、この線が交差します。直接アクセスできる内部がいくつかあり、オブジェクトが無効な状態になります。その私には思えるDataの状態を持っている、それは無効状態にすることができるという。

  • 取引を処理した後、最後の攻撃者が更新されない場合があります。
  • 最後の攻撃者は、新しい取引が発生していなくても更新できます。
  • アグレッサーが更新された場合でも、ランレングスは古い値を保持する場合があります。

データに対して有効な状態があれば、コードはすべて正常であり、続行できます。それ以外の場合:Dataクラスは、独自のデータ整合性を担当します。取引の処理に常にアグレッサーの更新が含まれる場合、この動作はDataクラスの一部である必要があります。アグレッサーを変更するときにランの長さをゼロに設定する必要がある場合、この動作はDataクラスの一部である必要があります。Data決してばかげた記録ではなかった。パブリックセッターを追加することで、すでにハイブリッドになっています。

これらの厳格な責任を緩和することを検討できるシナリオが1つありDataます。がプロジェクトに対してプライベートであり、したがってパブリックインターフェイスの一部ではない場合でも、クラスの適切な使用を保証できます。ただし、これDataにより、コードを中央の場所に集めるのではなく、コード全体で一貫性を維持する必要があります。

私は最近、カプセル化についての回答を書きました。カプセル化とは何か、およびカプセル化をどのようにして保証できるかについて、さらに詳しく説明します。


5

事実handleTrade()updateRun()常に一緒に起こる(第2の方法は、訪問者に実際には、データオブジェクト上のいくつかの他のメソッドを呼び出す)を、匂い時間カップリング。これは、特定の順序でメソッドを呼び出す必要があることを意味します。順序外でメソッドを呼び出すと、最悪の場合、何かが壊れたり、意味のある結果が得られなかったりするでしょう。良くない。

通常、その依存関係をリファクタリングする正しい方法は、各メソッドが結果を返すことです。この結果は、次のメソッドにフィードするか、直接操作することができます。

古いコード:

MyObject x = ...;
x.actionOne();
x.actionTwo();
String result = x.actionThree();

新しいコード:

MyObject x = ...;
OneResult r1 = x.actionOne();
TwoResult r2 = r1.actionTwo();
String result = r2.actionThree();

これにはいくつかの利点があります。

  • 個別の関心事を個別のオブジェクト(SRP)に移動します。
  • これにより、時間的な結合がなくなります。メソッドを順不同で呼び出すことは不可能であり、メソッドシグネチャは、それらを呼び出す方法に関する暗黙のドキュメントを提供します。ドキュメンテーションを見て、必要なオブジェクトを見て、逆に作業したことがありますか?オブジェクトZが必要ですが、Zを取得するにはYが必要です。Yを取得するには、Xが必要です。私はXを取得するために必要なWを持っています。これをすべてチェーンすると、Wを使用してZを取得できます。
  • このようにオブジェクトを分割すると、オブジェクトが不変になる可能性が高くなります。これには、この質問の範囲を超える多くの利点があります。簡単に言うと、不変オブジェクトはより安全なコードにつながる傾向があるということです。

これら2つのメソッド呼び出しの間には一時的な結合はありません。順序を入れ替えても動作は変わりません。
durron597 2014年

1
質問を読むときに、最初は逐次/時間的カップリングも考えましたが、updateRunメソッドがプライベートであることに気付きました。順次結合を回避することは良いアドバイスですが、API設計/パブリックインターフェイスにのみ適用され、実装の詳細には適用されません。本当の質問updateRunはビジターにあるべきかデータクラスにあるべきかであるようであり、私はこの答えがその問題にどのように対処するのかわかりません。
アモン

の可視性updateRunは関係this.dataありません。重要なのは、その実装が質問に存在せず、ビジターオブジェクトによって操作されているオブジェクトです。

どちらかといえば、このビジターがセッターの束を単に呼び出しており、実際には何も処理していないという事実は、一時的な結合が存在しない理由です。セッターが呼び出される順序は重要ではありません。

0

私の観点から見ると、クラスには「状態の値(メンバー変数)と動作の実装(メンバー関数、メソッド)」を含める必要があります。

「不幸なハイブリッドデータ構造」は、クラスステートのメンバー変数(またはそれらのゲッター/セッター)をパブリックにすべきではないものにすると公開されます。

そのため、データデータオブジェクトとワーカーオブジェクトに別々のクラスを用意する必要はありません。

状態メンバー変数を非公開に保つことができる必要があります(データベース層は非公開メンバー変数を処理できる必要があります)

フィーチャー羨望は、別のクラスのメソッドを過度に使用するクラスです。Code_smellを参照してください。メソッドと状態を持つクラスがあれば、これはなくなります。

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