4

私は、本質的に非常に柔軟である必要があるコードに取り組んでいます。つまり、後で他の人が拡張するのも非常に簡単です。しかし、私は今、適切に対処する方法を原則として知らない問題に直面しています:

私はかなり複雑な を持っていAlgorithmますが、これはある時点で収束するはずです。しかし、その複雑さのために、収束をチェックするためのいくつかの異なる基準があり、状況 (または入力) に応じて、異なる収束基準を有効にしたいと考えています。また、アルゴリズム自体に手を加えることなく、新しい収束基準を簡単に作成できるはずです。したがって、理想的には、ConvergenceChecker継承できる抽象クラスを持ち、アルゴリズムにそれらのベクトルを持たせたいと思います。たとえば、次のようになります。

//Algorithm.h (with include guards of course)
class Algorithm {
  //...
  vector<ConvergenceChecker*> _convChecker;
}
//Algorithm.cpp
void runAlgorithm() {
  bool converged=false;
  while(true){
    //Algorithm performs a cycle
    for (unsigned i=0; i<_convChecker.size(); i++) {
      // Check for convergence with each criterion
      converged=_convChecker[i]->isConverged();
      // If this criterion is not satisfied, forget about the following ones
      if (!converged) { break; }
    }
    // If all are converged, break out of the while loop
    if (converged) { break; }
  }
}

これに関する問題は、それぞれConvergenceCheckerが現在実行中の について何かを知る必要があることですAlgorithmが、それぞれがアルゴリズムとはまったく異なることを知る必要があるかもしれません。各サイクルで と を変更するとしますが、可能なものAlgorithm_foo _bar、もう 1 つは、を知るだけでよく、いつの日か必要なものが実装される可能性があります。これを解決するために私がすでに試みたいくつかの方法を次に示します。_fooBarConvergenceChecker_foo_foo_barConvergenceChecker_fooBar

  1. isConverged()関数に大きな引数リスト ( _foo_bar、およびを含む) を与え_fooBarます。短所: 引数として使用される変数のほとんどはほとんどの場合使用されず、Algorithm別の変数によって拡張される (または同様のアルゴリズムが変数から継承していくつかの変数を追加する) 場合、かなりのコードを変更する必要があります。-> 可能だが醜い
  2. 関数自体 (または関数へのポインター) を引数として指定しますisConverged()Algorithm問題: 循環依存。
  3. isConverged()フレンド関数として宣言します。問題 (とりわけ): 異なるConvergenceCheckerのメンバー関数として定義できません。
  4. 関数ポインターの配列を使用します。問題をまったく解決しません。また、それらをどこで定義しますか?
  5. (この質問を書いているときにこれを思いつきました)データを保持する別のクラスを使用し、たとえばフレンドクラスとしてAlgorithmData持ってから、関数の引数として提供します。2. と同様ですが、循環依存の問題を回避することもできます。(これはまだテストしていません。)AlgorithmAlgorithmData

これに関する解決策 (および 5. で発生する問題) を教えていただければ幸いです。

その他の注意事項:

  • 質問のタイトル: 「強く依存するクラス」が、おそらくコードの設計で非常に間違ったことをしている可能性が高いとすでに言っていることは承知していますが、それでも多くの人がその問題を抱えてしまう可能性があり、その可能性を聞きたいと思っています。それは避けるので、その醜い表現を維持したいと思います。
  • 簡単すぎる?: 実は、ここで提示した問題は完全ではありませんでした。Algorithm相互に継承するコードには多くの異なる がありConvergenceChecker、新しい が登場した場合でも、 はもちろん、それ以上の変更を加えることなく、適切なケースで理想的に機能するはずですAlgorithm。これについても気軽にコメントしてください。
  • 質問スタイル: 質問が抽象的すぎず、特殊すぎず、長くなりすぎず、理解できるものであることを願っています。ですから、私がこの質問をする方法についてコメントすることを躊躇しないでください。
4

4 に答える 4

0

個別のデータ/状態構造を使用するのは簡単に思えます。読み取り専用アクセスの const 参照としてチェッカーに渡すだけです。

class Algorithm {
public:
  struct State {
    double foo_;
    double bar_;
    double foobar_;
  };
  struct ConvergenceChecker {
    virtual ~ConvergenceChecker();
    virtual bool isConverged(State const &) = 0;
  }
  void addChecker(std::unique_ptr<ConvergenceChecker>);
private:
  std::vector<std::unique_ptr<ConvergenceChecker>> checkers_;
  State state_;

  bool isConverged() {
    const State& csr = state_;
    return std::all_of(checkers_.begin(),
                       checkers_.end(),
                       [csr](std::unique_ptr<ConvergenceChecker> &cc) {
                         return cc->isConverged(csr);
                       });
  }
};
于 2013-08-20T09:16:11.440 に答える
0

おそらく、デコレータ パターンは、(未知の) 一連の収束チェックを簡素化するのに役立ちます。このようにして、どの収束チェックが発生する可能性があるかについてアルゴリズム自体に依存しないようにすることができ、すべてのチェックにコンテナーを必要としません。

あなたはこれらの行に沿って何かを得るでしょう:

class ConvergenceCheck {
private:
  ConvergenceCheck *check;
protected:
  ConvergenceCheck(ConvergenceCheck *check):check(check){}
public:
  bool converged() const{
    if(check && check->converged()) return true;
    return thisCheck();
  }
  virtual bool thisCheck() const=0;
  virtual ~ConvergenceCheck(){ delete check; }
};
            
struct Check1 : ConvergenceCheck {
public: 
  Check1(ConvergenceCheck* check):ConvergenceCheck(check) {}   
  bool thisCheck() const{ /* whatever logic you like */ }
};

ConvergenceCheck*次に、1 つのメンバーのみを に保持しながら、収束チェックの任意の複雑な組み合わせを作成できますAlgorithm。たとえば、2 つの条件をチェックする場合 (Check1および で実装Check2):

ConvergenceCheck *complex=new Check2(new Check1(nullptr));

コードは完全ではありませんが、アイデアは得られます。さらに、パフォーマンスの狂信者で、仮想関数呼び出し ( thisCheck) を恐れている場合は、不思議なことに返されるテンプレート パターンを適用して、それをなくすことができます。


の制約をチェックするデコレーターの完全な例を次に示しintます。これは、どのように機能するかを示しています。

#include <iostream>

class Check {
private:
  Check *check_;
protected:
    Check(Check *check):check_(check){}
public:
  bool check(int test) const{
    if(check_ && !check_->check(test)) return false;
    return thisCheck(test);
  }
  virtual bool thisCheck(int test) const=0;
  virtual ~Check(){ delete check_; }
};

class LessThan5 : public Check {
public: 
  LessThan5():Check(NULL){};
  LessThan5(Check* check):Check(check) {};
  bool thisCheck(int test) const{ return test < 5; }
};

class MoreThan3 : public Check{
public: 
  MoreThan3():Check(NULL){}
  MoreThan3(Check* check):Check(check) {}   
  bool thisCheck(int test) const{ return test > 3; }
};

int main(){

    Check *morethan3 = new MoreThan3();
    Check *lessthan5 = new LessThan5();
    Check *both = new LessThan5(new MoreThan3());
    std::cout << morethan3->check(3) << " " << morethan3->check(4) << " " << morethan3->check(5) << std::endl;
    std::cout << lessthan5->check(3) << " " << lessthan5->check(4) << " " << lessthan5->check(5) << std::endl;
    std::cout << both->check(3) << " " << both->check(4) << " " << both->check(5);
    
}

出力:

0 1 1

1 1 0

0 1 0

于 2013-08-20T09:28:52.287 に答える