Supponiamo che sto scrivendo un codice C ++ per visualizzare oggetti "Foo". Ho due modi per ottenere un "Foo": calcolarlo dai dati, o prendere i pezzi di un "Foo" precompilato e costruire un nuovo "Foo".
Ora, una volta che viene calcolato un "Foo", è garantito che sia buono per la visualizzazione, ma cambiarlo può rompere questa ipotesi. Pertanto, ho deciso di rappresentare "Foos" nel mio codice da una classe Foo
che non ha metodi di muting: una volta costruita e inizializzata, non cambia.
Ma c'è un secondo modo per creare un "Foo": costruirlo da componenti di "Foo" precalcolati. Ho trovato diversi metodi per creare un Foo
dai dati precompilati:
Metodo 1: metodi Costruttore / Statici
Forse il metodo più ovvio sarebbe aggiungere un nuovo costruttore o un metodo statico a Foo
, chiamarlo fromPrecomputed
, che leggerà i componenti del Foo precompilato e creerà un nuovo oggetto Foo
, controllando che è valido Per spiegare perché mi piacerebbe evitarlo, devo complicare il mio esempio: diciamo che un componente di "Foo" è una raccolta di "Bars". Ora, in termini di implementazione, a volte una "Barra" è rappresentata come std::vector<std::vector<Bar> >
, a volte come Bar array[][2]
, a volte come std::vector<std::pair<Bar,Bar> >
, e così via ... Potrei avere l'utente di riorganizzare i loro dati in un modulo standardizzato e un singolo costruttore per questo standard, ma questo potrebbe richiedere all'utente di eseguire una copia extra. Non voglio fornire un metodo statico per ogni formato: readPrecomputedFormatA
, readPrecomputedFormatB
e così via: questo ingombra l'API.
Metodo 2: Crea Foo
mutabile
Se esponessi il metodo addBar(Bar)
di Foo
, potrei consentire all'utente di scorrere la loro raccolta di "Bar" a modo loro. Questo, tuttavia, rende Foo
mutabile. Quindi ho potuto calcolare un Foo
che abbia senso per la visualizzazione, quindi usare addBar
per aggiungere un Bar
che rende Foo
non più un "Foo". Non va bene.
Metodo 3: Crea una classe "builder" di un amico
Creo una classe chiamata FooBuilder
che ha il metodo addBar(Bar)
esposto. Rendo FooBuilder
un amico di Foo
e aggiungo un costruttore a Foo
che prende un FooBuilder
. Chiamando questo costruttore, controlla che FooBuilder
contenga un oggetto "Foo" valido, quindi scambia la sua rappresentazione vuota di Foo con ciò che è all'interno di FooBuilder
. Tutti sono felici.
L'unica "confusione" sul metodo n. 3 è che richiede un'amicizia, ma credo valga la pena mantenere l'incapsulamento. Ma questo mi ha fatto pensare: è un modello stabilito? O c'è un altro, migliore modo di farlo che non conosco?