lundi 14 mars 2016

Design Pattern for derived classes also having a derived child property

I'm looking for a class structure or design pattern to implement a base class that has a list of "base items", where several derived classes have the same list but that list is of derived "base items".

Here's a vastly stripped down example (ignore the accessibility of properties, they wouldn't actually all have public setters and default constructors):

public class BaseTransaction {
    public List<BaseTransactionItem> Items { get; set; }
    public void AddItem(string description, int quantity, decimal price)
    {
        // Add a new BaseTransactionItem to Items
    }
}

public class BaseTransactionItem {
    public string Description { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

public class OrderTransaction : BaseTransaction {
    public List<OrderTransactionItem> Items { get; set; }
    public int Deposit { get; set; }
    public void SetDeposit(int depositAmount)
    {
        // Do some stuff to set the deposit.
    }
}

public class OrderTransactionItem : BaseTransactionItem
{
    public int QuantityFulfilled { get; set; }
}

public class RetailTransaction : BaseTransaction {
    public List<RetailTransactionItem> Items { get; set; }
    public List<Tender> Tenders { get; set; }
    public void AddTender(Tender tender)
    {
        // Add a tender to the RetailTransaction
    }

    public decimal TotalTax
    {
        get { return Items.Sum(i => i.Tax); }
    }
}

public class RetailTransactionItem : BaseTransactionItem
{
    public decimal Tax { get; set; }
}

The way I need to work with these classes is that you start with a BaseTransaction and add some items to it, and then it can become either an OrderTransaction or a RetailTransaction. These both share most of their logic and properties with a BaseTransaction but have specific extra fields and methods, as well as the List<BaseTransactionItem> becoming a List<OrderTransactionItem> or a List<RetailTransactionItem> respectively.

Further more, after a BaseTransaction is "promoted" to a RetailTransaction, it may be "demoted" back to a BaseTransaction and then "promoted" to an OrderTransaction (but never from a RetailTransaction to an OrderTransaction in this case).

I've tried several approaches to this, with generics, the Decorator pattern (which doesn't seem appropriate), TypeConverters, and yet nothing seems to fit. The only possible solution I've thought of that works is having the RetailTransaction class have a constructor that takes a BaseTransaction and copying over all the properties and converting the list using .Cast<RetailTransactionItem> but this will make maintaining the derived classes pretty difficult.

If it wasn't for the list type needing to change this would be a simple case of using inheritance. I'm completely open to alternative approaches such as those favouring composition over inheritance but since the RetailTransaction and OrderTransaction classes truely are more specific versions of BaseTransaction, inheritance seems to fit - at least in my mind.

Aucun commentaire:

Enregistrer un commentaire