避免使用依赖于特定条件的对象创建的If-else代码气味

时间:2016-10-27 09:43:14

标签: java oop design-patterns instanceof java-6

是否有更好的方法来处理对象(Product)的实例化,这取决于另一种对象类型(Condition)而不是使用if-else与instanceof配对作为以下代码显示?

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

abstract class AbstractProduct {
    private AbstractCondition condition;
    public AbstractProduct(AbstractCondition condition) {
        this.condition = condition;
    }
    public abstract void doSomething();
}

class ProductA extends AbstractProduct {
    AbstractCondition condition;
    public ProductA(AbstractCondition condition) {
        super(condition);
    }

    @Override
    public void doSomething() {
        System.out.println("I'm Product A");
    }
}

class ProductB extends AbstractProduct {    
    public ProductB(AbstractCondition condition) {
        super(condition);
    }   

    @Override
    public void doSomething() {
        System.out.println("I'm Product B");
    }
}

class AbstractCondition { }

class ConditionA extends AbstractCondition { }

class ConditionB extends AbstractCondition { }

public class Try {
    public static void main(String[] args) {
        List<AbstractCondition> conditions = new ArrayList<AbstractCondition>();
        List<AbstractProduct> products = new ArrayList<AbstractProduct>();

        conditions.add(new ConditionA());               
        conditions.add(new ConditionB());               
        conditions.add(new ConditionB());               
        conditions.add(new ConditionA());

        for (AbstractCondition c : conditions) {
            tryDoSomething(c);
        }
    }

    public static void tryDoSomething(AbstractCondition condition) {
        AbstractProduct product = null;
        if (condition instanceof ConditionA) {
            product = new ProductA(condition);
        } else if (condition instanceof ConditionB) {
            product = new ProductB(condition);
        }
        product.doSomething();
    }
}

差异与我的真实代码上面的代码是:我有 NO 直接控制AbstractCondition及其子类型(因为它们在库中) ),但AbstractProduct的具体子类型的创建取决于具体条件。

我的目标是:尽量避免if-else中的tryDoSomething() 代码异味

我也想避免反思,因为它感觉像是在作弊,我认为这不是一个优雅,干净和可读的解决方案。

换句话说,我想用优秀的OOP原则(例如利用多态性)解决问题并解决一些设计模式(在这个特定情况下我显然不知道)。

3 个答案:

答案 0 :(得分:3)

由于无法编辑原始对象,因此需要创建从条件类型到产品类型的静态地图:

private static HashMap< Class<? extends AbstractCondition>, 
                        Class<? extends AbstractProduct>
                      > conditionToProduct;`

使用Condition,Product:

对进行静态初始化
static { 
  conditionToProduct.put(ConditionA.class, ProductA.class); 
  ... 
} 

并在运行时只查询地图:

Class<? extends AbstractProduct> productClass = conditionToProduct.get(condition.getClass());
productClass.newInstance();

答案 1 :(得分:2)

AbstractCondition需要知道类型或如何构建产品。

因此,请将以下某个功能添加到AbstractCondition

Class<? extends AbstractProduct> getProductClass()

AbstractProduct createProduct()

答案 2 :(得分:0)

您应该创建一个Factory类来帮助您。

interface IFactoryProduct{
    AbstractProduct getProduct(AbstractCondition condition)  throws Exception;
}

这将是您的界面,只需要像这样实现它。

class FactoryProduct implements IFactoryProduct{

    public AbstractProduct getProduct(AbstractCondition condition) throws Exception{
        return (AbstractProduct)getClass().getMethod("getProduct", condition.getClass()).invoke(this, condition);
    }

    public ProductA getProduct(ConditionA condition){
        return new ProductA();
    }

    public ProductB getProduct(ConditionB condition){
        return new ProductB();
    }

}

使用反射以正确的方法重定向将起到作用。如果你愿意,这可以升级为子类。

编辑:

一些例子:

    List<AbstractCondition> list = new ArrayList<AbstractCondition>();
    list.add(new ConditionA());
    list.add(new ConditionB());

    for(AbstractCondition c : list){
        try {
            System.out.println(f.getProduct(c));
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
  

labo.ProductA@c17164

     

labo.ProductB@1fb8ee3

更复杂的反射版本,允许接收子类:

public AbstractProduct getProduct(AbstractCondition condition) throws Exception{
    Method m = getMethodFor(condition.getClass());
    if(m == null )
        throw new Exception("No method for this condition " + condition.getClass().getSimpleName());
    else
        return (AbstractProduct) m.invoke(this, condition);
}

private Method getMethodFor(Class<? extends AbstractCondition> clazz ) throws Exception{
    try {
        return getClass().getMethod("getProduct", clazz);
    } catch (NoSuchMethodException ex) {
        if(clazz.getSuperclass() != AbstractCondition.class){
            return getMethodFor((Class<? extends AbstractCondition>)clazz.getSuperclass());
        }
        return null;
    }
}

这允许我发送ConditionC extending ConditionB以构建具有ConditionB的相同产品。对复杂遗产感兴趣。