Sikor Sikor - 15 days ago 9
Java Question

Most efficient way to remove duplicated code from multiple strategies

We have 3 types of attributes in our project: CategoryAttribute, ProductAttribute and ProductTypeAttribute. These are outside of our control as they come from autogenerated classes and may contain attribute values of different types e.g. text, number or image. Now, each attribute has its own strategy to retrieve attributeValue. For simplicity, let's assume that all 3 of them have TextStrategy, NumberStrategy and ImageStrategy.

Example strategy:

@Component
public class CategoryImageAttributeStrategy implements CategoryAttributeStrategy {

@Override
public boolean isApplicable(CategoryAttribute attribute) {
return attribute.getImage() != null;
}

@Override
public Object getAttributeValue(CategoryAttribute attribute) {
//return attribute value here
//may be different or may be the same
//for ProductImageAttributeStrategy and ProductTypeImageAttributeStrategy
}


}

While getting image value may be different for all of them, getting text value is the same and we end up with 3 classes of almost the same code and I really really really don't like duplicating code.

I thought about creating an abstract class/default interface for each strategy type e.g. DefaultTextStrategy that all 3 text strategies would inherit from and either use default code provided higher or override it with own implementation, however I'm not really satisfied with this approach as it requires to create even more classes for such a simple task.

Maybe is it even possible to combine strategies of the same type (e.g. image) into one?

I would really like to hear what more experienced folks have to say in this matter as I would like to learn and improve.

Thanks in advance for your time.

Answer

Here's what I did:

First, I created an interface for all types of strategies named "AttributeValueStrategy". Then added 3 callbacks (type specific, e.g. NumberValueCallback etc.). Now, each strategy implements callback interface of its type and AttributeValueStrategy interface. Then there's DefaultStrategyMethods class that contains default "getAtrribute" for each type and the actual strategy call the defaultStrategyMethods (like below) or just implements its own code.

@Override
public Object getAttributeValue(Object attribute) {
    return defaultStrategyMethods.getNumberValue(attribute, this);
}

Callbacks are created because only the actual strategy knows which class should it cast to (and has a method to do that), and DefaultStrategyMethods needs to use it so that's why I pass "this" as second argument (which is the callback itself).

No more duplicates, everything is clear and clean.