Come gestire le dipendenze ricorsive nelle lingue OO?

0

Sto designando un'architettura che utilizza alcuni oggetti differenti controller che implementano un'interfaccia abstract_controller .

Il loro obiettivo è incapsulare l'uso di alcuni dati.

Alcune fabbriche sono responsabili della creazione e inizializzazione dei controller.

Alcuni controllers potrebbero aver bisogno di un altro controllers , e ogni controllers utilizzato ha bisogno di alcune informazioni dal database, specialmente alla prima esecuzione.

Nb: nella mia applicazione, i clienti devono conoscere i cataloghi da cui possono ordinare.

Ad esempio, se su un'esecuzione ho bisogno di usare client_controller , client sono creati da client_factory (e in qualche modo fornito), ma in questo caso devo essere in grado di fornire informazioni su catalogs e inizializza il mio catalog_controller (e così via).

Quindi, qui ci sarebbero solo tre dipendenze:

  • client dipende dai dati nel database.
  • client dipende da catalogs
  • catalog dipende dai dati nel database. (supponendo che questa sia l'unica dipendenza di catalog_controller )

Quali sono le opzioni per indicare che xxx_controller richiede yyy_controller o anche zzz_knowledge ? Nella prima volta ho pensato di creare un'interfaccia requires_xxx_controller corrispondente con un predicato, ma a quel punto sembra che dovrei creare un'interfaccia per ogni controller , e quindi non sarebbe davvero estensibile.

La mia prossima idea era di creare requirement e has_requirements interfaccia, rendere controller , database_information (e alcuni altri tipi) implementare requirement e controller has_requirements restituirebbe l'elenco delle dipendenze che ha, che conserverei in un set ordinato, ordinato per ordine di inserzione, (che fortunatamente esiste in boost ), ma anche allora, sarei obbligato a trovare una soluzione per restituire una collezione di quei requirements che non posso costruire.

Quali difetti sto esponendo a questi casi? Esiste una soluzione comunemente accettata per questo problema?

Dubito che cambi qualcosa di grande, ma c++ è la lingua utilizzata in questa applicazione.

    
posta Pierre Antoine Guillaume 06.08.2017 - 01:04
fonte

4 risposte

0

Mi sento come se le risposte che hai ottenuto finora non rispondessero effettivamente alla domanda che avevi ...

Hai un certo numero di controller che derivano tutti da un'interfaccia abstract_controller e questo è buono in modo che altri codici possano gestire un controller senza doverne conoscere il tipo. Tuttavia, lo stabilimento di controllo deve conoscere i tipi specifici di controller e come costruirli . Non c'è modo di girarci intorno.

Nel tuo esempio, dici che un controller client necessita di "molti" (0..n) controller di catalogo. Vi suggerisco di creare una sorta di linguaggio di specifica (questo potrebbe essere JSON, XML, un semplice CSV o una speciale "specifica" classe C ++) che dice alla fabbrica quali controllori costruire e come collegarli. ControllerFactory riceve una specifica, la analizza, crea e configura i sub-controller necessari e quindi produce il controller necessario.

Per rendere estensibile ControllerFactory, è possibile definire altre interfacce, una per ogni tipo di fabbrica di sub-controller. Per il tuo esempio, avresti bisogno di un ClientControllerFactory e CatalogControllerFactory separati. In questa idea più estensibile, ogni sottotipo di fabbrica saprebbe come analizzare un particolare pezzo di una specifica.

Quando ClientControllerFactory riceve un oggetto di specifica, creerebbe le parti client e quindi delegherà la parte del componente di catalogo della specifica a CatalogControllerFactory, che quindi produrrebbe i cataloghi e li restituirà alla fabbrica del cliente per l'inserimento nel client .

Se vuoi renderlo ancora più estensibile, puoi definire una classe factory separata per ogni classe di controller. Quindi avresti un MasterFactory che viene consegnato una serie di ControllerFactories. Quando il master ottiene le specifiche, consegnerà le specifiche a ciascuna fabbrica contenuta che cercherà le parti delle specifiche che può costruire e le restituirà. Il master quindi assemblerebbe gli oggetti controller in base alle specifiche. In questo modo, aggiungere un nuovo sottotipo di controller sarebbe una semplice questione di definire la classe controller e la sua factory (che saprebbe leggere le specifiche per scoprire se è necessario un particolare controller) e inserire un'istanza del nuovo factory nella fabbrica principale.

    
risposta data 06.08.2017 - 15:03
fonte
7

Ogni volta che un pezzo di codice ha bisogno di qualcosa, dovrebbe prendere un parametro. Non fare niente di più complicato di così.

Se vuoi creare un oggetto che ha bisogno di una dipendenza usa i parametri del costruttore.

class client_controller {
     client_controller(database_information*, catalog_controller*);
}

Se ti serve solo una funzione, usa i parametri del metodo:

void do_foo(database_information*, catalog_controller*);

Quindi, quando costruisci quell'oggetto o chiami quelle funzioni, passa i parametri.

L'unica cosa che ottieni compiendo il percorso di costruzione di un quadro complesso attorno al passaggio delle dipendenze delle cose è dannoso per il tuo codice e rende difficile per te e gli altri capire il tuo codice.

    
risposta data 06.08.2017 - 04:11
fonte
1

In base al tuo specifico esempio di cliente / catalogo, seguirò un approccio diverso.

Poiché hai affermato che sia il client che il catalogo dipendono dai dati del database, creerei un client_repository e un catalog_repository , che sono responsabili solo della gestione delle operazioni del database .

Quindi creerò un ClientCatalogController (o ClientCatalogManager), che è responsabile per il recupero dei client, insieme ai rispettivi cataloghi , e gestirà altra logica richiesta dall'applicazione per quanto riguarda queste due entità.

In altre parole, non userei le fabbriche che hai menzionato, perché i dati provengono dal DB , e metterei tutta la logica di cui la mia applicazione ha bisogno in una classe manager (o controller).

Se hai bisogno di qualcosa più generico nonostante l'esempio specifico che hai fornito, vorrei dare un'occhiata a come funziona un framework come MEF . Fondamentalmente, cerca durante il runtime le interfacce che implementano ciò che ti aspetti .

link

    
risposta data 07.08.2017 - 18:44
fonte
-1

Questa affermazione non è corretta: "i clienti devono conoscere i cataloghi da cui possono ordinare". Presumibilmente ciò significa "Alcune funzioni nella classe Client devono chiamare una funzione nella classe Catalog". Ma ai tuoi clienti non interessa questo. Se hai o meno una classe "Cliente" e se dipende o meno da una classe "Catalogo" è una decisione di implementazione, non una regola aziendale.

È sempre possibile assegnare funzioni alle classi in modo tale che le classi non dipendano circolarmente. Oppure puoi rompere le dipendenze dirette indesiderate introducendo classi astratte (ad esempio il pattern "Observer").

    
risposta data 06.08.2017 - 03:59
fonte

Leggi altre domande sui tag