Aggiunta di funzionalità di base (ereditate) alle classi che non si controllano

4

Ho un set di classi da una libreria di terze parti. Queste classi utilizzano una struttura di ereditarietà per condividere la logica. Vorrei aggiungere un livello di astrazione al centro dell'albero di ereditarietà per aggiungere funzionalità a tutte le implementazioni di bambini (concrete).

Ecco un esempio semplificato delle classi nella lib di terze parti:

public interface IAnimal
{
    bool IsMammal { get; }
}

public abstract class Animal : IAnimal
{
    public abstract bool IsMammal { get; }
    public string Name { get; set; }
}

public class Cat : Animal
{
    public override bool IsMammal { get { return true; } }
    public void Pur() {}
}

public class Dog : Animal
{
    public override bool IsMammal { get { return true; } }
    public void Fetch() {}
}

public class Snake : Animal
{
    public override bool IsMammal { get { return false; } }
    public void ShedSkin() {}
}

Vorrei aggiungere il concetto di AnimalWithSuperPower . Questi tipi di animali dovrebbero avere 1 proprietà aggiuntiva; %codice%. Mi piacerebbe avere classi come SuperPower che derivano da CatWithSuperPower , Cat , & AnimalWithSuperPower in modo che possa accedere a tutte le funzionalità di quelle.

Ecco la definizione di Animal :

public enum SuperPower { Invisibility, SuperStrength, XRayVision }

La mia prima idea era di usare ereditarietà multipla. Sfortunatamente, C # non supporta più classi base.

private abstract class AnimalWithSuperPower : Animal
{
    public SuperPower SuperPower { get; set; }
}

// doesn't compile because you can't extend 2 classes
private class DogWithSuperPower : AnimalWithSuperPower, Dog {}

Il mio prossimo tentativo utilizza una combinazione di ereditarietà, composizione e generici per provare a fornire la funzionalità delle classi di base.

private abstract class AnimalWithSuperPower<TAnimalType> : Animal where TAnimalType : IAnimal
{
    public SuperPower SuperPower { get; set; }

    protected readonly TAnimalType Animal;

    protected AnimalWithSuperPower()
    {
        Animal = (TAnimalType) Activator.CreateInstance(typeof(TAnimalType));
    }
}

private class SuperCat : AnimalWithSuperPower<Cat>
{
    public override bool IsMammal { get { return Animal.IsMammal; } }
}

private class SuperCatWithPur : AnimalWithSuperPower<Cat>
{
    public override bool IsMammal { get { return Animal.IsMammal; } }

    public void Pur() // needing duplicate pass-through methods/properties like this is painful :(
    {
        Animal.Pur();
    }
}

private static void ExampleUsage()
{
    var invisibleCat = new SuperCat { SuperPower = SuperPower.Invisibility };
    invisibleCat.Pur(); // doesn't compile - can't access Pur() method because doesn't extend Cat

    var xrayCat = new SuperCatWithPur { SuperPower = SuperPower.XRayVision };
    xrayCat.Pur(); // only works because I exposed the method with the EXACT same signature
}

Questa soluzione non è molto buona (IMO) a causa di questi motivi:

  • SuperPower e SuperCat non sono in realtà istanze di SuperCatWithPur
  • qualsiasi metodo che desideri utilizzare da Cat deve essere specchiato nella classe contenitore
  • sembra un po 'disordinato: Cat è un AnimalWithSuperPower ma richiede anche un parametro di tipo Animal

Ho anche provato a farlo con un metodo di estensione, ma non era migliore dei due precedenti tentativi:

private abstract class AnimalWithSuperPower : Animal
{
    public SuperPower SuperPower { get; set; }
}

private static AnimalWithSuperPower WithSuperPower(this Animal animal, SuperPower superPower)
{
    var superAnimal = (AnimalWithSuperPower) animal;
    superAnimal.SuperPower = superPower;
    return superAnimal;
}

private static void ExampleUsage()
{
    var dog = new Dog { Name = "Max" };
    var superDog = dog.WithSuperPower(SuperPower.SuperStrength);
    superDog.Fetch(); // doesn't compile - superDog isn't an instance of Dog
}

Se avessi il controllo delle classi di terze parti, probabilmente potrei farlo in modo pulito introducendo una nuova classe nel mezzo dell'albero ereditario, ma non posso

La mia domanda:

Come posso modellare Animal in modo che:

    Le istanze
  • sono considerate di tipi AnimalWithSuperPower (o sottoclasse appropriata), Cat , & %codice%
  • tutti i metodi e le proprietà sono disponibili senza chiamate extra pass-through
posta Jesse Webb 26.07.2013 - 21:59
fonte

1 risposta

6

Perché non usi solo le interfacce?

Se ti preoccupi della condivisione delle funzionalità, puoi utilizzare i metodi di estensione per fungere da classe pseudo-base. Non è esattamente l'ideale, ma dovrebbe ottenere ciò che stai cercando.

Qualcosa sulla falsariga di:

public interface IAnimalWithSuperPower {
    public SuperPower power { get; set; }
}

public class SuperCat : Cat, IAnimalWithSuperPower {
    public SuperPower power { get; set; }
    public SuperCat() {
        SuperPower = SuperPower.SuperStrength;
    }
}

public static void UseSuperPower(this IAnimalWithSuperPower animal) {
    animal.power.doSomething();
}
    
risposta data 26.07.2013 - 22:45
fonte

Leggi altre domande sui tag