While refactoring some code, I found that we should be using some polymorphism in several places rather than having to have a bunch of if/else blocks all over the place.
While the object oriented classes were very easy to make, the issue comes when we have to decide what implementation to use. There seems to be many solutions but I wanted to see if there were any more elegant or "best practice" ways of doing this.
So essentially, say for the sake of example, I am going to choose a Credit Card and for each credit card type, I have an implementation. Therefore I have a class structure like this:
Each of the child classes would extend the CreditCard super class.
Now in the web application, I would pass down a String that represents the card type the user has chosen. Now I need to route that to the actual implementing class itself. This is where the plethora of choices comes into play.
Please do let me know if there are any better choices or if I'm stuck with these..
Factory:
@Autowired @Qualifier("visa") private CreditCard visa;
@Autowired @Qualifier("mastercard") private CreditCard mastercard;
@Autowired @Qualifier("amex") private CreditCard amex;
public CreditCard getCreditCard(String input) {
{
if ("visa".equals(input)) {
return visa;
} else if ("mastercard".equals(input)) {
return mastercard;
} else if ("amex".equals(input)) {
return amex;
}
return null;
}
Map:
@Autowired HashMap<String, CreditCard> creditCardImpls;
public CreditCard getCreditCard(String input) {
return creditCardImpls.get(input);
}
ApplicationContext getBean:
@Autowired private ApplicationContext applicationContext;
public CreditCard getCreditCard(String input) {
return applicationContext.getBean(input);
}
The issue I am seeing here is that with the factory, I'm going to have to autowire in potentially several different fields if we're going to add many more credit card types in the future. Then, the issue with the Map is that we're not using Spring to grab the bean. For t he getBean from the ApplicationContext, we're not following the IoC that Spring provides.
What would be the best solution or best practice for this problem?
First some comment on your solutions, then my own proposal.
input
) mappings, but also every time you want to introduce a new credit card type, you need to modify the factory class code.CreditCard
abstraction. On the contrary to your comment that "it's not using Spring to determine which bean to use", the map has bean IDs as keys and is autowired by Spring, so we are in fact using Spring extensively here. However, since the map keys are bean IDs, that mixes the business logic (credit card type - input
) with the technical implementation (Spring bean ID). What if you want to implement a separate bean to handle "mastercard platinum" credit card type? For technical reasons, you cannot have such a bean ID (due to the space). Or at some point you'll want that a certain bean can handle multiple card types. Then, you'd need to implement another translation map between the business identifiers and bean IDs. See my solution below for an enhancement.My suggestion would be to separate the business key from the bean ID. So we need the CreditCard
to be able to say which credit card type it can handle.
abstract class CreditCard {
public abstract String getType();
}
Then each subclass returns its own type.
Or
abstract class CreditCard {
private String type;
protected CreditCard(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
and each subclass calls the super(<the subclass type>)
constructor within their own constructor.
Then, in the factory, you can implement is as such.
@Autowire private Collection<CreditCard> creditCards;
private Map<String, CreditCard> creditCardsMap = new HashMap<>();
@PostCostruct
public void mapCreditCards() {
for (CreditCard creditCard : creditCards) {
creditCardsMap.put(creditCard.getType(), creditCard);
}
}
public CreditCard getCreditCard(String type) {
return creditCardsMap.get(type);
}
The @PostConstruct method will run after the creditCards
(all the beans with the type CreditCard
) have been autowired, thus allowing for both dynamic discovery and mapping under a business key rather than a technical key.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With