Se davvero non vuoi scoprire dettagli sulle operazioni "privilegiate", la risposta più pulita nei linguaggi OO popolari come Java e .NET è l'ereditarietà dell'interfaccia.
Uno che ho usato semi-frequentemente fa qualcosa del tipo:
-
IFooRepository
- contiene metodi GetFoo
, DeleteFoo
, SaveFoo
-
IReadOnlyFooRepository
- contiene solo il metodo GetFoo
Nell'esempio sopra, IFooRepository
potrebbe sottoclasse IReadOnlyFooRepository
. Inoltre, se esiste un modo ben definito di transizione tra lo stato di sola lettura e quello scrivibile, è possibile inserire un metodo Edit
in IReadOnlyFooRepository
che restituisce un'istanza di IFooRepository
e / o un AsReadOnly
metodo su IFooRepository
che restituisce un'istanza di IReadOnlyFooRepository
.
Questo non è senza precedenti, almeno in .NET. Una delle interfacce più comuni, IList<T>
, ha un metodo AsReadOnly
che restituisce un ReadOnlyCollection<T>
, che come puoi immaginare, non espone alcun tipo di operazioni di scrittura.
Non è difficile da implementare - in genere hai bisogno di una sola classe per implementare entrambe le interfacce, e i metodi Edit
/ AsReadOnly
possono solo restituire this
(o self
o qualunque cosa sia nella lingua scelta) . Naturalmente, se lo fai, non pensare a te stesso che è una forma di sicurezza , dato che un chiamante potrebbe potenzialmente lanciare solo tra i tipi - ma assumendo che tu voglia solo proteggere da errori stupidi piuttosto e non l'abuso intenzionale, è abbastanza buono.
Puoi ottenere una maggiore quantità di dettagli se vuoi, ad esempio, non hai necessariamente bisogno di un'interfaccia per derivare dall'altra, potresti avere IFooReader
e IFooWriter
senza metodi in comune. Oppure potresti definire un IFooComponent
(o qualcosa del genere) nella radice se vuoi condividere solo alcuni metodi, ma la maggior parte di essi è univoca. Sky è il limite, ma l'idea di base sta in piedi: usa le interfacce per ritagliare specifici blocchi di funzionalità specifici per uno stato particolare.
Questo richiede molto più lavoro di progettazione e aggiunge molti altri tipi al codice se i tuoi stati e le transizioni sono complessi, quindi assicurati che ne valga davvero la pena. È perfettamente funzionante e in effetti comune in molte architetture (come la maggior parte dei framework MVVM) per generare un'eccezione solo se un'operazione non è valida per lo stato corrente e fornisce anche metodi di "guardia" convenienti come CanWrite
, per evitare spargendo cerchi try-catch dappertutto. Va bene se hai 2 o 3 stati discreti; se hai 10 o 20, potresti pensare a uno schema di stato oa un progetto come quello sopra che rende le transizioni più esplicite.