面向对象的六大原则(三)里氏替换原则

声明:本系列博客整理来源于《Android源码设计模式解析与实战》,仅作为个人学习总结记录,任何组织和个人不得转载进行商业活动!

 

 

里氏替换原则(Lisvok Substitution Principle,LSP)

LSP的第一种定义是:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S使类型T的子类型。不太好理解,里氏替换原则第二种定义:所有引用基类的地方必须能透明的使用其子类的对象。

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

在Android中,Window与View的关系,如下图以下:

面向对象的六大原则(三)里氏替换原则_第1张图片

具体的代码实现:

/**
 * 窗口类
 */
public class Window {

    public void show(View child){
        child.draw();
    }

}

/**
 * 建立视图抽象,测量视图的宽高为公用代码,绘制实现交给具体的子类
 */
public abstract class View {


    public abstract void draw();
    public void measure(int width,int height){
        //测量视图大小
    }

}

/**
 * 按钮类的具体实现
 */
public class Button extends View {
    @Override
    public void draw() {
        //绘制按钮
    }
}

/**
 * TextView的具体实现
 */
public class TextView extends View {

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

}

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

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

优点有以下几点:

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

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

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

继承的缺点:

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

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

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

面向对象的六大原则(三)里氏替换原则_第2张图片

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

想象一种情况,当ImageLoader中的setImageCache(ImageCache cache)中的cache对象不能够被子类所替换,那么用户该如何设置不同的缓存对象,以及用户如何自定义自己的缓存实现,通过useDiskCache方法吗?显然不是的,里氏替换原则就为这类问题提供了指导原则,也就是建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的扩展性、灵活性。开闭原则和里氏替换原则往往是生死相依、不弃不离的,通过里氏替换来达到对扩展开放,对修改关闭的效果。然而,这两个原则都同时强调了OOP的重要特性 --- 抽象。因此,在开发过程中运用抽象是走向代码优化的重要一步。

 

 

<<点击 - 面向对象的六大原则之开闭原则

点击 - 面向对象的六大原则之依赖倒置原则>>

你可能感兴趣的:(设计模式)