2015-06-02 22 views
6

Sto cercando di capire il reale requisito dell'utilizzo di modelli per la progettazione basata su criteri. Passando attraverso i nuovi modelli basati su modelli in C++ ho scoperto che il design di classi basato su policy è un modo di progettazione altamente consigliato che consente di "plug-in" comportamenti diversi da classi di policy. Un esempio minimo è la seguente (una versione ridotta del wiki):Quando preferire la struttura basata su criteri basata su modelli basati su ereditarietà non basata su modelli

template <typename LanguagePolicy> 
class HelloWorld : private LanguagePolicy 
{ 
    using LanguagePolicy::message; 

public: 
    // Behaviour method 
    void run() const 
    { 
     // policy methods 
     cout << message(); 
    } 
}; 

class LanguagePolicyA 
{ 
protected: 
    std::string message() const 
    { 
     return "Hello, World!"; 
    } 
}; 
//usage 
HelloWorld<LanguagePolicyA> hello_worlda; 
hello_worlda.run(); // prints "Hello, World!" 

Una rapida analisi mostra che solo per ottenere diversi metodi plugable message() vengono ereditati da un tipo template cui definizione può essere fornita da chiunque (e identificato al momento della compilazione).

Tuttavia, lo stesso livello di astrazione (e metodi configurabili) può essere ottenuto senza l'utilizzo di un codice basato su modelli e del semplice polimorfismo di run time della vecchia scuola, come illustrato di seguito.

class HelloWorld 
{ 
    LanguagePolicy *lp; //list of all plugable class 
public: 
    HelloWorld(LanguagePolicy *lpn) { 
     lp = lpn; 
    } 

    // Behaviour method 
    void run() const 
    { 
     // policy methods 
     cout << lp->message(); 
    } 
}; 
class LanguagePolicy 
{ 
protected: 
    virtual std::string message() const; 
}; 

class LanguagePolicyA: LanguagePolicy 
{ 
protected: 
    std::string message() const 
    { 
     return "Hello, World!"; 
    } 
}; 
//usage 
HelloWorld helloworld(new LanguagePolicyA); 
helloworld.run(); 

funzionalità ed il livello di astrazione saggio non vedo molta differenza nelle due approccio (anche se il secondo approccio ha poche righe in più di codice per LanguagePolicy, penso che è necessario per gli altri utenti per conoscere l'interfaccia, altrimenti la comprensione di LanguagePolicy dipende dalla documentazione). Ma penso che più tardi sia "pulito" (proveniente da qualcuno che non ha usato molto il modello). Questo perché personalmente secondo me le classi non basate su modelli sono più pulite da guardare e capire. Un esempio estremamente valido è la famosa libreria VTK (Visualization Tool Kit) che risolve molti problemi diversi utilizzando il secondo approccio. Anche se non ci sono ampie documentazioni su VTK, molti di noi - i suoi utenti, possono solo dare un'occhiata ai suoi diagrammi di classe (a volte sono piuttosto grandi) e dedurre comportamenti di classi; e sviluppare pipeline altamente configurabili e complicate nella nostra applicazione (non è possibile eseguire l'imaging di VTK come modello :)). L'opposto è librerie come STL/BOOST che non credo sia possibile per chiunque sia in grado di identificare il funzionamento delle classi senza l'uso di un'ampia documentazione.

Quindi la mia domanda è, il modello di progettazione basato su modelli è davvero superiore (solo in questo scenario di progettazione basata su criteri) rispetto all'ereditarietà virtuale basata? Se è così, quando e perché?

risposta

1

Entrambi sono validi modi di strutturazione, in realtà dipende dai requisiti. Per esempio.

Polimorfismo tempo di esecuzione vs tempo di compilazione.

Quando si desidera/può/deve raggiungere il polimorfismo?

prestazioni sovraccarico di chiamate virtuali

Modelli generare il codice che non ha indirections

L'utilizzo effettivo della classe.

Quando è necessario memorizzare raccolte eterogenee, è necessaria una classe base, quindi è necessario utilizzare l'ereditarietà.

Un ottimo libro sulla progettazione basata su regole (un po 'datato ma buona comunque) è Modern C++ Design

+0

-> Il modello 'simula' il polimorfismo del tempo di esecuzione generando le classi in fase di compilazione. Quindi, perché non utilizzare direttamente il runtime? Perché ci importa? -> Darò un'occhiata, ma quanto costa veramente il sovraccarico sulle chiamate virtuali? Sono abbastanza significativi da causare un effetto? – krips89

+0

@ krips89 Il secondo punto. Esiste un sovraccarico delle prestazioni con le chiamate virtuali, che può essere importante in alcuni casi. –

+0

@ krips89 Dipende, ma se si prende in considerazione l'indirezione, il pasticcio con le linee della cache, la possibilità che una funzione basata su modelli sia in linea, ci può essere un vantaggio dall'uso delle politiche. –

1

dipende dalla situazione immagino ...Un possibile svantaggio di utilizzare i modelli è che il tipo dovrebbe essere noto al momento della compilazione:

HelloWorld<English> hw; // English is plugged at compile-time 

Nel tuo secondo esempio, dove si sta utilizzando una base di puntatore-a-, questo puntatore può puntare a una varietà di classi derivate. A cosa esattamente punta non è richiesto di essere conosciuto in fase di compilazione e può quindi essere determinato dall'input (utente) in fase di runtime. Un possibile lato negativo di questo approccio è l'overhead delle chiamate virtuali. In alcune applicazioni e su alcune piattaforme, questo potrebbe essere indesiderato.

+0

Sì; per quel che ho capito fino ad ora, l'unico problema che emerge nel secondo approccio è il "sovraccarico delle chiamate virtuali". Se guardo puramente nella prospettiva di progettazione (senza preoccuparmi del sovraccarico di implementazione) il secondo approccio risulta essere superiore. – krips89

+0

Questo è l'unico lato negativo che riesco a pensare in questo momento. Inoltre, basi astratte aiutano a forzare un'interfaccia su classi derivate, il che è bello. Il controllo delle interfacce statico sarà probabilmente disponibile in C++ 17 quando i concetti vengono aggiunti alla lingua (fantastico). – JorenHeit

Problemi correlati