Qual è l'idea alla base delle classi di denominazione con suffisso "Info", ad esempio: "SomeClass" e "SomeClassInfo"?

19

Sto lavorando a un progetto che riguarda dispositivi fisici e sono stato confuso su come denominare correttamente alcune classi in questo progetto.

Considerare i dispositivi reali (sensori e ricevitori) sono una cosa, e la loro rappresentazione nel software è un'altra, sto pensando di nominare alcune classi con il pattern del nome del suffisso "Info".

Ad esempio, mentre Sensor sarebbe una classe per rappresentare il sensore effettivo (quando è effettivamente collegato a un dispositivo funzionante), SensorInfo verrebbe utilizzato per rappresentare solo le caratteristiche di tale sensore. Ad esempio, al salvataggio del file, serializzo un SensorInfo nell'intestazione del file, invece di serializzare un Sensor , che non avrebbe nemmeno senso.

Ma ora sono confuso, perché c'è un ciclo vitale degli oggetti secondari in cui non riesco a decidere se dovrei usare l'uno o l'altro, o come ottenerne uno da un altro, o anche se entrambe le varianti dovrebbero essere effettivamente compresse in un solo classe.

Inoltre, la classe Employee di esempio troppo comune ovviamente è solo una rappresentazione della persona reale, ma nessuno suggerirebbe di nominare la classe EmployeeInfo invece, per quanto ne so.

Il linguaggio con cui lavoro è .NET, e questo schema di denominazione sembra essere comune in tutto il framework, ad esempio con queste classi:

  • Directory e DirectoryInfo classes;
  • File e FileInfo classes;
  • ConnectionInfo class (senza corrispondente Connection class);
  • DeviceInfo class (senza corrispondente Device class);

Quindi la mia domanda è: esiste una logica comune sull'uso di questo modello di denominazione? Ci sono casi in cui ha senso avere coppie di nomi ( Thing e ThingInfo ) e altri casi in cui dovrebbe esistere solo la classe ThingInfo o la classe Thing , senza la sua controparte?

    
posta heltonbiker 08.05.2015 - 16:50
fonte

7 risposte

21

Penso che "info" sia un termine improprio. Gli oggetti hanno stato e azioni: "info" è solo un altro nome per "stato" che è già inserito in OOP.

Che cosa realmente sta cercando di modellare qui? Hai bisogno di un oggetto che rappresenti l'hardware nel software in modo che possa essere utilizzato da un altro codice.

È facile da dire ma, come hai scoperto, c'è dell'altro. "Rappresentare l'hardware" è sorprendentemente ampio. Un oggetto che lo fa ha diverse preoccupazioni:

  • Comunicazione dispositivo di basso livello, sia che si tratti di interfaccia USB, una porta seriale, TCP / IP o connessione proprietaria.
  • Gestione dello stato. Il dispositivo è acceso? Pronto a parlare con il software? Occupato?
  • Gestione degli eventi. Il dispositivo ha prodotto i dati: ora abbiamo bisogno di generare eventi per passare ad altre classi che sono interessate.

Alcuni dispositivi, ad esempio i sensori, avranno meno preoccupazioni rispetto a un dispositivo multifunzione stampante / scanner / fax. Un sensore probabilmente produce solo un flusso di bit, mentre un dispositivo complesso può avere protocolli e interazioni complessi.

In ogni caso, tornando alla tua domanda specifica, ci sono diversi modi per farlo a seconda delle tue esigenze specifiche e della complessità dell'interazione hardware.

Ecco un esempio di come progetterei la gerarchia di classi per un sensore di temperatura:

  • ITemperatureSource: interfaccia che rappresenta tutto ciò che può produrre dati di temperatura: un sensore, potrebbe anche essere un file wrapper o dati codificati (pensa: test di simulazione).

  • Acme4680Sensore: sensore modello ACME 4680 (ottimo per rilevare quando il Roadrunner si trova nelle vicinanze). Questo può implementare più interfacce: forse questo sensore rileva sia la temperatura che l'umidità. Questo oggetto contiene lo stato a livello di programma come "il sensore è collegato?" e "quale era l'ultima lettura?"

  • Acme4680SensorComm: usato esclusivamente per comunicare con il dispositivo fisico. Non mantiene molto stato. Viene utilizzato per inviare e ricevere messaggi. Ha un metodo C # per ciascuno dei messaggi che l'hardware comprende.

  • HardwareManager: utilizzato per ottenere dispositivi. Questo è essenzialmente un factory che memorizza le istanze nella cache: dovrebbe esserci solo un'istanza di un oggetto device per ogni dispositivo hardware. Deve essere abbastanza intelligente da sapere che se il thread A richiede il sensore di temperatura ACME e il thread B richiede il sensore di umidità ACME, questi sono in realtà lo stesso oggetto e devono essere restituiti a entrambi i thread.

Al livello più alto avrai interfacce per ogni tipo di hardware. Descrivono le azioni che il tuo codice C # avrebbe intrapreso sui dispositivi, utilizzando i tipi di dati C # (non ad esempio gli array di byte che potrebbero essere utilizzati dal driver di dispositivo raw).

Allo stesso livello hai una classe di enumerazione con un'istanza per ogni tipo di hardware. Il sensore di temperatura potrebbe essere di un tipo, il sensore di umidità un altro.

Un livello al di sotto di questo sono le classi effettive che implementano tali interfacce: rappresentano un dispositivo simile all'Acme4680Sensor che ho descritto sopra. Qualsiasi classe particolare può implementare più interfacce se il dispositivo può eseguire più funzioni.

Ogni classe di dispositivi ha la sua classe di comunicazione privata (comunicazione) che gestisce l'attività di basso livello di comunicazione con l'hardware.

Al di fuori del modulo hardware, l'unico livello visibile è l'interfaccia / enum più l'HardwareManager. La classe HardwareManager è l'astrazione di fabbrica che gestisce l'istanziazione delle classi di dispositivi, le istanze di cache (tu veramente non vuoi due classi di dispositivi che parlano allo stesso dispositivo hardware), ecc. Una classe che ha bisogno di un tipo particolare del sensore chiede all'HardwareManager di ottenere il dispositivo per l'enum particolare, che poi calcola se è già istanziato, se non come crearlo e inizializzarlo, ecc.

L'obiettivo qui è quello di disaccoppiare la logica aziendale dalla logica hardware di basso livello. Quando scrivi un codice che stampa i dati del sensore sullo schermo, a quel codice non dovrebbe importare quale tipo di sensore hai se e solo se questo disaccoppiamento è centrato su quelle interfacce hardware.

Nota: ci sono associazioni tra HardwareManager e ogni classe di dispositivi che non ho disegnato perché il diagramma si sarebbe trasformato in zuppa di frecce.

    
risposta data 08.05.2015 - 17:28
fonte
11

Potrebbe essere un po 'difficile trovare una singola convenzione unificante perché queste classi sono distribuite su un certo numero di spazi dei nomi, ( ConnectionInfo sembra essere in CrystalDecisions e DeviceInfo in System.Reporting.WebForms ).

Guardando questi esempi, però, sembrano esserci due usi distinti del suffisso:

  • Distinguere una classe che fornisce metodi statici con una classe che fornisce metodi di istanza. Questo è il caso delle classi System.IO , come sottolineato dalle loro descrizioni:

    Directory :

    Exposes static methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.

    DirectoryInfo :

    Exposes instance methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.

    Info sembra una scelta un po 'strana qui, ma rende la differenza relativamente chiara: una classe Directory potrebbe ragionevolmente rappresentare una particolare directory o fornire metodi helper relativi alle directory generali senza mantenere nessuno stato, mentre DirectoryInfo potrebbe essere solo il primo.

  • Sottolineando che la classe contiene solo informazioni e non fornisce un comportamento che potrebbe ragionevolmente essere previsto dal nome non suffisso .

    Penso che l'ultima parte di quella frase potrebbe essere il pezzo del puzzle che distingue, diciamo, ConnectionInfo da EmployeeInfo . Se avessi una classe chiamata Connection , mi sarei ragionevolmente aspettato che effettivamente mi fornisse la funzionalità che ha una connessione- Sarei alla ricerca di metodi come void Open() , ecc. Tuttavia, nessuno sano di mente sarebbe si aspetta che una classe Employee possa effettivamente fare ciò che fa un Employee effettivo, o cercare metodi come void DoPaperwork() o bool TryDiscreetlyBrowseFacebook() .

risposta data 08.05.2015 - 17:18
fonte
3

In generale, un oggetto Info incapsula informazioni sullo stato di un oggetto in un determinato momento . Se chiedo al sistema di guardare un file e darmi un oggetto FileInfo associato alla sua dimensione, mi aspetto che l'oggetto riporti la dimensione del file al momento della richiesta (o, per essere più precisi, la dimensione del file in un determinato momento tra quando è stata effettuata la chiamata e quando è stata restituita). Se la dimensione del file cambia tra il momento in cui la richiesta viene restituita e il tempo in cui viene esaminato l'oggetto FileInfo , vorrei non aspettarsi che tale modifica si rifletta nell'oggetto FileInfo .

Si noti che questo comportamento sarebbe molto diverso da quello di un oggetto File . Se una richiesta per aprire un file su disco in modalità non esclusiva produce un oggetto File che ha una proprietà Size , mi aspetto che il valore restituito cambierà di conseguenza quando la dimensione del file del disco cambia, poiché File l'oggetto non rappresenta semplicemente lo stato di un file - rappresenta il file stesso .

In molti casi, gli oggetti che si collegano a una risorsa devono essere ripuliti quando i loro servizi non sono più necessari. Poiché gli oggetti *Info non si collegano alle risorse, non richiedono la pulizia. Di conseguenza, nei casi in cui un oggetto Info soddisferà i requisiti di un cliente, potrebbe essere meglio utilizzare il codice uno anziché utilizzare un oggetto che rappresenterebbe la risorsa sottostante, ma la cui connessione a tale risorsa dovrebbe essere ripulito.

    
risposta data 08.05.2015 - 19:33
fonte
2

Considering the actual devices (sensors and receivers) are one thing, and their representation in software is another, I am thinking about naming some classes with the "Info" suffix name pattern.

For example, while a Sensor would be a class to represent the actual sensor (when it is actually connected to some working device), SensorInfo would be used to represent only the characteristics of such sensor. For example, upon file save, I would serialize a SensorInfo to the file header, instead of serializing a Sensor, which sort of wouldn't even make sense.

Non mi piace questa distinzione. Tutti gli oggetti sono "rappresentazione [s] nel software". Questo è ciò che la parola "oggetto" significa.

Ora, potrebbe essere sensato separare le informazioni su una periferica dal codice effettivo che si interfaccia con la periferica. Quindi, ad esempio, un sensore ha un SensorInfo, che contiene la maggior parte delle variabili di istanza, insieme ad alcuni metodi che non richiedono hardware, mentre la classe Sensor è responsabile dell'interazione effettiva con il sensore fisico. Non hai-un Sensor a meno che il tuo computer non abbia un sensore, ma potresti plausibilmente avere un SensorInfo .

Il problema è che questo tipo di design può essere generalizzato a (quasi) qualsiasi classe. Quindi devi stare attento. Ovviamente non avresti una classe SensorInfoInfo , per esempio. E se hai una variabile Sensor , potresti trovarti a violare la legge di Demetra interagendo con il suo SensorInfo membro. Niente di tutto ciò è fatale, ovviamente, ma il design delle API non è solo per gli autori di librerie. Se mantieni la tua API pulita e semplice, il tuo codice sarà più gestibile.

Le risorse del filesystem come le directory, a mio parere, sono molto vicine a questo limite. Ci sono alcune situazioni in cui si desidera descrivere una directory che non è accessibile localmente, true, ma lo sviluppatore medio probabilmente non si trova in una di queste situazioni. Complicare la struttura di classe in questo modo è, a mio avviso, inutile. L'approccio di Contrast Python in pathlib : esiste una singola classe che "è più probabile che tu abbia bisogno" e varie classi ausiliarie che la maggior parte degli sviluppatori può tranquillamente ignorare. Se davvero ne hai bisogno, comunque, forniscono in gran parte la stessa interfaccia, solo con metodi concreti messi a nudo.

    
risposta data 09.05.2015 - 01:29
fonte
2

Direi che il contesto / dominio è importante, poiché abbiamo codice di logica di business di alto livello e modelli di basso livello, componenti di architettura e così via ...

'Info', 'Data', 'Manager', 'Object', 'Class', 'Model', 'Controller' ecc. possono essere suffissi puzzolenti, specialmente a un livello inferiore, poiché ogni oggetto ha alcune informazioni o dati, in modo che le informazioni non siano necessarie.

I nomi di classe del dominio aziendale dovrebbero essere come tutti gli stakeholder ne parlano, non importa se suona strano o non è corretto al 100%.

Buoni suffissi per le strutture dati sono ad esempio "Elenco", "Mappa" e per i modelli di suggerimento "Decoratore", "Adattatore" se ritieni che sia necessario.

Per lo scenario del sensore, non mi aspetto che SensorInfo salvi ciò che è il tuo sensore, ma SensorSpec . Info imho è più una informazione derivata, come FileInfo è qualcosa come la dimensione, non si salva o il percorso file che è costruito dal percorso e il nome del file, ecc.

Un altro punto:

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

Questo mi ricorda sempre di pensare a un nome solo per pochi secondi e se non ne trovo uno, uso nomi strani e "TODO" - li contrassegno. Posso sempre modificarlo in seguito poiché il mio IDE fornisce supporto per il refactoring. Non è uno slogan aziendale che deve essere buono, ma solo un codice che possiamo cambiare ogni volta che vogliamo. Tienilo a mente.

    
risposta data 09.05.2015 - 02:35
fonte
1

ThingInfo può essere un ottimo proxy di sola lettura per la Cosa.

vedi link

Proxy: "Fornisci un surrogato o segnaposto per un altro oggetto per controllarne l'accesso."

Tipicamente il ThingInfo avrà proprietà pubbliche senza setter. Queste classi e i metodi sulla classe sono sicuri da usare e non importeranno alcuna modifica ai dati di supporto, all'oggetto o a qualsiasi altro oggetto. Non si verificheranno cambiamenti di stato o altri effetti collaterali. Questi possono essere usati per report e servizi web o ovunque tu abbia bisogno di informazioni sull'oggetto ma vuoi limitare l'accesso all'oggetto stesso.

Usa il ThingInfo quando è possibile e limita l'uso della Cosa Attuale alle volte in cui hai effettivamente bisogno di cambiare l'oggetto Cosa. Rende molto più veloce la lettura e il debugging quando ci si abitua all'utilizzo di questo modello.

    
risposta data 14.05.2015 - 02:10
fonte
1

Finora nessuno in questa domanda sembra aver colto la vera ragione di questa convenzione di denominazione.

Un DirectoryInfo non è la directory. È un DTO con dati su la directory. Possono esserci molte di queste istanze che descrivono la stessa directory. Non è un'entità. È un oggetto valore da buttare via. Un DirectoryInfo non rappresenta la directory attuale. Puoi anche considerarlo come un handle o controller per una directory.

Contrasto con una classe denominata Employee . Questo potrebbe essere un oggetto di entità ORM ed è il singolo oggetto che descrive quel dipendente. Se si trattava di un oggetto valore senza identità, dovrebbe essere chiamato EmployeeInfo . Un Employee rappresenta infatti il dipendente effettivo. Una classe DTO simile al valore chiamata EmployeeInfo non rappresenterebbe chiaramente il dipendente, ma piuttosto lo descriveva o memorizza i dati su di esso.

Esiste in realtà un esempio nel BCL in cui esistono entrambe le classi: A ServiceController è una classe che descrive un servizio Windows. Può esserci un numero qualsiasi di tali controller per ciascun servizio. Un ServiceBase (o una classe derivata) è il servizio effettivo e non concettualmente ha senso avere più istanze di esso per servizio distinto.

    
risposta data 18.05.2015 - 20:56
fonte

Leggi altre domande sui tag