4

私はこれらの線に沿った何かを扱っています:

非常に複雑なクラスと、クラスの初期化時に設定されない、または外出先で設定されるものに依存するメンバーがあります。そのクラスのオブジェクトは、そのメンバーが設定されていない場合でも意味があります。他のメンバーに加えられた他の変更に応じてリセットすることもできます。

ここで、この「特別な」メンバーの設定には計算コストがかかると想定します。そのため、要求に応じて計算を遅らせています。

それで:

class Class
{
    X x;
    Y y;
    SpecialClass specialObject;
public:
    void setX(const X& newX);
    void setY(const Y& newY);

    //----
    SpecialClass getSpecialObject() /*const*/
    { 
        computeSpecialObject();
        return specialObject();
    }
private:
    void computeSpecialObject()
    {
        //specialObject depends on x and y
        //and is expensive to compute
        //this method is a bottleneck
    }
};

compute変更するたびに、xまたはコストがかかるため、メソッドを呼び出したくないyので、ジレンマが残ります。

  • const?を削除します 論理的には、ゲッターはである必要がありますconstが、そうではありません。オブジェクトでは呼び出せないという欠点もありconstます。
  • 私は作ることができますがspecialObject mutable、それは正しいことではないようです。
  • 恒常性を捨てる?繰り返しますが、魚のように見えます。
  • 取得する前に電話computeSpecialObjectしますか?-誰かが忘れたらどうしますか?彼らは時代遅れの結果を得るでしょう。

これに対応するデザインパターンはありますか?良いアプローチ?それとも、クラスのデザインが間違っているのでしょうか。(私はこの最後のものに傾倒しますが、クラスを変更することは実際にはオプションではありません)

注:私はメンバーを作成しましたmutable。より良い解決策があるかどうか知りたいです。

4

4 に答える 4

4

specialObjectを作成することはできますmutableが、それは正しいことではないようです。

なぜそうなのか?それがまさにmutable存在する理由です。オブジェクトを物理的に変更せずにconst関数を論理的に使用できるようにするためです(オブジェクトを作成する場合は、 スレッドセーフを確保することを忘れないでください。私が何を意味するかはご存知でしょう)。constmutable

SpecialClassもちろん、これは、オブジェクトの初期化がオブジェクトの論理状態を変更するものでない限り当てはまります。これは、そうconstしないことを約束しているためです。

その場合、関数自体は本質的には単純ではなく、単に:候補とはconst異なる名前を付ける必要があります。getSpecialObject()computeAndReturnSpecialObject()

于 2013-02-22T22:24:38.933 に答える
3

クラスに単に「埋め込む」のではなく、をそのままconstにして、ポインタを作成またはspecialObject mutable保持します。specialObject

bool dirtyまた、フラグを追加してmutable、計算を無効にする変更が加えられるたびに設定します。次に、内部のフラグを確認し、computeSpecialObject設定されている場合にのみ作業を行います。ポインタを使用するdeleteと、変更によって既存の計算が無効になるたびに古い計算オブジェクトを使用することもできますが、これにより、ワームの別の缶全体が開かれます。

それとも私は何かが足りないのですか?

于 2013-02-22T22:24:33.610 に答える
0

これは常に細い線であり、必要のないときに呼び出さないのに対し、呼び出し元に穴が開いてしまうため、正しくない結果が返される可能性があります。

私はcomputeを特別なオブジェクトのメソッドに移動し、このクラスをそのクラスのcomputeメソッドの引数のラッパーとして扱います。ボーナスボールは、計算をユニットテストできることです。

次に、SpecialObject.Compute(x、y)を再度呼び出す必要がある場合、または単に最後の結果を返す必要がある場合を決定するだけの問題です。可能であれば、Xが変更されたが、Yが変更されていない場合、計算を単純化することもできます。つまり、いくつかの中間結果を保持します。

それがあなたにどれほど適用できるかはわかりませんが、私が定期的に行うことの1つは、計算を行うものを注入することです。そのため、デフォルトでこのパターンに陥る傾向があります。

于 2013-02-22T22:46:04.680 に答える
0

あなたが行くことができる2つの方向があります、OOP多かれ少なかれFunctional。1つは状態操作をあまり気にせず、むしろ振る舞いを含み、もう1つは振る舞いを完全に忘れて、戻された状態を気にします。

OOP

私にとって重要なOOP原則は、、Tell, Don't Askまたはwrite no getters or settersです。

自律的になるように、何をすべきかを指示されるようにオブジェクトを設計します。何かをするために使用できるオブジェクトを返すように依頼しないでください。そもそもやりたいことをやるように言ってください。オブジェクトに何かをするように指示している場合は、オブジェクトの状態が変化することを期待している可能性がありますが、そうであるのは正しくありませんconst

あなたSpecialClassはいくつかのサービスを提供するかもしれませんdoService()Class代わりに、に伝えることができますdoSpecialService()。これは正しく変更可能です。

別の方法は、このオブジェクトを作成して、他のオブジェクトを使用して作成することです。したがって、関数はconstにすることができますが、const以外のパラメーターを取ります。

class Class {
public:
    void doService(ServiceProvider& serviceProvider) const {
        serviceProvider.doService(x, y);
    }
};

これを使用すると、指定されたとSpecialServiceProvider&の正しいものを作成するaを渡すことになります。変更可能です。サービスの提供において状態を変更することは正しいように思われます。たぶん、( 、)ペアのマップキャッシングオブジェクトを持つことができます。SpecialClassXYSpecialClassXY

機能的

もう1つの方向は、オブジェクトを不変にすることです。新しい状態が必要な場合は常に、古い状態をベースとして使用して作成します。これは、カメが(ほぼ)完全に下がるまで、ノックオン効果をもたらす可能性があります。

class SpecialBuilder {
public:
    SpecialBuilder withX(const X& newX) const;
    SpecialBuilder withY(const Y& newY) const;
    SpecialClass build() const;
};

SpecialBuilder specialBuilder;
SpecialClass special = specialBuilder.withX(x).withY(y).build();

SpecialBuilder不変であるため、返された各データ間でデータを共有できます。

于 2013-02-22T22:46:05.233 に答える