What if classes with one interface have a similar but different method signature?

What if classes with the same interface have a similar but different method signature?

Let's say I have a project for calculating various costs (finally, to get the full cost).

My program has several calculator classes, namely ACostCalculator , BCostCalculator , etc. When the calculate() method is called to calculate the cost, the cost container is also transferred to these cost calculators. In a good scenario, I can create a CostCalculator interface for each cost calculator.

However, different resources were required to calculate different costs. In my current program, it looks like this:

 //getResource() are costly method while several costs need this. So do it outside calculate() method. ResourceA resourceA = getResourceA(); ResourceB resourceB = getResourceB(); CostContainer costContainer = new CostContainer(); CostCalculator aCostCalculator = new ACostCalculator(); ... CostCalculator eCostCalculator = new ECostCalculator(); aCostCalculator.calculate(costContainer); bCostCalculator.calculate(costContainer) cCostCalculator.calculate(costContainer, resourceA); dCostCalculator.calculate(costContainer, resourceA); eCostCalculator.calculate(costContainer, resourceA, resourceB); 

If the signature is exactly the same, I can make the loop convenient for this right away. However, since they are similar, but different, I can’t even create a good interface.

I am not sure if there are good ways to do this. What I can imagine is a generalization of the entire calculate() method to

 calculate(CostContainer costContainer, List<Object> resources); 

Any ideas? Thanks for answering.

+6
source share
4 answers

If the resources remain the same for the lifetime of the calculators: pass the resources to the constructor of the calculators.

 ResourceA resourceA = getResourceA(); ResourceB resourceB = getResourceB(); CostContainer costContainer = new CostContainer(); CostCalculator aCostCalculator = new ACostCalculator(); CostCalculator bCostCalculator = new BCostCalculator(); CostCalculator cCostCalculator = new CCostCalculator(resourceA); CostCalculator dCostCalculator = new DCostCalculator(resourceA); CostCalculator eCostCalculator = new ECostCalculator(resourceA, resourceB); aCostCalculator.calculate(costContainer); bCostCalculator.calculate(costContainer); cCostCalculator.calculate(costContainer); dCostCalculator.calculate(costContainer); eCostCalculator.calculate(costContainer); 
+5
source

The problem of changing signatures for the general interface sounds the same as the problem that the adapter design template solves (object adapter variant) :

Adapter pattern (GoF)

For your situation, you should only use adapters for inappropriate calculators. In fact, there are only two types of adapters: Type1 for signing (costContainer, resourceA) and Type2 for signing (costContainer, resourceA, resourceB) . Using your example:

Adapter pattern applied to your example

Advantages of the adapter are that it is a well-known design pattern (published by GoF in 1995); it allows ultimately calculate(...) methods to have different signatures. Adapters can be updated dynamically if context changes (such as resource changes) occur.

The disadvantages are obviously additional classes, indirection, etc. This is more complicated than the selected answer, but more flexible, especially if you cannot modify the adapted users API.

+2
source

You can use variable arguments :

 public interface CostCalculatorInterface { public void calculate(CostContainer container, Object... resources); } 

(or replace Object another superclass of ResourceA and ResourceB ).

In classes that implement the interface, resources will be Object[] , so you can refer to them as resources[0] , resources[1] , etc.

0
source

This is what you really use with Generics:

  Resource resourceA = new ResourceA(); Resource resourceB = new ResourceB(); CostContainer costContainer = new CostContainer(); CostCalculator<Resource> costCalculatorA = new ACostCalculator(); costCalculatorA.calculate(costContainer,resourceA,resourceB); CostCalculator<Resource> costCalculatorB = new BCostCalculator(); costCalculatorB.calculate(costContainer,resourceA); interface Resource { //Your code } class ResourceA implements Resource { //Your code } class ResourceB implements Resource { //Your code } class CostContainer { //Your code } interface CostCalculator<T extends Resource> { void calculate(CostContainer costContainer, T... resources); } class ACostCalculator implements CostCalculator<Resource>{ @Override public void calculate(CostContainer costContainer, Resource... resources) { System.out.println("Test"); } } class BCostCalculator implements CostCalculator<Resource>{ @Override public void calculate(CostContainer costContainer, Resource... resources) { System.out.println("Test2"); } } 
-1
source

All Articles