抽象类统一管理不同球类对象的OOP设计实现疑问
我现在不确定类的具体实现方式。现有Baseball和Basketball两类,二者特性完全不同,均继承抽象类Sports,初始代码如下:
public abstract class Sports { public abstract String getSport(); public abstract String getCoach(); public abstract void getTeamSize() { System.out.println("The size of the team is xxx."); } } public class Baseball extends Sports { public List<Baseballs> getBaseballs() { // 获取另一个对象baseballs的列表 } // 实现抽象方法 } public class Basketball extends Sports { public List<Basketballs> getBasketballs() { // 获取另一个对象basketballs的列表 } // 实现抽象方法 }
我希望创建List<Sports> sports集合管理所有球类对象,但遍历该集合时需根据对象类型打印对应的专属列表(如Baseballs或Basketballs列表)。请问是否需要通过类型转换来调用对应方法?这种设计是否不合理?
我还想到另一种实现方案,但需手动处理且存在问题,代码如下:
public class Sports { String sportType = "[Sport]"; List<Basketball> basketballs; List<Baseball> baseballs; public void getTeamSize() { System.out.println("The size of the team is 10."); } public List<Basketball> getBasketballs() { return this.basketballs; } public List<Baseball> getBaseballs() { return this.baseballs; } }
遍历该对象时可通过switch判断sportType来处理:
switch (type) { case "Baseball": // 遍历并打印baseball列表 break; case "Basketball": // 遍历并打印basketball列表 break; }
但此方案会存在未使用的属性(如Baseball对象中的basketballs属性),请问合理的OOP实现方式是什么?
其实你完全不用纠结类型转换或者冗余属性的问题,利用OOP的多态特性就能优雅解决这个需求,完美契合开闭原则和单一职责原则。
核心思路:让子类自己管自己的专属逻辑
简单来说,就是把“打印专属列表”这个行为,交给各个球类子类自己去实现,抽象类只定义统一的行为规范。这样遍历List<Sports>的时候,直接调用统一方法就行,根本不用判断对象类型。
第一步:给抽象类加统一的行为规范
修改Sports抽象类,新增一个抽象方法printExclusiveList(),用来定义“打印专属列表”这个统一行为:
public abstract class Sports { public abstract String getSport(); public abstract String getCoach(); public abstract void getTeamSize(); // 新增:统一的打印专属列表方法 public abstract void printExclusiveList(); }
第二步:子类实现自己的专属打印逻辑
在Baseball和Basketball里,分别实现printExclusiveList()方法,各自处理自己的专属列表打印,同时每个子类只持有自己需要的列表属性,完全没有冗余:
public class Baseball extends Sports { private List<Baseballs> baseballs; // 用构造方法初始化列表 public Baseball(List<Baseballs> baseballs) { this.baseballs = baseballs; } @Override public String getSport() { return "Baseball"; } @Override public String getCoach() { return "棒球教练"; } @Override public void getTeamSize() { System.out.println("The size of the baseball team is 9."); } @Override public void printExclusiveList() { System.out.println("棒球专属列表:"); for (Baseballs item : baseballs) { // 这里打印Baseballs对象的具体信息,比如item.getName()之类的 System.out.println(item.toString()); } } // 如果外部需要获取列表,也可以保留这个方法,但打印逻辑已经封装在子类里了 public List<Baseballs> getBaseballs() { return baseballs; } }
public class Basketball extends Sports { private List<Basketballs> basketballs; public Basketball(List<Basketballs> basketballs) { this.basketballs = basketballs; } @Override public String getSport() { return "Basketball"; } @Override public String getCoach() { return "篮球教练"; } @Override public void getTeamSize() { System.out.println("The size of the basketball team is 5."); } @Override public void printExclusiveList() { System.out.println("篮球专属列表:"); for (Basketballs item : basketballs) { System.out.println(item.toString()); } } public List<Basketballs> getBasketballs() { return basketballs; } }
第三步:遍历集合时直接调用统一方法
现在管理List<Sports>的时候,遍历代码超级简洁,多态会自动帮你调用对应子类的实现,完全不用类型转换或者switch判断:
List<Sports> sports = new ArrayList<>(); // 假设已经初始化了baseballList和basketballList sports.add(new Baseball(baseballList)); sports.add(new Basketball(basketballList)); for (Sports sport : sports) { sport.printExclusiveList(); sport.getTeamSize(); // 其他抽象方法也能正常调用 }
为什么这个方案更优?
- 彻底避免类型转换:不用在遍历的时候判断对象类型,代码更简洁,也不会出现类型转换错误的风险。
- 符合开闭原则:以后如果新增其他球类(比如Football),只需要新增一个
Football子类实现Sports的抽象方法就行,完全不用修改现有的遍历逻辑。 - 消除冗余属性:每个子类只持有自己需要的列表属性,不会像你之前的方案那样出现“Baseball对象带着basketballs属性”这种不合理的情况,完美符合单一职责原则。
解答你之前的几个疑问
- 是否需要类型转换?:用上面的多态方案完全不需要,类型转换属于“向下转型”,是一种比较脆弱的写法,容易出错,应该尽量避免。
- 第一种设计是否不合理?:你最初的抽象类设计没问题,只是缺少了统一的行为方法来封装子类的专属逻辑,补充之后就非常合理了。
- 第二种方案的问题:它违背了单一职责原则,一个类持有了不属于自己的属性,而且switch判断的写法会让代码变得臃肿,新增球类时还要修改switch分支,完全不符合开闭原则。
内容的提问来源于stack exchange,提问作者Ziic

