I have to manage dues for customers. I made a class named Due. It stores dues for two types of customers X,Y. It goes like this.
public abstract class Due {
private CustomerType customerType; //can be either X or Y
protected DueType dueType;
private String dueId;
private BigDecimal dueAmount;
private Date dueDate;
public Due(CustomerType type) {
this.customerType = type;
}
//getters , setters goes here.
}
Also DueType are different for both X and Y. Thus I have made an interface named DueType and enums XDueType, YDueType implements it.
public interface DueType {
}
public enum XDueType implements DueType {
A, B;
}
public enum YDueType implements DueType {
C, D;
}
And i have two different classes, each specifically for X's and Y's due. Like this
public class XDue extends Due {
public XDue() {
super(CustomerType.X);
}
public XDueType getDueType() {
return (XDueType) this.dueType;
}
}
public class YDue extends Due {
public YDue() {
super(CustomerType.Y);
}
public YDueType getDueType() {
return (YDueType) this.dueType;
}
}
I have to do a lot of operations on Due irrespective of its type (These operations will be same for XDue or YDue).
My question is should I use inheritance here like above or not. I could have made just one class Due only. That could have simply done the job for me. But the former method appears like more semantically correct to me. Also, going forward XDue, YDue can become less similar. Thus I think its better to keep them separate from now. Am I correct?
It sounds like the class needs a bit of refactoring - its a mix of inheritance and composition at the moment. I suggest going fully one-way or another
Full inheritance would move all the conditional logic into the derived clases, and have any "types" returned be stateless singletons (enums are good for this). Ideally, with a little further refactoring you can move any type depenedant code that switches on these types into the derived classes too.
public enum DueType { X, Y }
public enum CustomerType {X,Y}
public abstract class Due {
private String dueId;
private BigDecimal dueAmount;
private Date dueDate;
public Due(CustomerType type) {
this.customerType = type;
}
abstract DueType getDueType();
abstract CustomerType getCustomerType();
}
public class XDue extends Due {
@Override public DueType getDueType() {
return DueType.X;
}
@Override public CustomerType getCustomerType() {
return CustomerType.X;
}
}
Or go full composition, where the Due is final and defers it's decisions to contained strategy types. Again any logic which switches on type can usually be moved into the Strategy classes.
public interface DueType /* Really a strategy now */ {
void doSomeThing(Due d);
}
public interface CustomerType /* Really a strategy now */ {
void doSomething(Due d);
}
public final class Due {
private String dueId;
private BigDecimal dueAmount;
private Date dueDate;
DueType dueType;
CustomerType customerType;
public Due(CustomerType type, DueType dueType) {
this.customerType = type;
this.dueType = dueType
}
}
public class XDueType implements DueType {
...
}
public clasx XCustomerType implements CustomerType {
...
}
The advantage of composition over inheritance is that its cleaner to implement additional variations on DueType and Customer type that are unrelated, ie a Due
object with CustomerTypeA
and DueTypeB
, without needing to create a DueAB
class. (But if this is not possible in your situation this potential for differences is a bad thing).
Another advantage is that you can change the strategies at run-time without creating a completely new Due
object. But again that may not be something that is needed / desired.