Search code examples
javaoverloadingcompositeclass-hierarchy

Hierarchy issue on Composite with Java


I'm trying to do use a Composite pattern on Java in order to do a report, and I'm clearly forgetting how hierarchy and method overload work.

Let's say I have the following models:

public class Product {

    public String get(){
        return "Product";
    }

}

public class BProduct extends Product {

    public String getB() {
        return "BBBBB";
    }

}

public class CProduct extends Product {

    public String getC() {
        return "CCCCC";
    }

}

And the following converters:

import java.util.List;

public class Converter {

    private List<Converter> converters;

    public Converter() {

    }

    public Converter(List<Converter> converters) {
        this.converters = converters;
    }

    public void execute(Product product) {
        for (Converter converter : converters) {
            converter.execute(product);
        }
    }

}

public class BConverter extends Converter {

    @Override
    public void execute(Product product) {
        innerExecute(product);
    }

    public void innerExecute(Product product) {
        System.out.println(product.get() + " done on B normal.");
    }

    public void innerExecute(BProduct b) {
        System.out.println(b.getB() + " done on B special.");
    }

}

public class CConverter extends Converter {

    @Override
    public void execute(Product product) {
        System.out.println(product.get() + " done on C normal.");
    }

    public void execute(CProduct c) {
        System.out.println(c.getC() + " done on C special.");
    }

}

Testing it with the following test:

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class ProductConverterTest {

    @Test
    public void test() {
        List<Converter> converters = new ArrayList<>();
        converters.add(new BConverter());
        converters.add(new CConverter());
        Converter converter = new Converter(converters);

        List<Product> list = new ArrayList<>();
        list.add(new Product());
        list.add(new BProduct());
        list.add(new CProduct());

        for (Product product : list) {
            converter.execute(product);
        }
    }
}

Gets as output:

Product done on B normal.
Product done on C normal.
Product done on B normal.
Product done on C normal.
Product done on B normal.
Product done on C normal.

When what I want is:

Product done on B normal.
Product done on C normal.
BBBBB done on B special.
Product done on C normal.
Product done on B normal.
CCCCC done on C special.

NOTE: I want to do this without using instanceof. I already know how to do it with it. What I want to know is if it can be done without it.


Solution

  • the reason for this behaviour is that #execute method is overloaded. the choice of which overloaded method to invoke is made at compile time. in order to make it work as you want you would have to use dynamic selection, which would lead to either instanceOf check or overwriting the methods or pass/retrieve class of a product and compare like:

    public class Converter {
    
        private List<Converter> converters;
    
        public Converter() {
    
        }
    
        public Converter(List<Converter> converters) {
            this.converters = converters;
        }
    
        public void execute(Product product) {
            for (Converter converter : converters) {
                converter.execute(product);
            }
        }
    
    }
    
    public class BConverter extends Converter {
    
        @Override
        public void execute(Product product) {
            if (product.getClass() == BProduct.class) {
                innerExecute((BProduct)product);
            } else {
                innerExecute(product);
            }
        }
    
        public void innerExecute(Product product) {
            System.out.println(product.get() + " done on B normal.");
        }
    
        public void innerExecute(BProduct b) {
            System.out.println(b.getB() + " done on B special.");
        }
    
    }
    
    public class CConverter extends Converter {
    
        @Override
        public void execute(Product product) {
            if (product.getClass() == CProduct.class) {
                innerExecute((CProduct)product);
            } else {
                innerExecute(product);
            }
        }
    
        public void innerExecute(Product product) {
            System.out.println(product.get() + " done on C normal.");
        }
    
        public void innerExecute(CProduct b) {
            System.out.println(b.getC() + " done on C special.");
        }
    
    }