如何用OOP建模属性与方法重叠的子类?以Java借还站为例
我要在Java中建模一个物品借还站的场景,这也是个通用OOP问题:存在3个及以上类,它们有部分共同属性和方法,同时各自有专属的属性和方法。一开始看起来是典型的继承场景,但问题在于第三个类同时拥有仅属于第一个类和第二个类的部分属性与方法,还需要支持多态。
具体来说,要建模BikeStation(自行车站)、CanoeStation(独木舟站)和BikeAndCanoeStation(自行车+独木舟站),它们要有支持多态的通用行为方法。我初步的继承建模方案如下:
// 抽象父类(Station) public abstract class Station { // 公共属性 // 公共方法 public abstract void doSomething(); }
// 子类1(BikeStation) public class BikeStation extends Station { private int someBikeSpecificAttribute; // 更多自行车站专属属性 public void doSomething() { // 自行车站专属逻辑 } // 更多自行车站专属方法 }
// 子类2(CanoeStation) public class CanoeStation extends Station { private int someCanoeSpecificAttribute; // 更多独木舟站专属属性 public void doSomething() { // 独木舟站专属逻辑 } // 更多独木舟站专属方法 }
// 子类3(BikeAndCanoeStation) public class BikeAndCanoeStation extends Station { private int someBikeSpecificAttribute; // 和BikeStation中的属性重复 private int someCanoeSpecificAttribute; // 和CanoeStation中的属性重复 // 更多混合站专属属性 public void doSomething() { // 复用BikeStation的专属逻辑 // 复用CanoeStation的专属逻辑 } // 更多混合站专属方法 }
但直接复制前两个类的属性和方法到第三个类不符合良好OOP原则,会产生冗余代码且不利于后续扩展。我曾考虑使用接口或策略模式,但它们仅能部分解决行为(方法)问题。请问如何用良好的OOP思想(优先Java实现)建模该场景?
方案1:组合+委托(优先推荐)
核心是把自行车、独木舟的专属功能抽离为独立的组件类,让各站点类通过组合这些组件来复用代码,同时保留Station继承体系的多态能力,完全符合合成复用原则。
步骤1:定义专属功能组件
// 自行车专属功能组件 public class BikeComponent { private int someBikeSpecificAttribute; // 自行车站专属行为 public void doBikeSpecificStuff() { // 实现自行车站的专属逻辑 } // 属性访问方法 public int getSomeBikeSpecificAttribute() { return someBikeSpecificAttribute; } public void setSomeBikeSpecificAttribute(int someBikeSpecificAttribute) { this.someBikeSpecificAttribute = someBikeSpecificAttribute; } } // 独木舟专属功能组件 public class CanoeComponent { private int someCanoeSpecificAttribute; // 独木舟站专属行为 public void doCanoeSpecificStuff() { // 实现独木舟站的专属逻辑 } // 属性访问方法 public int getSomeCanoeSpecificAttribute() { return someCanoeSpecificAttribute; } public void setSomeCanoeSpecificAttribute(int someCanoeSpecificAttribute) { this.someCanoeSpecificAttribute = someCanoeSpecificAttribute; } }
步骤2:重构站点类
// 抽象父类保持不变,定义公共属性和行为 public abstract class Station { protected String name; protected String address; public void commonOperation() { // 公共逻辑实现,比如站点信息展示、借还记录统计等 } public abstract void doSomething(); } // 自行车站:组合BikeComponent public class BikeStation extends Station { private final BikeComponent bikeComponent = new BikeComponent(); @Override public void doSomething() { bikeComponent.doBikeSpecificStuff(); } // 对外暴露自行车专属方法(如果业务需要) public void handleBikeCheckout() { bikeComponent.doBikeSpecificStuff(); } // 委托属性访问 public int getSomeBikeSpecificAttribute() { return bikeComponent.getSomeBikeSpecificAttribute(); } } // 独木舟站:组合CanoeComponent public class CanoeStation extends Station { private final CanoeComponent canoeComponent = new CanoeComponent(); @Override public void doSomething() { canoeComponent.doCanoeSpecificStuff(); } // 对外暴露独木舟专属方法 public void handleCanoeCheckout() { canoeComponent.doCanoeSpecificStuff(); } // 委托属性访问 public int getSomeCanoeSpecificAttribute() { return canoeComponent.getSomeCanoeSpecificAttribute(); } } // 混合站:同时组合两个组件 public class BikeAndCanoeStation extends Station { private final BikeComponent bikeComponent = new BikeComponent(); private final CanoeComponent canoeComponent = new CanoeComponent(); @Override public void doSomething() { bikeComponent.doBikeSpecificStuff(); canoeComponent.doCanoeSpecificStuff(); } // 暴露两种专属方法 public void handleBikeCheckout() { bikeComponent.doBikeSpecificStuff(); } public void handleCanoeCheckout() { canoeComponent.doCanoeSpecificStuff(); } // 委托两种属性的访问 public int getSomeBikeSpecificAttribute() { return bikeComponent.getSomeBikeSpecificAttribute(); } public int getSomeCanoeSpecificAttribute() { return canoeComponent.getSomeCanoeSpecificAttribute(); } }
优势:完全消除代码冗余,组件可独立扩展(比如给BikeComponent新增功能时,所有使用它的站点都会受益),后续新增KayakStation或BikeAndKayakStation只需新增组件类和组合,扩展性极强。
方案2:接口拆分+能力契约
如果需要明确各站点的能力边界,可将自行车、独木舟的专属能力定义为接口,再通过组合接口实现类来复用逻辑,符合接口隔离原则。
步骤1:定义能力接口
// 自行车能力接口 public interface BikeCapable { void doBikeSpecificStuff(); int getSomeBikeSpecificAttribute(); void setSomeBikeSpecificAttribute(int value); } // 独木舟能力接口 public interface CanoeCapable { void doCanoeSpecificStuff(); int getSomeCanoeSpecificAttribute(); void setSomeCanoeSpecificAttribute(int value); }
步骤2:实现接口的通用逻辑
// 自行车能力的默认实现类 public class DefaultBikeCapability implements BikeCapable { private int someBikeSpecificAttribute; @Override public void doBikeSpecificStuff() { // 默认自行车专属逻辑 } @Override public int getSomeBikeSpecificAttribute() { return someBikeSpecificAttribute; } @Override public void setSomeBikeSpecificAttribute(int value) { this.someBikeSpecificAttribute = value; } } // 独木舟能力的默认实现类 public class DefaultCanoeCapability implements CanoeCapable { private int someCanoeSpecificAttribute; @Override public void doCanoeSpecificStuff() { // 默认独木舟专属逻辑 } @Override public int getSomeCanoeSpecificAttribute() { return someCanoeSpecificAttribute; } @Override public void setSomeCanoeSpecificAttribute(int value) { this.someCanoeSpecificAttribute = value; } }
步骤3:重构站点类
public abstract class Station { protected String name; protected String address; public void commonOperation() { // 公共逻辑 } public abstract void doSomething(); } // 自行车站:继承Station并实现BikeCapable public class BikeStation extends Station implements BikeCapable { private final BikeCapable bikeCapability = new DefaultBikeCapability(); @Override public void doSomething() { bikeCapability.doBikeSpecificStuff(); } // 委托接口方法实现 @Override public void doBikeSpecificStuff() { bikeCapability.doBikeSpecificStuff(); } @Override public int getSomeBikeSpecificAttribute() { return bikeCapability.getSomeBikeSpecificAttribute(); } @Override public void setSomeBikeSpecificAttribute(int value) { bikeCapability.setSomeBikeSpecificAttribute(value); } } // 独木舟站类似 public class CanoeStation extends Station implements CanoeCapable { private final CanoeCapable canoeCapability = new DefaultCanoeCapability(); @Override public void doSomething() { canoeCapability.doCanoeSpecificStuff(); } @Override public void doCanoeSpecificStuff() { canoeCapability.doCanoeSpecificStuff(); } @Override public int getSomeCanoeSpecificAttribute() { return canoeCapability.getSomeCanoeSpecificAttribute(); } @Override public void setSomeCanoeSpecificAttribute(int value) { canoeCapability.setSomeCanoeSpecificAttribute(value); } } // 混合站:同时实现两个接口 public class BikeAndCanoeStation extends Station implements BikeCapable, CanoeCapable { private final BikeCapable bikeCapability = new DefaultBikeCapability(); private final CanoeCapable canoeCapability = new DefaultCanoeCapability(); @Override public void doSomething() { bikeCapability.doBikeSpecificStuff(); canoeCapability.doCanoeSpecificStuff(); } // 实现BikeCapable方法 @Override public void doBikeSpecificStuff() { bikeCapability.doBikeSpecificStuff(); } @Override public int getSomeBikeSpecificAttribute() { return bikeCapability.getSomeBikeSpecificAttribute(); } @Override public void setSomeBikeSpecificAttribute(int value) { bikeCapability.setSomeBikeSpecificAttribute(value); } // 实现CanoeCapable方法 @Override public void doCanoeSpecificStuff() { canoeCapability.doCanoeSpecificStuff(); } @Override public int getSomeCanoeSpecificAttribute() { return canoeCapability.getSomeCanoeSpecificAttribute(); } @Override public void setSomeCanoeSpecificAttribute(int value) { canoeCapability.setSomeCanoeSpecificAttribute(value); } }
优势:通过接口明确了各站点的能力,便于后续做类型判断(比如if (station instanceof BikeCapable)),同时逻辑复用也得到保障。
方案3:接口默认方法(仅解决行为复用)
如果主要需求是复用行为逻辑,属性数量较少或可通过其他方式复用,可以用Java 8+的接口默认方法,但注意接口不能定义实例字段,属性仍需在子类中定义。
// 自行车行为接口 public interface BikeOperations { default void doBikeSpecificStuff() { // 默认自行车专属行为实现 } } // 独木舟行为接口 public interface CanoeOperations { default void doCanoeSpecificStuff() { // 默认独木舟专属行为实现 } } public abstract class Station { protected String name; protected String address; public void commonOperation() { // 公共逻辑 } public abstract void doSomething(); } public class BikeStation extends Station implements BikeOperations { private int someBikeSpecificAttribute; @Override public void doSomething() { doBikeSpecificStuff(); } // 属性的getter/setter } public class CanoeStation extends Station implements CanoeOperations { private int someCanoeSpecificAttribute; @Override public void doSomething() { doCanoeSpecificStuff(); } // 属性的getter/setter } public class BikeAndCanoeStation extends Station implements BikeOperations, CanoeOperations { private int someBikeSpecificAttribute; private int someCanoeSpecificAttribute; @Override public void doSomething() { doBikeSpecificStuff(); doCanoeSpecificStuff(); } // 属性的getter/setter }
适用场景:行为逻辑复用需求大于属性复用需求,且属性定义简单的场景。
内容的提问来源于stack exchange,提问作者Flow

