Comunemente gli oggetti dominio hanno proprietà che possono essere rappresentate da un tipo incorporato ma i cui valori validi sono un sottoinsieme dei valori che possono essere rappresentati da quel tipo.
In questi casi, il valore può essere memorizzato utilizzando il tipo incorporato ma è necessario assicurarsi che i valori siano sempre convalidati nel punto di entrata, altrimenti potremmo finire per lavorare con un valore non valido.
Un modo per risolverlo è archiviare il valore come personalizzato struct
che ha un singolo private readonly
campo di supporto del tipo incorporato e il cui costruttore convalida il valore fornito. Possiamo quindi essere sempre sicuri di usare solo valori validati usando questo struct
tipo.
Possiamo anche fornire operatori di cast da e verso il tipo incorporato sottostante in modo che i valori possano entrare e uscire senza interruzioni come tipo sottostante.
Prendiamo ad esempio una situazione in cui dobbiamo rappresentare il nome di un oggetto dominio e i valori validi sono qualsiasi stringa compresa tra 1 e 255 caratteri inclusi. Potremmo rappresentarlo usando la seguente struttura:
public struct ValidatedName : IEquatable<ValidatedName>
{
private readonly string _value;
private ValidatedName(string name)
{
_value = name;
}
public static bool IsValid(string name)
{
return !String.IsNullOrEmpty(name) && name.Length <= 255;
}
public bool Equals(ValidatedName other)
{
return _value == other._value;
}
public override bool Equals(object obj)
{
if (obj is ValidatedName)
{
return Equals((ValidatedName)obj);
}
return false;
}
public static implicit operator string(ValidatedName x)
{
return x.ToString();
}
public static explicit operator ValidatedName(string x)
{
if (IsValid(x))
{
return new ValidatedName(x);
}
throw new InvalidCastException();
}
public static bool operator ==(ValidatedName x, ValidatedName y)
{
return x.Equals(y);
}
public static bool operator !=(ValidatedName x, ValidatedName y)
{
return !x.Equals(y);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return _value;
}
}
L'esempio mostra il string
cast in implicit
quanto ciò non può mai fallire, ma il string
cast in explicit
quanto ciò genererà valori non validi, ma ovviamente questi potrebbero essere entrambi implicit
o explicit
.
Si noti inoltre che si può inizializzare questa struttura solo tramite un cast string
, ma si può verificare se tale cast fallirà in anticipo usando il IsValid
static
metodo.
Questo sembra essere un buon modello per imporre la convalida dei valori di dominio che possono essere rappresentati da tipi semplici, ma non lo vedo usato spesso o suggerito e sono interessato al perché.
Quindi la mia domanda è: quali sono i vantaggi e gli svantaggi dell'utilizzo di questo modello e perché?
Se ritieni che questo sia un cattivo modello, vorrei capire perché e anche quello che ritieni sia la migliore alternativa.
NB Inizialmente avevo posto questa domanda su Stack Overflow ma era stata messa in attesa principalmente come basata sull'opinione (ironicamente soggettiva in sé) - speriamo che qui possa avere più successo.
Sopra è il testo originale, sotto un altro paio di pensieri, in parte in risposta alle risposte ricevute prima che andasse in attesa:
- Uno dei principali punti sollevati dalle risposte riguardava la quantità di codice della piastra della caldaia necessario per lo schema sopra, specialmente quando sono richiesti molti di questi tipi. Tuttavia, a difesa del modello, questo potrebbe essere in gran parte automatizzato usando i modelli e in realtà per me non sembra poi così male, ma questa è solo la mia opinione.
- Da un punto di vista concettuale, non sembra strano quando si lavora con un linguaggio fortemente tipizzato come C # applicare il principio fortemente tipizzato ai valori compositi, piuttosto che estenderlo a valori che possono essere rappresentati da un'istanza di un tipo incorporato?