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.