2

多数のトランザクションの合計に基づいて貸借対照表を生成し、結果を次のような形式で表示できるプログラムをセットアップしようとしています。

バランスシート ここで重要な属性は、最上位の口座 (資産など) がサブ口座のツリーに分解され、最下位の口座 (「葉」) のみが独自の残高を追跡することです (上位レベルの口座の残高は、サブアカウントの残高の合計のみ)。

私の頼りになるアプローチは、継承を使用することです。

class Account{
   string name;
   virtual int getBalance() =0; //generic base class has no implementation
   virtual void addToBalance(int amount) =0;
};
class ParentAccount : public Account{
   vector<Account*> children;
   virtual int getBalance() {
      int result = 0;
      for (int i = 0; i < children.size(); i++)
          result += children[i]->getBalance();
      return result;
   }
   virtual void addToBalance(int amount) {
      cout << "Error: Cannot modify balance of a parent account" << endl;
      exit(1);
   }
};
class ChildAccount : public Account{
   int balance;
   virtual int getBalance() { return balance; }
   virtual void addToBalance(int amount) {balance += amount;}
};

どのアカウントが存在するかはコンパイル時にはわからないため、ツリー アカウントを動的に生成する必要があります。継承は、任意の深さのツリー構造を簡単に生成できるため (ParentAccounts は ParentAccounts である子を持つことができます)、getBalance()再帰を使用するような関数を簡単に実装できるため、ここで役立ちます。

バランスの変更など、派生クラスに固有の機能を取り込もうとすると、少し厄介になります (バランスはその子のバランスによって定義されるためChildAccount、これはオブジェクトに対してのみ可能です)。ParentAccount私の計画は、次のような関数がprocessTransaction(string accountName, int amount)ツリー構造を検索して正しい名前のアカウントを探し、そのアカウントを呼び出すaddToBalance(amount)ことです (* 以下の注)。上記のツリー構造では を見つけることしかできないため、上記で行ったようにすべてのクラスに対して実装するか、を呼び出す前にAccount*を実装する必要があります。最初のオプションはもう少しエレガントに見えますが、定義する必要があるという事実addToBalance(amount)dynamic_castAccount*ChildAccount*addToBalance()ParentAccount::addToBalance()(エラーではありますが)私にはちょっと奇妙に思えます。

私の質問は次のとおりです。このぎこちなさの名前とそれを解決するための標準的なアプローチはありますか、それとも継承を完全に誤って適用しているだけですか?

*注:検索用にアカウントを整理するもっと効率的な方法があることは認識していますが、私の主な目的は、直感的に解釈してデバッグできるプログラムを作成することです。私の現在の理解レベルに基づくと、これには計算効率が犠牲になります (この場合は少なくとも)。

4

3 に答える 3

1

はい、あなたは正しい継承のケースではないことを正しく推測しました。

virtual void addToBalance(int amount) {
   cout << "Error: Cannot modify balance of a parent account" << endl;
   exit(1);
}

それが間違っていることを明確に示してclass ParentAccount : public Accountいます。ParentAccount は Account と IS-A 関係を持っていません。

これを修正するには 2 つの方法があります。1 つは継承を解除することParentAccountです。しかし、getBalance()一貫性は、それが過剰反応である可能性があることを示しています. したがって、 (および)addToBalance()から除外するだけで、階層が正しくなります。AccountParentAccount

もちろん、これは をChildAccount呼び出す前にポインターを取得する必要があることを意味しますaddToBalance()が、とにかく取得する必要があります。実用的な解決策は多数あります。たとえば、単純に に 2 つのベクトルを配置しParentAccount、1 つは別の ParentAccounts 用、もう 1 つは ChildAccounts 用、またはdynamic_cast, または ... を使用することができます (アカウントで他に何をする必要があるかによって異なります)。

このぎこちなさの名前は、LSP (リスコフ置換原理) の破綻、またはより単純に、IS-A 関係の破綻です。

于 2013-04-19T07:45:30.377 に答える
0

概念的には、子アカウントと親アカウントはありませんが、アカウントとオブジェクトのツリーがあり、そのリーフ ノードには実際のアカウントへのポインターが含まれています。

この構造をコードで直接表現することをお勧めします。

class Account
{
public:
    int getBalance(); 
    void addToBalance(int amount);
// privates and implementation not shown for brevity
};


class TreeNode
{
public:
    // contains account instance on leaf nodes, and nullptr otherwise.
    Account* getAccount(); 

    // tree node members for iteration over children, adding/removing children etc

private:
    Account* _account; 
    SomeContainer _children
};

ツリーをたどって口座残高などを収集したい場合は、ツリー構造上で直接行うことができます。これは、親アカウントを介してルートを取得するよりも単純で混乱が少ないです。さらに、実際のアカウントとそれらを含むツリー構造が別のものであることは明らかです。

于 2013-04-19T08:35:16.177 に答える