2

他の開発者が使用する API の一部として抽象基本クラスを定義しています。これは、Android API を定義し、開発者がそれを使用して電話アプリを作成できるようにするようなものです。API の作成者 (それは私です) はいつ特定の機能を提供する必要があり、いつ作成者は開発者 (つまり API のユーザー) にその機能の定義を任せるべきでしょうか?

関連する例を次に示します。たとえば、 MyObjの一般的なツリーを定義しているとしましょう(ここで、ユーザーはMyObjから派生し、子 (親) を持つクラスを作成でき、型と値によってフィルター処理できます。

class MyObj
{
 public:
  // These must be provided
  MyObj const * parent();
  bool setParent(MyObj const * i_obj);

  std::vector<MyObj const * > children() const;
  bool addChild(MyObj const * i_obj);
  bool removeChild(MyObj const * i_obj);


  // The following functions can be implemented
  // by using the functions listed above.
  // Should I provide them (by leaving them in the class or
  // defining them as utility functions, etc), or should I let 
  // the developers define them?
  MyObj const * root() const;
  bool hasChild(MyObj const * i_obj) const;
  std::vector<MyObj const * > descendants() const;
  std::vector<MyObj const * > childrenFiltered(FilterType i_type, FilterValue i_val) const;
  std::vector<MyObj const * > descendantsFiltered(FilterType i_type, FilterValue i_val) const;

 private:
  std::vector<MyObj * > m_children;
  MyObj * m_parent;
};

考慮事項:

  • 開発者 (API のユーザー)がその機能を提供できないようなプライベート変数の使用が含まれる場合は、私がそれを提供する必要があります。
  • 多くの開発者 (API のユーザー) が使用する機能がある場合は、自分で定義します。そうすれば、開発者 A は、開発者 B が作成する非常に便利なユーティリティ関数の同じセットを作成する必要がなくなります。どちらも私が提供するものを使用します。
    • 一方で、基本クラスがどのように使用されるかを予測しようとするのではなく、開発者がやりたいことを何でもできるようにするために必要な最小限の機能を提供するのが最善かもしれません。
  • 機能がコア機能である場合は、それを提供します (他のパブリック関数のみを使用して実装できる場合でも)。
    • 何かがコア機能であるかどうかを判断することは、かなりあいまいなビジネスのようです

関連記事:

4

1 に答える 1

1

ライブラリの設計はかなり複雑な作業であり、さまざまな考慮事項があります。このトピックに関する私が持っているメモに基づいて、おそらくこれで本を埋めることができます. 上記の質問は個々のクラスに集中しているようで、これがこの回答の内容です。クラスやアルゴリズムのシステムがある場合など、物事がより複雑になると、何がどこに行くかを決定する上でより複雑なレイヤーが現れます。

クラスの場合、基本的なルールは実際には比較的単純です。

  1. クラスは十分でなければなりません。つまり、抽象化の基本的な部分であり、クラスの内部整合性を維持するために必要なすべてが必要です。一度構築されたオブジェクトは、破壊されるまで有効な状態のままです (ただし、メンバー関数は基本的な例外保証のみをサポートするという例外があります)。オブジェクトを破壊可能な状態に変える可能性があります; これらのいずれも持たないように努力する必要があります)。
  2. クラスは効率的でなければなりません。つまり、公開されたインターフェースでは一般的なユースケースを効率的に実装できないが、クラスの内部構造を使用することでこれがサポートされる場合は、対応する機能を提供することをお勧めします。たとえば、公開されたイテレータstd::list<T>::sort()などで使用するよりも効率的です。std::merge_sort()
  3. クラスは最小限にする必要があります。「いい」ので、オブジェクトを大きくするものは含めないでください。
  4. 必要に応じて、インターフェイスを少し良くする基本的な転送機能を提供したい場合があります。たとえば、シーケンスでは、push_back()ユーザーに のようなものを使用するよう要求する代わりに、関数が必要になる場合がありますcont.insert(value, cont.end())。書くのは簡単ではないが、インターフェイスで実行できるものはすべて、インターフェイスの一部ではありません

ある程度までは、これはかなり必要最小限のクラスをもたらします。ユーザーは、それを使用して独自のアルゴリズムを作成できます。多くの重複を避けるために、おそらくクラスの抽象化の観点から実装されているアルゴリズムも提供することをお勧めします。たとえば、クラスがノードのすべての子に対して動作するツリーを表す場合、すべての種類のツリーで動作する必要があります。たとえば、std::find()すべてのシーケンスで機能します。コンテナに追加することは、コンテナのキータイプを検索するときの連想コンテナの場合のように、コンテナが大幅に改善できる場合にのみ意味があります。他に何かを見つける必要がある場合std::find()これらについてもあなたが望むものです。ただし、適切なアルゴリズム ライブラリを作成し、適切な抽象化を考え出すことは、適切なクラスを作成するよりもはるかに難しい作業です。

ところで、上記の「インターフェース」では、代替ライブラリを検索するように指示されます。何もない場合は、自分で作成することが代替案であるかどうかを真剣に検討します. 特定のコンテナーまたはポインターを公開するインターフェースを備えた C++ ライブラリーは、使用を検討する前にかなりの価値を追加する必要があります。

于 2012-01-20T21:07:27.397 に答える