2

現在作成しているコードには、シリアル番号で区別される 2 つの個別の型に属することができるオブジェクトがあります。このようなもの:

class Chips {
public:
    Chips(int shelf) {m_nShelf = shelf;}
    Chips(string sSerial) {m_sSerial = sSerial;}
    virtual string GetFlavour() = 0;
    virtual int GetShelf() {return m_nShelf;}
protected:
    string m_sSerial;
    int m_nShelf;
}

class Lays : Chips {
    string GetFlavour() 
    {
        if (m_sSerial[0] == '0') return "Cool ranch";
        else return "";
    }
}

class Pringles : Chips {
    string GetFlavour() 
    {
        if (m_sSerial.find("cool") != -1) return "Cool ranch";
        else return "";
    }
}

さて、これを実装するための明白な選択は、ファクトリ デザイン パターンを使用することです。どのシリアルがどのクラス タイプに属しているかを手動で確認することは、それほど難しくありません。

ただし、これには、他のすべてのクラスを認識し、それらを nameで参照するクラスが必要です。これは、特に大量のサブクラスを追加する必要がある場合は特に、真に汎用的ではありません。

さらに複雑なことに、オブジェクトの実際のシリアル番号を知る前に、オブジェクトをしばらく保持する必要がある場合があります。つまり、基本クラスを抽象化するのではなく、ダミー関数でいっぱいに記述して、何らかの方法でインスタンスに置き換える必要がある場合があります。シリアルを取得したときの子クラスの1つ。これも理想的とは言えません。

ファクトリ デザイン パターンは、これに対処するための本当に最善の方法ですか?

4

3 に答える 3

2

次のように、Base クラスのみを認識するファクトリを作成できます。

純粋な仮想メソッドを基本クラスに追加 virtual Chips* clone() const=0;します。すべての派生に対してそれを実装しoperator=ますが、新しい派生へのポインターを返すようにします。(デストラクタがある場合は、それも仮想である必要があります)

ファクトリ クラスを定義できるようになりました。

Class ChipsFactory{
  std::map<std::string,Chips*> m_chipsTypes;

public:
  ~ChipsFactory(){
     //delete all pointers... I'm assuming all are dynamically allocated.
     for( std::map<std::string,Chips*>::iterator it = m_chipsTypes.begin();
          it!=m_chipsTypes.end(); it++) {
        delete it->second;
     }
  }
  //use this method to init every type you have
  void AddChipsType(const std::string& serial, Chips* c){
    m_chipsTypes[serial] = c;
  }      
  //use this to generate object
  Chips* CreateObject(const std::string& serial){
    std::map<std::string,Chips*>::iterator it = m_chipsTypes.find(serial);
    if(it == m_chipsTypes.end()){
       return NULL;
    }else{
       return it->clone();
    }   
  }
};

すべての型でファクトリを初期化すると、そこから初期化されたオブジェクト型のポインタを取得できます。

于 2013-11-12T10:04:57.957 に答える
1

なぜここで継承を気にするのですか?私が見る限り、動作はすべての Chips インスタンスで同じです。その振る舞いは、フレーバーがシリアル番号によって定義されるということです。

シリアル番号がいくつかのことだけを変更する場合は、単純なマップを使用して、シリアル番号に基づいて実行時に動作 (std::function) を挿入または検索できます (なぜ複雑なのか!)。このようにして、シリアル番号マッピングを介して、異なるチップ間で共通の動作が共有されます。

シリアル番号が多くのことを変更する場合、設計が少し逆になっていると思います. その場合、実際に持っているのはチップの構成を定義するシリアル番号であり、設計はそれを反映する必要があります。このような:

class SerialNumber {
public:
    // Maybe use a builder along with default values
    SerialNumber( .... ); 
    // All getters, no setters.
    string getFlavour() const;
private:
    string flavour;
    // others (package colour, price, promotion, target country etc...)
}

class Chips {
public:
    // Do not own the serial number... 'tis shared.
    Chips(std::shared_ptr<SerialNumber> poSerial):m_poSerial{poSerial}{} 
    Chips(int shelf, SerialNumber oSerial):m_poSerial{oSerial}, m_nShelf{shelf}{}
    string GetFlavour() {return m_poSerial->getFlavour()};
    int GetShelf() {return m_nShelf;}
protected:
    std::shared_ptr<SerialNumber> m_poSerial;
    int m_nShelf;
}

// stores std::shared_ptr but you could also use one of the shared containers from boost.
Chips pringles{ chipMap.at("standard pringles - sour cream") };

このようにして、製品の一連のシリアル番号を取得すると、製品の動作は変わりません。唯一の変更点は、SerialNumber にカプセル化された「構成」です。Chipsクラスを変更する必要がないことを意味します。とにかく、誰かがクラスを構築する方法を知る必要があります。もちろん、テンプレートベースのインジェクションも可能ですが、コードは正しいタイプをインジェクトする必要があります。

最後のアイデア。ctorSerialNumberが文字列 (XML や JSON など) を取る場合、管理者タイプの人によって定義された後、実行時にプログラムに構成を読み取らせることができます。これにより、ビジネス ニーズをコードから切り離すことができ、将来を見据えた堅牢な方法となります。

ああ...ハンガリー表記を使用しないことをお勧めします。オブジェクトまたはパラメーターのタイプを変更する場合は、名前も変更する必要があります。さらに悪いことに、それらを変更するのを忘れる可能性があり、他の人が間違った仮定をすることになります. プログラミングに vim/notepad を使用していない限り、IDE はその情報をより明確な方法で提供します。

@ user1158692 - インスタンス化するパーティは、私が提案した設計の 1 つChipsについて知る必要があるだけSerialNumberであり、その提案された設計では、SerialNumberクラスがクラスを構成するように動作することが規定されていますChips。その場合、使用者Chipsは親密な関係であるため、SerialNumber について知っている必要があります。クラス間の親密な関係こそが、コンストラクターを介して注入する必要がある理由です。もちろん、必要に応じて代わりにセッターを使用するようにこれを変更するのは非常に簡単ですが、表現された関係のために、これはお勧めできません。

シリアル番号を知らずにチップのインスタンスを作成することが絶対に必要であるとは本当に思えません。これは、クラスの設計に必要な問題ではなく、アプリケーションの問題であると思います。また、このクラスは SerialNumber なしではあまり使用できません。また、SerialNumber なしでクラスの構築を許可した場合は、デフォルト バージョンを使用する必要があります (これらのいずれかを構築する方法をチップスに知らせるか、グローバル参照を使用する必要があります)。多くのチェックでクラスを汚染することになります。

shared_ptr に関する苦情については...所有権のセマンティクスと責任を明確にすることをどのように提案しますか? おそらく生のポインターが解決策になるでしょうが、それは危険で不明確です。shared_ptr は、設計者がポインターを所有しておらず、責任を負わないことを明確に示しています。

于 2013-11-12T11:01:28.807 に答える