工厂模式和多态性

时间:2016-05-20 13:23:18

标签: java polymorphism factory-pattern

我的计划是ControlPanelFactory为每个不同的ControlPane类构建自定义Model

abstact class Model {
}

class ModelA extends Model {
}

class ModelB extends Model {
}

现在我的计划是让Factory类根据方法重载传递的ControlPane类创建不同的Model实例:

class ControlPaneFactory {
    private ControlPaneFactory() {
    }

    public static ControlPanel build(ModelA model) {
        return new ControlPaneA(model);
    }

    public static ControlPanel build(ModelB model) {
        return new ControlPaneB(model);
    }
}

然而,这是非常有问题的,因为在调用factorys方法时,我只有Model类型的变量,所以我需要首先使用instanceof,这是一个巨大的代码。采用精简工厂方法的相同方法:

public static ControlPane build(Model model) {
    if (model instanceof ModelA) 
        return new ControlPaneA(model);
    else if (model instanceof ModelB) 
        return new ControlPaneB(model);
    else throw new IllegalArgumentException("Unsupported model");
}

我想过在模型类中使用枚举来指定Model的类型,但是这似乎也是一个违反DRY的错误选项。

此外,我更希望实现ControlPane类的Model独立(即在一个特定类中)的实例化。有没有"很好"解决这个问题的方法?

2 个答案:

答案 0 :(得分:2)

如果您希望所有工厂方法都在同一个对象中,那么您将以某种方式需要一个switch / instanceof if group,或者ModelsControlPanels的地图。

或者,您可以将工厂方法移动到Model类。从本质上讲,这是抽象工厂模式,但您可以使用Model对象来实现它。有几种方法可以看到这一点。可以说它增加了ModelControlPanel对之间的耦合,但我建议您尝试实现这一目标。还可以说它使您的工厂代码不再可重用,因为您需要Model对象来运行它,但是您为工厂对象提供的示例接口仍然需要构建Model。随着摆脱贫血模型,我认为这是实现它的合理方式,并降低了工厂对象的复杂性(如果你仍然需要它)。

我选择这样的事情:

abstact class Model {
    public abstract ControlPanel buildControlPanel();
}

class ModelA extends Model {
    public ControlPanel buildControlPanel() {
        return new ControlPanelA(this);
    }
}

class ModelB extends Model {
    public ControlPanel buildControlPanel() {
        return new ControlPanelB(this);
    }
}

// Don't really need this anymore...
class ControlPaneFactory {
    public static ControlPanel build(Model model) {
        return model.buildControlPanel();
    }
}

如果需要,这仍然允许您为ControlPanel提供截然不同的构造函数的灵活性,并且在程序启动时您不需要注册对象地图。

您可以将buildControlPanel()方法从Model移出,并创建一个正确的抽象工厂模式,而是从Model个对象返回一个具体的工厂。但我觉得这样可以增加你所拥有的课程数量而不会实际提供任何真正的改进。如果您有很多使用相同构建代码的Model类(例如ModelAModelBModelC都对应ControlPanelX),那么它可能会是一个很好的方式,但听起来并不像你那样。

但是,在一天结束时,选择要实例化的具体类的switch或if语句并不是世界上最糟糕的事情。其他库使用了类似的东西,例如Eclipse's EMF Switch Class

答案 1 :(得分:1)

尝试使代码更通用,这意味着ControlPanel不应该依赖于特定的模型。但是,如果真的有必要,你可以试试这个:

public class ControlPanelFactory {
    private static Map<Class<? extends Model>, Class<? extends ControlPanel>> modelPanelMap = new HashMap<>();

    public static void addModelPaneRelation(Class<? extends Model> model, Class<? extends ControlPanel> pane) {
        modelPanelMap.put(model, pane);
    }

    public static ControlPanel build(Model model) {
        try {
            return modelPanelMap.get(model.getClass())
                    .getConstructor(model.getClass())
                    .newInstance(model);
        } catch (Exception exception) {
            // Handle exceptions
        }

        return null;
    }
}

启动应用程序时,您应该进行某种配置。其执行方式如下:

ControlPanelFactory.addModelPaneRelation(ModelA.class, ControlPanel.class);

至少这将提取Panel如何依赖Model的逻辑。我再次认为这不是最干净的解决方案。