Romanzo polimorfismo - qualche motivo per questo codice?

3

Come parte del mio lavoro su un'applicazione C # legacy, mi sono imbattuto in un nuovo (per me) uso di un'interfaccia & implementazioni concrete. Non riesco a pensare ad alcun motivo per cui dovresti fare quanto segue, ma sono abbastanza spesso, quindi forse qualcun altro può?

public interface IContract
{
    ContractImplementation1 Contract { get; }
    bool IsCollection { get; }
    bool Touched { get; set; }
}

public class ContractImplementation1 : IContract
{
    public ContractImplementation1(string propertyOne, string propertyTwo, string propertyThree, string propertyFour)
    {
        PropertyOne = propertyOne;
        PropertyTwo = propertyTwo;
        PropertyThree = propertyThree;
        PropertyFour = propertyFour;
    }

    public ContractImplementation1 Contract { get { return this; } }
    public bool IsCollection { get { return false; } }
    public bool Touched { get; set; }

    public string PropertyOne { get; private set; }
    public string PropertyTwo { get; private set; }
    public string PropertyThree { get; private set; }
    public string PropertyFour { get; private set; }

    public override string ToString()
    {
        if (string.IsNullOrEmpty(PropertyFour))
            return string.Format("{0} => {1}: {2}", PropertyOne, PropertyTwo, PropertyThree);
        else
            return string.Format("{0} => {1}: {2} {3}", PropertyOne, PropertyTwo, PropertyThree, PropertyFour);
    }

}

public class ContractImplementation2 : IContract
{
    public ContractImplementation1 Contract { get { return null; } }
    public bool IsCollection { get { return true; } }
    public bool Touched { get; set; }

    public List<ContractImplementation1> Contracts = new List<ContractImplementation1>();
}

Non riesco a capire come funziona il super-tipo con una proprietà che è un sottotipo di se stesso.
Seguendo la risposta di Cosmin: Non riesco a capire perché si dovrebbe avere il sottotipo come una proprietà dato che la proprietà si ripresenta sull'implementazione (piuttosto che un "genitore" dello stesso tipo cioè una diversa istanziazione del super-tipo).

    
posta stevef4000 26.02.2013 - 12:21
fonte

3 risposte

8

Le classi che hai mostrato sono implementazioni di nodi in una sorta di struttura ad albero. La proprietà IsCollection determina se si tratta di un nodo foglia o un nodo (?). Se IsCollection è true, non ci sono "dati" su questo nodo, quindi devi continuare lungo la struttura. Se IsCollection è false, questo è un nodo foglia, quindi è valido esaminare la proprietà Contract .

Il modello di dominio sembra indicare che un contratto può essere costituito da altri contratti e così via.

La ragione più probabile per cui è così com'è, è perché inizialmente avevamo appena ContractImplementation1 , e poi si sono resi conto che i contratti potevano essere nidificati in questo modo. Quindi qualcuno ha creato l'interfaccia IContract per il nodo generico in un albero (e in realtà avrebbe dovuto chiamarlo IContractNode o qualcosa) e quindi ha creato ContractImplementation2 per essere il ramo. Questo non era il modo giusto per farlo. La strada giusta sarebbe più simile a:

interface IContractNode 
{
    bool IsLeaf { get; }
    // Only access this is IsLeaf is true
    Contract Contract { get; }
    // Only access this if IsLeaf is false
    ReadOnlyCollection<IContractNode> Children { get; }
}

class ContractNodeLeaf : IContractNode 
{
    public ContractNodeLeaf(Contract contract)
    {
        if(contract == null) throw new ArgumentNullException("contract");
        this.Contract = contract;
    }
    public bool IsLeaf { get { return true; } }
    public Contract Contract { get; private set; } 
    public ReadOnlyCollection<IContractNode> Children 
        { get { return new List<IContractNode>().AsReadOnly(); }
}

class ContractNodeBranch : IContractNode 
{
    public ContractNodeBranch(ReadOnlyCollection<IContractNode> children)
    {
        if(children== null) throw new ArgumentNullException("children");
        this.Children= children;
    }
    public bool IsLeaf { get { return false; } }
    public Contract Contract 
        { get { throw new InvalidOperationException("I'm not a leaf!"); } 
    public ReadOnlyCollection<IContractNode> Children { get; private set;}
}

... dove Contract è la classe originale, da prima della modifica.

    
risposta data 26.02.2013 - 13:52
fonte
9

I can't get my head around the super-type having a property that is a sub-type of itself.

Il codice che hai postato, con PropertyOne , PropertyTwo ... PropertyFour è probabilmente solo un esempio, quindi mi assumo la libertà di fornire il mio esempio di un caso in cui il super-tipo ha proprietà che sono sottotipi di se stesso ha un senso ovvio:

Diciamo che hai una classe Human con sottoclasse Male e sottoclasse Female . Se vuoi aggiungere le proprietà Mother e Father a Human , ha senso che siano del sottotipo Female e Male .

Dato questo esempio, la risposta generica è: se il super-tipo e il sottotipo hanno abbastanza in comune, perché no? Posso immaginare questo tipo di situazione che si presenta abbastanza spesso in strutture simili ad alberi.

    
risposta data 26.02.2013 - 12:34
fonte
1

Sembra un'implementazione (mal fatta) del pattern Composito . Il suo scopo principale è quello di consentire di trattare un oggetto allo stesso modo di un insieme di tali oggetti, senza dover distinguere tra i due ovunque nel codice chiamante. Dico mal fatto perché se usato correttamente non dovresti aver bisogno della proprietà IsCollection .

Questo è uno schema molto utile e comune con gli alberi. Puoi chiamare i metodi su un nodo senza preoccuparti se è una foglia o contiene un'intera sottostruttura.

    
risposta data 26.02.2013 - 20:33
fonte

Leggi altre domande sui tag