Un sistema di tipo strong dovrebbe aiutarti
L'uso di un linguaggio strongmente tipizzato ha lo scopo di prevenire errori durante il runtime catturandoli in fase di compilazione. L'uso di un oggetto anonimo (o un dizionario per esempio) sconfigge questo scopo e consente la compilazione di bug nel programma che verrà visualizzato solo durante il runtime.
Ad esempio, dai un'occhiata al codice qui sotto:
public object GetUsers() {
var users = MyRepo.GetAll<User>();
return new {
count: users.Count(),
items: users
}
}
public object GetNewUsers() {
var users = MyRepo.GetNewest<User>();
return new {
Count: users.Count(),
items: users
}
}
La prima risposta utilizza un parametro count
in minuscolo dove la seconda risposta contiene un parametro Count
in maiuscolo. In una classe in cui questi metodi possono essere separati da pochi blocchi di codice è molto facile perdere questo errore di battitura. Ancora peggio, questo sarà felicemente compilato e gestito. Se dovessimo usare un DTO dattilografato, questa situazione non sarebbe possibile (in effetti non sarebbe nemmeno compilabile).
O che ne dici di questo esempio:
public object GetSystemInstallDate(){
var date = AppHost.GetInstallDate();
return new {
date
};
}
Cosa succede se refactoring modifica il metodo GetInstallDate
per restituire un DateTimeOffset
invece di un DateTime
oggetto? Il codice sarà felicemente compilato ma l'output per il client sarà cambiato. Questo è un esempio debole, ma ci sono molti altri casi in cui cose come questa possono accadere. Tipi numerici, refactoring enum, ecc ...
refactoring
Se la tua risposta anonima è utilizzata in più di una posizione, non potrai refactoring usando gli strumenti di fantasia come il programma di ricerca. Dovrai refactoring manuale che non è mai un compito divertente. Se viene utilizzato in una singola posizione, il refactoring è più semplice ma ancora più utile rispetto alla definizione della classe, in primo luogo.
DRY
Direi che usando i tipi anonimi non ti stai proteggendo dalla violazione di DRY; piuttosto, ti stai rendendo più propenso a violarlo. Un tipo anonimo può essere utilizzato solo in un posto; tuttavia, se ciò che rappresenta rappresenta sempre richiesto altrove, l'intero tipo dovrà essere ricreato.
Usando un oggetto strongmente tipizzato, devi solo definire il tipo in un singolo posto e quindi usare quel tipo dove è necessario. Questo ti impedisce di dover scrivere tutti i nomi delle proprietà, i valori di formattazione, ecc ...
public object GetTop10Movies() {
return new { lastmodified = DateTime.Now, items }
}
public object GetNewest10Movies() {
// We now have to repeat the previous anonymous object
}
YAGNI
Questa è una linea guida e non una regola. Se fosse una regola si ridurrebbe a "ignorare il futuro", che è sempre una ricetta per il disastro. Allo stesso tempo, cercare di coprire tutti i possibili scenari futuri è un'idea orribile e porterà a un codice base troppo astratto e complicato. Il trucco per trovare il giusto equilibrio tra pensare in anticipo e risolvere i problemi a portata di mano.
Un altro modo di pensare YAGNI in questa situazione è: hai bisogno di compilare la sicurezza del tipo orario?
Direi che il lavoro coinvolto nella creazione di una classe per rappresentare la tua risposta è solo leggermente più del lavoro necessario per digitare un oggetto anonimo. La differenza di complessità è essenzialmente nulla (in effetti con il mondo C # un oggetto anonimo sarebbe molto probabilmente considerato una più complessa rotta da prendere).
Per un investimento di tempo piuttosto minimo si ottengono alcuni vantaggi reali e utilizzabili dallo sviluppatore. Potrebbero non essere necessari nel senso che senza di essi l'applicazione non funzionerà, ma se non avessi bisogno di un linguaggio strongmente tipizzato metterei in discussione l'uso di C # in primo luogo.
Manutenzione
The team convention was to return only DTOs and the DTOs were managed in a completely different project (among about 50 projects in the solution), I felt that in this case perpetuating that pattern was excessive and difficult to maintain.
Non sono sicuro di dove arriverebbe la difficoltà nel mantenere questo schema, tranne che per l'idea di avere 50 progetti tutti raggruppati in un'unica soluzione. Avrai una delle due situazioni:
- È necessario un nuovo tipo di risposta.
Aggiungi un nuovo file al progetto DTO che rappresenta il tuo tipo e poi usalo nella tua applicazione (dovrebbe esistere un riferimento tra i progetti).
- È necessario un tipo di risposta esistente.
Inizia a digitare lo spazio dei nomi dell'assieme DTO e seleziona il tipo di cui hai bisogno.
IsupposeacasecanbemadeforDTOsasspecificationdocumentation,butwhenitbecomesmoreofamaintenancechoretodeclarethemandtolookthemupintheirspecialfolderinaprojectIwouldarguethattherearemoreappropriateplacesandmeanstodocumenttheinterface;indeedtheanonymousobjectdeclarationseemstobedocumentationenough.
Nonc'èpostomiglioreperdocumentareiltuocodicepiuttostocheavereiltuostessocodice.IlnomedellatuarispostaDTOdovrebbedescriveredicosasitratta.Latuadichiarazioneanonimanondicenulladiciòcherappresenta,tidicesolociòchecontiene.Dovercercareladocumentazioneesterna(comeunsitoWebounfilediaiuto)èancorapiùcomplicato.
Unascatolacheconsistediduebulloni,duerondelleeundadonondescriveaffattoacosaserve.Unascatolaconl'adesivo"monitor wall mount" ti consente di sapere esattamente di cosa si tratta semplicemente guardando la confezione.
new { id, model }
// What is this? A vehicle? An economic model? Representation of DNA?
new Vehicle { id = id, model = model }
// Oh! A vehicle!