Get all associate/composite objects inside an object (in Abstract way)

Solution 1:

I think you might want to step back from the design for a moment. What I've heard is this:

A payment consists of one or more components, and each component can be one of a variety of types

What it sounds like you need is a Payment table, then a PaymentComponent table with a foreign key relation back to the Payment table. You can then implement inheritance on the PaymentComponent table for your various forms of payment.

Solution 2:

You can try to use an abstraction layer or a data acces layer that will be generic of type T. Or at least make the methods generic.

Solution 3:

You basically have a few of issues here:

  1. How to model the payment types

    Lets assume we want to go at this the classic OOP way:

    You need a base class, Payment (or PaymentBase) which is abstract and various class which inherit from it e.g. PaymentInCash, PaymentWithCreditCard and etc.

    An alternative could be adding PaymentDetails to Payment and creating a hierarchy of PaymentDetails, if you choose to do this, the replace Payment with PaymentDetails in all the following points.

    For payments with multiple methods, you could either:

    a. Have a collection of PaymentDetails under a Payment
    or
    b. Create a type called AggregatePayment which has a list of Payments.

  2. How to map the payment types to tables

    Both TPT and TPH are valid here...

    For TPT use one table for Payment and one table for each kind of payment.
    All the inheriting type's tables PK's should be an FK to the base type's table.
    If you have multiple levels of hierarchy, you can use either TPT or TPH on the second (or any other) level if you are using EF.

    For TPH use one table with a discriminator column (e.g. PaymentType), and mark each column that is not shared between all entities in the hierarchy as nullable. Do not use the same column for different properties in different entities. In EF, map each entity to the same table with the condition PaymentType = (number goes here) and (column name(s) that should not be null) is not null.

    My recommendation is, if you have many narrow types (few properties each) go with TPH, if you have a few wide types go with TPT.

  3. Which design pattern / code technique to use for payment algorithm

    You have more options here:

    a. Use partial classes and put an abstract ProcessPayment() method on the base class and override in the inheriting classes.

    b. Use a base PaymentProcessor class and a specific PaymentProcessor per payment type e.g. PaymentInCashProcessor. In this method you can use reflection to load the correct PaymentProcessor type by either storing a dictionay or better yet, using generics:

abstract class PaymentProcessor
{
}

abstract class PaymentProcessor<TPayment> : PaymentProcessor
    where TPayment : class, Payment
{
}

class PaymentInCashProcessor : PaymentProcessor<PaymentInCash>
{
}

// Use reflection to find types that inherits from PaymentProcessor<PaymentInCash>
// create an instance of the type you found
// then cast the instance to PaymentProcessor<PaymentInCash> to use