首页 > 解决方案 > 当一个子类调用另一个子类时,哪种面向对象设计更好?

问题描述

在我的场景中,我们为客户提供多种计划。(planA、planB、planC等)planA低于planB,planB低于planC。客户可以从较低的计划转移到较高的计划,但反之则不行。如果客户在 planA 上并想要“激活”planB,则必须取消 planA。本质上,一个计划可以被“激活”和“停用”。我想到了 2 个设计。

interface Plan {
   activate();
   deactivate();
} 

这个接口将被每个计划(planA、planB、planC等)继承。activate 方法将被继承,看起来像这样:

activate() {
   Plan planToCancel = getLowerGradePlanToCancel()
   planToCancel.cancel();
   // perform business logic to activate plan.
}

选项 B类似于策略模式:我有 2 个接口:

interface Activate {
  activate();
}

interface Deactivate {
  deactivate()
}

每个计划都将继承这些接口。然后我的业务逻辑看起来像这样:

activatePlan(planName, planToDeactivate) {
  Activate activate = Factory.getActivateInstanceForPlan(planName);
  DeActivate dectivate = Factory.getActivateInstanceForPlan(planToDeactivate);
  deactivate.deactivate();
  activate.activate();  
}

在这两种设计中,哪一种更合适(面向对象),为什么?代码中唯一可能发生变化的是将来会添加更多计划。

标签: javadesign-patterns

解决方案


我有一个不同的方法,你有一个管理所有计划的类,而计划接口被封装并且只显示其 API 的必要性。我认为这种方法将对每个添加的计划进行最少的代码修改,此外,它可以防止用户犯错误(例如降级计划)。

基本接口:

interface Plan {
    public Plan next();
    public boolean isActivated();
    // for debug purposes
    public String planDescription();
}

interface PlansManager {
        public Plan nextPlan(Plan current);
}

基本思想是有一些在所有计划中实现静态(相互)行为的 SemiConcretePlan 类,公共 API 是 next & isActivated 而激活和取消方法是私有的(您不希望用户取消计划而不切换到下一个或激活一个已取消的计划,保留之前的计划指针)并且只有 PlansManager 或计划本身将处理激活和取消,PlansManager 激活第一个计划并返回它,下一个方法使用 PlansManager 获取下一个也是唯一的SemiConcertePlan 激活当前并取消先前的计划。

这里是 SemiConcertePlan:

abstract class SemiConcretePlan implements Plan {
    private PlansManager m_plansManager;
    private boolean m_isActivated;
    private int m_id;
    private static int s_idGenerator = 0, s_firstActivatedId = 1;

    public SemiConcretePlan(PlansManager plansManager){
        m_plansManager = plansManager;
        m_id = generateId();
        m_isActivated = (m_id == s_firstActivatedId);
    }

    private int generateId() {
        return ++s_idGenerator;
    }

    private void activatePlan() { 
        this.m_isActivated = true; 
    }

    private void cancelPlan() { 
        this.m_isActivated = false; 
    }

    public boolean isActivated() { 
        return this.m_isActivated; 
    }

    public Plan next() {
        this.cancelPlan();
        SemiConcretePlan nextPlan = (SemiConcretePlan) m_plansManager.nextPlan(this);
        nextPlan.activatePlan();

        return nextPlan;
    }

    public boolean equals(Object other) {
        if (this == other)
                return true;
        if (other == null || !(other instanceof SemiConcretePlan) || this.hashCode() != other.hashCode())
                return false;
        SemiConcretePlan otherPlan = ((SemiConcretePlan) other);
        if (m_id != ((SemiConcretePlan) otherPlan).m_id)
                return false;

        return true;
    }

    public abstract int hashCode();
    public abstract String planDescription();
}

planDescription 方法是动态方法的一个示例,类 PlansManager 需要 hashCode 来散列 map 中的计划,将当前计划映射到更高(下一个)计划。

这是 AscedingPlansManager 类:

class AscedingPlansManager implements PlansManager{
    private List<Plan> m_plansList;
    private Map<Plan, Plan> m_planToHigherPlanMapping;

    public AscedingPlansManager() {
        m_plansList = new LinkedList();
        m_planToHigherPlanMapping = new HashMap();
        Plan[] plans = {
                new PlanA(this),
                new PlanB(this),
                new PlanC(this),
                new PlanD(this)
        };

        for(int i = 0; i < plans.length - 1; ++i) {
            m_plansList.add(plans[i]);
            m_planToHigherPlanMapping.put(plans[i], plans[i+1]);
        }

        m_plansList.add(plans[plans.length - 1]);
        m_planToHigherPlanMapping.put(plans[plans.length - 1], plans[plans.length - 1]);
    }

    public Plan nextPlan(Plan current) {
        return m_planToHigherPlanMapping.getOrDefault(current, null);
    }

    private void activatePlan(Plan plan) {
        try {
            Method privateActivateMethod = SemiConcretePlan.class.getDeclaredMethod("activatePlan");
            privateActivateMethod.setAccessible(true);
            privateActivateMethod.invoke(plan);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public void cancelAll() {
        for(Plan plan: m_plansList)
            try {
                Method privateActivateMethod = SemiConcretePlan.class.getDeclaredMethod("cancelPlan");
                privateActivateMethod.setAccessible(true);
                privateActivateMethod.invoke(plan);
            } catch(Exception e) {
                e.printStackTrace();
            }
    }

    public Plan firstPlan() {
        Plan first = m_plansList.get(0);
        this.activatePlan(first);

        return first;
    }

    public boolean[] plansToActivationState() {
        boolean[] ret = new boolean[m_plansList.size()];
        int index = 0;
        for(Plan plan: m_plansList)
            ret[index++] = plan.isActivated();

        return ret;
    }
}

我知道这是一个庞大的代码,但我认为它会使添加计划变得容易,您只需更改 hashCode 方法,计划的顺序可以在 AscedingPlansManager 的构造函数中更改或从头开始创建不同的管理器类。

这是完整的代码,你可以看到我需要对 PlanD 类做多少改动:

导入 java.util。; 导入 java.lang.reflect。;

interface Plan {
    public Plan next();
    public boolean isActivated();
    // for debug purposes
    public String planDescription();
}

interface PlansManager {
        public Plan nextPlan(Plan current);
}

abstract class SemiConcretePlan implements Plan {
    private PlansManager m_plansManager;
    private boolean m_isActivated;
    private int m_id;
    private static int s_idGenerator = 0, s_firstActivatedId = 1;

    public SemiConcretePlan(PlansManager plansManager){
        m_plansManager = plansManager;
        m_id = generateId();
        m_isActivated = (m_id == s_firstActivatedId);
    }

    private int generateId() {
        return ++s_idGenerator;
    }

    private void activatePlan() { 
        this.m_isActivated = true; 
    }

    private void cancelPlan() { 
        this.m_isActivated = false; 
    }

    public boolean isActivated() { 
        return this.m_isActivated; 
    }

    public Plan next() {
        this.cancelPlan();
        SemiConcretePlan nextPlan = (SemiConcretePlan) m_plansManager.nextPlan(this);
        nextPlan.activatePlan();

        return nextPlan;
    }

    public boolean equals(Object other) {
        if (this == other)
                return true;
        if (other == null || !(other instanceof SemiConcretePlan) || this.hashCode() != other.hashCode())
                return false;
        SemiConcretePlan otherPlan = ((SemiConcretePlan) other);
        if (m_id != ((SemiConcretePlan) otherPlan).m_id)
                return false;

        return true;
    }

    public abstract int hashCode();
    public abstract String planDescription();
}

class AscedingPlansManager implements PlansManager{
    private List<Plan> m_plansList;
    private Map<Plan, Plan> m_planToHigherPlanMapping;

    public AscedingPlansManager() {
        m_plansList = new LinkedList();
        m_planToHigherPlanMapping = new HashMap();
        Plan[] plans = {
                new PlanA(this),
                new PlanB(this),
                new PlanC(this),
                new PlanD(this)
        };

        for(int i = 0; i < plans.length - 1; ++i) {
            m_plansList.add(plans[i]);
            m_planToHigherPlanMapping.put(plans[i], plans[i+1]);
        }

        m_plansList.add(plans[plans.length - 1]);
        m_planToHigherPlanMapping.put(plans[plans.length - 1], plans[plans.length - 1]);
    }

    public Plan nextPlan(Plan current) {
        return m_planToHigherPlanMapping.getOrDefault(current, null);
    }

    private void activatePlan(Plan plan) {
        try {
            Method privateActivateMethod = SemiConcretePlan.class.getDeclaredMethod("activatePlan");
            privateActivateMethod.setAccessible(true);
            privateActivateMethod.invoke(plan);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public void cancelAll() {
        for(Plan plan: m_plansList)
            try {
                Method privateActivateMethod = SemiConcretePlan.class.getDeclaredMethod("cancelPlan");
                privateActivateMethod.setAccessible(true);
                privateActivateMethod.invoke(plan);
            } catch(Exception e) {
                e.printStackTrace();
            }
    }

    public Plan firstPlan() {
        Plan first = m_plansList.get(0);
        this.activatePlan(first);

        return first;
    }

    public boolean[] plansToActivationState() {
        boolean[] ret = new boolean[m_plansList.size()];
        int index = 0;
        for(Plan plan: m_plansList)
            ret[index++] = plan.isActivated();

        return ret;
    }
}

class PlanA extends SemiConcretePlan {
    public PlanA(PlansManager plansManager) { 
        super(plansManager);
    }

    public int hashCode() { 
        return 1; 
    }

    public String planDescription() { 
        return "This is PlanA"; 
    }
}

class PlanB extends SemiConcretePlan {
    public PlanB(PlansManager plansManager) {
        super(plansManager);
    }

    public int hashCode() {
        return 2;
    }

    public String planDescription() {
        return "This is PlanB";
    }
}

class PlanC extends SemiConcretePlan {
    public PlanC(PlansManager plansManager) {
        super(plansManager);
    }

    public int hashCode() {
        return 3;
    }

    public String planDescription() {
        return "This is PlanC";
    }
}

class PlanD extends SemiConcretePlan {
    public PlanD(PlansManager plansManager) {
        super(plansManager);
    }

    public int hashCode() {
        return 4;
    }

    public String planDescription() {
        return "This is PlanD";
    }
}


public class Main{
         public static void main(String []args){
                AscedingPlansManager ascedingPlansManager = new AscedingPlansManager();
                Plan currentPlan = ascedingPlansManager.firstPlan();

                int i = 0, maxIterations = 5;
                while((++i) <= maxIterations) {
                    System.out.println(currentPlan.planDescription());
                    System.out.println(Arrays.toString(ascedingPlansManager.plansToActivationState()));
                    currentPlan = currentPlan.next();
                }

                ascedingPlansManager.cancelAll();
                System.out.println("After canceling all plans");
                System.out.println(Arrays.toString(ascedingPlansManager.plansToActivationState()));
         }
}

我仍然不确定我的实现,我通常使用朋友修饰符访问 c++ 中的私有方法,如果您想讨论任何内容,请随时这样做。


推荐阅读