设计困境。什么是最好的设计?

Design dilemma. What's the best design?

我有一个设计难题,其中:

  1. 我的网页上有一个 UI 操作,允许用户从类型列表中 select 一个类型。

  2. 每种类型都由一个 POJO 定义。我需要将此 POJO(对于用户 selected 的类型)包装在父 class 中并使用它来执行操作。

  3. 每种类型都会导致不同的操作。

如何设计父 class 以支持用户 select 的任何类型?我想在我的应用程序中稍后使用 selected POJO 时避免 instanceof 检查。

要避免 instanceof,您可以重写一个方法,然后执行您需要的 UI 操作。

public void doAction(Type1 type) { //... }
public void doAction(Type2 type) { //... }

这种方法可以采用多种形式

interface Action {
    void perform();
}

public class ActionFactory {
    public static Action getAction(Type1 type) { //... }
    public static Action getAction(Type2 type) { //... }
}

哪种解决方案最好取决于您如何定义 UI 中的选项。

定义可能选择的最简单方法是使用枚举。如果你走那条路,我建议使用 EnumMap 将类型映射到动作,或(假设动作是可变的或需要根据请求创建)动作工厂。

例如:

EnumMap<Type, Class<Action>> typeToActionMap = new EnumMap<>();
typeToActionMap.put(Type.Large, LargeAction.class);
typeToActionMap.put(Type.Medium, MediumAction.class);
typeToActionMap.put(Type.Small, SmallAction.class);

上面我使用了一个动作 class 作为一种工厂。如果动作可以是单例,则可以使映射的值成为动作。正如@dhke 所建议的,您可以使用 Guava 的 ClassToInstanceMap 到此映射:

ClassToInstanceMap<Action> actionTypeToAction = MutableClassToInstanceMap.create();
typeToActionMap.putInstance(LargeAction.class, new LargeAction(...));
typeToActionMap.putInstance(MediumAction.class, new MediumAction(...));

如果您已经为每个选择设置了唯一的类型,那么您只需要找到一种方法来进行映射。一个简单的方法是让你的类型都实现一个通用接口:

public interface ActionProvider {

  Class<? extends Action> getActionClass();

实施ActionRegistry.

class ActionRegistry {
    private static Map<ActionType, AbstractAction> REGISTRY = ...

    public static void registerAction(AbstractAction a) {
        REGISTRY.put(a.getType(), a);
    }

    public static AbstractAction getAction(ActionType type) {
        REGISTRY.get(type);
    }
}

然后,当您select UI 中的 type 时,您可以:

ActionRegistry.getAction(type).run();

您可以尝试 AbstractAction,这样当它的子类被实例化时,它会自动在注册表中注册。