【面向对象】面向对象设计原则及常见设计模式的总结


参考文章

  • https://juejin.im/entry/5917d38dda2f60005de8def4
  • https://www.jianshu.com/p/a3474f4fee57

面向对象思想设计原则

  • 单一职责原则
    • 其实就是开发人员经常说的“高内聚,低耦合
    • 也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都应遵循这一原则
  • 开闭原则
    • 核心思想是:一个对象对扩展开放,对修改关闭
    • 其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。
    • 也就是说开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的。
  • 里氏替换原则
    • 核心思想:在任何父类出现的地方都可以用它的子类来替代
    • 其实就是说:同一个继承体系中的对象应该有共同的行为特征。父类的功能子类也应该能实现。
  • 依赖注入原则
    • 核心思想:要依赖于抽象,不要依赖于具体实现
    • 其实就是说在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体实现类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
  • 接口分离原则
    • 核心思想不应该强迫程序依赖它们不需要使用的方法
    • 其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。
  • 迪米特原则
    • 核心思想一个对象应当对其他对象尽可能少的了解
    • 其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用

设计模式

介绍

为了让我们的代码能更好地遵循上面的原则,从而达到易于维护的目的。我们就有必要了解一下设计模式。

  • 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
  • 设计模式不是一种方法和技术,而是一种思想

  • 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用

  • 学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成

设计模式的分类

我们可以将设计模式大致这样分类:

  • 创建型模式(对象的创建):简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)
  • 结构型模式(对象的组成(结构)):外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)
  • 行为型模式(对象的行为):模版方法模式、观察者模式、状态模式、责任链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)

常见的设计模式

设计模式的分类虽然很多,但是我们平时常用的也就下面的几种而已

  • 简单工厂模式和工厂方法模式(接口)
  • 模版设计模式(抽象类)
  • 装饰设计模式(IO流)
  • 单例设计模式(多线程)
  • 建造者模式
  • 适配器模式(GUI)

简单工厂模式

简单工厂模式,又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例。

比如,下面是一个简单工厂模式的例子:

运行后控制台结果:

狗吃骨头

猫吃鱼

我们运用了简单工厂模式后,不用每次用的时候去new对象,而是直接去调用这个工厂类里面的具体方法,它会给我们返回一个已经new好的对象。

我们来分析一下简单工厂模式的优点与缺点

  • 优点
    • 客户端不需要负责对象的创建,明确了各个类的职责。更有利于遵守单一职责原则。
  • 缺点
    • 这个静态工厂负责所有对象的创建。每当有新的对象增加,我们都需要去修改工厂类,这样不利于后期的维护。

工厂方法模式

工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类来实现。

我们将上面的例子改为工厂方法模式:

观察用工厂方法模式,会发现多了几个类。但是当我们这时要加入一种动物猪的时候,不再需要修改工厂类的代码,只需创建一个猪类继承抽象动物类,重写抽象方法,再创建一个猪的工厂类实现工厂类并实现它的抽象方法即可。这样的代码有很强的维护性和扩展性。

下面分析一下工厂方法模式的优点及缺点:

  • 客户端不需要再负责对象的创建,明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性。遵守了开闭原则,减少了修改。
  • 需要额外编写代码,增加了我们的工作量。

单例模式

单例模式的出现是为了确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。

那么如何实现这个类在内存中仅有一个对象呢

  • 构造私有
  • 本身提供一个对象
  • 通过公共的方法让外界访问

下面分别介绍单例模式中的懒汉式以及饿汉式。

饿汉式

饿汉式就是在类一加载时,就创建对象。

饿汉式的特点就是类一加载就创建对象,可以在代码中Student类中体现到。那么我们怎样才能在用这个对象的时候才去创建它呢,我们就要来看下懒汉式了。

懒汉式

懒汉式就是在对象使用的时候,再去创建对象.

这样,就完成了懒汉式单例。可以看到,它是在使用到对象的时候,才去创建的。

对比

饿汉式我们经常在开发中使用,它是不会出问题的单例模式

懒汉式我们在回答时用,它是可能会出问题的单例模式。

关于单例模式需要了解的思想:

  • 懒加载思想(延迟加载)
  • 线程安全问题(考虑下面3个方面)
    • 是否多线程环境
    • 是否有共享数据
    • 是否有多条语句操作共享数据

如果都是,就会存在线程的安全问题,我们上面的懒汉式代码是不完整的,应该给对象中的方法加上synchronized关键字,这样才算完整。

模板方法模式

模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现。

下面是模板方法的示例:

步骤1: 创建抽象模板结构(Abstract Class):炒菜的步骤

步骤2: 创建具体模板(Concrete Class),即”手撕包菜“和”蒜蓉炒菜心“的具体步骤

**步骤3: **客户端调用

这样就成功实现了模板方法模式

下面分析一下它的优点及缺点:

  • 优点
    • 提高代码复用性
      将相同部分的代码放在抽象的父类中
    • 提高了拓展性
      将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为
    • 实现了反向控制
      通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制 & 符合“开闭原则”
  • 缺点
    • 引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度。

装饰模式

装饰模式:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式中有这四个角色:抽象构件、具体构件、抽象装饰类 、具体装饰类

下面是装饰模式的一个示例

组件类

组件装饰者

继承类ListBox

继承类TextBox

黑框装饰者

滚动条装饰者

客户端调用

对装饰模式举个例子

IO流中就用到了装饰模式

下面分析一下装饰模式的优点及缺点:

  • 优点
    • 使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能
  • 缺点
    • 正因为可以随意组合,所以就可能出现一些不合理的逻辑

适配器模式

适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作。

适配器模式分为两种

  • 类适配器模式 :通过实现Target接口以及继承Adaptee类来实现接口转换
  • 对象适配器模式:实现Target接口和代理Adaptee的某个方法来实现接口转换

角色介绍

  • 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
  • 源(Adaptee)角色:现在需要适配的接口。
  • 适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

类适配器模式

类的适配器模式是把适配的类的API转换成为目标类的API。

步骤1.创建Target接口

步骤2.创建需要适配的类Adaptee

步骤3.创建适配器类Adapter

步骤4.定义使用目标类,并通过Adapter类调用所需要的方法从而实现目标

这样,就达到了我们的目的。让Target成功使用了Adaptee类的specificRequest方法。

对象适配器模式

与类的适配器模式相同,对象的适配器模式也是把适配的类的API转换成为目标类的API。

步骤1.创建Target接口

步骤2.创建需要适配的类Adaptee

步骤3.创建适配器类Adapter(使用包含的方式)

步骤4.定义使用目标类,并通过Adapter类调用所需要的方法从而实现目标

两种模式的对比

  • 类适配器模式
    • 优点
    • 使用方便,代码简化
      仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例
    • 缺点
    • 高耦合,灵活性低
      使用对象继承的方式,是静态的定义方式
  • 对象适配器模式
    • 优点
    • 灵活性高、低耦合
      采用 “对象组合”的方式,是动态组合方式
    • 缺点
    • 使用复杂
      需要引入对象实例

适配器模式的优缺点分析

  • 优点
    • 更好的复用性
      系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
    • 透明、简单
      客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单 & 更直接
    • 更好的扩展性
      在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
    • 解耦性
      将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码
    • 符合开放-关闭原则
      同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改待适配类
  • 缺点
    • 过多的使用适配器,会让系统非常零乱,不易整体进行把握

建造者模式

建造者模式可以将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 、

下面是以组装电脑为例的实例

步骤1: 定义组装的过程(Builder):组装电脑的过程

步骤2: 电脑城老板委派任务给装机人员(Director)

步骤3: 创建具体的建造者(ConcreteBuilder):装机人员

步骤4: 定义具体产品类(Product):电脑

步骤5: 客户端调用

下面我们来介绍一下建造者模式的优缺点

  • 优点
    • 易于解耦
      将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
    • 易于精确控制对象的创建
      将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 易于拓展
      增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
  • 缺点
    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

Android Developer in GDUT