Liskov替换原则:LSP替换原则和继承、多态的概念高度相关。
Liskov替换原则就是再一次强调,把父类当成语义接口去继承,否则不要去继承。 我们希望程序具有拓展性,而拓展性要求两个类的可替换性,所以才会把强调可替换性的LSP原则列为设计原则。LSP原则下,父类提供语义接口,子类必须在符合父类语义的前提下提供实现方法。
违反Liskov替换原则的例子
class Students extends ArrayList<Student> {
...
}
// 显然糟糕透了。Students 和 ArrayList<Student> 语义上风马牛不像及
多态
表面上,多态就是实现同一个接口的不同类,拥有不同的实现。实质上,多态是指一个职责有多种实现形态。一个类的职责,由类的接口表达,由类的 public 方法表达。
强类型的Java
在Java中,因为Java是强类型语言,有严格类型限制,如果输入类型是限定类型及其子类型,才能被传入,使得多态只能发生在具有父子关系( extends ParentClass 或 implement Interface )的类型之间。
多态面前:父类的概念等价等价于接口
当一个软件程序中,所有的代码复用都由组合完成,父类只提供语义接口不提供代码,造成事实上父类充当的是接口,extends ParentClass 和 implement Interface 没差别。也就是说,接口、父类的概念是等价的,为了叙述方便,可以统称为接口。
继承
子类继承父类。子类复用父类的代码,还继承得到什么?
子类还继承得到父类的语义。子类 override 掉父类的实现(代码),实质是在继承父类的语义。 所以,我们可以把继承分为两类:继承语义的多态和继承实现的代码复用。
继承只能一次,往往让继承完成了代码复用就放弃了用继承完成多态,是选择代码复用还是多态?
前者在许多时候被认为是一种反模式,应该极力避免用继承进行代码复用,原因是,包括Java在内的许多语言,继承只能一次,使用继承实现了代码复用往往就放弃了用继承完成多态,这是二选一的问题。代码复用完全可以用组合实现。多态非常重要,赋予程序可拓展性。所以推荐用继承完成多态,用组合完成代码复用。
单一职责原则
设计程序可以类比成开公司,凡事都有成本,当程序规模本身就很小,就像小公司本身业务就简单,员工身兼数职,不存在管理问题。而随着程序功能的越来越繁杂,我们就希望多加人手,每个人干自己职能范围的事,相应的事只需要找相应的人,出了事也好追责。
// 一开始
class User {
// 修改密码
void changePassword(String password);
// 加入一个项目
void joinProject(Project project);
// 接管一个项目,成为管理员
void takeOverProject(Project project);
...
}
// 新增需求
void changePhoneNumber(PhoneNumber phoneNumber):
// 又新增需求
int countProject();
//所以到最后交由两个人来做
// 一个人负责满足个人信息管理
class PersonalInfoImp {
// 修改密码
void changePassword(String password);
...
}
// 一个人负责满足满足项目管理的需求
class MemberInfoImp{
// 加入一个项目
void joinProject(Project project);
// 接管一个项目,成为管理员
void takeOverProject(Project project);
...
}
开放封闭原则
//重构前
class ReportService {
public void process() {
// 获取当天的订单
List<Order> orders = fetchDailyOrders();
// 生成统计信息
OrderStatistics statistics = generateOrderStatistics(orders);
// 生成统计报表
generateStatisticsReport(statistics); // 被重构点
// 发送统计邮件
sendStatisticsByMail(statistics); // 被重构点
}
}
//重构后
interface OrderStatisticsConsumer {
void consume(OrderStatistics statistics);
}
class StatisticsReporter implements OrderStatisticsConsumer {
public void consume(OrderStatistics statistics) {
generateStatisticsReport(statistics);
}
}
class StatisticsByMailer implements OrderStatisticsConsumer {
public void consume(OrderStatistics statistics) {
sendStatisticsByMail(statistics);
}
}
class ReportService {
private List<OrderStatisticsConsumer> consumers;
void process() {
// 获取当天的订单
List<Order> orders = fetchDailyOrders();
// 生成统计信息
OrderStatistics statistics = generateOrderStatistics(orders);
for (OrderStatisticsConsumer consumer: consumers) { // 重构点
consumer.consume(statistics);
}
}
}