Quite a mouth-full of a question but its a OO principle I've struggling with. Lets say i have an e-commerce app and there is the concept of payment method, examples could be CreditCard, Paypal, Apple pay etc. The user has a choice of which payment method to select so i need to present them all in a list on the screen and depending on the selection this will be used to drive a UI, presenting different text/images/interactions as well as will be serialised slightly differently into a Payment request over the wire.

Here is some code:

public class PaypalPayment : PaymentMethod {

    public string Token;
    public string String;

}

public class CreditCardPayment : PaymentMethod {

    public Address Address;
    public CreditCard CreditCard;

}

interface PaymentMethod {

}


public class Booking {
    public PaymentMethod PaymentMethod; //generic object

    //or

    public PaypalPayment PaypalPayment;
    public CreditCardPayment CreditCardPayment;
}

So in my booking class i can either have a generic payment object referred to by the interface but i cant get to the underlying type without casting which sucks, as in reality them don't share any common properties doman-wise. Alternatively i have multiple properties which feels bad in a different way. The user can only select one payment method so the others will have to null or some kind of null object, i would have to either query an enum or ask each payment method if it is null. Also my payment method select screen is a bit more cumbersome as i cant just iterate on a generic type i have to explicitly build up the list.

In theory i could add some methods to the PaymentMethod interface, such as Serialise() or some UI presentation methods which all the payment methods have in common but then i would have to implement them in my model object which i don't want to do in my model layer.

Overall i dont have a clean solution for this in your typical object orientated language. I wrote this in c# but this could apply to any OO language.

2

There are 2 best solutions below

3
Holger Thiemann On

To have an interface and can't get to the underlying type is exactly what abstraction and loose coppling is striving for. This should not suck but should be desirable. To get all possible instances to choose from, you could use a repository that returns a collection of all your payment methods. To implement this repository you could use your favored ORM and load them from the database, use your favored IoC/DI Container and let it create all implementors of your interface, or hard code the creation. Whatever suits you needs and the needs of the project. If you use an interface for the repository as well, you can later swap the implementation.

0
guillaume31 On

Data seems to have been segregated away from logic in your design. As a consequence, Booking probably has to indulge in Inappropriate Intimacy with its Payment in order for the behavior to take place, hence the casting problem.

An idiomatic OO implementation would 1/ define a clear responsibility and 2/ encapsulate operations and data for it in the same class. Then you can have an abstraction on top of a family of these classes so that their behavior can be called uniformly by consumer code.

The Strategy aka Policy pattern might be a good choice for payment.

UI wise, it may be better to avoid using abstractions and have different UIs altogether for different payment methods. Or, you could have an abstract UI payment model whose concrete implementations know how to render themselves.