C'è un problema di progettazione che ho trovato mentre implementavo un'interfaccia:
Diciamo che esiste un'interfaccia Device
che promette di fornire funzionalità PerformA()
e GetB()
. Questa interfaccia sarà implementata per più modelli di un dispositivo. Cosa succede se un modello ha una funzionalità aggiuntiva CheckC()
che non ha equivalenti in altre implementazioni?
Ho trovato diverse soluzioni, nessuna delle quali sembra conforme alle linee guida sulla progettazione dell'interfaccia:
- Per aggiungere il metodo
CheckC()
all'interfaccia e lasciare uno dei suoi implementazioni vuote:
interface ISomeDevice
{
void PerformA();
int GetB();
bool CheckC();
}
class DeviceModel1 : ISomeDevice
{
public void PerformA() { // do stuff }
public int GetB() { return 1; }
public bool CheckC() {
bool res;
// assign res a value based on some validation
return res;
}
}
class DeviceModel2 : ISomeDevice
{
public void PerformA() { // do stuff }
public int GetB() { return 1; }
public bool CheckC() {
return true; // without checking anything
}
}
Questa soluzione sembra errata in quanto una classe implementa un'interfaccia senza veramente implementando tutti i metodi richiesti.
- Per omettere il metodo
CheckC()
dall'interfaccia e utilizzare il cast esplicito per chiamarlo:
interface ISomeDevice
{
void PerformA();
int GetB();
}
class DeviceModel1 : ISomeDevice
{
public void PerformA() { // do stuff }
public int GetB() { return 1; }
public bool CheckC() {
bool res;
// assign res a value based on some validation
return res;
}
}
class DeviceModel2 : ISomeDevice
{
public void PerformA() { // do stuff }
public int GetB() { return 1; }
}
class DeviceManager
{
private ISomeDevice myDevice;
public void ManageDevice(bool newDeviceModel)
{
myDevice = (newDeviceModel) ? new DeviceModel1() : new DeviceModel2();
myDevice.PerformA();
int b = myDevice.GetB();
if (newDeviceModel)
{
DeviceModel1 newDevice = myDevice as DeviceModel1;
bool c = newDevice.CheckC();
}
}
}
Questa soluzione sembra rendere l'interfaccia incoerente.
- Per il dispositivo che supporta
CheckC()
: per aggiungere la logica diCheckC()
nella logica di un altro metodo presente nell'interfaccia. Questa soluzione non è sempre possibile.
Quindi, qual è il design corretto da utilizzare in questi casi? Forse la creazione di un'interfaccia dovrebbe essere abbandonata del tutto a favore di un altro design?