Replace Conditional With Polymorphism
I am trying to understand this clean code practice with an example. Consider a class Product having switch case for discount. I am trying to replace switch statement with polymorphism.
Before code:
class Product {
String priceCode;
int discount;
Product(String priceCode) {
setDiscount(priceCode);
}
public int getDiscount() {
return discount;
}
public void setDiscount(String priceCode) {
switch (priceCode) {
case "CODE1":
discount = // some logic;
case "CODE2":
discount = // some other logic;
case "CODE3":
discount = // some other logic;
}
}
}
In below code as you can see I removed switch statement but I still have if conditions to create an object of discountStrategy. My question is I still have if conditions which I am trying to remove with Polymorphism.
After code:
class Product {
String priceCode;
DiscountStrategy discountStrategy;
Product(String priceCode) {
setDiscount(priceCode);
}
public int getDiscount() {
return discountStrategy.getDiscount();
}
public void setDiscount(String priceCode) {
if (priceCode.equals("CODE1")) {
discountStrategy = new DiscountStrategy1();
} else if (priceCode.equals("CODE2")) {
discountStrategy = new DiscountStrategy2();
}
// ...
}
}
interface DiscountStrategy {
public int getDiscount();
}
class DiscountStrategy1 implements DiscountStrategy {
public int getDiscount() {
// calculate & return discount;
}
}
class DiscountStrategy2 implements DiscountStrategy {
public int getDiscount() {
// calculate & return discount;
}
}
class DiscountStrategy3 implements DiscountStrategy {
public int getDiscount() {
// calculate & return discount;
}
}
Can you please help me understand this concept with better implementation of this example?
Answer
I think that Product class must not be aware about the discount creation process, it should only use a discount. So, my suggestion is to create a discount factory with a Map that will hold different discount implementations:
class DiscountFactory {
private static final Map<String, DiscountStrategy> strategies = new HashMap<>();
private static final DiscountStrategy DEFAULT_STRATEGY = () -> 0;
static {
strategies.put("code1", () -> 10);
strategies.put("code2", () -> 20);
}
public DiscountStrategy getDiscountStrategy(String priceCode) {
if (!strategies.containsKey(priceCode)) {
return DEFAULT_STRATEGY;
}
return strategies.get(priceCode);
}
}
After that, the Product class can be simplified:
class Product {
private DiscountStrategy discountStrategy;
Product(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public int getDiscount() {
return discountStrategy.getDiscount();
}
}
Functional interface will allow you to create different implementations using lambda expressions:
interface DiscountStrategy {
int getDiscount();
}
And finally, example of the use of a product together with discount:
DiscountFactory factory = new DiscountFactory();
Product product = new Product(factory.getDiscountStrategy("code1"));
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM