Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

As part of my work on a legacy C# application I\'ve come across a novel (to me)

ID: 643762 • Letter: A

Question

As part of my work on a legacy C# application I've come across a novel (to me) use of an interface & concrete implementations. I can't think of any reason why you'd do the following, but I'm pretty thick, so maybe someone else can?

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>();
}
I can't get my head around the super-type having a property that is a sub-type of itself.
Following Cosmin's answer: I can't get my head around why you'd have the sub-type as a property given that the property returns itself on the implementation (rather than a 'parent' of the same type i.e. a different instantiation of the super-type).

Explanation / Answer

The classes you've shown are implementations of nodes in some kind of tree structure. The IsCollection property determines if this is a leaf node or a branch(?) node. If IsCollection is true, then there is no "data" at this node, so you have to continue down the tree. If IsCollection is false, then this is a leaf node, so it's valid to look at the Contract property.

The domain model seems to indicate that a contract can be made up of other contracts, and so on.

The most likely reason for it being the way it is, is because originally we just had ContractImplementation1, and then they realized that contracts could be nested like this. So someone created the IContract interface for the generic node-in-a-tree (and really should have named it IContractNode or something) and then created ContractImplementation2 to be the branch. This wasn't really the right way to do it. The right way would be more like:

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;}
}
... where Contract is the original class, from before the change.