Come posso progettarlo in modo più efficace secondo il design che intendo, pur assicurando che sono in linea con i principi di progettazione SOLID?

5

So cos'è l'SRP, ma sto mettendo in discussione il mio attuale design di un oggetto che io chiamo un'entità. Di seguito è riportata una foto del design a cui mi riferisco. Se spostassi il codice dagli oggetti GeometricInformation e TransactionInformation rispettivamente agli abstract GeometricEntityBase e TranslactionalEntityBase, questo sarebbe considerato un grave attacco di SRP? Spero che gli esperti qui possano aiutare a creare buchi in questo progetto o affermarlo se opportuno.

Per riassumere l'intento, ho una API CAD su cui sto lavorando. Ogni entità nel dominio CAD è rappresentata da un'entità nel codice. Per consentire alle Entità di portare solo quegli attributi appropriati al suo scopo, ci sono tre livelli di ereditarietà che l'Entità può usare. Ad esempio, se solo le funzioni Entity di base (come avere un ID, ecc.) Possono ereditare da EntityBase. Se richiede ulteriori proprietà geometriche per identificare i confini, il punto di inserimento, la matrice di trasformazione, ecc., Quindi erediterà da GeometricEntityBase. Se deve essere un'entità pienamente transazionale (tramite un framework transazionale personalizzato), allora è necessario che trasponga gli attributi di entrambi i plus transazionali (come lo stato dell'entità (nuovo, pulito, sporco), documento, ecc.).

Per garantire SRP, ho utilizzato il modello dell'adattatore e impostato gli attributi e le operazioni geometriche su un oggetto chiamato GeometricInformation e allo stesso modo le transazioni su TransactionInformation. Funziona alla grande, ma ora sto iniziando a rendermi conto che per consentire modifiche alle proprietà geometriche, ho bisogno di convalidare lo stato dell'entità. In altre parole, l'oggetto GeometricInformation deve conoscere gli attributi dell'oggetto TransactionInformation. Le soluzioni sembrano coinvolgere un modello di osservatore tale che il GeomtetricInformation possa osservare TransactionInformation (che sembra forzato) o asserire una proprietà XXXEntityBase "CanModify" che può essere modificata e osservata da un oggetto XXXInformation. Sfortunatamente ciò creerebbe una dipendenza bidirezionale dal momento che ogni oggetto XXXInformation avrebbe dovuto sapere che era padre.

A causa del fatto aggiuntivo che WCF viene utilizzato per comunicare le entità a un livello dati di livello inferiore, esiste anche un processo di traduzione per convertirli in equivalenti DTO. Alla fine, sto pensando di spostare il codice dagli oggetti XXXInformation ai loro equivalenti astratti che hanno comunque ben poco in essi, ma sono preoccupato che spezzare SRP possa potenzialmente causare problemi a valle. D'altro canto, ciò semplificherebbe di più le entità e consentirà al processo di traduzione di migliorare in termini di prestazioni, quindi sicuramente un po 'di dare e avere.

    
posta bjhuffine 01.02.2016 - 20:21
fonte

1 risposta

5

Questo è esattamente il motivo per cui l'ereditarietà non dovrebbe mai essere la scelta predefinita per la creazione di funzionalità / funzionalità riutilizzabili.

Il tuo problema si riduce alla tua gerarchia di ereditarietà che in alcuni scenari richiede conoscenza attraverso parti del grafico di ereditarietà, facendo sì che il grafico abbia ancora più relazioni tra ogni nodo in esso contenuto. Queste relazioni sono tra tipi e sono una forma di accoppiamento. Uno che è meglio evitare semplicemente non coinvolgendo l'ereditarietà per la stragrande maggioranza degli scenari.

Consideralo in questo modo, quando usi l'ereditarietà finisci per sovrapporre le dipendenze l'una sull'altra in modo che non possano essere modificate indipendentemente. Qualsiasi modifica alla classe base avrà effetto su ogni classe derivata, qualsiasi modifica alla classe derivata cambierà la sua relazione con la classe base e ogni altra classe derivante da essa. La tua situazione ora è che vuoi fare in modo che due classi derivate abbiano funzioni che la classe base non ha, ma non ha senso promuoverlo perché è specializzato in queste due classi.

È qui che entra in gioco la composizione. Invece di creare una gerarchia di ereditarietà di cose simili, è spesso meglio affrontarla dal punto di vista del fatto che si hanno pezzi di funzionalità che si desidera. Isolando le unità in parti di funzionalità puoi scegliere quali pezzi utilizzare e come.

Nel tuo esempio, ad esempio, vuoi comportamenti transazionali e vuoi la validazione dei limiti. Quelli dovrebbero probabilmente essere due unità indipendenti non correlate. Quindi, quando si ha una classe che vuole il comportamento della transazione, inserisce una classe / modulo transazionale / what-have-you come un campo membro o proprietà. Se vuole anche la convalida dei limiti, inserisce un'unità di validazione dei confini. Quindi può comporre i due passando informazioni avanti e indietro tra attività transazionali e validazione dei confini.

Un altro difetto molto grande che stai cadendo qui sembra essere una strong adesione ai modelli di design come soluzioni. Non sono soluzioni perché non risolvono i problemi, semplicemente danno idee concettuali su come potresti incontrare alcune caratteristiche del design. La realtà è che si desidera soddisfare una serie di buone caratteristiche di progettazione (come SRP e il resto di SOLID per esempio), ma si desidera farlo in una soluzione su misura per il proprio dominio problematico . Non cercare un "osservatore", prova semplicemente e progetta fondamentalmente una soluzione ideale al problema di separare le preoccupazioni che hai, in un modo che soddisfi tutte le caratteristiche di design positive che puoi. Se trovi che il modello di progettazione è il risultato, fallo, ma non iniziare dal punto di vista dell'utilizzo di uno.

Inizia sempre a risolvere un problema suddividendolo in sotto-problemi finché non ne hai di facili da risolvere. Quindi, quando si reintegrano tali soluzioni in un insieme coerente, si desidera che tali integrazioni vengano eseguite in un modo che soddisfi il più possibile i principi di progettazione SOLID, nonché qualsiasi altra caratteristica di progettazione e implementazione importante, date le esigenze della soluzione. Forse ha bisogno di eseguire fast e quindi rinunciare ad integrare le soluzioni insieme in modo astratto / pulito per quanto desideri, ad esempio, forse ha bisogno di affidabilità, quindi aggiungi molto di più. I pattern sono solo idee concettuali, non da seguire alla lettera o presi come soluzioni letterali.

    
risposta data 01.02.2016 - 20:46
fonte

Leggi altre domande sui tag