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