Android 构建扩展性更好的系统 —— 里氏替换原则

里氏替换原则

里氏替换原则英文全称是 LiskovSubstitutionPrinciple,缩写是 LSP。LSP 的第一种定义是:如果对每一个类型为 S 的对象 O1,都有类型为 T 的对象 O2,使得以 T 定义的所有程序 P 在所有的对象 O1 都代换成 O2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。上面这种描述确实不太好理解,我们再看看另一个直截了当的定义。里氏替换原则第二种定义:所有引用基类的地方必须能透明地使用其子类的对象。

我们知道,面向对象的语言的三大特点是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来说就是,所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。说了那么多,其实最终总结就两个字:抽象。

例子

Android 中的 Window 与 View 的关系。

java 复制代码
public class Window {
   public void show(View child)  {
      // 绘制各种视图
      child.draw();
   }
}
java 复制代码
public abstract class View {
   public abstract void draw() ;
   
    public void measure(int width, int height){
        // 测量视图大小
    }

}
java 复制代码
public class TextView extends View {

   @Override
   public void draw() {
      // 绘制文本
   }

}
java 复制代码
public class ImageView extends View {

   @Override
   public void draw() {
      // 绘制图片
   }

}

上述示例中,Window 依赖于 View,而 View 定义了一个视图抽象,measure 是各个子类共享的方法,子类通过覆写 View 的 draw 方法实现具有各自特色的功能,在这里,这个功能就是绘制自身的内容。任何继承自 View 类的子类都可以传递给 show 函数,就是所说的里氏替换。通过里氏替换,就可以自定义各式各样、千变万化的 View,然后传递给 Window,Window 负责组织 View,并且将 View 显示到屏幕上。

总结

里氏替换原则的核心原理是抽象,抽象又依赖于继承这个特性,在 OOP 当中,继承的优缺点都相当明显。优点有以下几点:

(1)代码重用,减少创建类的成本,每个子类都拥有父类的方法和属性;

(2)子类与父类基本相似,但又与父类有所区别;

(3)提高代码的可扩展性。

继承的缺点:

(1)继承是侵入性的,只要继承就必须拥有父类的所有属性和方法;

(2)可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类的属性和方法。

事物总是具有两面性,如何权衡利与弊都是需要根据具体情况来做出选择并加以处理。里氏替换原则指导我们构建扩展性更好的软件系统,还是接着前面的 ImageLoader 来做说明。

# Android 优化代码的第一步 ------ 单一职责原则

# Android 让程序更稳定、更灵活 ------ 开闭原则

图 1-2 所示也很好地反应了里氏替换原则,即 MemoryCache、DiskCache、DoubleCache 都可以替换 ImageCache 的工作,并且能够保证行为的正确性。ImageCache 建立了获取缓存图片、保存缓存图片的接又规范,MemoryCache 等根据接又规范实现了相应的功能,用户只需要在使用时指定具体的缓存对象就可以动态地替换 ImageLoader 中的缓存策略。这就使得 ImageLoader 的缓存系统具有了无限的可能性,也就是保证了可扩展性。

想象一种情况,当 ImageLoader 中的 setlmageCache(ImageCachecache) 中的 cache 对象不能够被子类所替换,那么用户如何设置不同的缓存对象,以及用户如何自定义自己的缓存实现?里氏替换原则就为这类问题提供了指导原则,也就是建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的扩展性、灵活性。

开闭原则和里氏替换原则往往是生死相依、不弃不离的,通过里氏替换来达到对扩展开放,对修改关闭的效果。然而,这两个原则都同时强调了一个 OOP 的重要特性 ------ 抽象,因此,在开发过程中运用抽象是走向代码优化的重要一步。

相关推荐
vker10 小时前
第 1 天:单例模式(Singleton Pattern)—— 创建型模式
java·设计模式
晨米酱1 天前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript