NVIパターンで重要なのは、privateな仮想関数があって、それがpublicな非仮想関数によって呼ばれることだ。The essence of the non-virtual interface pattern is that you have private virtual functions, which are called by public non-virtual functions (the non-virtual interface).
[hr]
この利益は 派生したクラスが基底クラスのinterfaceのどの部分でもoverrideできるかのように、基底classがその振る舞いをより強く制御する ということだ(?) 。The advantage of this is that the base class has more control over its behaviour than it would if derived classes were able to override any part of its interface.
In other words, the base class (the interface) can provide more guarantees about the functionality it provides.
言い換えれば基底クラス(そのinterface)は インターフェイスが与える機能性についてのより強い保証を与えることが出来る(?)
要するに基底側でctorやdtor、init/final、new/delete、malloc/free様の、ある処理群の前後どちらかあるいは両方に共通の処理を行うことを基底クラスが制御できる。だからpublicな仮想関数よりも強い制限ができる。ということかな。
[hr]
簡単な例、animalクラスとそれから派生した幾つかのクラスを考えよう:As a simple example, consider the good old animal class with a couple of typical derived classes:
コードの引用省略
[hr]
この普通のpublicでvertualなinterfaceの使い方は、お馴染みのものだ。しかし幾つかの問題がある。This uses the usual public virtual interface that we're used to, but it has a couple of problems:
- Each derived animal is repeating code -- the only part that changes is the string, yet each derived class needs the whole std::cout << ... << std::endl; boilerplate code.
- The base class can't make guarantees about what speak() does. A derived class may forget the new line, or write it to cerr or anything for that matter.
- それぞれの派生したクラスで同じコードを繰り返している。つまり、変更点は文字列だけだが、それぞれの派生クラスにはstd::cout<<...<<std::endl;という共通のコードが必要になる。
- そのクラスはspeak関数がするだろうことについて保証しない。派生したクラスでは改行を忘れたり(coutではなく)cerrで書いたり、そういう何かおかしなことをするかもしれない。
挙げた問題を解決するため、多態的な振る舞いを許すprivateな仮想関数によって補足/追加される非仮想関数のパターンを使える。To fix this, you can use a non-virtual interface that is supplemented by a private virtual function that allows polymorphic behaviour:
この基底クラスは coutで書き出されるだろうこと、改行を文字列の最後に伴うだろうこと を保証する。さらに派生クラスで同じようなコードを繰り返し記述する必要が無いように、メンテナンスも楽になる。Now the base class can guarantee that it will write out to std::cout and end with a new line. It also makes maintenance easier as derived classes don't need to repeat that code.
[hr]
これ訳してて思ったけれど、これって共通部分を委譲にしてインターフェイスでラップしたら似たようなことできる・・・? 何気に仮想関数がprivateでも良いらしいことに驚く。でも親の仮想関数を呼ばないのなら要らないよね。基底クラスの仮想関数はoverrideする派生クラスには見えてないといけないと思ってたからprotectedにしてた。