Cosa fare se ho bisogno di più di una classe base in C #? [chiuso]

0

Diciamo che ho una griglia con campi quadrati. Per i campi ho una classe astratta Field . Questa classe ha diverse sottoclassi, ad esempio EmptyField o RoadField . Alcuni di questi campi possono essere collegati tra loro, quindi ho un'interfaccia IConnectable che queste classi implementano. Ora ogni campo collegabile dovrebbe avere un oggetto Connections . Tuttavia, le interfacce non possono avere oggetti membro. La mia idea era che al posto di un'interfaccia io usassi un'altra classe astratta per questo, ConnectableField , che è una sottoclasse di Field e la classe genitore per tutte le classi collegabili. Ma cosa faccio se voglio aggiungere un'altra classe astratta, ad esempio un RotatableField che ha un oggetto Rotation . Non riesco a creare un'interfaccia a causa dell'oggetto membro. Ma non posso ereditarmi anche perché una classe può avere solo una classe base. Cosa devo fare?

Modifica

(Il seguente non è in realtà il motivo per cui ho posto questa domanda, è solo un esempio di perché non penso di poter usare le interfacce.)

Non riesco a renderlo un'interfaccia perché voglio dichiarare un enum, come questo:

interface IConnectable {
    public enum Orientation_CW { ZERO, NINETY, ONEEIGHTY, TWOSEVENTY };
}

Non mi consentirà di farlo con il seguente errore:

Error CS0524 'IConnectable.Orientation_CW': interfaces cannot declare types

    
posta gartenriese 10.03.2016 - 15:28
fonte

3 risposte

3

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.

    
risposta data 10.03.2016 - 17:49
fonte
3

Definire l'enumerazione separatamente, quindi includerla come membro nell'interfaccia. Non è possibile definire un'enumerazione nella definizione di un'interfaccia, ma l'enumerazione definita può essere un membro nell'interfaccia.

Puoi farlo nello stesso file dell'interfaccia se vuoi, come ho fatto qui:

namespace YourNamespace
{
    public enum MyEnum
    {
        A,B,C
    }
    public interface IEnumInterface
    {
        MyEnum SelectedEnum { get; set; }
    }
}

MA RISPONDI ALLA TUA DOMANDA: Vorrei aggiungere interfacce per le classi da implementare con le enumerazioni appropriate. Da lì, le tue classi implementeranno quindi IRotatable, IConnectable, ecc.

    
risposta data 10.03.2016 - 15:58
fonte
1

Hai 2,5 scelte:

Soluzione n. 1 Hai una classe che implementa IConnectableField e IRotatableField in cui funzioni specifiche potrebbero essere classi virtuali e derivate che non richiedono che nessuna di queste funzioni possa eseguire l'override senza alcun comportamento.

Soluzione n. 2 Utilizza l'interfaccia e disponi di una gerarchia con solo uno (il preferito *) di 2: IConnectableField o IRotatableField come una classe basata e l'altro come interfaccia. È possibile creare una classe che implementa entrambe le interfacce, il che consente di risolvere un problema per qualsiasi classe derivata che non richiede le funzioni di interfaccia preferite *. Puoi quindi derivare da una classe di base superiore come "Field" e implementare manualmente con codice duplicato (svantaggio dell'ereditarietà singola), il tuo comportamento previsto.

Soluzione n. 2.5 Usa la soluzione n. 2 e per evitare il codice duplicato, inserisci il codice in una classe statica di supporto che sappia come ruotare o connettere un campo.

    
risposta data 10.03.2016 - 16:16
fonte

Leggi altre domande sui tag