PER Rendere il tuo codice liberamente abbinato ecco alcune semplici cose da ricordare:
Parte 1:
Tecnicamente noto come "Separation of Concern". Ogni classe ha un ruolo specifico, dovrebbe gestire la logica aziendale o la logica dell'applicazione. Cerca di evitare le lezioni che combinano entrambe le responsabilità. Ad esempio, una classe che gestisce (a lungo termine) i dati è una logica applicativa mentre una classe che utilizza i dati è una logica aziendale.
Personalmente mi riferisco a questo (nel mio piccolo mondo) come create it or use it
. Una classe dovrebbe creare un oggetto o usare un oggetto che non dovrebbe mai fare entrambi.
Parte 2:
Come implementare la separazione delle preoccupazioni.
Come punto di partenza ci sono due semplici tecniche:
Nota: gli schemi di progettazione non sono assoluti.
Dovrebbero essere personalizzati per la situazione, ma hanno un tema di fondo simile a tutte le applicazioni. Quindi non guardare gli esempi qui sotto e dire che devo seguire questo rigidamente; questi sono solo esempi (e leggermente inventati).
Iniezione di dipendenza :
Qui è dove si passa un oggetto utilizzato da una classe. L'oggetto che passi in base a un'interfaccia in modo che la tua classe sappia cosa fare con esso ma non ha bisogno di conoscere l'effettiva implementazione.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Qui iniettiamo il flusso in un Tokenizer. Il tokenizer non sa di che tipo è il flusso fintanto che implementa l'interfaccia di std :: istream.
Modello localizzatore di servizio :
Il modello di localizzazione del servizio è una leggera variazione dell'iniezione di dipendenza. Piuttosto che dare un oggetto che può usare, tu passi un oggetto che sa come individuare (creare) l'oggetto che vuoi usare.
class Application
{
public:
Application(Persister& p)
: persistor(p)
{}
void save()
{
std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
saveDialog.DoSaveAction();
}
void load()
{
std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
loadDialog.DoLoadAction();
}
private:
Persister& persistor;
};
Qui passiamo l'oggetto dell'applicazione a un oggetto resistore. Quando si esegue un'azione di salvataggio / caricamento, utilizza la persistenza per creare un oggetto che sappia effettivamente come eseguire l'azione. Nota: di nuovo il persistenza è un'interfaccia e puoi fornire diverse implementazioni a seconda della situazione.
È utile quando è richiesto un oggetto unico potentially
ogni volta che istanziate un'azione.
Personalmente ritengo che questo sia particolarmente utile nella scrittura dei test unitari.
Nota dei pattern:
I modelli di design sono un argomento enorme per se stesso. Questo non è affatto un elenco esclusivo di schemi che puoi usare per aiutare con l'accoppiamento libero; questo è solo un punto di partenza comune.
Con l'esperienza ti renderai conto che stai già usando questi modelli è solo che non hai usato i loro nomi formali. Standardizzando i loro nomi (e facendo in modo che tutti li imparino) scopriamo che è facile e più veloce comunicare idee.