In primo luogo, non esiste un'unica soluzione concepibile modellata in qualsiasi linguaggio OO utilizzando l'ereditarietà da una classe base, che non potrebbe essere modellata in modo efficace utilizzando la composizione.
L'ereditarietà da una classe base può occasionalmente rendere il codice più conveniente da scrivere rispetto alla composizione, ma nulla sul comportamento risultante della classe derivata sarà migliore o diverso rispetto alla soluzione alternativa che usa la composizione .
Tipicamente, usando l'ereditarietà, ti stai solo risparmiando dalla scrittura di una piccola quantità di codice extraplanplan; ma nel salvare quel codice in più, si sta arrendendo molta flessibilità costringendo la classe derivata a dipendere interamente dalla sua classe base.
Quindi, tenendo presente quanto sopra, e per rispondere direttamente alla tua domanda, per risolvere un problema in cui potresti considerare eredità multipla come soluzione, ciò che stai cercando in realtà è una soluzione che è più flessibile, quindi è necessario fare affidamento sulla composizione e accettare un po 'di codice standard.
es.
// ERROR!
public class MyIllegalClass : MyBaseClass, MyOtherBaseClass
{
}
Può essere rappresentato in questo modo, anche se non avrai alcuna flessibilità extra:
public class MyClass
{
public MyBaseClass Base { get; private set; }
public MyOtherBaseClass OtherBase { get; private set; }
public MyClass()
{
// NOT a good solution. MyClass is still dependent on these.
Base = new MyBaseClass();
OtherBase = new MyOtherBaseClass();
}
}
Per una maggiore flessibilità:
// Better - has more flexibility than multiple inheritance
public class MyClass
{
public MyBaseClass Base { get; private set; }
public MyOtherBaseClass OtherBase { get; private set; }
public MyClass(MyBaseClass base, MyOtherBaseClass otherBase)
{
// BETTER. Anything which derives from these will also work.
Base = base;
OtherBase = otherBase;
}
}
Nel tuo esempio specifico, il problema che hai non è solo "Cosa succede se voglio un RotatableField
?" ma anche "Cosa succede se voglio un RotatableConnectableField
?" e in seguito potresti persino domandarti "cosa succede se desidero un ColourableRotatableField
?" ecc.
A mano a mano che aumenta la complessità del tuo Field
, l'ereditarietà ti condurrà lungo un percorso molto disordinato, cioè cercando di riconciliare Rotatable , Connectable e Colourable comportamenti, con qualsiasi altra cosa possa venire in futuro; ecco perché altre risposte hanno suggerito l'uso delle interfacce - La tua classe Field
non dovrà preoccuparsi di nulla sul fatto che usi un rotatore, un connettore, ecc.
es. utilizzando l'iniezione delle dipendenze del costruttore e trattando separatamente Rotate / Connect:
public class Field
{
private IRotator _rotator;
private IConnector _connector;
public Field(IRotator rotator, IConnector connector)
{
_rotator = rotator;
_connector = connector;
}
}
public class Program
{
public static void Main(string[] args)
{
var myField = new Field(new NinetyDegreeRotator(), new FieldConnector());
}
}
oppure invece di iniettare Rotate
e Connect
come preoccupazioni separate, potresti razionalizzarli in un'unica interfaccia:
public class Field
{
public void AddModifier(IFieldModifier modifier)
{
modifier.Modify(this);
}
}
public class Program
{
public static void Main(string[] args)
{
var myField = new Field();
myField.AddModifier(new NinetyDegreeRotator());
myField.AddModifier(new FieldConnector());
myField.AddModifier(new BorderColour(Colour.Red));
// etc.
}
}
In entrambi i casi, Field
rimarrebbe indipendente dal comportamento che ruota o si connette. L'interfaccia (o le interfacce) ti darà maggiore estensibilità in futuro quando decidi di voler cambiare quel comportamento in qualche altro modo.