java - 当一个子类调用另一个子类时,哪种面向对象设计更好?
问题描述
在我的场景中,我们为客户提供多种计划。(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();
}
在这两种设计中,哪一种更合适(面向对象),为什么?代码中唯一可能发生变化的是将来会添加更多计划。
解决方案
我有一个不同的方法,你有一个管理所有计划的类,而计划接口被封装并且只显示其 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++ 中的私有方法,如果您想讨论任何内容,请随时这样做。
推荐阅读
- iterator - 在 LLVM 中迭代机器指令
- django - 使用具有多对多字段的 Django F 表达式
- python - nn.Linear 之后矩阵的链式规则输出
- java - 按每个数字及其索引的乘积对整数数组进行排序?‽
- php - 如何允许人们将我的 Facebook 应用程序订阅到他们的主页?
- javascript - 使用 javascrip 更改 html 文本并再次返回
- python - Openvpn 看不到 Mysql 数据库的变化
- javascript - 为什么以及何时会立即忽略变量声明,并在后续查询中覆盖函数声明?
- javascript - JavaScript PointerEvent - Firefox / Chrome / Edge 中的“pointerover”/“pointerout”触发
- perl - 如何获取 Perl 对象(不是类)的结构和继承历史?