Ritorno a questa domanda ogni due anni, quindi ora ho deciso di risolverlo una volta per tutte, chiedendo qui.
Quindi, la sequenza:
- Sto scrivendo una semplice applicazione che analizza il file Json (configurazione per app di terze parti, ulteriormente denominata config ) e consente all'utente di apportare alcune modifiche con esso. Ho scelto una piattaforma, ho creato un'interfaccia utente di base, ho associato alcuni pulsanti a NewFileCommand, OpenFileCommand, SaveFileCommand, ecc.
- Ora ho bisogno di memorizzare da qualche parte la configurazione aperta (e deserializzata in una classe), così ho creato una nuova classe StorageService. Ha alcuni metodi come OpenFile (), ReloadFile (), SaveFile (), ecc. Vengono richiamati dal ViewModel, StorageService apre il file, legge la configurazione e lo memorizza come campo . Quando necessario, lo salva in un file, finora così buono.
- In questo momento, voglio aggiungere alcune manipolazioni con la configurazione stessa. Io davvero non voglio metterlo nello StorageService. Alcuni metodi come GetConfigPropertyByNameAndDoSomeMagicWithIt () devono essere sicuramente collocati su un altro servizio.
- Quindi, ho creato un altro servizio, OperationsService. Ora voglio che abbia un accesso alla configurazione, memorizzato in StorageService e aggiungere metodi GetConfigPropertyByNameAndDoSomeMagicWithIt (). Non voglio esporre StorageService.config nell'API pubblica, perché ho già tutti gli accessor necessari. Per tutti gli altri, ad eccezione di OperationsService. Che voglio praticamente fare quello, chi può accedere a StorageService.config. Sembra un buon caso d'uso per il vecchio concetto C ++ della classe "friend", dove OperationsService può essere dichiarato amico di StorageService e accedere ai suoi interni.
- Sto ricordando che ho già affrontato un problema simile in precedenza, quindi ho cercato su Google "Classe amichevole C #", aperto il primo link nella speranza di trovare una soluzione moderna - e ho capito che ho un sentimento deja vu.
Quindi, qual è il flusso nel mio approccio attuale? Come risolverlo nel modo di "architettura moderna OOP corretta"?
Rendi OperationsService una classe interna di StorageService? Ciò significherebbe che tutto il codice verrebbe memorizzato nello stesso file (praticamente una violazione di SOLID).
Rendi OperationsService una classe interna di StorageService e rendila parziale e spostati in un altro file? Sembra un po 'raccapricciante.
In generale, non voglio nemmeno che OperationsService sia esposto (nemmeno per registrarlo nel contenitore IoC) per il resto dell'app - solo per fare il lavoro pesante. OperationsService non sarebbe così grande, quindi non voglio creare un terzo (una sorta di servizio wrapper).
In termini di oggetti posso riformulare il problema come "l'oggetto A ha un campo privato, che dovrebbe essere accessibile solo per l'oggetto B. La responsabilità è IO, la responsabilità dell'oggetto è una logica, quindi non possono essere unite".
Per riassumere, abbiamo:
- config, che è POCO e contiene dati deserializzati da Json.
- StorageService, che contiene OpenFile (), SaveFile (). Inoltre, contiene l'istanza attuale della configurazione.
- OperationsService teorico, che contiene una logica in GetConfigPropertyByNameAndDoSomeMagicWithIt (). Per poter eseguire questa logica, deve avere anche un accesso alla configurazione. La domanda è come mettere in relazione OperationsService e StorageService.