Wzorce: NVI / metoda szablonowa

Czytając mądrą książeczką o której pisałem tutaj, przeczytałem fragment o wzorcu NVI – Non Virtual Interface (nazywanym także metodą szablonową – polskie wiki[słaby art], wersja ang.).
Zamierzam się trzymać tej pierwszej nazwy, ponieważ pod taką nazwą ja go poznałem.
Cała idea polega na tym, że udostępnia się publiczną nie wirtualną metodę, z której może korzystać klient naszej (i potomnych) klasy.
Implementacja tej metody polega na wywołaniu kolejnej metody, która wykonuje właściwe zadanie. Metoda wykonująca właściwe zadanie oznaczona jest jako niepubliczna i wirtualna.
Klasy pochodne mogą i w zasadzie powinny ją przeciążać na własny użytek. Przykładowy kod można zmodyfikować tak, aby metoda była czysto wirtualna, co automatycznie wymusi jej implementacje w klasach potomnych. Ja pozostanę przy przykładzie z książki.

Jaki jest z tego pożytek?
Udostępniając metodę publiczną nie wirtualną, definiujemy jedyny dostęp do funkcjonalności na której zależy użytkownikowi, uniemożliwiamy zmianę tego domyślnego zachowania w klasach pochodnych (przecież nie jest wirtualna). I co najważniejsze (IMO) wszelkie zmiany następują w klasie bazowej, wpływając na wszystkie klasy potomne. A więc bugfix, dodatkowe logowanie, try/catch wszystkiego tego dokonujemy tylko w jednym miejscu. Czy to nie śliczne?

Oczywista oczywistość, jeżeli ktoś będzie chciał nadpisać metodę to ją nadpisze. Wzorce nie są idioto odporne.

Przykładowy kod c++

#include  <iostream> 
using  namespace  std;

class  A
{
public :
 void  DoWork()
 {
  //I can add some code here to bug fix something 
  DoCoreWork();
  //also I can add some log/comment here - all changes will affect me and my inheritace 
 }

private :
 virtual  void  DoCoreWork()
 {
  cout<<"Actually doing some work here!n" ;
 }
};

class  B : public  A
{
 virtual  void  DoCoreWork()
 {
  cout<<"Actually I refuse to workn" ;
 }
};

int  _tmain(int  argc, _TCHAR* argv[])
{
 A* a1 = new  A();
 A* a2 = new  B();

 a1->DoWork();
 a2->DoWork();

 return  0;
}

Oraz przykład dla c#

1: using  System;
2: namespace  Blog
3: {
4:     public  class  A 
5:     {
6:         public  void  DoWork()
7:         {
8:             //The same situation as befere. All fixes and updates can be put here 
9:             CoreDoWork();
10:             //Or here it depends on the case. 
11:         }
12:         protected  virtual  void  CoreDoWork()
13:         {
14:             Console .WriteLine("Actually doing some work" );
15:         }
16:     }
17:     public  class  B  : A 
18:     {
19:         protected  override  void  CoreDoWork()
20:         {
21:             Console .WriteLine("Refuse to work at all" );
22:         }
23:     }
24:     public  class  Program 
25:     {
26:         static  void  Main()
27:         {
28:             A  a1 = new  A ();
29:             A  a2 = new  B ();
30:             a1.DoWork();
31:             a2.DoWork();
32:         }
33:     }
34: }
35: 

Na koniec dodam, że wszelkie podobieństwo kodu do tego w książce nie jest przypadkowe, zostało on z niej zaczerpnięty.

Co o tym sądzicie? Wykorzystacie to kiedyś? Uważacie że jest do bani? Gdzieś się pomyliłem? Macie inne zdanie? Jak zawsze można śmiało komentować.

JS

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.