23种设计模式笔记

设计模式定义
是指在软件开发中,经过验证的,用于解决在特定环境下、重复出现的、特定问题的解决方案。
设计模式通常由模式名称、环境和问题、解决方案、效果几部分组成。

常见的面向对象设计原则

  1. 单一职责原则
    指的是一个类应该仅有一个引起它变化的原因
  2. 开放-关闭原则
    指的是一个类应该对扩展开放,对修改关闭。一般也简称开闭原则。
  3. 里氏替换原则
    指的是子类型必须能够替换掉他们的父类型。
  4. 依赖倒置原则
    指的是不应该强迫客户依赖于他们不用的方法。要做到依赖倒置,典型的应该做到:①高层模块不应该依赖于底层模块,二者都应该依赖于抽象。②抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
  5. 接口隔离原则
    指的是不应该强迫用户依赖于他们不用的方法。
  6. 最少知识原则
    指的是只和你的朋友谈话,即应该尽量减少对象之间的交互,对象只和自己的朋友谈话,也就是只和自己的朋友交互,从而松散类之间的耦合。
  7. 其他原则

简单工厂
定义:提供一个创建对象实例的功能,而无需关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。
简单工厂的本质:选择实现
优点:帮助封装、解耦
缺点:可能增加客户端的复杂度、不方便扩展子工厂
何时选用简单工厂:

  1. 如果想要完全分装隔离具体的实现,对外部只能通过接口来操作封装体
  2. 如果想要把对外创建对象的职责集中管理和控制,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制。
    代码示例:
    Api.java
    public interface Api {
    public void test(String s);
    }

Impl1.java
public class Impl1 implements Api{

@Override
public void test(String s) {
	System.out.println("Impl1 s="+s);
	
}

}

Impl2.java
public class Impl2 implements Api{
public void test(String s) {
System.out.println(“Impl2 s=”+s);
}
}

Client.java
public class Client {
public static void main(String[] args) {
Api api = Factory.getApi(1);
api.test(“使用简单工厂Impl1”);
}
}

Factroy.java
public class Factory {

public  static Api getApi(int condition) {
	Api api=null;
	if(condition==1) {
		api = new Impl1();
	}else if(condition==2) {
		api = new Impl2();
	}
	
	return api;
}

}

可配置的简单工厂
FactoryTest.properties
ImplClass=cn.jxau.simplefactory.Impl2

PFactory.java
public class PFactory {
public static Api createApi() {
//读取配置文件来获取需要创建的类
Properties p = new Properties();
InputStream in = null;
try {
in = PFactory.class.getResourceAsStream(“FactoryTest.properties”);
p.load(in);
}catch(Exception e) {
System.out.println(“装载工厂配置文件出错了,具体的堆栈信息如下:”);
e.printStackTrace();
}finally {
try {
in.close();
}catch(Exception e) {
e.printStackTrace();
}

	}
	//用反射去创建
	Api api = null;
	try {
		api = (Api)Class.forName(p.getProperty("ImplClass")).newInstance();
	}catch(Exception e) {
		e.printStackTrace();
	}
	
	return api;
}

}

外观模式(Facade)
定义:为子系统中的一组接口提供一个一致的界面,Façade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式的目的:不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。
本质:是封装交互,简化调用
优点:松散耦合、简单易用、更好的划分访问的层次。
缺点:过多的或者不太合理的Façade也容易让人迷惑。到底是调用Façade好呢,还是直接调用模块好呢。

适配器模式(Adapter)
定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口的不兼容而不能在一起工作的哪些类可以一起工作。
优点:更好的复用性、更好的扩展性
缺点:过多地使用适配器,会让系统非常零乱,不容易整体进行把握。比如,明明看到的是A接口,其实内部被适配成了B接口来实现。
本质:转换匹配,复用功能
何时选用适配器模式:

  1. 如果你想要使用一个已经存在的类,但是它的接口不符合你的要求,可以使用适配器模式来把已有的实现转换成你需要的接口
  2. 如果你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作
  3. 如果你想使用一个已经存在的子类,但是不可能对每一个子类都进行匹配,使用适配器模式直接适配这些子类的父类就可以了

单例模式(Singleton)
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
范围:一个虚拟机范围
实现:

  1. 懒汉式(延迟加载)
  2. 饿汉式
    1)构造方法私有化
    2)提供获取实例的方法
    3)把获取实例的方法变成静态的
    4)定义存储实例的·属性
    5)把这个属性也定义成静态的
    6)实现控制实例的创建getInstance()
    7)完整的实现
    二者不同在于如何实现getInstance方法
    优缺点:
  3. 时间和空间
    懒汉式是典型的时间换空间
    饿汉式是典型的空间换时间
  4. 线程安全
    不加同步的懒汉式是线程不安全的
    饿汉式是线程安全的
    利用synchronize实现懒汉式线程安全
    使用“双重检查加锁”既可以实现线程安全,又能够使性能不受到很大的影响
    更好的单例实现方式:Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识
    本质:控制实例数目
    代码示例:
    AppConfig.java
    /**
  • 读取配置文件,单利实现 饿汉式
  • /
    public class AppConfig {
    /
    *
    • 定义一个变量来存储创建好的类实例,直接在这里创建类实例,只能创建一次
    • */
      private static AppConfig instance = new AppConfig();
      public static AppConfig getInstance() {
      return instance;
      }
      private String parameterA;
      private String parameterB;
      public String getParameterA() {
      return parameterA;
      }
      public String getParameterB() {
      return parameterB;
      }
      private AppConfig() {
      //调用读取配置文件方法
      readConfig();
      }
      private void readConfig() {
      Properties p= new Properties();
      InputStream in = null;
      try {
      in = AppConfig.class.getResourceAsStream(“AppConfig.properties”);
      p.load(in);
      this.parameterA = p.getProperty(“paramA”);
      this.parameterB = p.getProperty(“paramB”);
      }catch(IOException e) {
      System.out.println(“装配配置文件出错了,具体堆栈信息如下:”);
      e.printStackTrace();
      }finally {
      try {
      in.close();
      }catch(IOException e) {
      e.printStackTrace();
      }
      }
      }
      }

Client.java
public class Client {
public static void main(String[] args) {
//创建读取配置文件的对象
AppConfig config = AppConfig.getInstance();
String paramA = config.getParameterA();
String paramB = config.getParameterB();
System.out.println(“paramA=”+paramA +" paramB="+paramB);
}
}

AppConfig.properties
paramA=32164A
paramB=ghjsjkB

工厂方法模式(Factory Method)
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个子类的实例化延迟到其子类。
依赖注入(IOC):应用程序依赖容器创建并注入它所需要的外部资源
控制反转(DI):容器控制应用程序,由容器反向地向应用程序注入其所需要的外部资源
本质:延迟到子类来选择实现
优点:可以在不知具体实现的情况下编程、更容易扩展对象的新版本、连接平行的类层次
缺点:具体产品对象和工厂方法的耦合性

抽象工厂模式(Abstract Factory)
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
优点:分离接口和实现、使得切换产品簇变得容易
缺点:不太容易扩展新的产品、容易造成类层次复杂
本质:选择产品簇的实现
相关模式:
工厂方法模式一般是针对单独的产品对象的创建,而抽象工厂模式注重产品簇对象的创建,这就是它们的区别
如果把抽象工厂创建的产品簇简化,这个产品簇只有一个产品,那么这个时候的抽象工厂跟工厂方法是差不多的,也就是抽象工厂可以退化成工厂方法,而工厂方法又可以退化成简单工厂,这也是它们的联系

生成器模式(Builder)
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
构成:Builder模式都存在这么两个部分,一部分是部件构建和产品装配,另一部分是整体构建的算法
优点:松散耦合、可以很容易地改变产品内部表示、更好的复用性
本质:分离整体构建算法和部件构造(生成器模式的重心还是在于分离整体构建算法和部件构造,而分步骤构建对象不过是整体构建算法的一个简单实现,或者说是一个附带产物)
何时选用生成器模式:

  1. 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时
  2. 如果同一个构建过程有着不同的表示时

原型模式(Prototype)
定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
功能:一个是通过克隆来创建新的对象实例,另一个是为克隆出来的新的对象实例复制原型实例属性的值
浅度克隆和深度克隆
优点:对客户端隐藏具体的实现类型、在运行时动态改变具体的实现类型
缺点:最大的缺点就是在于每个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦,必须要能够递归地让所有的相关对象都正确地实现克隆
本质:克隆生成对象
何时选用原型模式:

  1. 如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆原型来得到
  2. 如果需要实例化的类是在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例

中介者模式(Mediator)
定义:用一个中介者对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立地改变它们之间的交互。
优点:松散耦合、集中控制交互、多对多变成一对多
缺点:潜在缺点是过度集中化,如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分复杂,而且难于管理和维护。
本质:封装交互
何时选用中介者模式:

  1. 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱,可以采用中介者模式,把这些对象的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
  2. 如果一个对象引用很多的对象,并直接更这些对象交互,导致难以复用该对象,可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象只需要和中介者对象交互就可以了

代理模式(Proxy)
定义:为其他对象提供一种代理以控制对这个对象的访问
代理的分类:虚代理、远程代理、copy-on-write代理、保护代理、Cache代理、防火墙代理、同步代理、智能指引
Java中的代理:静态代理和动态代理
本质:控制对象的访问
特点
远程代理:隐藏了一个对象存在于不同的地址空间的事实,也即是客户通过远程代理去访问一个对象,根本就不关心这个对象在哪里,也不关心如何通过网络去访问到这个对象。从客户的角度来讲,它只是在使用代理对象而已
虚代理:可以根据需要来创建“大”对象,只有到必须要创建对象的时候,虚代理才会创建对象,从而大大加快程序的运行速度,并节省资源。
保护代理:可以在访问一个对象的前后,执行很多的附加操作,除了进行权限控制之外,还可以进行很多跟业务相关的处理,而不需要修改被代理的对象。也就是说可以通过代理来给目标对象增加功能。
智能指引:和保护代理类似,也是允许在访问一个对象的前后,执行很多附加的操作,这样一来就可以做很多额外的事情,比如,引用计数等

观察者模式(Observer)
定义:定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它
的对象都得到通知并被自动更新。

观察者模式的实现中分为推模型和拉模型
推模型:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通
常是目标对象的全部或部分数据,相当于是在广播通信
拉模型:目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。

Java中的观察者模式:在java.util包里面有一个类Observable,它实现了大部分我们需要的目标的功能;还有一个接口Observer,其中定义了update的方法,就是观察者的接口

优点:实现了观察者和目标之间的抽象耦合、实现了动态联动、支持广播通信
缺点:可能会引起无谓的操作

本质:触发联动

何时选用观察者模式

  1. 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
  2. 如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变
  3. 当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的。

简单变形—区别对待观察者
解决方式有两种,一种是目标可以通知,但是观察者不做任何操作;另外一种是在目标里面进行判断,干脆就不通知了。

命令模式(Command)
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

参数化配置:可以用不同的命令对象,去参数化配置客户的请求

可撤销的操作:放弃该操作,回到未执行该操作前的状态(补偿式/反操作式、恢复存储式)

宏命令:简单点说就是包含多个命令的命令,是一个命令的组合

队列请求:对命令对象进行排队,组成工作队列,然后依次取出命令对象来执行

日志请求:就是把请求的历史记录保存下来,一般是采用永久存储的方式

优点:更松散的耦合、更动态的控制、很自然的复合命令、更好的扩展性

本质:封装请求

何时选用命令模式:

  1. 如果需要抽象出需要执行的动作,并参数化这些对象
  2. 如果需要在不同的时刻指定、排列和执行请求
  3. 如果需要支持取消操作
  4. 如果需要支持当系统崩溃时,能将系统的操作功能重新执行一遍
  5. 在需要事务的系统中

迭代器模式(Iterator)
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示

功能:以不同的方式遍历聚合对象,比如向前、向后等;对同一个聚合同时进行多个遍历;以不同遍历策略来遍历聚合,比如是否需要过滤等;多态迭代,为不同的聚合结构提供统一的迭代接口,也就是说通过一个迭代接口可以访问不同的聚合结构

迭代器模式的关键思想就是把对聚合对象的遍历和访问从聚合对象中分离出来,单独放入迭代器中

内部迭代器:指的是由迭代器自己来控制迭代下一个元素的步骤,客户端无法干预
外部迭代器:指的是由客户端来控制迭代下一个元素的步骤,如客户端必须显示地调用next来迭代下一个元素

双向迭代器:可以同时向前和向后遍历数据的迭代器

优点:更好的封装性(无需暴露聚合对象的内部表示)、可以以不同的遍历方式来遍历一个聚合、简化了聚合的接口(聚合本身不需要定义有关迭代器的接口)、简化客户端调用(为遍历不同的聚合对象提供了统一的接口)、同一个聚合上可以有多个遍历

本质:控制访问聚合对象中的元素

何时使用迭代器模式

  1. 如果你希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示的时候
  2. 如果你希望有多种遍历方式访问聚合对象
  3. 如果你希望为遍历不同的聚合对象提供一个统一的接口

组合模式(Composite)
定义:将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

目的:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。

优点:定义了包含基本对象和组合对象的类层次结构、统一了组合对象和叶子对象、简化了客户端调用、更容易扩展
缺点:很难限制组合中的组件类型

本质:统一了叶子对象和组合对象

何时选用组合模式:

  1. 如果你想表示对象的部分-整体层次结构
  2. 如果你希望统一地使用组合结构中的所有对象

模板方法模式(Template Method)
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中去。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

功能:在于固定算法骨架,而让具体算法实现可扩展

优点:实现代码复用
缺点:算法骨架不容易升级

本质:固定算法骨架

何时选用模板方法模式:

  1. 需要固定算法骨架,实现一个算法的不变的部分,并把可变的行为留给子类来实现的情况。
  2. 各个子类中具有公共行为,应该抽取出来集中在一个公共类中去实现,从而避免代码重复
  3. 需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展

策略模式(Strategy)
定义:定义一系列的算法,把他们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户变化

功能:把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换

优点:定义一系列算法、避免多重条件语句、更好的扩展性
缺点:客户必须了解每种策略的不同、增加了对象数目、只适合扁平的算法结构

本质:分离算法,选择实现

何时选用策略模式:

  1. 出现有许多相关的类,仅仅是行为有差别的情况下
  2. 出现同一个算法,有很多不同实现的情况下
  3. 需要封装算法中,有与算法相关的数据情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构
  4. 出现一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的时候,可以使用策略模式来代替这些条件语句

状态模式(State)
定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

优点:简化应用逻辑控制、更好地分离状态和行为、更好的扩展性、显示化进行状态转换
缺点:一个状态对应一个状态处理类,会使程序引入太多的状态类,这样程序变得杂乱

本质:根据状态来分离和选择行为

何时选用状态模式:

  1. 如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为
  2. 如果一个操作中含有庞大的分支语句,而且这些分支依赖于该对象的状态

备忘录模式(Memento)
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在这个对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。

功能:在不破坏封装性的前提下,捕获一个对象的内部状态

窄接口和宽接口

优点:更好的封装性、简化了原发器、窄接口和宽接口
缺点:可能会导致高开销

本质:保存和恢复内部状态(保存是手段,恢复才是目的)

何时选用备忘录模式:

  1. 如果必须保存一个对象在某一时刻的全部或者部分状态,方便在以后需要时候,可以把该对象恢复到先前的状态
  2. 如果需要保存一个对象的内部状态,但是如果用接口来让其他对象直接得到这些需要保存的状态,将会暴露对象的实现细节并破坏对象的封装性

享元模式(Flyweight)
定义:运用共享技术有效地支持大量细粒度的对象

优点:减少对象数量,节省内存空间
缺点:维护共享对象,需要额外开销

本质:分离和共享(分离的是对象状态中变与不变的部分,共享的是对象中不变的部分)

何时选用享元模式:

  1. 如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量
  2. 如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存
  3. 如果对象的大多数状态都可以转变为外部状态,比如通过计算得到,或是从外部传入等,可以使用享元模式来实现内部状态和外部状态的分离
  4. 如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象

解释器模式(Interpreter)
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

优点:易于实现语法、易于扩展新的语法
缺点:不适合复杂的语法

本质:分离实现,解释执行

何时选用解释器模式

  1. 当有一个语言需要解释执行,并且可以将语言中的句子表示成为一个抽象语法树的时候。

装饰模式(Decorator)
定义:动态地给一个对象添加额外的职责。就增加功能来说,装饰模式比生成子类更加灵活

优点:比继承更加灵活、更容易复用功能、简化高层定义
缺点:会产生很多细粒度对象

本质:动态组合
动态是手段,组合才是目的。这里的组合有两个意思,一个是动态功能的组合,也就是动态进行装饰器的组合;另外一个是指对象组合,通过对象组合来实现为被装饰对象透明地增加功能

何时选用装饰模式:

  1. 如果需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责
  2. 如果不适合使用子类来扩展的时候

职责链模式(Chain of Responsibility)
定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象形成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

功能:职责链模式主要用来处理“客户端发出一个请求,有多个对象都机会来请求这一个请求,但是客户端不知道究竟是谁会来处理他的请求”这样的情况。也就是需要让请求者和接收者解耦,这样就可以动态地切换和组合接收者了。

优点:请求者和接收者松散耦合、动态组合职责
缺点:产生更多细粒度对象、不一定能被处理

本质:分离职责,动态组合
分离是前提,动态组合才是职责链模式的精华所在

何时选用职责链模式:

  1. 如果有多个对象可以处理通一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的
  2. 如果你想在不明确指定接收者的情况下,向多个对象中的其中一个提交请求的话
  3. 如果想要动态指定处理一个请求的对象集合

桥接模式(Bridge)
定义:将抽象部分与它的实现部分分离,使得它们都可以独立地变化

桥接模式是可以连续组合使用的,一个桥接模式的实现部分,可以作为下一个桥接模式的抽象部分

优点:分离抽象和实现部分、更好的扩展性、可以动态地切换实现、可减少子类的个数

本质:分离抽象和实现

何时选用桥接模式:

  1. 如果你不希望在抽象部分和实现部分采用固定的绑定关系
  2. 如果出现抽象部分和实现部分都能够扩展的情况
  3. 如果希望实现部分的修改不会对客户产生影响
  4. 如果采用继承的方案会导致产生很多子类

访问者模式(Visitor)
定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

功能:访问者模式能够给一系列对象透明地添加新功能,从而避免在维护期间对这一系列对象进行修改,而且还能变相实现复用访问者所具有的功能

优点:好的扩展性、好的复用性、分离无关行为
缺点:对象结构变化很困难、破坏封装

本质:预留通路。回调实现

何时选用访问者模式:

  1. 如果想对一个对象结构实施一些依赖于对象结构中具体类的操作
  2. 如果想对一个对象结构中的各个元素进行很多不同而且不相关的操作,为了避免这些操作使类变得混乱
  3. 如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值