Immagina una classe device
che rappresenta un dispositivo mobile fisico reale, con campi come Enabled
, Platform
, Model
IMEI
, ecc. Quindi, una classe operation
, che è qualcosa che deve essere eseguito su un determinato dispositivo, ad esempio DeviceEnablementOperation
o LockOperation
. Questa è una relazione "un dispositivo su più operazioni" e un'operazione non può esistere senza essere correlata a un dispositivo esistente. Posso modellarlo in modo tale che il sistema non consenta determinati tipi di operazioni ai dispositivi con determinate piattaforme? Ad esempio, un dispositivo Android non consentirebbe un DeviceEnablementOperation
collegato ad esso, mentre un dispositivo Windows Phone sarebbe.
La nostra modellazione attuale è iniziata considerando solo i dispositivi Android. All'epoca, abbiamo creato il concetto di operazioni come fonte di eventi di un uomo povero, in modo da poter tenere traccia di ciò che è stato fatto contro un determinato dispositivo tramite la nostra API. In seguito, abbiamo iniziato a supportare anche i dispositivi iOS e abbiamo introdotto il campo Platform
utilizzando una classe di enumerazione per differenziarli, in questo modo:
public enum PlatformType : short
{
/// <summary>
/// Device is an Android.
/// </summary>
Android = 0,
/// <summary>
/// Device is an Windows platform.
/// </summary>
Windows = 1,
//// Can not place the name iOS.
/// <summary>
/// Device is an iOS platform.
/// </summary>
Ios = 2,
}
In quel momento, i requisiti venivano soddisfatti correttamente, ma ora è necessario limitare determinati tipi di operazioni a determinate piattaforme. Il modello corrente non lo limita automaticamente, quindi l'implementazione corrente considera un elenco hardcoded di operazioni supportate per ogni piattaforma e lo usa per rifiutare la creazione di alcune operazioni su determinati dispositivi.
Quello che mi piacerebbe fare è cambiare la modellazione in modo che questa restrizione venga applicata in modo più efficace. Ho pensato di utilizzare l'ereditarietà e di creare AndroidDevice
, IosDevice
e WindowsPhoneDevice
classi, ma non riesco ancora a vedere come potrei limitare la relazione con le operazioni stesse per coprire questo requisito. Un dispositivo ha operazioni, ma non tutti i dispositivi supportano tutte le operazioni.
La modellazione per le operazioni stesse è un'eredità molto semplice, come questa:
public abstract class Operation
{
protected Operation()
{
this.Logs = new List<OperationLog>();
}
public DateTime? CreationDate { get; set; }
public virtual Device Device { get; set; }
public int DeviceId { get; set; }
public OperationStatus GeneralStatus { get; set; }
public int Id { get; set; }
public DateTime? LastUpdate { get; set; }
public ICollection<OperationLog> Logs { get; internal set; }
public DateTime? ReceiveDate { get; set; }
}
E poi i tipi di operazione derivati, come quelli che ho menzionato:
public sealed class LockOperation : Operation
{
/// <summary>
/// Gets or sets a value indicating whether the device must be locked or not.
/// </summary>
public bool Lock { get; set; }
/// <summary>
/// Gets or sets the device's lock password.
/// </summary>
public string Password { get; set; }
}
e
public sealed class EnablementOperation : Operation
{
/// <summary>
/// Gets or sets a value indicating whether the device should be enabled or disabled.
/// </summary>
public bool Enabled { get; set; }
}
È possibile modellarlo per riflettere meglio il nuovo requisito che voglio? In qualche modo avrei bisogno di un modo per dire se una determinata operazione è supportata per un determinato dispositivo e consentire solo la relazione di esistere se l'operazione è supportata.
Suppongo di poter provare qualcosa utilizzando i generici e l'ereditarietà sia sul dispositivo che sulle classi operative, ad esempio:
public abstract class Device<TOperation>
{
public ICollection<TOperation> Operations { get; set; }
...
}
public sealed class AndroidDevice : Device<AndroidOperation> {...}
public abstract class Operation {...}
public abstract class AndroidOperation {...}
public sealed LockOperation : AndroidOperation {...}
Ma questo particolare approccio avrebbe bisogno di più eredità per funzionare, e dal momento che sto usando C # non è possibile. Potrei quindi provare a utilizzare le interfacce per differenziare le operazioni, ma ritengo sospetto che sarebbe un problema quando si effettua il mapping su un database in un secondo momento utilizzando un ORM.
Inoltre non riesco davvero a rompere tutto nelle proprie classi completamente isolate, perché ho bisogno del concetto generale di "dispositivi che hanno operazioni", indipendentemente dalla piattaforma o dal tipo di operazione. Ecco perché le classi di base esistono nel nostro modello attuale.