インターフェイスを設計するとき、誰かが非仮想インターフェイスパターンを使用することを推奨しました。誰かがこのパターンの利点が何であるかを簡単に概説できますか?
2 に答える
非仮想インターフェースパターンの本質は、パブリック非仮想関数(非仮想インターフェース)によって呼び出されるプライベート仮想関数があることです。
これの利点は、派生クラスがそのインターフェースの任意の部分をオーバーライドできる場合よりも、基本クラスがその動作をより詳細に制御できることです。言い換えると、基本クラス(インターフェース)は、それが提供する機能についてより多くの保証を提供できます。
簡単な例として、いくつかの典型的な派生クラスを持つ古き良き動物クラスを考えてみましょう。
class Animal
{
public:
virtual void speak() const = 0;
};
class Dog : public Animal
{
public:
void speak() const { std::cout << "Woof!" << std::endl; }
};
class Cat : public Animal
{
public:
void speak() const { std::cout << "Meow!" << std::endl; }
};
これは、私たちが慣れている通常のパブリック仮想インターフェイスを使用しますが、いくつかの問題があります。
- 各派生動物はコードを繰り返しています。変更されるのは文字列だけですが、各派生クラスには
std::cout << ... << std::endl;
ボイラープレートコード全体が必要です。 - 基本クラスは、何をするかについて保証することはできません
speak()
。派生クラスは、新しい行を忘れたりcerr
、そのことについて何かに書き込んだりする可能性があります。
これを修正するには、ポリモーフィックな動作を可能にするプライベート仮想関数によって補完される非仮想インターフェイスを使用できます。
class Animal
{
public:
void speak() const { std::cout << getSound() << std::endl; }
private:
virtual std::string getSound() const = 0;
};
class Dog : public Animal
{
private:
std::string getSound() const { return "Woof!"; }
};
class Cat : public Animal
{
private:
std::string getSound() const { return "Meow!"; }
};
std::cout
これで、基本クラスは、新しい行に書き出して終了することを保証できます。また、派生クラスはそのコードを繰り返す必要がないため、メンテナンスが容易になります。
Herb Sutterは、非仮想インターフェイスに関する優れた記事を書いたので、チェックすることをお勧めします。
これはwikiの記事で、いくつかの例を使ってもう少し詳しく説明しています。本質は、基本クラスの中心的な場所で重要な条件(ロックの取得や解放など)を確保しながら、プライベートまたは保護された仮想関数を使用してさまざまな実装を提供することを可能にすることです。
クラス階層の任意のクラスのユーザーは、外部からは見えない実装に呼び出しをディスパッチするパブリックインターフェイスを常に呼び出します。