Non-virtual interface design pattern question in C#/C++の個人的な訳

zxc
記事: 79
登録日時: 13年前
住所: 日本の背骨(?)あたり

Non-virtual interface design pattern question in C#/C++の個人的な訳

投稿記事 by zxc » 10年前

C#/C++におけるNVIパターンの疑問より引用、訳は雑。
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).
NVIパターンで重要なのは、privateな仮想関数があって、それがpublicな非仮想関数によって呼ばれることだ。

[hr]
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のどの部分でもoverrideできるかのように、基底classがその振る舞いをより強く制御する ということだ(?) 。
言い換えれば基底クラス(そのinterface)は インターフェイスが与える機能性についてのより強い保証を与えることが出来る(?)
 要するに基底側でctorやdtor、init/final、new/delete、malloc/free様の、ある処理群の前後どちらかあるいは両方に共通の処理を行うことを基底クラスが制御できる。だからpublicな仮想関数よりも強い制限ができる。ということかな。

[hr]
As a simple example, consider the good old animal class with a couple of typical derived classes:
簡単な例、animalクラスとそれから派生した幾つかのクラスを考えよう:
 コードの引用省略

[hr]
This uses the usual public virtual interface that we're used to, but it has a couple of problems:
この普通のpublicでvertualなinterfaceの使い方は、お馴染みのものだ。しかし幾つかの問題がある。
  1. 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.
  2. 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.
  1. それぞれの派生したクラスで同じコードを繰り返している。つまり、変更点は文字列だけだが、それぞれの派生クラスにはstd::cout<<...<<std::endl;という共通のコードが必要になる。
  2. そのクラスはspeak関数がするだろうことについて保証しない。派生したクラスでは改行を忘れたり(coutではなく)cerrで書いたり、そういう何かおかしなことをするかもしれない。
[hr]
To fix this, you can use a non-virtual interface that is supplemented by a private virtual function that allows polymorphic behaviour:
 挙げた問題を解決するため、多態的な振る舞いを許すprivateな仮想関数によって補足/追加される非仮想関数のパターンを使える。
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.
この基底クラスは coutで書き出されるだろうこと、改行を文字列の最後に伴うだろうこと を保証する。さらに派生クラスで同じようなコードを繰り返し記述する必要が無いように、メンテナンスも楽になる。

[hr]

 これ訳してて思ったけれど、これって共通部分を委譲にしてインターフェイスでラップしたら似たようなことできる・・・? 何気に仮想関数がprivateでも良いらしいことに驚く。でも親の仮想関数を呼ばないのなら要らないよね。基底クラスの仮想関数はoverrideする派生クラスには見えてないといけないと思ってたからprotectedにしてた。

アバター
usao
記事: 1889
登録日時: 12年前

Re: Non-virtual interface design pattern question in C#/C++の個人的な訳

投稿記事 by usao » 10年前

Template Method と同じもの?


>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.

このパターンの有利な点というのは,
派生クラスがそのインタフェースのどの部分も上書きできちゃう場合 よりも
基底クラスがその振る舞いをより(詳細に)コントロールできる
ことである.

…みたいな感じ……かな?(全然自信ない)

public : virtual void Do(){ ... }
とかだと派生クラスはDo()の処理内容を好き勝手にオーバーライドできる(そのことを基底クラス側はどうにもできない)けど
このパターンはDo()の一部だけをオーバーライドさせる(その他の箇所は基底クラスの手の内にある)ということを
"more control over its behaviour" と.

zxc
記事: 79
登録日時: 13年前
住所: 日本の背骨(?)あたり

Re: Non-virtual interface design pattern question in C#/C++の個人的な訳

投稿記事 by zxc » 10年前

 その訳の方が自分のものよりもすっきりしていますね。

アバター
GRAM
記事: 164
登録日時: 14年前

Re: Non-virtual interface design pattern question in C#/C++の個人的な訳

投稿記事 by GRAM » 10年前

publicな関数がインターフェースを提供して、
privateな仮想関数で実装するというだけでは?
C++ Coding Standards―101のルールって本でこのやり方が提案されてますけど、
ぶっちゃけ哲学の領域だとは思う。
実用上は前処理や後処理を必ずやらないといけない場合には、自分は比較的使うやり方ですかね?

ISLe
記事: 2650
登録日時: 14年前

Re: Non-virtual interface design pattern question in C#/C++の個人的な訳

投稿記事 by ISLe » 10年前

この手の話は、コーディング自体の効率を上げるものではなくて
企業など不特定多数が関わる現場で不特定多数が利用するコードによる事故を発生しにくくするものかと思います。

事故が起きなければ無駄と考えるかたもいるかもしれませんが、いったん事故が起きると進捗に甚大な被害が出ることになります。
哲学というより安全神話的な話じゃないでしょうかね。