SOLID原则学习笔记——Liskov替换原则

241 阅读3分钟

Liskov替换原则:LSP替换原则和继承、多态的概念高度相关。

Liskov替换原则就是再一次强调,把父类当成语义接口去继承,否则不要去继承。 我们希望程序具有拓展性,而拓展性要求两个类的可替换性,所以才会把强调可替换性的LSP原则列为设计原则。LSP原则下,父类提供语义接口,子类必须在符合父类语义的前提下提供实现方法。

违反Liskov替换原则的例子

class Students extends ArrayList<Student> {
  ...
}
// 显然糟糕透了。Students 和 ArrayList<Student> 语义上风马牛不像及

多态

表面上,多态就是实现同一个接口的不同类,拥有不同的实现。实质上,多态是指一个职责有多种实现形态。一个类的职责,由类的接口表达,由类的 public 方法表达。

强类型的Java

在Java中,因为Java是强类型语言,有严格类型限制,如果输入类型是限定类型及其子类型,才能被传入,使得多态只能发生在具有父子关系( extends ParentClassimplement Interface )的类型之间。

多态面前:父类的概念等价等价于接口

当一个软件程序中,所有的代码复用都由组合完成,父类只提供语义接口不提供代码,造成事实上父类充当的是接口,extends ParentClassimplement 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);
    }
  }
}