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 的重要特性 ------ 抽象,因此,在开发过程中运用抽象是走向代码优化的重要一步。

相关推荐
软考真题app4 小时前
软件设计师考试结构型设计模式考点全解析
设计模式·软件设计师·结构型设计模式·考试考点
xiaolin03339 小时前
【设计模式】- 行为型模式1
设计模式·状态模式·责任链模式·策略模式·命令模式·模板方法模式·行为型模式
沐土Arvin10 小时前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
bao_lanlan11 小时前
兰亭妙微:用系统化思维重构智能座舱 UI 体验
ui·设计模式·信息可视化·人机交互·交互·ux·外观模式
总是难免12 小时前
设计模式 - 单例模式 - Tips
java·单例模式·设计模式
Java致死16 小时前
设计模式Java
java·开发语言·设计模式
ghost1431 天前
C#学习第23天:面向对象设计模式
开发语言·学习·设计模式·c#
敲代码的 蜡笔小新1 天前
【行为型之迭代器模式】游戏开发实战——Unity高效集合遍历与场景管理的架构精髓
unity·设计模式·c#·迭代器模式
敲代码的 蜡笔小新2 天前
【行为型之命令模式】游戏开发实战——Unity可撤销系统与高级输入管理的架构秘钥
unity·设计模式·架构·命令模式
m0_555762902 天前
D-Pointer(Pimpl)设计模式(指向实现的指针)
设计模式