设计模式概念

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

二、设计模式的六大原则

总原则: 开闭原则

  1. 对扩展开放,对修改关闭
    1. 单一职责原则(职责即类改变的原因)
不要存在多余1个导致类变更的原因,如果有多个,则表示有多个职责,他们会耦合在一起。
一个职责的变化,则会导致其余职责来兼容,削弱了执行其余职责的能力。

解决方式是:解耦和提高内聚性。
  1. 里式替换原则(开闭原则的补充)
任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石。
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用;
而衍生类也能够在基类的基础上增加新的行为。
  1. 依赖倒转原则(开闭原则的基础)
面向接口编程,依赖于抽象而不依赖于具体。
写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
  1. 接口隔离原则
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。
使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
  1. 迪米特法则(最少知道原则)

    只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。
    我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。
    1我们要求陌生的类不要作为局部变量出现在类中。
  2. 合成复用原则

原则是尽量首先使用合成/聚合的方式,而不是使用继承。

创建性模式:简单工厂、方法工厂、单例、建造者、原型

结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式

一、方法工厂模式

  1. 将继承统一接口的一些类,在创建对象的时候统一管理。

  2. 依赖倒转原则、里式替换原则、高内聚、低耦合。

  3. 更换新的角色、增加新的角色,都能够更加方便,改动少。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 简单的工厂模式。
    // 不利于扩展、不符合开闭。
    public class LoggerFactory {
    public static Logger getLogger(String logType){
    if("Log1".equals(logType)){
    return new Log1();
    }
    if("Log2".equals(logType)){
    return new Log2();
    }
    return null;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 方法工厂模式
    public class LoggerFactory {

    private static IFactory iFactory;

    public static Logger getLogger(){
    return iFactory.getLogger();
    }

    public interface IFactory {
    Logger getLogger();
    }

    public class Log1Factory implements IFactory {

    @Override
    public Logger getLogger() {
    return new Log1();
    }
    }

二、单例模式

  1. 主要应用于需要单个实例的场景(比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。)
  2. 这样子省资源,创建实例很耗费资源,又没有必要过多创建实例。
  3. 需要注意:何时加载?单线程?线程安全?

三、建造者模式

  1. 它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。(汽车)

  2. 优点:

    1. 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
    2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
    3. 可以更加精细地控制产品的创建过程
    4. 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则”
  3. 缺点:

    1. 产品之间差异很大,不建议。(汽车族、轮船、飞机=> 运输工具)
    2. 产品内部变化复杂,不建议。
  4. 高扩展性、符合开闭原则、依赖倒转原则。(kfc套餐)

四、适配器模式

  1. 类的适配器模式(里式替换原则)

    1. (1)目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。

      (2)源(Adapee)角色:现在需要适配的接口。

      (3)适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

  2. 对象的适配器模式

  3. 接口的适配器模式

  4. 何时使用:

(1)类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

(2)对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个包装类,持有原类的一个实例,在包装类的方法中,调用实例的方法就行。

五、装饰者模式

  1. 装饰器模式的应用场景:

    1、需要扩展一个类的功能。

    2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

    缺点:产生过多相似的对象,不易排错!

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

1
2
3
4
5
IThirdParty thirdPartyOne =new ThirdParty();
  IThirdParty decorator1 =new Decorator1(thirdPartyOne);
  IThirdParty decorator2 =new Decorator2(decorator1);
  
  System.out.println(decorator2.sayMsg());
  1. 本身就是针对源对象的方法执行前后做处理,即为装饰。采取的方式,就是使用一个包换源对象需要装饰的方法的接口,其余装饰者都实现这个接口即可。并且每个装饰者里都有这个源对象作为自己的一个属性。
  2. 组合的情况很多的时候,比如调酒,原料和其余组合起来有很多很多情况。
  3. 符合开闭原则

六、代理模式

  1. 和装饰者实现角度类似,只是说使用角度是,代替源对象做一些事情,并且再源对象前后做一些事。因为源对象信息不够全面的时候。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Proxy implements Sourceable {  

    private Source source;
    public Proxy(){
    super();
    this.source = new Source();
    }
    @Override
    public void method() {
    before();
    source.method();
    atfer();
    }
    private void atfer() {
    System.out.println("after proxy!");
    }
    private void before() {
    System.out.println("before proxy!");
    }
    }
  2. 然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

七、外观模式

  1. 外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class Computer {  
    private CPU cpu;
    private Memory memory;
    private Disk disk;

    public Computer(){
    cpu = new CPU();
    memory = new Memory();
    disk = new Disk();
    }

    public void startup(){
    System.out.println("start the computer!");
    cpu.startup();
    memory.startup();
    disk.startup();
    System.out.println("start computer finished!");
    }

    public void shutdown(){
    System.out.println("begin to close the computer!");
    cpu.shutdown();
    memory.shutdown();
    disk.shutdown();
    System.out.println("computer closed!");
    }
    }

八、桥接模式

  1. 你有多个工具要使用的时候,为了可以自由切换,你需要一个桥,一个adapter。你使用的时候,只需要告诉他你要哪个工具,然后你使用的时候就会用那个工具。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class BridgeTest {  

    public static void main(String[] args) {

    Bridge bridge = new MyBridge();

    /*调用第一个对象*/
    Sourceable source1 = new SourceSub1();
    bridge.setSource(source1);
    bridge.method();

    /*调用第二个对象*/
    Sourceable source2 = new SourceSub2();
    bridge.setSource(source2);
    bridge.method();
    }
    }

九、组合模式

  1. 组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便。(二叉树)

  2. 根节点和叶子节点需要实现相同的接口。根节点存储着许多children节点。

  3. 优点:

    1. 可以一致的处理对象容器和对象,无需关心单个对象还是容器。
    2. 将客户端与复杂的对象容器结构。
  4. 缺点:容器中不能指定特定对象,只有在运行时候做类型检查。

  5. 主要的特点就是统一的去处理叶子和容器。

  6. 客户端尽量不要直接调用树叶类中的方法(在我上面实现就是这样的,创建的是一个树枝的具体对象;),而是借用其父类(Graphics)的多态性完成调用,这样可以增加代码的复用性。

  7. 目录结构、应用软件中的菜单、办公系统中的公司组织结构,均可用。

  8. 透明模式和安全模式

十、享元模式

  1. 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

十一、策略模式

  1. 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。

  2. 实际上所有模式可以只分为类模式和对象模式两种,类模式是用继承而对象模式是用委托Bridge模式和Strategy模式相似就是因为他们都将任务委托给了另外一个接口的具体实现,他们之间的区别在于Bridge的目的是让底层实现和上层接口可以分别演化,从而提高移植性而Strategy的目的是将复杂的算法封装起来,从而便于替换不同的算法。因此可以想象一般情况下Bridge的实现几乎不会在运行时更改而Strategy的算法则很有可能需要在运行时更换,这就导致在细节方面需要考虑的因素可能会很不相同。

  3. strategy模式是为了扩展和修改,并提供动态配置。它往往可以在同一环境当中使用不同的策略,就是调用不同的派生类。其内部实现是自由的,不受已有的类接口的限制(很多时候根本就不调用现成的接口)。bridge模式是往往是为了利用已有的方法或类。它将原来不统一,不兼容的接口封装起来,变成统一的接口。它的应用往往是不同的环境或平台下只能选择一 种,比如说在windows平台下只能用WinClass,而在unix平台下只能用UnixClass.它的主要作用不是配置而是定义通用接口。

  4. 桥接(Bridge)模式是结构型模式的一种,而策略(strategy)模式则属于行为模式.

  5. 桥接模式表达的主要意义其实是接口隔离的原则,即把本质上并不内聚的两种体系区别 开来,使得它们可以松散的组合.

  6. 策略在解耦上还仅仅是某一个算法的层次,没有到体系这一层次。

  7. 策略仅仅是考虑算法的替换,而桥接考虑的则是不同平台下须要调用不同的工具,接口仅仅是定义一个方法。

十二、模板方法模式

  1. 主要是使用抽象类,将一般的方法实现,将不相同实现的方法抽象。可以实现代码的复用性更好。

十三、观察者模式

  1. 就是注册观察者,在被观察对象改变后,通知观察者。