Esecuzione di comunicazione seriale nel costruttore per inizializzare un oggetto

1

Ho una classe UnitInfo che rappresenta una raccolta di informazioni sull'unità con metodi per ottenere le informazioni sull'unità in modo strutturato, come una specifica codifica, ecc. Queste informazioni sull'unità sono costituite da informazioni che devono essere lette dall'unità tramite comunicazione porta seriale. Il costruttore della classe riceve il numero di serie, che non può essere interrogato dall'unità, e un'interfaccia di comunicazione seriale di istanza, che quindi utilizza per interrogare l'unità per le informazioni:

public class UnitInfo {
    public int SerialNumber { get; }
    public int InternalMemorySize { get; }
    // And more properties...

    public UnitInfo(ISerialCom serialCom, int serialNumber) {
        SerialNumber = serialNumber;
        InternalMemorySize = serialCom.GetInternalMemorySize();
        // More serial communication to set more properties
    }

    // Get methods to retrieve all the unit information in a specific encoding
    // i.e a collection of bytes.
}

Dalla mia navigazione SO e Google, capisco che di solito è normale lavorare in un costruttore, ma non ho ancora trovato un esempio di esecuzione di comunicazioni via seriale, GPIB, ecc. in un costruttore. Sto anche pensando che questa possa essere una violazione del principio di dipendenza dalla dipendenza in quanto la classe dipende da un'implementazione di un'interfaccia per inizializzarsi; è meglio passare tutte le informazioni sull'unità alla classe?

    
posta pavuxun 26.09.2018 - 14:28
fonte

2 risposte

1

Questo design è perfettamente a posto. Sebbene tu stia comunicando con le porte esterne, è presumibilmente collegato all'hardware direttamente al computer, quindi le comunicazioni avvengono rapidamente.

Dato che stai lavorando in C #, le eccezioni nel costruttore come risultato del fatto che l'utente stacca la spina (letteralmente) non sono un problema, perché il Common Language Runtime è responsabile della pulizia, anche se mi assicurerei che ISerialCom object implementa IDisposable se devi gestire la connessione (ma non è responsabilità di questo costruttore).

La cosa bella di accettare un oggetto che fornisce queste informazioni vitali è che rende la costruzione di questo oggetto piuttosto a prova di proiettile. Non puoi passare accidentalmente la dimensione della memoria interna al posto del numero di serie.

    
risposta data 26.09.2018 - 15:00
fonte
3

Esecuzione del lavoro nel costruttore

Questa è una di quelle cose che alla fine è una questione di opinione. Mentre la tua risposta attualmente accettata è una opinione, non è quella che vorrei condividere, e Non sono solo con quello :

Work in the constructor such as: creating/initializing collaborators, communicating with other services, and logic to set up its own state removes seams needed for testing, forcing subclasses/mocks to inherit unwanted behavior. Too much work in the constructor prevents instantiation or altering collaborators in the test.

(Enfasi mia)

Tuttavia, non sarei d'accordo al 100% con Misko Hevery (opinioni diverse ...), che vedono "Qualcosa in più di un'assegnazione di campi" come un segnale di avvertimento. La convalida "breve e dolce" come i controlli nulli o l'affermazione di un int in un determinato intervallo sono accettabili.

La mia metafora preferita per questo: Il lavoro di un dentista non è quello di costruire l'ufficio di un dentista .

Consigli pratici

Non dovrebbe essere troppo difficile creare un UnitInfoFactory che è responsabile della creazione di quei UnitInfo s. Se UnitInfo è molto piccolo, anche un metodo Factory statico va bene.

Potresti immediatamente essere in grado di creare un UnitInfo con solo due intere invece di dover ottenere un'implementazione di ISerialCom in qualche modo (ignorando il codice che hai lasciato nell'esempio ovviamente) . Questo è d'oro se stai facendo test unitari, perché anche con una struttura di derisione è ancora un lavoro aggiuntivo per creare quel finto. Tuttavia, anche se non si sta testando l'unità, il codice scritto per essere testabile tende ad essere più pulito rispetto ad altri codici.

Nessuna violazione dei principi DI

Tuttavia, questa parte qui non è una questione di opinione:

I'm also thinking this may be a violation of the Dependency Injection principle as the class is dependent upon some implementation of an interface to initialize itself.

Per quanto riguarda il codice di esempio, potrei fornire un'implementazione fittizia di ISerialCom che restituisce sempre zero per GetInternalMemorySize() e funzionerebbe comunque. In quanto tale, la tua classe non "dipende da qualche implementazione di un'interfaccia", dipende solo dall'interfaccia stessa.

Penso che l'equivoco risieda nel fatto che la classe ha ancora bisogno di una (y) implementazione per funzionare, ma non è questo il principio di DI. Come David Arno ha già menzionato nei commenti, i problemi sorgono quando si utilizza new nel costruttore.

Il miglior codice mnemonico che ho trovato è " new is glue ": ogni volta che usi new nel tuo codice, quel codice è legato a quella specifica implementazione . Se usi ripetutamente new nei costruttori , creerai una catena di implementazioni specifiche . E poiché non puoi "avere" un'istanza di una classe senza costruirla, non puoi separarla da quella catena.

Ma sembra che tu non lo stia facendo, quindi va bene.

    
risposta data 26.09.2018 - 17:19
fonte

Leggi altre domande sui tag