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.
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.