Alla ricerca di alcuni consigli di progettazione OO


12

Sto sviluppando un'app che verrà utilizzata per aprire e chiudere le valvole in un ambiente industriale e pensavo a qualcosa di semplice come questo: -

public static void ValveController
{
    public static void OpenValve(string valveName)
    {
        // Implementation to open the valve
    }

    public static void CloseValve(string valveName)
    {
        // Implementation to close the valve
    }
}

(L'implementazione scriverà alcuni byte di dati sulla porta seriale per controllare la valvola - un "indirizzo" derivato dal nome della valvola e un "1" o "0" per aprire o chiudere la valvola).

Un altro sviluppatore ha chiesto se dovremmo invece creare una classe separata per ogni valvola fisica, di cui ce ne sono dozzine. Sono d'accordo che sarebbe meglio scrivere codice come PlasmaValve.Open()piuttosto che ValveController.OpenValve("plasma"), ma è eccessivo?

Inoltre, mi chiedevo come affrontare al meglio il progetto con in mente un paio di ipotetici requisiti futuri: -

  1. Ci viene chiesto di supportare un nuovo tipo di valvola che richiede valori diversi per aprirla e chiuderla (non 0 e 1).
  2. Ci viene chiesto di supportare una valvola che può essere impostata su qualsiasi posizione compresa tra 0 e 100, anziché semplicemente "aperta" o "chiusa".

Normalmente userei l'ereditarietà per questo tipo di cose, ma recentemente ho iniziato a concentrarmi sulla "composizione sull'ereditarietà" e mi chiedo se ci sia una soluzione più semplice da usare usando la composizione?


2
Vorrei creare una classe valvola generica che abbia un identificatore per la valvola specifica (non una stringa, forse un enum) e tutte le informazioni necessarie per il flusso di controllo all'interno dei metodi OpenValve / CloseValve. In alternativa, è possibile rendere astratta la classe valv e realizzare implementazioni separate per ognuna, in cui la valvola di apertura / chiusura chiama semplicemente la logica all'interno della classe di valvole specificata nel caso in cui valvole diverse abbiano meccanismi di apertura / chiusura diversi. Il meccanismo comune sarebbe definito nella classe base.
Jimmy Hoffa,

2
Non preoccuparti di ipotetici requisiti futuri. YAGNI.
pdr,

3
@pdr YAGNI è una lama a doppio taglio, sono d'accordo che vale la pena seguire in generale, ma preso all'estremo si potrebbe dire che fare qualsiasi cosa per aiutare la futura manutenibilità o leggibilità stia violando YAGNI, per questo motivo trovo l'ambito di YAGNI troppo ambiguo per molti. Detto questo, molte persone riconoscono dove usare YAGNI e dove lanciarlo perché rendere conto del futuro ti risparmierà molto dolore. Penso solo che uno dovrebbe stare attento a suggerire che la gente segua YAGNI quando non sai dove atterreranno su quello spettro.
Jimmy Hoffa,

2
L'uomo, la "composizione per eredità" è sopravvalutata. Realizzerei una classe / interfaccia astratta di Valve e poi li sottoclassi in PlasmaValve. E poi mi assicurerei che il mio ValveController funzionerebbe con Valve (s), senza preoccuparsi di quale sottoclasse siano esattamente.
MrFox,

2
@suslik: Assolutamente. Ho anche visto un codice eccellente chiamato spaghetti da persone che non capiscono i principi SOLIDI. Potremmo continuare per sempre con questo. Il mio punto è che ho visto più problemi causati dall'allontanamento dei principi stabiliti (nati da molti anni di esperienza) di quanti ne abbia visti causati dall'eccessiva aderenza. Ma sono d'accordo che entrambi gli estremi sono pericolosi.
pdr,

Risposte:


12

Se ogni istanza dell'oggetto valvola eseguisse lo stesso codice di questo ValveController, allora sembra che più istanze di una singola classe sarebbero la strada giusta da percorrere. In questo caso, configura semplicemente quale valvola controlla (e come) nel costruttore dell'oggetto valvola.

Tuttavia, se ogni controllo della valvola necessita di un codice diverso per funzionare e l'attuale ValveController sta eseguendo un'istruzione di commutazione gigante che fa cose diverse a seconda del tipo di valvola, allora il polimorfismo è stato reimplementato male. In tal caso, riscrivilo in più classi con una base comune (se ciò ha senso) e lascia che il principio della responsabilità singola sia la tua guida alla progettazione.


1
+1 per menzionare le istruzioni switch basate sul tipo come odore di codice. Vedo spesso questo tipo di dichiarazioni switch in cui lo sviluppatore afferma che stava solo seguendo KISS. Esempio perfetto di come i principi del design possano essere pervertiti heh
Jimmy Hoffa,

2
Più istanze potrebbero anche semplificare il collegamento delle valvole in una sequenza, consentendo di modellare le tubazioni effettive dell'impianto come grafico diretto nel codice. È inoltre possibile aggiungere una logica di business alle classi, nel caso in cui sia necessario fare qualcosa come aprire una valvola quando un'altra si chiude per evitare l'accumulo di pressione o chiudere tutte le valvole a valle in modo da non ottenere un effetto "colpo d'ariete" quando la valvola viene riaperta.
TMN,

1

La mia lamentela principale sta usando le stringhe per il parametro che identifica la valvola.

Almeno crea una Valveclasse che abbia getAddressnella forma le esigenze di implementazione sottostanti e passa quelle alla ValveControllere assicurati che non puoi creare valvole inesistenti. In questo modo non dovrai gestire stringhe errate in ciascuno dei metodi di apertura e chiusura.

Se si creano metodi di praticità che chiamano open e close in, ValveControllerdipende da te, ma ad essere sincero terrei tutte le comunicazioni alla porta seriale (compresa la codifica) in una singola classe che altre classi chiameranno quando necessario. Ciò significa che quando è necessario migrare su un nuovo controller, è necessario modificare solo una classe.

Se ti piacciono i test, dovresti anche creare ValveControllerun singleton in modo da poterlo deridere (o creare una macchina di addestramento per gli operatori).


Non ho mai visto nessuno raccomandare un singleton per motivi di test prima - di solito va diversamente.
Kazark,

onestamente il singleton è più per evitare la statica e quindi la comunicazione può essere sincronizzata
maniaco del cricchetto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.