How to simplify Java code by going from inheritance to composition
I wrote the lower level classes of a multilevel inherited class, but am confused as to how to combine them with one program. Someone suggested that I use composition instead of inheritance, and someone suggested that I use enum
to create an anonymous subclass instead of the complex structure of inheritance. How can we use composition instead of inheritance, or use enum
instead of inheritance, which I used? What should I do to simplify the problem and how to do it?
I am using Java to write code to calculate tax. I have a base class TaxPayer
. A taxpayer can have several incomeSource
. There are many types of TaxPayer
or incomeSource
. For each source of income, there can be many income headings that are used for the calculation taxableIncome
. taxRate
will be different for different types TaxPayer
and taxableIncome
. The end result will be taxRate
applied to taxableIncome
.
Base class Taxpayer is defined as
public abstract class TaxPayer {
private List<IncomeSource> incomeSource;
double taxRate;
Address address;
other attributes here;
public Double getTaxRate(){
return 0.25; //default tax rate
}
}
public abstract class IncomeSource {
private String incomeSourceName;
private Double incomeHeading1, incomeHeading2, incomeHeading3, ...;
private Double taxableIncome = incomeHeading1 + incomeHeading2 - incomeHeading3;
}
There may be some subclass incomeSource
with different income headings. Similarly, the taxpayer type can be modeled into the following inheritance structure
Base Class: Taxpayer
* IndividualPerson
* Male, Female, OldAge
* Business
* Bank, ITIndustry, HydroElectricIndustry
* TaxFree
* SocialOrganization, ReligiousOrganization, PoliticalParty etc.
Subclasses TaxPayer
usually change taxRate
applied to taxableIncome
, and sometimes change taxableIncome
to some logic. For example:
abstract class IndividualPerson extends TaxPayer{
if (incomeSource.taxableIncome > 250000) taxRate = ratex;
if (incomeSource.taxableIncome > 500000) taxRate = ratey;
@override
public getTaxRate() {
return taxRate;
}
}
class Female extends IndividualPerson {
if (incomeSource.getNumberOfIncomeSource() > 1) taxRate = taxRate + rate1;
else taxRate = taxRate - rate2
if (address.isRural() = true) taxRate = taxRate - rate3;
if (attributeX = true) taxRate = taxRate + rate4;
if ("Some other attribute" = true) taxableIncome = taxableIncome - someAmount;
}
We have to check the attributes TaxPayer
and incomeSource
to determine taxRate
. Sometimes it taxableIncome
can be changed according to the type TaxPayer
and other conditions.
I am confused how to combine the lower level classes together to make useful code. Someone suggested that I shouldn't use complex multi-level inheritance, instead use composition, or enum
to achieve the desired result with less complexity. How to do it? Please provide answers with enough information for beginners to understand.
source to share
Better to change the composition. You can do it this way to go from multiple inheritance or interface to composition
-
Inheritance:
Class A{ } Class B extends Class A{ } Class C extends Class B{ }
-
Interface:
Interface A{ } Interface B implements Class A{ } Class C implements Class B{ }
-
Composition:
class C{ A objA = new A(); B objB = new B(); pubic void test(){ objA.doSomething(); } public void methodA(){ objA.method(); } }
source to share
Ok, I will not program your case, but I will give you some guidelines for what composition and how to solve it with enums. Your problem is that you have a TaxPayer and the only thing that is common is the default tax rate.
With composition, instead of using inheritance, you have a TaxPayer instance in your class:
public class TaxPayer {
private List<IncomeSource> incomeSource;
double taxRate;
Address address;
other attributes here;
public Double getTaxRate(){
return 0.25; //default tax rate
}
}
public class Male {
private TaxPayer taxPayer = new TaxPayer();
public Double getTaxRate(){
return taxPayer.getTaxRate();
}
}
So, instead of abstraction, you bind the methods to what you want to use in TaxPayer.
Using enums, the solution will look something like this:
public enum TaxPayer {
MALE,
FEMALE {
public Double getTaxRate(){
return 1; //custom tax rate
}
};
public Double getTaxRate(){
return 0.25; //default tax rate
}
}
Now you can use:
TaxPayer.MALE.getTaxRate();
in your code!
The third option will define an interface and define defaultTaxRate as a constant.
public interface TaxPayer {
Double getTaxRate();
}
public class Male implements TaxPayer {
public Double getTaxRate(){
return Constants.DEFAULT_TAX_RATE;
}
}
public class Constants {
public static final double DEFAULT_TAX_RATE = 0.25;
}
And now for the important bit, why do you need it because of the expansion.
First, you can only extend 1 class, so it makes your class less versatile and more difficult to use. The enumeration and interface will resolve this for you (since you can still apply the strategy pattern to it).
public void foo(TaxPayer anyImplementationOfTaxPayer){
bar(anyImplementationOfTaxPayer.getTaxRate());
}
As you can see, using an interface or enumeration, it's much more extensible than an abstract class.
Secondly, if you add a method to an abstract class, then ALL classes that will inherit from it inherit this method and are forced to handle / execute the behavior of this additional method or have a partially unusable API.
source to share