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.

+3


source to share


2 answers


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();
        }
    }
    
          

+2


source


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.

+4


source







All Articles