1 引子
我们都知道,士兵根据作战性质可以分很多种,比如步兵、骑兵和弓箭手等;又会根据个人的资质不同,会将其分会不同的等级,这里可简单理解为等级A、等级B和等级C。所以此处三种士兵,分别有三个等级,应该共有9个实体类。大概的逻辑图如图所示:
上图很明显,反应出多层继承的问题。
①抽象士兵-步兵/骑兵/弓箭手:根据作战性质进行划分;
②步兵/骑兵/弓箭手-A等/ B等/C等:根据个人资质进行划分;
这种多层继承虽然很明了的构造出具体的类,但细想起来,每个人类的功能和职责却很模糊,例如“A等步兵”类,即反应作战性质,又反应个人资质(等级),这违背了类设计的“单一职责”原则。
另外如果我新增一个等级D,则我需要新增三个类;又或者新增一个“水兵”,则我需要新增四个类;最后的结果就是类的数量非常冗余,导致类的可扩展性差。
仔细分析以上两点可知,类的“作战性质”和“等级”两种属性耦合在一起是导致扩展性差的根本原因。我改变“作战性质”或者“等级”中的任意一个属性,另外一个属性也需要跟着一起变化,即属性紧紧的耦合在一起了。要想解决这个问题,就需要对两种属性进行解耦,使得任意属性的改变都是独立的。改造后的代码逻辑:
抽象士兵类Soldier:
/**
* 抽象士兵类
*/
public abstract class Soldier {
private String name;
Rank rank;
public Soldier(Rank rank, String name){
this.rank = rank;
this.name = name;
}
public String getName() {
return name;
}
public abstract void type();
}
Soldier类包含属性name和属性rank。Rank是一个表示等级的接口。
等级接口类Rank:
/**
* 等级接口
*/
public interface Rank {
void rankByAchieve(); // 根据成就来划分等级
}
“等级”原本属于抽象类里面的一个属性,这里把等级抽象成接口,抽象类Soldier拥有接口引用,遵循类的“依赖倒置”原则。
等级A类:
public class RankForA implements Rank {
@Override