一、概念
- 设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,二十某类问题的通用解决方案,设计模式(
Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 - 设计模式的本质提高软件的维护性,通用性和扩展性,并降低软件的复杂度。
- 《设计模式》是经典的书,作者是
Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides Design(俗称"四人组 GOF")。 - 设计模式并不局限于某种语言,
java,php,C++都有设计模式
1.1、七大原则
1.1.1、单一职责原则
- 概念:即一个类只负责一项职责。如果A类同时负责职责1和职责2,那当职责1发生变化时,职责2也可能会受到影响(执行报错等)。
- 注意事项和细节:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性和可维护性。
- 降低变更引起的风险。
- 通常情况下,应该遵守单一职责原则,只有当逻辑足够简单时,才可以在代码级违反。只有类的方法足够少,才能在代码级别保持单一职责原则。
1.1.2、接口隔离原则
- 概念:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小接口上。
1.1.3、依赖倒置(倒转)原则
- 依赖倒转(
Dependence Inversion Principle)指的是:- 高层模块不应该依赖底层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 中心思想是面向接口编程。
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
- 设计理念: 相当于细节的多变性,抽象的东西要稳定的多(
java中,抽象指的是接口或者抽象类,细节指的是具体的实现类)。以抽象为基础搭建的架构比细节为基础搭建的架构更稳定。 - 注意事项和细节:
- 底层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好
- 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序拓展和优化
- 继承时遵循里氏替换原则。
1.1.4、里氏替换原则
- OO中的继承性的思考和说明:
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定的规范和契约,虽然不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
- 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
- 概念:如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。里氏替换原则告诉我们,继承实际上让两个类耦合性增加,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
1.1.5、开闭原则
- 开闭原则(
Open Closed Principle)是变成中最基础、最重要的设计原则。 - 一个软件实体如类,模块和函数应该对拓展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过拓展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 变成中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
1.1.6、迪米特法则
- 一个对象应该对其他对象白痴最少的了解。
- 类与类关系越密切,耦合度越大。
- 迪米特法则(
Demeter Principle)又叫最少知道原则,即一个类对自己的依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄漏任何信息。 - 迪米特法则还有个更简单的定义:只与直接的朋友通信。
- 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现乘员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
- 核心准则
- 迪米特法则的核心是降低类之间的耦合
- 由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。
1.1.7、合成复用原则
原则是尽量使用合成/聚合的方式,而不是使用继承。
1.2、重要性


1.3、UML图标

1.4、类型
- 创建型模式:单例模式、抽象工程模式、原型模式、建造者模式、工厂模式。
- 结构型模式:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式。
- 行为模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。
一、单例模式
1.1、概念
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
- 比如
Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级,一般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用单例模式。 - 单例模式有八种方式:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
1.2、饿汉式(静态常量)
- 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
- 缺点:在类装载的时候就完成实例化,没有达到
Lazy Loading的效果,如果从始至终从未使用过这个实例,则会造成内存的浪费。 - 这种方式基于
classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果 - 结论:这种模式可以用,可能造成内存浪费。
publicclass Singleton1 {
// 指向自己实例的私有静态引用,主动创建
privatestatic Singleton1 singleton1 = new Singleton1();
// 构造方法私有化,禁止被外部实例化
private Singleton1(){}
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static Singleton1 getInstantiate(){
return singleton1;
}
}
1.3、饿汉式(静态代码块)
静态代码块跟静态变量方法的实现差不多,只是创建对象放到了静态代码块中,优点在于初始化单例时可以做些其他初始化操作。
publicclass Singleton2 {
// 构造方法私有化,禁止被外部实例化
private Singleton2(){}
// 指向自己实例的私有静态引用,
privatestatic Singleton2 singleton2;
// 通过静态代码块实例化
static {
singleton2 = new Singleton2();
//其他初始化操作... 比如给属性赋值
}
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static Singleton2 getInstantiate(){
return singleton2;
}
}
1.4、懒汉式(线程不安全)
延迟加载:懒汉式是使用到单例时才会创建实例,,后续使用到的都是同一个实例。懒汉式可以减少资源消耗和内存占用。
这种方式获取实例方法没有任何同步限制,在多线程环境下,可能会带来线程安全问题,所以一般不使用。
publicclass Singleton3 {
// 构造方法私有化,禁止被外部实例化
private Singleton3(){}
// 指向自己实例的私有静态引用
privatestatic Singleton3 singleton3;
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static Singleton3 getInstantiate(){
//如果未实例化,创建实例
if (singleton3 == null) {
singleton3 = new Singleton3();
}
//返回实例
return singleton3;
}
}
1.5、懒汉式(线程安全,同步方法)
可以通过synchronized关键字修饰获取实例的方法,使其成为同步方法,这样就可以保证线程安全问题
publicclass Singleton4 {
// 构造方法私有化,禁止被外部实例化
private Singleton4(){}
// 指向自己实例的私有静态引用
privatestatic Singleton4 singleton4;
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static synchronized Singleton4 getInstantiate(){
//如果未实例化,创建实例
if (singleton4 == null) {
singleton4 = new Singleton4();
}
//返回实例
return singleton4;
}
}
1.6、懒汉式(线程安全,同步代码块)
如果获取实例的方法中除了实例化单例,还会有其他额外的操作,给整个方法加上同步锁耗费过大,因此可以采用同步代码块的方式,减小锁的锁定范围,减少系统消耗。
publicclass Singleton5 {
// 构造方法私有化,禁止被外部实例化
private Singleton5(){}
// 指向自己实例的私有静态引用
privatestatic Singleton5 singleton5;
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static Singleton5 getInstantiate(){
//同步代码块
synchronized (Singleton5.class) {
if (singleton5 == null) {
singleton5 = new Singleton5();
}
}
//其他初始化操作... 比如给属性赋值
//返回实例
return singleton5;
}
}
1.7、懒汉式(双检锁/双重校验锁DCL)
- 优缺点说明:
Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。- 这样,实例化代码只用执行一次,后面再次访问时,判断
if (singleton == null),直接return实例化对象,也避免的反复进行方法同步。 - 线程安全;延迟加载;效率较高。
- 结论:在实际开发中,推荐使用这种单例设计模式。
publicclass Singleton6 {
// 构造方法私有化,禁止被外部实例化
private Singleton6(){}
// 指向自己实例的私有静态引用
//这里使用volatile修饰是避免CPU指令的重排序导致的对象未完全初始化结束的引用逸出
privatestaticvolatile Singleton6 singleton6;
/**以自己实例为返回值的静态的公有方法,静态工厂方法*/
public static synchronized Singleton6 getInstantiate(){
//第一次判断不加锁,提高执行效率,避免了不必要的同步
if (singleton6 == null) {
synchronized (Singleton6.class) {
if (singleton6 == null) {
singleton6 = new Singleton6();
}
}
}
//其他初始化操作... 比如给属性赋值
//返回实例
return singleton6;
}
}
1.8、登记式/静态内部类
通过类加载的机制来实现单例模式
静态内部类在主类加载的时候不会被加载,即实现了懒汉模式,减少内存占用。
类加载的过程是JVM层面保证了线程安全性,即同一个类只能加载一次。
publicclass Singleton7 {
// 构造方法私有化,禁止被外部实例化
private Singleton7(){}
//2.创建静态私有内部类,定义单例属性实例(静态内部类在主类装载时不会被装载)
privatestaticclass SingletonInstance {
privatestaticfinal Singleton7 Singleton7 = new Singleton7();
}
//3.提供一个公有的静态方法返回实例对象
public static Singleton7 getInstance() {
return SingletonInstance.Singleton7;
}
}
1.9、枚举式
上述七种方式在反序列化(将一个单例对象写到磁盘上,再读回来)时,会重新创建实例得到一个新的实例,而 枚举类天生单例,线程安全,且为懒汉式。
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化
publicenum Singleton8 {
INSTANCE;
}
1.10、使用
1.10.1、单例模式在JDK中的使用
JDK中的Runtime类,每个JVM进程都对应一个Runtime实例,保存JVM的运行中的一些参数信息。因此JDK设计Runtime类为单例形式。
publicclass Runtime {
// 构造方法私有化,禁止被外部实例化
private Runtime(){}
//饿汉式 创建Runtime实例
privatestatic Runtime currentRuntime = new Runtime();
//获取Runtime实例
public static Runtime getRuntime() {
return currentRuntime;
}
}
1.10.2、与其他模式的关系
外观模式一般可以转换为单例模式, 因为一般情况下只需要一个外观对象。
当对象的所有共享状态简化为一个享元对象时,享元模式就和单例模式类似。但本质不同:
单例只会有一个单例实体, 但是享元类可以有多个实体, 各实体的内在状态也可以不同。
单例对象可以是可变的。享元对象是不可变的。
抽象工厂模式、生成器模式、原型模式都可以用单例来实现
二、工厂模式
2.1、简单工厂模式
2.1.1、概念
简单工厂模式又称为静态工厂模式,实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类的实例。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
2.1.2、角色及其职责
- 工厂(Creator)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
- 抽象产品(Product)角色:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
- 具体产品(Concrete Product)角色:是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
2.1.3、举例
//把创建 Pizza 对象封装到一个类中,这样我们有新的 Pizza 种类时。
//只需要修改该类就可,其它有创建到 Pizza 对象的代码就不需要修改了
//简单工厂类
class SimpleFactory {
//更加 orderType 返回对应的 Pizza 对象
public Pizza createPizza(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
//简单工厂模式 也叫 静态工厂模式
public static Pizza createPizza2(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式 2");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
//OrderPizza.java
class OrderPizza {
// 构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购披萨的类型
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName(" 胡椒披萨");
}
//输出 pizza 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
//定义一个简单工厂对象
SimpleFactory simpleFactory;
Pizza pizza = null;
//构造器
public OrderPizza(SimpleFactory simpleFactory) {
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory) {
String orderType = ""; //用户输入的
this.simpleFactory = simpleFactory; //设置简单工厂对象
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
//输出 pizza
if (pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println(" 订购披萨失败 ");
break;
}
} while (true);
}
// 写一个方法,可以获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
interface Pizza {
void setName(String name);
void prepare();
void bake();
void box();
void cut();
}
class GreekPizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " GreekPizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " GreekPizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " GreekPizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " GreekPizza cut.....");
}
}
class CheesePizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " CheesePizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " CheesePizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " CheesePizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " CheesePizza cut.....");
}
}
class PepperPizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " PepperPizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " PepperPizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " PepperPizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " PepperPizza cut.....");
}
}
2.1.4、优缺点
- 优点
- 工厂类是处理必要的逻辑判断是简单工厂的核心。根据客户端的参数决定创建具体对象。
- 通过使用工厂类,客户端与产品类解耦。
- 缺点
- 由于工厂类集中了所有实例的创建逻辑,违反了开闭原则,将全部创建逻辑集中到了一个工厂类中。
- 新增产品类时需要修改工厂类,扩展性较差,不易维护。
2.1.5、对比
1、简单工厂模式和策略模式区别
- 策略模式和简单工厂模式都是通过多态来实现不同子类的选取。
- 在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。
- 简单工厂模式中只需要传递相应的条件就能得到想要的一个对象,然后通过这个对象实现算法的操作。
- 策略模式使用时必须首先创建策略对象作为参数传递进去,通过该对象调用不同的算法。
2、工厂方法模式和简单工厂模式的区别
- 简单工厂模式
- 工厂类负责创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
- 工厂方法模式
- 客户端不知道它所需要的对象的类。
- 抽象工厂类通过其子类来指定创建哪个对象。
2.2、工厂方法模式
2.2.1、概念
- 工厂方法模式(
Factory Method Pattern),也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 - 工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现“开-闭 原则”,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
2.2.2、结构
- 抽象产品角色(
Product):定义产品的接口。 - 具体产品角色(
ConcreteProduct) :实现接口产品的具体产品类。 - 抽象工厂角色(
Creator) :声明工厂方法(FactoryMethod),返回一个产品。 - 真实的工厂(
ConcreteCreator):实现FactoryMethod工厂方法,由客户调用,返回一个产品的实例。
2.2.3、举例
//披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如 北京的奶酪
//pizza、北京的胡椒 pizza 或者是伦敦的奶酪 pizza、伦敦的胡椒 pizza
abstract class OrderPizza {
//定义一个抽象方法,createPizza , 让各个工厂子类自己实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购披萨的类型
do {
orderType = getType();
pizza = createPizza(orderType); //抽象方法,由工厂子类完成
//输出 pizza 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
// 写一个方法,可以获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
class BJOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
// TODO Auto-generated method stub
return pizza;
}
}
class LDOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
// TODO Auto-generated method stub
return pizza;
}
}
interface Pizza {
void setName(String name);
void prepare();
void bake();
void box();
void cut();
}
class BJCheesePizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " BJCheesePizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " BJCheesePizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " BJCheesePizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " BJCheesePizza cut.....");
}
}
class BJPepperPizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " BJPepperPizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " BJPepperPizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " BJPepperPizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " BJPepperPizza cut.....");
}
}
class LDCheesePizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " LDCheesePizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " LDCheesePizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " LDCheesePizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " LDCheesePizza cut.....");
}
}
class LDPepperPizza implements Pizza {
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void prepare() {
System.out.println(this.name + " LDPepperPizza prepare.....");
}
@Override
public void bake() {
System.out.println(this.name + " LDPepperPizza bake.....");
}
@Override
public void box() {
System.out.println(this.name + " LDPepperPizza box.....");
}
@Override
public void cut() {
System.out.println(this.name + " LDPepperPizza cut.....");
}
}
2.2.4、优缺点
- 优点:
- 工厂和具体产品之间的解耦
- 遵循单一职责原则,将产品创建代码放在程序的单一位置, 易维护。
- 遵循开闭原则,无需更改现有客户端代码, 就可以在程序中引入新的产品类型。
- 缺点:在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度。
2.2.5、对比
| 简单工厂模式 | 工厂方法模式 |
|---|---|
| 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。 | 客户端不知道它所需要的对象的类。 |
| 工厂类负责创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂。 | 抽象工厂类通过其子类来指定创建哪个对象。 |
2.3、抽象工厂模式
2.3.1、概念
- 抽象工厂模式是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
- 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。将工厂抽象成两层,
AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
2.3.2、结构
- 抽象工厂:接口声明了一组创建各种抽象产品的方法。
- 具体工厂:实现抽象工厂的构建方法。每个具体工厂都对应特定产品对象,且仅初始化此种产品对象,其构建方法签名必须返回相应的抽象产品。
- 抽象产品:为构成系列产品的一组不同但相关的产品声明接口。
- 具体产品 :是抽象产品的多种不同类型实现,都必须实现相应的抽象产品。
- 客户端:只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互,客户端不会与工厂创建的特定产品变体耦合。
2.3.3、举例

2.3.4、优缺点
- 优点
- 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
- 单一职责原则。可以将产品生成代码抽取到同一位置, 使得代码易于维护。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
- 缺点:抽象方法模式的最大缺点就是产品族本身的扩展非常困难,增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,代码可能会比之前更加复杂。
2.3.4、对比
- 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品, 生成器则允许在获取产品前执行一些额外构造步骤。
- 抽象工厂模式通常基于一组工厂方法, 但也可以使用原型模式来生成这些类的方法。
- 当只需对客户端代码隐藏子系统创建对象的方式时, 可以使用抽象工厂来代替外观模式。
- 抽象工厂,生成器和原型都可以用单例模式来实现。
三、原型模式(克隆模式)
3.1、概念
- 原型模式(
Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 - 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
- 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
3.2、深拷贝浅拷贝
3.2.1、浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
- 浅拷贝是使用默认的 clone()方法来实现。
3.2.2、深拷贝
- 复制对象的所有基本数据类型的成员变量值。
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。
- 深拷贝实现方式 1:重写 clone 方法来实现深拷贝。
- 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)。
在这里插入代码片
3.3、举例
class Sheep implements Serializable, Cloneable {
private String name;
private int age;
private String color;
private String address = "蒙古羊";
public Sheep friend; //是对象, 克隆是会如何处理, 默认是浅拷贝
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
}
//克隆该实例,使用默认的 clone 方法来完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
// TODO Auto-generated method stub
return sheep;
}
//深拷贝实例
Sheep deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
Sheep copyObj = (Sheep) ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
class Client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
// TODO Auto-generated method stub
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep) sheep.clone(); //克隆
Sheep sheep3 = (Sheep) sheep.clone(); //克隆
Sheep sheep4 = (Sheep) sheep.clone(); //克隆
Sheep sheep5 = (Sheep) sheep.deepClone(); //深拷贝
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
}
}

3.4、优缺点
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则。
3.5、对比
- 较为简单, 而且可以更方便地通过子类进行定制的对象使用工厂方法模式,更灵活但更加复杂使用抽象工厂模式、 原型模式或生成器模式。
- 原型模式可用于保存命令模式的历史记录。
- 大量使用组合模式和装饰模式的设计通常可从对于原型的使用中获益。你可以通过该模式来复制复杂结构, 而非从零开始重新构造。
- 原型模式并不基于继承, 因此没有不可以多继承的缺点,但是原型需要对被复制对象进行初始化。工厂方法基于继承, 但是它不需要初始化步骤
四、建造者模式
4.1、概念
- 建造者模式是一种创建型设计模式,能够分步骤创建复杂对象。该模式允许使用相同的创建代码生成不同类型和形式的对象。
- 建造者模式(
Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。 - 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
4.2、结构
- Product(产品角色): 一个具体的产品对象。
- Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
- ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
- Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
4.3、举例
- 声明产品类,确定产品属性组成。
- 声明一个接口或抽象类作为抽象建造者,定义一个返回产品的抽象方法,以及建造产品所需要的抽象方法。
- 为每个形式的产品创建具体建造者类, 并实现其构造步骤。
- 声明指挥者类,它可以使用同一生成器对象来封装多种构造产品的方式。
- 在客户端代码会同时创建生成器和主管对象,将生成器对象传递给主管对象进行关联
class Client {
public static void main(String[] args) {
//盖普通房子
CommonHouse commonHouse = new CommonHouse();
//准备创建房子的指挥者
HouseDirector houseDirector = new HouseDirector(commonHouse);
//完成盖房子,返回产品(普通房子)
House house = houseDirector.constructHouse();
//System.out.println("输出流程");
System.out.println("--------------------------");
//盖高楼
HighBuilding highBuilding = new HighBuilding();
//重置建造者
houseDirector.setHouseBuilder(highBuilding);
//完成盖房子,返回产品(高楼)
houseDirector.constructHouse();
}
}
class CommonHouse extends HouseBuilder {
@Override
public void buildBasic() {
// TODO Auto-generated method stub
System.out.println(" 普通房子打地基 5 米 ");
}
@Override
public void buildWalls() {
// TODO Auto-generated method stub
System.out.println(" 普通房子砌墙 10cm ");
}
@Override
public void roofed() {
// TODO Auto-generated method stub
System.out.println(" 普通房子屋顶 ");
}
}
// 抽象的建造者
abstract class HouseBuilder {
protected House house = new House();
//将建造的流程写好, 抽象的方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();
//建造房子好, 将产品(房子) 返回
public House buildHouse() {
return house;
}
}
class HighBuilding extends HouseBuilder {
@Override
public void buildBasic() {
// TODO Auto-generated method stub
System.out.println(" 高楼的打地基 100 米 ");
}
@Override
public void buildWalls() {
// TODO Auto-generated method stub
System.out.println(" 高楼的砌墙 20cm ");
}
@Override
public void roofed() {
// TODO Auto-generated method stub
System.out.println(" 高楼的透明屋顶 ");
}
}
//产品->Product
class House {
private String baise;
private String wall;
private String roofed;
public String getBaise() {
return baise;
}
public void setBaise(String baise) {
this.baise = baise;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
this.roofed = roofed;
}
}
class HouseDirector {
HouseBuilder houseBuilder = null;
//构造器传入 houseBuilder
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
//通过 setter 传入 houseBuilder
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
//如何处理建造房子的流程,交给指挥者
public House constructHouse() {
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}
}
4.4、优缺点
- 优点
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都独立,可以方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合开闭原则。
- 缺点
- 当建造者过多时,会产生很多类,难以维护。
- 产品必须有共同点,范围有限制。
- 若产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
4.5、对比
- 工厂模式用于处理 如何获取实例对象,建造者模式用于处理如何建造实例对象 。
- 建造者模式比工厂模式多了一个指挥者 的角色,如果把指挥者的逻辑放在客户端,那么建造者模式剩余的部分就可以看作是一个简单的工厂模式。
- 职责不同:
- 建造者模式中的建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给指挥类。由指挥类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。
- 工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品。
五、适配器模式(对象适配器)
5.1、概念
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
5.2、结构
- 客户端接口 (Client Interface) :目标角色,描述了其他类与客户端代码合作时必须遵循的协议。
- 服务(Service) 初始角色,实现了具体功能的类,但客户端与其接口不兼容, 无法直接调用。
- 适配器 (Adapter) :适配器角色,负责与客户端和服务交互,在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。
- 客户端(Client):只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。向程序中添加新类型的适配器或者服务类的接口被更改或替换时无需修改客户端代码就可以创建新的适配器类。
5.3、实现
class client {
public static void main(String[] args) {
/**
* 对于客户端而言,只是在调用ChinaPlugImpl没有变
*/
ChinaPlugImpl chinaPlug = new ChinaPlugImpl();
chinaPlug.chinaCharge("America");
}
}
/**
* 客户端接口: 国标的三孔插座
*/
interface ChinaPlug {
public void chinaCharge(String type);
}
/**
* 客户端接口实现类: 被一个或多个客户端类使用服务类
*/
class ChinaPlugImpl implements ChinaPlug {
@Override
public void chinaCharge(String type) {
if (!type.equalsIgnoreCase("America")) {
System.out.println("正在使用国标三孔插座充电");
} else {
PlugAdapter adapter = new PlugAdapter(new AmericaPlugImpl());
adapter.chinaCharge(type);
}
}
}
/**
* 服务接口: 欧美的三孔插座
*/
interface AmericaPlug {
public void americaCharge();
}
/**
* 服务接口实现类: 欧美的三孔插座 第三方或存在众多依赖的类
*/
class AmericaPlugImpl implements AmericaPlug {
@Override
public void americaCharge() {
System.out.println("正在使用欧美三孔插座充电");
}
}
/**
* 适配器
*/
class PlugAdapter extends ChinaPlugImpl {
/**
* 成员变量用于保存对于服务对象的引用
*/
private AmericaPlug americaPlug;
public PlugAdapter(AmericaPlug americaPlug) {
/**
* 如果AmericaPlug 有多个实现类,可以传类型进来进行初始化,类似工厂模式
*/
this.americaPlug = americaPlug;
}
/**
* 将实际工作委派给服务对象
*/
@Override
public void chinaCharge(String type) {
americaPlug.americaCharge();
}
}
5.4、优缺点
- 优点
- 可以让任何两个没有关联的类一起运行;
- 单一职责原则:可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则:客户端通过客户端接口与适配器进行交互, 就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
- 缺点
- 由于C#和JAVA都不支持多重继承,所以最多只能适配一个适配者类。
- 需要新增一系列接口和类,代码整体复杂度增加。
5.5、对比
- 适配器模式与桥接模式
- 桥接模式:通常会于开发前期进行设计, 使程序的各个部分独立开发以便开发。
- 适配器模式:通常在已有程序中使用, 让相互不兼容的类能很好地合作。
- 适配器模式与装饰模式
- 适配器模式:可以对已有对象的接口进行修改。
- 装饰模式:能在不改变对象接口的前提下「强化对象」功能,并且支持递归组合。
- 适配器模式与代理模式
- 适配器能为被封装对象提供不同的接口。
- 代理模式能为对象提供相同的接口。
- 适配器模式与外观模式
- 外观模式:为现有对象定义了一个新接口,通常会作用于整个对象子系统上。
- 适配器模式:会运用已有的接口,通常只封装一个对象。
六、桥接模式
6.1、概念
- 桥接模式,也被称为柄体(
Handle and Body)模式或接口(Interface)模式,是一种桥接模式,也被称为柄体(Handle and Body)模式或接口(Interface)模式,是一种结构型设计模式。它的主要目的是将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种分离可以通过组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。 - 假设有一个几何形状
Shape类,从它能扩展出两个子类:圆形Circle和方形Square。你希望对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色Red和蓝色Blue的形状子类。但是,由于你已有两个子类,所以总共需要创建四个类才能覆盖所有组合,例如蓝色圆形BlueCircle和红色方形RedSquare。在层次结构中新增形状和颜色将导致代码复杂程度指数增长。在这种情况下,桥接模式就能起到作用,它将形状和颜色解耦,使得两者可以相对独立地变化。
6.2、结构
- 抽象类(
Abstraction):提供高层控制逻辑的抽象类,并依赖一个于完成底层实际工作的实现对象Implementor(实现类接口)(Abstraction与Implementor的关联关系),与Implementor之间具有关联关系,既可以包含抽象业务方法,也可以包含具体业务方法。 - 精确抽象类(
Refined Abstraction):提供控制逻辑的变体。继承抽象类(Abstraction), 实现了在Abstraction中声明的抽象业务方法,可以调用在Implementor中定义的业务方法。(可以没有这个)。 - 实现类接口(
Implementor):所有具体实现声明通用接口。可以和Abstraction的接口完全不同,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做组合聚合等复杂的操作。 - 具体实现类(
Concrete Implementor)」:实现Implementor接口,提供基本操作的不同实现。 - 客户端 (
Client):仅关心如何与抽象部分合作。客户端需要将抽象对象与一个实现对象连接起来。
设计模式(9):桥接模式
6.3、实现

修改后的结构

class Client {
public static void main(String[] args) {
//获取折叠式手机 (样式 + 品牌 )
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("=======================");
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
System.out.println("==============");
UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("==============");
UpRightPhone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
abstract class Phone {
//组合品牌
private Brand brand;
//构造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
//折叠式手机类,继承 抽象类 Phone
class FoldedPhone extends Phone {
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 折叠样式手机 ");
}
public void close() {
super.close();
System.out.println(" 折叠样式手机 ");
}
public void call() {
super.call();
System.out.println(" 折叠样式手机 ");
}
}
class UpRightPhone extends Phone {
//构造器
public UpRightPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 直立样式手机 ");
}
public void close() {
super.close();
System.out.println(" 直立样式手机 ");
}
public void call() {
super.call();
System.out.println(" 直立样式手机 ");
}
}
//接口
interface Brand {
void open();
void close();
void call();
}
class Vivo implements Brand {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println(" Vivo 手机开机 ");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println(" Vivo 手机关机 ");
}
@Override
public void call() {
// TODO Auto-generated method stub
System.out.println(" Vivo 手机打电话 ");
}
}
class XiaoMi implements Brand {
@Override
public void open() {
// TODO Auto-generated method stud
System.out.println(" 小米手机开机 ");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println(" 小米手机关机 ");
}
@Override
public void call() {
// TODO Auto-generated method stub
System.out.println(" 小米手机打电话 ");
}
}
6.4、优缺点
- 优点:
- 开闭原则。新增抽象部分和实现部分, 且它们之间不会相互影响。
- 单一职责原则。抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
- 客户端代码仅与高层抽象部分进行交互, 不会接触到产品的详细信息,实现了解耦。
- 缺点:
- 对高内聚的类使用该模式可能会让代码更加复杂。
- 桥接模式使用范围具有一定的局限性,要求正确识别出两个独立变化的维度。
6.5、对比
- 抽象工厂(Abstract Factory): 可以用来创建和配置一个特定的Bridge模式。
- 适配器模式:用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。而Bridge模式是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。
- 装饰模式:装饰模式和桥接模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系。但是实现不同。
- 装饰模式把子类中比基类中多出来的部分放到单独的类里面,以适应新功能增加的需要,通过j将新功能的类封装到基类的对象中实现多功能组合。
- 桥接模式把基类的实现化细节进行抽象,在构造方法中把原来的基类进行抽象化,实现系统在多个维度上的独立变化 。
七、装饰者模式
7.1、概念
- 装饰者模式:又名包装(Wrapper)模式,是一种结构型设计模式。它动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)。它向一个现有的对象添加新的功能,同时又不改变其结构。
- 装饰器模式实际上是继承关系的一种组合代替继承的替代方案。所以我们在理解装饰模式之前需要先理解什么是组合以及为什么要用组合替代继承在Java的面向对象中有继承与封装的特性,继承表示子类依赖了父类中的实现,而父类的内部细节对于子类是可见的,所以继承常常被认为破坏了封装性,相互矛盾。而组合就是利用封装的特性来实现继承功能。
- 装饰者模式实际上类似AOP思想的一种实现方式,跟AOP类似的它可以控制被装饰者行为,在调用父类方法之前或之后给被装饰者添加附加的行为。
7.2、结构
- 抽象构件(
Component)角色:定义一个抽象接口,声明封装器和被封装对象的公用接口。 - 具体构件(
ConcreteComponent)角色:实现抽象构件,定义了基础行为, 装饰类可以改变这些行为。 - 抽象装饰(
Decorator)角色:继承抽象构件,并持有一个抽象构件对象实例的成员变量,可以通过其子类扩展具体构件的功能。 - 具体装饰(
ConcreteDecorator)角色:实现抽象装饰的相关方法,并调用父类方法之前或之后给具体构件对象添加附加的行为。 - 客户端 (
Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。
7.3、实现
class CoffeeBar {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
// 1. 点一份 LongBlack
Drink order = new LongBlack();
System.out.println("费用 1=" + order.cost());
System.out.println("描述=" + order.getDes());
// 2. order 加入一份牛奶
order = new Milk(order);
System.out.println("order 加入一份牛奶 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入一份巧克力 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入 2 份巧克力 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入 2 份巧克力 描述 = " + order.getDes());
System.out.println("===========================");
Drink order2 = new DeCaf();
System.out.println("order2 无因咖啡 费用 =" + order2.cost());
System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
order2 = new Milk(order2);
System.out.println("order2 无因咖啡 加入一份牛奶 费用 =" + order2.cost());
System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());
}
}
abstract class Drink {
public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法
//子类来实现
public abstract float cost();
}
class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //组合
// TODO Auto-generated constructor stub
this.obj = obj;
}
@Override
public float cost() {
// TODO Auto-generated method stub
// getPrice 自己价格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
// obj.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
//具体的 Decorator, 这里就是调味品
class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品 的价格
}
}
class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
class Soy extends Decorator {
public Soy(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 豆浆 ");
setPrice(1.5f);
}
}
class Coffee extends Drink {
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}
}
class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
class ShortBlack extends Coffee {
public ShortBlack() {
setDes(" shortblack ");
setPrice(4.0f);
}
}
class DeCaf extends Coffee {
public DeCaf() {
setDes(" 无因咖啡 ");
setPrice(1.0f);
}
}

7.4、优缺点
- 优点:
- 装饰器是继承的替代方案,比继承灵活,在不改变原有对象的情况下动态的给一个对象扩展功能,即插即用。
- 通过使用不同装饰类的排列组合,可以实现不同效果。
- 装饰器模式完全遵守开闭原则。
- 缺点:装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
7.5、对比
- 组合模式和装饰的结构图相似, 两者都依赖递归组合来组织无限数量的对象。可以使用装饰来扩展组合树中特定对象的行为。
- 装饰为被封装对象添加了额外的职责, 组合是对其子节点的结果进行了 求和。
- 装饰模式可让给一个对象扩展功能,改变对象的外表。策略模式则是在本质上改变对象的实现。
- 装饰模式和代理模式结构相似,都是一个对象将部分工作委派给另一个对象。代理通常自行管理其服务对象的生命周期, 装饰的生成则总是由客户端进行控制。
八、组合模式
8.1、概念
- 组合模式:又叫作整体-部分(
Part-Whole)模式,它将对象组合成树状的层次结构,用来表示整体-部分的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型模式。 - 实现方式:
- 安全式:不声明访问和管理子类的接口,管理工作由树枝构件完成,只定义一些通用的方法。
- 透明式:抽象构件还声明访问和管理子类的接口
8.2、结构
- 抽象构件(Component)角色:
- 主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
- 透明式的组合模式中抽象构件还声明访问和管理子类的接口
- 安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成,只定义一些通用的方法。
- 树叶构件(Leaf)角色:在组合中表示叶节点,叶节点没有子节点,用于继承或实现抽象构件基本行为。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,有子节点,用于继承和实现抽象构件基本行为,它的主要作用是存储存储子部件并在Component接口实现与子部件有关的操作。
- 客户端(Client):通过Component接口操作组合部件的对象。
8.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//从大到小创建对象 学校
OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
//创建 学院
OrganizationComponent computerCollege = new College("计算机学院", " 计算机学院 ");
OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
//创建各个学院下面的系(专业)
computerCollege.add(new Department("软件工程", " 软件工程不错 "));
computerCollege.add(new Department("网络工程", " 网络工程不错 "));
computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
//
infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 "));
infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
//将学院加入到 学校
university.add(computerCollege);
university.add(infoEngineercollege);
//university.print();
infoEngineercollege.print();
}
}
abstract class OrganizationComponent {
private String name; // 名字
private String des; // 说明
protected void add(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
//构造器
public OrganizationComponent(String name, String des) {
super();
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
//方法 print, 做成抽象的, 子类都需要实现
protected abstract void print();
}
class College extends OrganizationComponent {
//List 中 存放的 Department
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public College(String name, String des) {
super(name, des);
// TODO Auto-generated constructor stub
}
// 重写 add
@Override
protected void add(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub
// 将来实际业务中,Colleage 的 add 和 University add 不一定完全一样
organizationComponents.add(organizationComponent);
}
// 重写 remove
@Override
protected void remove(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
// TODO Auto-generated method stub
return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
return super.getDes();
}
// print 方法,就是输出 University 包含的学院
@Override
protected void print() {
// TODO Auto-generated method stub
System.out.println("--------------" + getName() + "--------------");
//遍历 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
class Department extends OrganizationComponent {
//没有集合
public Department(String name, String des) {
super(name, des);
// TODO Auto-generated constructor stub
}
//add , remove 就不用写了,因为他是叶子节点
@Override
public String getName() {
// TODO Auto-generated method stub
return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
return super.getDes();
}
@Override
protected void print() {
// TODO Auto-generated method stub
System.out.println(getName());
}
}
//University 就是 Composite , 可以管理 College
class University extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public University(String name, String des) {
super(name, des);
// TODO Auto-generated constructor stub
}
// 重写 add
@Override
protected void add(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub
organizationComponents.add(organizationComponent);
}
// 重写 remove
@Override
protected void remove(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
// TODO Auto-generated method stub
return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
return super.getDes();
}
// print 方法,就是输出 University 包含的学院
@Override
protected void print() {
// TODO Auto-generated method stub
System.out.println("--------------" + getName() + "--------------");
//遍历 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}

7.4、优缺点
- 优点
- 可以利用多态和递归机制更方便地使用复杂树结构
- 开闭原则:在组合体内加入新的对象,客户端不会更改源代码,可以一致地处理单个对象和组合对象。
- 缺点
- 设计较复杂,需要明确类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
7.5、对比
- 创建复杂组合树时使用生成器模式,使构造步骤以递归的方式运行。
- 责任链模式一般和组合模式结合使用。
- 迭代器模式可以用来遍历组合树。
- 访问者模式可以用来对整个组合树执行操作。
- 享元模式可以用来实现组合树的共享叶节点以节省内存。
- 组合模式和装饰模式的结构图很相似, 两者都依赖递归组合来组织无限数量的对象。
- 装饰模式只有一个子组件,并且为被封装对象添加了额外的职责。
- 组合模式只是操作对子节点的原有行为得到结果。
九、外观模式
9.1、概述
- 外观模式(Facade),也叫"过程模式":外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。
- 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如:在 pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
9.2、结构
- 抽象外观(Facade)角色:为多个子系统对外提供一个共同的接口。
- 具体外观(concrete Facade)角色:实现抽象外观角色,持有各个子系统的应用对象,并初始化,重定向客户端请求,操作子系统。
- 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
- 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
9.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//这里直接调用。。 很麻烦
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
homeTheaterFacade.ready();
homeTheaterFacade.play();
homeTheaterFacade.end();
}
}
class HomeTheaterFacade {
//定义各个子系统对象
private TheaterLight theaterLight;
private Popcorn popcorn;
private Stereo stereo;
private Projector projector;
private Screen screen;
private DVDPlayer dVDPlayer;
//构造器
public HomeTheaterFacade() {
super();
this.theaterLight = TheaterLight.getInstance();
this.popcorn = Popcorn.getInstance();
this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance();
this.dVDPlayer = DVDPlayer.getInstanc();
}
//操作分成 4 步
public void ready() {
popcorn.on();
popcorn.pop();
screen.down();
projector.on();
stereo.on();
dVDPlayer.on();
theaterLight.dim();
}
public void play() {
dVDPlayer.play();
}
public void pause() {
dVDPlayer.pause();
}
public void end() {
popcorn.off();
theaterLight.bright();
screen.up();
projector.off();
stereo.off();
dVDPlayer.off();
}
}
class DVDPlayer {
//使用单例模式, 使用饿汉式
private static DVDPlayer instance = new DVDPlayer();
public static DVDPlayer getInstanc() {
return instance;
}
public void on() {
System.out.println(" dvd on ");
}
public void off() {
System.out.println(" dvd off ");
}
public void play() {
System.out.println(" dvd is playing ");
}
//....
public void pause() {
System.out.println(" dvd pause ..");
}
}
class Popcorn {
private static Popcorn instance = new Popcorn();
public static Popcorn getInstance() {
return instance;
}
public void on() {
System.out.println(" popcorn on ");
}
public void off() {
System.out.println(" popcorn ff ");
}
public void pop() {
System.out.println(" popcorn is poping ");
}
}
class Projector {
private static Projector instance = new Projector();
public static Projector getInstance() {
return instance;
}
public void on() {
System.out.println(" Projector on ");
}
public void off() {
System.out.println(" Projector ff ");
}
public void focus() {
System.out.println(" Projector is Projector ");
}
//...
}
class Screen {
private static Screen instance = new Screen();
public static Screen getInstance() {
return instance;
}
public void up() {
System.out.println(" Screen up ");
}
public void down() {
System.out.println(" Screen down ");
}
}
class Stereo {
private static Stereo instance = new Stereo();
public static Stereo getInstance() {
return instance;
}
public void on() {
System.out.println(" Stereo on ");
}
public void off() {
System.out.println(" Screen off ");
}
public void up() {
System.out.println(" Screen up.. ");
}
}
class TheaterLight {
private static TheaterLight instance = new TheaterLight();
public static TheaterLight getInstance() {
return instance;
}
public void on() {
System.out.println(" TheaterLight on ");
}
public void off() {
System.out.println(" TheaterLight off ");
}
public void dim() {
System.out.println(" TheaterLight dim.. ");
}
public void bright() {
System.out.println(" TheaterLight bright.. ");
}
}

9.4、优缺点
- 优点:
- 完全符合迪米特法则,提高了模块的相对独立性,提高了类的可复用率和系统的扩展性。
- 降低了子系统与客户端之间的耦合度,子系统的变化不会影响客户端。
- 对客户屏蔽了子系统组件,减少了客户端处理的对象数目,并使得子系统使用起来更加容易。
- 降低了系统中的编译依赖性,简化了系统之间的移植过程。
- 缺点:
- 不符合开闭原则,增加新的子系统可能需要修改外观类代码。
- 不能限制客户使用子系统类。
- 注意:
- 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
- 外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展。
- 通过合理的使用外观模式,可以帮我们更好的划分访问的层次。
- 当系统需要进行分层设计时,可以考虑使用 Facade 模式。
- 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性。
- 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
9.5、对比
- 当一个外观对象可以支持需求的情况下可以将外观类转换为单例模式类。
- 当只需对客户端代码隐藏子系统创建对象的方式时,外观模式可以用抽象工厂模式代替实现。
- 外观模式和中介者模式都是将多个紧密耦合的类中组织起来合作
- 外观模式为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。子系统不会感知到外观的存在,子系统之间可以直接进行交流。
- 中介者模式将系统中组件的沟通行为中心化, 各组件只能感知中介者对象, 无法直接相互交流。
十、享元模式
10.1、概念
- 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象。
- 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。
- 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。
- 享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。
- 运用共享技术有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来减少创建对象的数量,以减少内存占用和提高性能。属于结构型模式,本质就是缓存共享对象,降低内存消耗。
- 内部状态、外部状态:由于享元模式区分了内部状态和外部状态,所以可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。
- 内部状态:在享元对象内部不随外界环境改变而改变的共享部分,可在许多对象中重复使用的数据的成员变量。
- 外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态,每个对象各自不同的情景数据的成员变量。
- 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方。
10.2、结构
- 抽象享元(Flyweight)角色:享元接口,具体享元类的基类,定义具体享元内在状态公共接口,非享元的外部状态以参数的形式通过方法传入。
- 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口,指定共享的内部状态。
- 非享元(Unsharable Flyweight)角色:不可共享的外部状态,以参数的形式注入具体享元的相关方法中。
- 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色,并对外提供访问共享享元的接口。
- 当客户对象请求享元对象时,享元工厂会提供一个已经创建的享元对象或者新建一个(如果不存在)。
- 客户端(Client)角色:维持一个对享元对象的引用,计算或存储享元的外部状态。
10.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建一个工厂类
WebSiteFactory factory = new WebSiteFactory();
// 客户要一个以新闻形式发布的网站
WebSite webSite1 = factory.getWebSiteCategory("新闻");
webSite1.use(new User("tom"));
// 客户要一个以博客形式发布的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.use(new User("jack"));
// 客户要一个以博客形式发布的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.use(new User("smith"));
// 客户要一个以博客形式发布的网站
WebSite webSite4 = factory.getWebSiteCategory("博客");
webSite4.use(new User("king"));
System.out.println("网站的分类共=" + factory.getWebSiteCount());
}
}
//具体网站
class ConcreteWebSite extends WebSite {
//共享的部分,内部状态
private String type = ""; //网站发布的形式(类型)
//构造器
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
// TODO Auto-generated method stub
System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
}
}
class User {
private String name;
public User(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
abstract class WebSite {
public abstract void use(User user);//抽象方法
}
// 网站工厂类,根据需要返回压一个网站
class WebSiteFactory {
//集合, 充当池的作用
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
public WebSite getWebSiteCategory(String type) {
if (!pool.containsKey(type)) {
//就创建一个网站,并放入到池中
pool.put(type, new ConcreteWebSite(type));
}
return (WebSite) pool.get(type);
}
//获取网站分类的总数 (池中有多少个网站类型)
public int getWebSiteCount() {
return pool.size();
}
}
10.4、优缺点
- 优点:
- 相同或者相似的对象只有一个,降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
- 享元模式的外部状态相对独立,不影响内部状态,使得享元对象可以在不同的环境中被共享。
- 缺点:
- 增加程序的复杂性:为了使对象可以共享,需要将一些不能共享的状态外部化。
- 读取享元模式的外部状态会使得运行时间长。
10.5、对比
- 享元模式是工厂方法模式的一个改进,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存。
- 可以使用享元模式实现组合模式的共享树叶节点以节省内存。
- 享元模式是生成大量的小型对象,外观模式是用一个对象来代表整个子系统。
- 当只有一个享元对象时,享元模式和单例模式类似。都是一个对象背复用,不同点在于:
- 享元模式可以有多个实体, 其内在状态也可以不同,单例模式则是严格控制单个进程中只有一个实例对象。
- 享元模式可以实现对外部的单例,也可以在需要的使用创建更多的对象。
十一、代理模式
11.1、概念
- 代理模式是一种结构型设计模式, 能够提供对象的替代品或其占位符(即是给某一个对象提供一个代理,并由代理对象控制对原对象的引用)。代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。例如电脑桌面的快捷方式就是一个代理对象,快捷方式是它所引用的程序的一个代理。
- 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
- 代理模式有不同的形式, 主要有三种静态代理、动态代理 (
JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
11.2、结构
Client(客户端):客户端访问代理者与访问被代理者具有类似的效果,其无法区分访问的是代理者还是被代理者。Subject(抽象主题角色):代理者与被代理者共同实现的接口,在任何使用真实主题的地方都可以使用代理主题。Proxy(代理主题角色):代理者,会全权代理RealSubject所具有的功能,在实现其功能的基础上做一些额外的工作。- 包含了对真实主题的引用」,从而可以在任何时候操作真实主题对象
- 在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题。
- 代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。
RealSubject(真实主题角色):被代理者,代理角色所代表的真实对象,其为具有某种特定行为的实现者。
11.3、实现
11.3.1、静态代理
- 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
- 静态代理总结:
- 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,同时,一旦接口增加方法,目标对象与代理对象都要维护。
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();
//创建代理对象, 同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
teacherDaoProxy.teach();
}
}
//接口
interface ITeacherDao {
void teach(); // 授课的方法
}
class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println(" 老师授课中 。。。。。");
}
}
//代理对象,静态代理
class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; // 目标对象,通过接口来聚合
//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println("开始代理 完成某些操作。。。。。 ");//方法
target.teach();
System.out.println("提交。。。。。");//方法
}
}
11.3.2、JDK动态代理
- 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象。 动态代理也叫做:JDK 代理、接口代理。
- JDK的动态代理只能针对实现了接口的类生成代理。原因是:从原理上讲因为JVM动态生成的代理类时继承了Proxy类,实现了代理的接口,最终形式
public final class $Proxy0 extends Proxy implements HelloInterface{}(HelloInterface为被代理类实现的接口)。从使用上讲,创建代理类时必须传入被代理类实现的接口。 而java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。 - JDK动态代理是利用拦截器(拦截器必须实现
InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。在JDK动态代理中,有一个类和接口是实现动态代理所必须用到的InvocationHandler(Interface)、Proxy(Class)
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
ITeacherDao target = new TeacherDao();
//给目标对象,创建代理对象, 可以转成 ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println("proxyInstance=" + proxyInstance.getClass());
//通过代理对象,调用目标对象的方法
//proxyInstance.teach();
proxyInstance.sayHello(" tom ");
}
}
//接口
interface ITeacherDao {
void teach(); // 授课方法
void sayHello(String name);
}
class ProxyFactory {
//维护一个目标对象 , Object
private Object target;
//构造器 , 对 target 进行初始化
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象 生成一个代理对象
public Object getProxyInstance() {
/*
DynamicProxy(动态代理类)是这样一种class:它是在运行时生成的class,在生成它时必须提供一组interface给它,然后该class就宣称它实现了这些 interface。这个DynamicProxy其实就是一个Proxy,它不会做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* 1. ClassLoader loader : 指定当前目标对象使用的类加载器(获取加载器的方法固定),定义了由哪个ClassLoader来对生成的代理对象进行加载。
* 2. Class<?>[] interfaces: 目标对象实现的接口类型(使用泛型方法确认类型),Interface对象的数组,表示的是将要给需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样就能调用这组接口中的方法了。
* 3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入。表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
* 每一个动态代理类都必须要实现接口InvocationHandler,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,
* 这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
/**
*
* @param proxy JDK动态生成的最终代理对象
* @param method 所要调用真实对象的某个方法的Method对象
* @param args 调用真实对象某个方法时接受的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("JDK 代理开始~~");
//反射机制调用目标对象的方法
Object returnVal = method.invoke(target, args);
System.out.println("JDK 代理提交");
return returnVal;
}
});
}
}
class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println(" 老师授课中.... ");
}
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub
System.out.println("hello " + name);
}
}
11.3.3、Cglib 代理
- 静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理(Cglib 代理)
- Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属到动态代理。
- Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截。
- 在 AOP 编程中如何选择代理模式:
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
- Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的方式拦截所有父类方法的调用,顺势织入横切逻辑。(利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理)。在Cglib动态代理中,同样有一个类和接口是实现动态代理所必须用到的,MethodInterceptor(Interface)、Enhancer(Class)。net.sf.cglib.proxy.Enhancer- 主要增强类,通过字节码技术动态创建委托类的子类实例。
Enhancer是CGLIB中最常用的一个类,和Java动态代理中引入的Proxy类差不多。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。
net.sf.cglib.proxy.MethodInterceptor- 常用的方法拦截器接口,需要实现
intercept方法,实现具体拦截处理。 - 常用方法:methodProxy.invokeSuper(obj,args);在拦截方法内可以调用多次。
- 常用的方法拦截器接口,需要实现
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
TeacherDao target = new TeacherDao();
//获取到代理对象,并且将目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发 intecept 方法,从而实现 对目标对象的调用
String res = proxyInstance.teach();
System.out.println("res=" + res);
}
}
class ProxyFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
//构造器,传入一个被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象: 是 target 对象的代理对象
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
/**
* 重写 intercept 方法,会调用目标对象的方法
*
* @param arg0 obj:动态生成的代理对象。
* @param method method :实际调用的方法。
* @param args args:调用方法入参。
* @param arg3 methodProxy:java Method类的代理类,可以实现委托类对象的方法的调用。
* @return .
* @throws Throwable .
*/
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("Cglib 代理模式 ~~ 开始");
Object returnVal = method.invoke(target, args);
System.out.println("Cglib 代理模式 ~~ 提交");
return returnVal;
}
}
class TeacherDao {
public String teach() {
System.out.println(" 老师授课中 , 我是 cglib 代理,不需要实现接口 ");
return "hello";
}
}
11.4、优缺点
- 优点:
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
- 缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
11.5、对比
- 代理模式和装饰模式的异同:代理模式和装饰模式的实现方式类似,有着相似的结构,主要不同点
- 代理模式强调的是限制,装饰模式强调的是增强。
- 代理模式自行管理其服务对象的生命周期,装饰模式的生成则总是由客户端进行控制。
- 代理模式关注与被代理对象行为的控制,装饰模式关注于在一个对象上动态的添加方法。
- 代理模式可以对客户端隐藏被代理对象的具体实现,装饰模式是将原始对象转为一个参数传递给装饰者的构造器中。
- 代理模式能为对象提供相同的接口,装饰模式则能为对象提供加强的接口。
- 代理模式和委托模式的异同
- 代理模式:是把一些事情交给某人帮忙去完成。
- 委托模式:是当某件事情发生的时候,顺便干某件事情。委托就相当于一个触发器。
- 代理模式和适配器模式的异同
- 代理模式:不能改变所代理类的接口,只是增加限制
- 适配器模式:主要改变所考虑对象的接口,能为被封装对象提供不同的接口。
11.6、其他代理模式
- 防火墙代理(
Firewall):内网通过代理穿透防火墙,实现对公网的访问。 - 缓存代理(
Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则 ok,如果取不到资源,再到公网或者数据库取,然后缓存。 - 远程代理(
Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。 - 同步代理:主要使用在多线程编程中,完成多线程间同步工作
- 延迟初始化/虚拟代理(
Virtual Proxy):如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时,可使用代理模式,无需在程序启动时就创建该对象,可将对象的初始化延迟到真正有需要的时候。 - 访问控制/保护代理(
Protect Proxy):控制对一个对象的访问,可以根据客户端凭据给不同的用户提供不同级别的使用权限。如果只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序), 此时可使用代理模式。 Copy-on-Write代理。- 同步化(
Synchronization)代理。 - 智能引用代理(
Smart Reference Proxy):可在没有客户端使用某个重量级对象时立即销毁该对象,还可以记录客户端是否修改了服务对象,其他客户端还可以复用未修改的对象。代理会将所有获取了指向服务对象或其结果的客户端记录在案。代理会时不时地遍历各个客户端, 检查它们是否仍在运行。如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。
十二、模板方法模式
12.1、概念
- 模板方法模式(
Template Method Pattern),又叫模板模式(Template Pattern),属于行为型模式(仅仅使用继承机制),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 - 简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
- 主要作用将这些通用算法抽象出来,子类实现差异性行为,对多个行为方法进行编排。
12.2、结构
- 抽象模板类(Abstract Class)角色:定义算法的轮廓和骨架。由一个模板方法和若干个基本方法构成。
- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是整个算法中的一个步骤,包含以下几种类型。
- 抽象方法:在抽象类中声明,由具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法:可以省略,用于判断的逻辑方法和需要子类重写的空方法两种。通过在具体子类中重写钩子方法来改变抽象父类中的运行结果。
- 具体实现(Concrete Class)角色:实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
12.3、实现
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//制作红豆豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
System.out.println("----制作花生豆浆----");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
}
}
class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println(" 加入上好的花生 ");
}
}
class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println(" 加入上好的红豆 ");
}
}
//抽象类,表示豆浆
abstract class SoyaMilk {
//模板方法, make , 模板方法可以做成 final , 不让子类去覆盖.
final void make() {
select();
addCondiments();
soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 ");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");
}
}
12.4、优缺点
- 优点
- 父类封装了不变部分,子类扩展可变部分,便于程序的扩展,提升代码复用。
- 符合开闭原则,子类可以通过扩展方式增加相应的功能。
- 缺点
- 每个不同的实现都需要定义一个子类,增加类的个数,提高了系统的复杂度。
- 无法避免继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
12.5、对比
- 工厂方法模式是模板方法模式的一种特殊形式。工厂方法可以作为一个大型模板方法中的一个步骤。
- 模板方法模式与策略模式在形式上类似,不同点在于:
- 模板方法模式基于继承机制:允许你通过扩展子类中的部分内容来改变部分算法。作用在类层次,是静态的。
- 策略模式基于组合机制:可以通过对相应行为提供不同的策略来改变对象的部分行为。作用在对象层次,允许在运行时切换行为。
12.6、总结
- 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
- 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
- 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
- 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
- 一般模板方法都加上
final关键字,防止子类重写模板方法。 - 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。
十三、命令模式
13.1、概念
- 命令模式(
Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。 - 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
- 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
- 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象。
13.2、结构
Invoker:调用者角色。Command:是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类。Receiver:接受者角色,知道如何实施和执行一个请求相关的操作。ConcreteCommand:将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute。
13.3、实现
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用命令设计模式,完成通过遥控器,对电灯的操作
//创建电灯的对象(接受者)
LightReceiver lightReceiver = new LightReceiver();
//创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
//需要一个遥控器
RemoteController remoteController = new RemoteController();
//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("--------按下灯的开按钮-----------");
remoteController.onButtonWasPushed(0);
System.out.println("--------按下灯的关按钮-----------");
remoteController.offButtonWasPushed(0);
System.out.println("--------按下撤销按钮-----------");
remoteController.undoButtonWasPushed();
System.out.println("=========使用遥控器操作电视机==========");
TVReceiver tvReceiver = new TVReceiver();
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
//给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作
remoteController.setCommand(1, tvOnCommand, tvOffCommand);
System.out.println("--------按下电视机的开按钮-----------");
remoteController.onButtonWasPushed(1);
System.out.println("--------按下电视机的关按钮-----------");
remoteController.offButtonWasPushed(1);
System.out.println("--------按下撤销按钮-----------");
remoteController.undoButtonWasPushed();
}
}
//创建命令接口
interface Command {
//执行动作(操作)
public void execute();
//撤销动作(操作)
public void undo();
}
class LightOffCommand implements Command {
// 聚合 LightReceiver
LightReceiver light;
// 构造器
public LightOffCommand(LightReceiver light) {
super();
this.light = light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
light.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
light.on();
}
}
class LightOnCommand implements Command {
//聚合 LightReceiver
LightReceiver light;
//构造器
public LightOnCommand(LightReceiver light) {
super();
this.light = light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
//调用接收者的方法
light.on();
}
@Override
public void undo() {
// TODO Auto-generated method stub
//调用接收者的方法
light.off();
}
}
class LightReceiver {
public void on() {
System.out.println(" 电灯打开了.. ");
}
public void off() {
System.out.println(" 电灯关闭了.. ");
}
}
/**
* 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
* 其实,这样是一种设计模式, 可以省掉对空判断
*
* @author Administrator
*/
class NoCommand implements Command {
@Override
public void execute() {
// TODO Auto-generated method stub
}
@Override
public void undo() {
// TODO Auto-generated method stub
}
}
class RemoteController {
// 开 按钮的命令数组
Command[] onCommands;
Command[] offCommands;
// 执行撤销的命令
Command undoCommand;
// 构造器,完成对按钮初始化
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
// 给我们的按钮设置你需要的命令
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开按钮
public void onButtonWasPushed(int no) { // no 0
// 找到你按下的开的按钮, 并调用对应方法
onCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = onCommands[no];
}
// 按下开按钮
public void offButtonWasPushed(int no) { // no 0
// 找到你按下的关的按钮, 并调用对应方法
offCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
class TVOffCommand implements Command {
// 聚合 TVReceiver
TVReceiver tv;
// 构造器
public TVOffCommand(TVReceiver tv) {
super();
this.tv = tv;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.on();
}
}
class TVOnCommand implements Command {
// 聚合 TVReceiver
TVReceiver tv;
// 构造器
public TVOnCommand(TVReceiver tv) {
super();
this.tv = tv;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.on();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.off();
}
}
class TVReceiver {
public void on() {
System.out.println(" 电视机打开了.. ");
}
public void off() {
System.out.println(" 电视机关闭了.. ");
}
}
13.4、注意事项
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的
execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。 - 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。
- 容易实现对请求的撤销和重做。
- 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意。
- 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
- 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟
CMD(DOS命令)订单的撤销/恢复、触发-反馈机制。
十四、访问者模式
14.1、概述
- 访问者模式(
Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 - 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题。
- 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
- 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类。
14.2、结构

Visitor:是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作。ConcreteVisitor:是一个具体的访问值 实现每个有Visitor声明的操作,是每个操作实现的部分。ObjectStructure:能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素。Element:定义一个accept方法,接收一个访问者对象。ConcreteElement:为具体元素,实现了accept方法。
14.3、实现
- 双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型。
//说明
//1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递 Woman 中(第一次分派)
//2. 然后 Woman 类调用作为参数的 "具体方法" 中方法 getWomanResult, 同时将自己(this)作为参数
// 传入,完成第二次的分派
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建 ObjectStructure
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
//成功
Success success = new Success();
objectStructure.display(success);
System.out.println("===============");
Fail fail = new Fail();
objectStructure.display(fail);
System.out.println("=======给的是待定的测评========");
Wait wait = new Wait();
objectStructure.display(wait);
}
}
abstract class Action {
//得到男性 的测评
public abstract void getManResult(Man man);
//得到女的 测评
public abstract void getWomanResult(Woman woman);
}
class Fail extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub
System.out.println(" 男人给的评价该歌手失败 !");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub
System.out.println(" 女人给的评价该歌手失败 !");
}
}
class Success extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub
System.out.println(" 男人给的评价该歌手很成功 !");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub
System.out.println(" 女人给的评价该歌手很成功 !");
}
}
class Wait extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub
System.out.println(" 男人给的评价是该歌手待定 ..");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub
System.out.println(" 女人给的评价是该歌手待定 ..");
}
}
abstract class Person {
//提供一个方法,让访问者可以访问
public abstract void accept(Action action);
}
class Man extends Person {
@Override
public void accept(Action action) {
// TODO Auto-generated method stub
action.getManResult(this);
}
}
class Woman extends Person {
@Override
public void accept(Action action) {
// TODO Auto-generated method stub
action.getWomanResult(this);
}
}
14.4、优缺点
- 优点
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高。
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统。
- 缺点
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难。
- 违背了依赖倒转原则,访问者依赖的是具体元素,而不是抽象元素。
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的。
十五、迭代器模式
15.1、概念
- 迭代器模式(
Iterator Pattern)是常用的设计模式,属于行为型模式。 - 迭代器模式是针对集合对象而生的,对于集合对象肯定会有对集合的添加、删除、遍历集合等操作,基于单一职责原则,我们一般使用不同的类来承担不同的责任,迭代器模式就是用迭代器类来承担遍历集合的职责。
- 提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
15.2、结构

- 抽象聚合(
Aggregate)角色:声明添加、删除,遍历聚合对象的方法。遍历方法的返回类型必须被声明为迭代器接口Iterator,这样具体集合就可以返回各种不同种类的迭代器。例如:Java中的Collection接口,List接口,Set接口等。 - 具体聚合(
Concrete Aggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。并持有一个存储集合数据的对象。例如:List接口的有序列表实现ArrayList,链表实现LinkedList。Set接口的哈希列表的实现HashSet等。 - 抽象迭代器(
Iterator)角色:声明访问和遍历聚合元素的接口,一般需要包含hasNext()、first()、next()等方法。 - 具体迭代器(
Concrete iterator)角色:实现抽象迭代器接口,是遍历集合的一种特定算法,完成对聚合对象的遍历并记录遍历的当前位置。 - 客户端(
Client):通过Iterator和Aggregate依赖子类。
15.3、实现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建学院
List<College> collegeList = new ArrayList<>();
ComputerCollege computerCollege = new ComputerCollege();
InfoCollege infoCollege = new InfoCollege();
collegeList.add(computerCollege);
//collegeList.add(infoCollege);
OutPutImpl outPutImpl = new OutPutImpl(collegeList);
outPutImpl.printCollege();
}
}
interface College {
String getName();
//增加系的方法
void addDepartment(String name, String desc);
//返回一个迭代器,遍历
Iterator createIterator();
}
class ComputerCollege implements College {
Department[] departments;
int numOfDepartment = 0;// 保存当前数组的对象个数
public ComputerCollege() {
departments = new Department[5];
addDepartment("Java 专业", " Java 专业 ");
addDepartment("PHP 专业", " PHP 专业 ");
addDepartment("大数据专业", " 大数据专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "计算机学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc);
departments[numOfDepartment] = department;
numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new ComputerCollegeIterator(departments);
}
}
class ComputerCollegeIterator implements Iterator {
//这里我们需要 Department 是以怎样的方式存放=>数组
Department[] departments;
int position = 0; //遍历的位置
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
//判断是否还有下一个元素
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if (position >= departments.length || departments[position] == null) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub
Department department = departments[position];
position += 1;
return department;
}
//删除的方法,默认空实现
public void remove() {
}
}
//系
class Department {
private String name;
private String desc;
public Department(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
class InfoColleageIterator implements Iterator {
List<Department> departmentList; // 信息工程学院是以 List 方式存放系
int index = -1;//索引
public InfoColleageIterator(List<Department> departmentList) {
this.departmentList = departmentList;
}
//判断 list 中还有没有下一个元素
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if (index >= departmentList.size() - 1) {
return false;
} else {
index += 1;
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub
return departmentList.get(index);
}
//空实现 remove
public void remove() {
}
}
class InfoCollege implements College {
List<Department> departmentList;
public InfoCollege() {
departmentList = new ArrayList<Department>();
addDepartment("信息安全专业", " 信息安全专业 ");
addDepartment("网络安全专业", " 网络安全专业 ");
addDepartment("服务器安全专业", " 服务器安全专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "信息工程学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc);
departmentList.add(department);
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new InfoColleageIterator(departmentList);
}
}
class OutPutImpl {
//学院集合
List<College> collegeList;
public OutPutImpl(List<College> collegeList) {
this.collegeList = collegeList;
}
//遍历所有学院,然后调用 printDepartment 输出各个学院的系
public void printCollege() {
//从 collegeList 取出所有学院, Java 中的 List 已经实现 Iterator
Iterator<College> iterator = collegeList.iterator();
while (iterator.hasNext()) {
//取出一个学院
College college = iterator.next();
System.out.println("=== " + college.getName() + "=====");
printDepartment(college.createIterator()); //得到对应迭代器
}
}
public void printDepartment(Iterator iterator) {
while (iterator.hasNext()) {
Department d = (Department) iterator.next();
System.out.println(d.getName());
}
}
}
15.4、优缺点
- 优点
- 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
- 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
- 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
- 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式。
- 缺点
- 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类。
15.5、对比
- 可以使用迭代器模式来遍历组合模式树。
- 可以同时使用工厂方法模式和[迭代器**来让子类集合返回不同类型的迭代器,使迭代器与集合相匹配。
- 可以同时使用备忘录模式和迭代器来获取当前迭代器的状态,并且在需要的时候进行回滚。
- 可以同时使用访问者模式和迭代器来遍历复杂数据结构,并对其中的元素执行所需操作。
十六、观察者模式
16.1、概念
- 观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为
Subject,依赖的对象为Observer,Subject通知Observer变化,比如这里的奶站是Subject,是1的一方。用户时Observer,是多的一方。
16.2、结构
- 抽象主题(
Subject)角色:抽象目标类,提供了一个用于保存观察者对象的引用和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。 - 具体主题(
Concrete Subject)角色:具体目标类,实现抽象目标中的通知方法。当具体主题的发生改变时,通知所有注册过的观察者对象。 - 抽象观察者(
Observer)角色:抽象观察者类, 一般情况下该接口只包含一个 update抽象方法。 该方法在接到具体主题的更改通知时被调用。 - 具体观察者(
Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
16.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个 WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
BaiduSite baiduSite = new BaiduSite();
//注册到 weatherData
weatherData.registerObserver(currentConditions);
weatherData.registerObserver(baiduSite);
//测试
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
weatherData.removeObserver(currentConditions);
//测试
System.out.println();
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
}
}
//观察者接口,有观察者来实现
interface Observer {
public void update(float temperature, float pressure, float humidity);
}
class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显示
public void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
class CurrentConditions implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显示
public void display() {
System.out.println("***Today mTemperature: " + temperature + "***");
System.out.println("***Today mPressure: " + pressure + "***");
System.out.println("***Today mHumidity: " + humidity + "***");
}
}
//接口, 让 WeatherData 来实现
interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
/**
* 类是核心
* 1. 包含最新的天气情况信息
* 2. 含有 观察者集合,使用 ArrayList 管理
* 3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息
* @author Administrator
*
*/
class WeatherData implements Subject {
private float temperatrue;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
//加入新的第三方
public WeatherData() {
observers = new ArrayList<>();
}
public float getTemperature() {
return temperatrue;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
//调用 接入方的 update
notifyObservers();
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature;
this.pressure = pressure;
this.humidity = humidity;
//调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
dataChange();
}
//注册一个观察者
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(observers.contains(o)) {
observers.remove(o);
}
}
//遍历所有的观察者,并通知
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
}
}
}

16.4、优缺点
- 优点
- 符合依赖倒置原则,降低了发布者与观察者之间的耦合。观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。这样,增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类
WeatherData不会修改代码,遵守了ocp原则。 - 发布者与观察者之间建立了一套触发机制
- 符合开-闭原则,主题接口仅仅依赖于观察者接口
- 符合依赖倒置原则,降低了发布者与观察者之间的耦合。观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。这样,增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类
- 缺点
- 主题(发布者)与观察者之间可能出现循环引用。
- 当观察者对象很多时,通知的发布耗时,影响程序的效率。
16.5、对比
观察者模式与其他类似模式在处理请求发送者和接收者之间的连接方式不同:
- 责任链模式按照顺序将请求动态传递给一系列的接收者, 直至其中一名接收者对请求进行处理。
- 命令模式在发送者和请求者之间建立单向连接。
- 中介者模式清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
- 观察者模式允许接收者动态地订阅或取消接收请求。
十七、中介者模式
17.1、概念
- 中介者模式(
Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互 - 中介者模式属于行为型模式,使代码易于维护。
- 比如 MVC 模式,C(
Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用。
17.2、结构
Mediator:就是抽象中介者,定义了同事对象到中介者对象的接口。Colleague:是抽象同事类。ConcreteMediator:具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理 HashMap,并接受某个同事对象消息,完成相应的任务。ConcreteColleague:具体的同事类,会有很多,每个同事只知道自己的行为, 而不了解其他同事类的行为(方法),但是他们都依赖中介者对象。
17.3、实现

class ClientTest {
public static void main(String[] args) {
//创建一个中介者对象
Mediator mediator = new ConcreteMediator();
//创建 Alarm 并且加入到 ConcreteMediator 对象的 HashMap
Alarm alarm = new Alarm(mediator, "alarm");
//创建了 CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的 HashMap
CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine");
//创建 Curtains , 并 且加入到 ConcreteMediator 对象的 HashMap
Curtains curtains = new Curtains(mediator, "curtains");
TV tV = new TV(mediator, "TV");
//让闹钟发出消息
alarm.SendAlarm(0);
coffeeMachine.FinishCoffee();
alarm.SendAlarm(1);
}
}
//同事抽象类
abstract class Colleague {
private Mediator mediator;
public String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public Mediator GetMediator() {
return this.mediator;
}
public abstract void SendMessage(int stateChange);
}
class TV extends Colleague {
public TV(Mediator mediator, String name) {
super(mediator, name);
// TODO Auto-generated constructor stub
mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub
this.GetMediator().GetMessage(stateChange, this.name);
}
public void StartTv() {
// TODO Auto-generated method stub
System.out.println("It's time to StartTv!");
}
public void StopTv() {
// TODO Auto-generated method stub
System.out.println("StopTv!");
}
}
//具体的同事类
class Alarm extends Colleague {
//构造器
public Alarm(Mediator mediator, String name) {
super(mediator, name);
// TODO Auto-generated constructor stub
//在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合]
mediator.Register(name, this);
}
public void SendAlarm(int stateChange) {
SendMessage(stateChange);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub
//调用的中介者对象的 getMessage
this.GetMediator().GetMessage(stateChange, this.name);
}
}
class CoffeeMachine extends Colleague {
public CoffeeMachine(Mediator mediator, String name) {
super(mediator, name);
// TODO Auto-generated constructor stub
mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub
this.GetMediator().GetMessage(stateChange, this.name);
}
public void StartCoffee() {
System.out.println("It's time to startcoffee!");
}
public void FinishCoffee() {
System.out.println("After 5 minutes!");
System.out.println("Coffee is ok!");
SendMessage(0);
}
}
class Curtains extends Colleague {
public Curtains(Mediator mediator, String name) {
super(mediator, name);
// TODO Auto-generated constructor stub
mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub
this.GetMediator().GetMessage(stateChange, this.name);
}
public void UpCurtains() {
System.out.println("I am holding Up Curtains!");
}
}
abstract class Mediator {
//将给中介者对象,加入到集合中
public abstract void Register(String colleagueName, Colleague colleague);
//接收消息, 具体的同事对象发出
public abstract void GetMessage(int stateChange, String colleagueName);
public abstract void SendMessage();
}
//具体的中介者类
class ConcreteMediator extends Mediator {
//集合,放入所有的同事对象
private HashMap<String, Colleague> colleagueMap;
private HashMap<String, String> interMap;
public ConcreteMediator() {
colleagueMap = new HashMap<>();
interMap = new HashMap<>();
}
@Override
public void Register(String colleagueName, Colleague colleague) {
// TODO Auto-generated method stub
colleagueMap.put(colleagueName, colleague);
// TODO Auto-generated method stub
if (colleague instanceof Alarm) {
interMap.put("Alarm", colleagueName);
} else if (colleague instanceof CoffeeMachine) {
interMap.put("CoffeeMachine", colleagueName);
} else if (colleague instanceof TV) {
interMap.put("TV", colleagueName);
} else if (colleague instanceof Curtains) {
interMap.put("Curtains", colleagueName);
}
}
//具体中介者的核心方法
//1. 根据得到消息,完成对应任务
//2. 中介者在这个方法,协调各个具体的同事对象,完成任务
@Override
public void GetMessage(int stateChange, String colleagueName) {
// TODO Auto-generated method stub
//处理闹钟发出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) {
if (stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap.get("CoffeeMachine")))).StartCoffee();
((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
} else if (stateChange == 1) {
((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
}
} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
.UpCurtains();
} else if (colleagueMap.get(colleagueName) instanceof TV) {//如果 TV 发现消息
} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗帘发出的消息,这里处理...
}
}
@Override
public void SendMessage() {
// TODO Auto-generated method stub
}
}

17.4、注意事项
- 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦。
- 减少类间依赖,降低了耦合,符合迪米特原则。
- 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响。
- 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意。
十八、备忘录模式
18.1、概念
- 备忘录模式(
Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 - 备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。
- 备忘录模式属于行为型模式。
18.2、结构
originator:对象(需要保存状态的对象)。Memento: 备忘录对象,负责保存好记录,即Originator内部状态。Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率。- 说明:如果希望保存多个
originator对象的不同时间的状态,也可以,只需要要HashMap<String, 集合>。
18.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState(" 状态#1 攻击力 100 ");
//保存了当前的状态
caretaker.add(originator.saveStateMemento());
originator.setState(" 状态#2 攻击力 80 ");
caretaker.add(originator.saveStateMemento());
originator.setState(" 状态#3 攻击力 50 ");
caretaker.add(originator.saveStateMemento());
System.out.println("当前的状态是 =" + originator.getState());
//希望得到状态 1, 将 originator 恢复到状态 1
originator.getStateFromMemento(caretaker.get(0));
System.out.println("恢复到状态 1 , 当前的状态是");
System.out.println("当前的状态是 =" + originator.getState());
}
}
class Caretaker {
//在 List 集合中会有很多的备忘录对象
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento memento) {
mementoList.add(memento);
}
//获取到第 index 个 Originator 的 备忘录对象(即保存状态)
public Memento get(int index) {
return mementoList.get(index);
}
}
class Memento {
private String state;
//构造器
public Memento(String state) {
super();
this.state = state;
}
public String getState() {
return state;
}
}
class Originator {
private String state;//状态信息
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//编写一个方法,可以保存一个状态对象 Memento
//因此编写一个方法,返回 Memento
public Memento saveStateMemento() {
return new Memento(state);
}
//通过备忘录对象,恢复状态
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
18.4、注意事项
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
- 注意:如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
- 适用的应用场景
- 后悔药。
- 打游戏时的存档。
Windows里的ctri + z。IE中的后退。- 数据库的事务管理。
- 为了节约内存,备忘录模式可以和原型模式配合使用。
十九、解释器模式
19.1、概念
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。
- 解释器模式(
Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)。 - 应用场景
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来表达。
- 一个简单语法需要解释的场景。
19.2、结构
- 环境角色(
Context):含有解释器之外的全局信息。一般包含各个解释器需要的数据或是公共的功能,用来传递被所有解释器共享的数据。 - 抽象表达式(
Abstract Expression):定义解释器的接口,声明一个抽象的解释操作(解释方法interpret()),这个方法为抽象语法树中所有的节点所共享。 - 终结符表达式(
Terminal Expression):实现抽象表达式角色,为文法中的终结符相关的解释操作。语法中的每一个终结符都有一个具体终结表达式与之相对应。 - 非终结符表达式(
NonTermial Expression):实现抽象表达式角色,为文法中的非终结符相关的解释操作。语法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是语法中的运算符或者其他关键字。 - 说明:输入
Context he TerminalExpression信息通过Client输入即可。
19.3、实现

class ClientTest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String expStr = getExpStr(); // a+b
HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
// 获得表达式
public static String getExpStr() throws IOException {
System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
// 获得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for (char ch : expStr.toCharArray()) {
if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
System.out.print("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
class Calculator {
// 定义表达式
private Expression expression;
// 构造函数传参,并解析
public Calculator(String expStr) { // expStr = a+b
// 安排运算先后顺序
Stack<Expression> stack = new Stack<>();
// 表达式拆分成字符数组
char[] charArray = expStr.toCharArray();// [a, +, b]
Expression left = null;
Expression right = null;
//遍历我们的字符数组, 即遍历 [a, +, b]
//针对不同的情况,做处理
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+': //
left = stack.pop();// 从 stack 取出 left => "a"
right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
stack.push(new AddExpression(left, right));// 然后根据得到 left 和 right 构建 AddExpresson 加入 stack
break;
case '-': //
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default:
//如果是一个 Var 就创建要给 VarExpression 对象,并 push 到 stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个 charArray 数组后,stack 就得到最后 Expression
this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
//最后将表达式 a+b 和 var = {a=10,b=20}
//然后传递给 expression 的 interpreter 进行解释执行
return this.expression.interpreter(var);
}
}
/**
* 抽象类表达式,通过 HashMap 键值对, 可以获取到变量的值
*
* @author Administrator
*/
abstract class Expression {
// a + b - c
// 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value 就是就是具体值
// HashMap {a=10, b=20}
public abstract int interpreter(HashMap<String, Integer> var);
}
/**
* 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
* 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是 Expression 类的实现类
*
* @author Administrator
*/
class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
//因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
@Override
public int interpreter(HashMap<String, Integer> var) {
// TODO Auto-generated method stub
return 0;
}
}
/**
* 变量的解释器
*
* @author Administrator
*/
class VarExpression extends Expression {
private String key; // key=a,key=b,key=c
public VarExpression(String key) {
this.key = key;
}
// var 就是{a=10, b=20}
// interpreter 根据 变量名称,返回对应值
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
/**
* 加法解释器
*
* @author Administrator
*/
class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
//处理相加
//var 仍然是 {a=10,b=20}..
//一会我们 debug 源码,就 ok
public int interpreter(HashMap<String, Integer> var) {
//super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
//super.right.interpreter(var): 返回 right 表达式对应值 b = 20
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//求出 left 和 right 表达式相减后的结果
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
19.4、优缺点
- 优点
- 可扩展性比较好,可以通过继承等机制来改变或扩展文法。
- 易于实现简单文法,语法树中的每个表达式节点类都是相似的。
- 缺点
- 执行效率较低,对于复杂的表达式通常使用大量的循环和递归调用。
- 会引起类膨胀,需要给每条规则至少定义一个类。
19.5、注意事项
- 对比:
- 解释器模式:通过解释器来解释这个语言中的句子或表达式的方式,是对语法的操作。
- 适配器模式:让接口不同的类通过适配器模式可以一起工作,针对接口的操作。
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性。
- 应用场景:编译器、运算表达式计算、正则表达式、机器人等。
- 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。
二十、状态模式
20.1、概念
20.2、结构
- 上下文(
Context)角色:定义了客户端需要的接口,内部持有一个具体状态对象的引用,并负责具体状态的切换。 - 抽象状态(
State)角色:声明特定于状态方法的接口,用以封装上下文角色中的特定状态所对应的行为,可以有一个或多个行为。 - 具体状态(
Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。 - 客户端(
Client)角色:定义上下文角色,执行状态行为。
20.3、实现

/**
* 状态抽象类
*
* @author Administrator
*/
abstract class State {
// 扣除积分 - 50
public abstract void deductMoney();
// 是否抽中奖品
public abstract boolean raffle();
// 发放奖品
public abstract void dispensePrize();
}
/**
* 奖品发放完毕状态
* 说明,当我们 activity 改变成 DispenseOutState, 抽奖活动结束
*
* @author Administrator
*/
class DispenseOutState extends State {
// 初始化时传入活动引用
RaffleActivity activity;
public DispenseOutState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("奖品发送完了,请下次再参加");
}
@Override
public boolean raffle() {
System.out.println("奖品发送完了,请下次再参加");
return false;
}
@Override
public void dispensePrize() {
System.out.println("奖品发送完了,请下次再参加");
}
}
/**
* 发放奖品的状态
*
* @author Administrator
*/
class DispenseState extends State {
// 初始化时传入活动引用,发放奖品后改变其状态
RaffleActivity activity;
public DispenseState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("不能扣除积分");
}
@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}
//发放奖品
@Override
public void dispensePrize() {
if (activity.getCount() > 0) {
System.out.println("恭喜中奖了");
// 改变状态为不能抽奖
activity.setState(activity.getNoRafflleState());
} else {
System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
activity.setState(activity.getDispensOutState());
//System.out.println("抽奖活动结束");
//System.exit(0);
}
}
}
/**
* 不能抽奖状态
*
* @author Administrator
*/
class NoRaffleState extends State {
// 初始化时传入活动引用,扣除积分后改变其状态
RaffleActivity activity;
public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}
// 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
@Override
public void deductMoney() {
System.out.println("扣除 50 积分成功,您可以抽奖了");
activity.setState(activity.getCanRaffleState());
}
// @Override
// public void deductMoney() {
// System.out.println("不能扣除积分");
// }
@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}
//发放奖品
@Override
public void dispensePrize() {
if (activity.getCount() > 0) {
System.out.println("恭喜中奖了");
// 改变状态为不能抽奖
activity.setState(activity.getNoRafflleState());
} else {
System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
activity.setState(activity.getDispensOutState());
//System.out.println("抽奖活动结束");
//System.exit(0);
}
}
}
/**
* 可以抽奖的状态
*
* @author Administrator
*/
class CanRaffleState extends State {
RaffleActivity activity;
public CanRaffleState(RaffleActivity activity) {
this.activity = activity;
}
//已经扣除了积分,不能再扣
@Override
public void deductMoney() {
System.out.println("已经扣取过了积分");
}
//可以抽奖, 抽完奖后,根据实际情况,改成新的状态
@Override
public boolean raffle() {
System.out.println("正在抽奖,请稍等!");
Random r = new Random();
int num = r.nextInt(10);
// 10%中奖机会
if (num == 0) {
// 改变活动状态为发放奖品 context
activity.setState(activity.getDispenseState());
return true;
} else {
System.out.println("很遗憾没有抽中奖品!");
// 改变状态为不能抽奖
activity.setState(activity.getNoRafflleState());
return false;
}
}
// 不能发放奖品
@Override
public void dispensePrize() {
System.out.println("没中奖,不能发放奖品");
}
}
/**
* 抽奖活动 //
*
* @author Administrator
*/
class RaffleActivity {
// state 表示活动当前的状态,是变化
State state = null;
// 奖品数量
int count = 0;
// 四个属性,表示四种状态
State noRafflleState = new NoRaffleState(this);
State canRaffleState = new CanRaffleState(this);
State dispenseState = new DispenseState(this);
State dispensOutState = new DispenseOutState(this);
//构造器
//1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
//2. 初始化奖品的数量
public RaffleActivity(int count) {
this.state = getNoRafflleState();
this.count = count;
}
//扣分, 调用当前状态的 deductMoney
public void debuctMoney() {
state.deductMoney();
}
//抽奖
public void raffle() {
// 如果当前的状态是抽奖成功
if (state.raffle()) {
//领取奖品
state.dispensePrize();
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
//这里请大家注意,每领取一次奖品,count--
public int getCount() {
int curCount = count;
count--;
return curCount;
}
public void setCount(int count) {
this.count = count;
}
public State getNoRafflleState() {
return noRafflleState;
}
public void setNoRafflleState(State noRafflleState) {
this.noRafflleState = noRafflleState;
}
public State getCanRaffleState() {
return canRaffleState;
}
public void setCanRaffleState(State canRaffleState) {
this.canRaffleState = canRaffleState;
}
public State getDispenseState() {
return dispenseState;
}
public void setDispenseState(State dispenseState) {
this.dispenseState = dispenseState;
}
public State getDispensOutState() {
return dispensOutState;
}
public void setDispensOutState(State dispensOutState) {
this.dispensOutState = dispensOutState;
}
}
/**
* 状态模式测试类
*
* @author Administrator
*/
class ClientTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建活动对象,奖品有 1 个奖品
RaffleActivity activity = new RaffleActivity(1);
// 我们连续抽 300 次奖
for (int i = 0; i < 30; i++) {
System.out.println("--------第" + (i + 1) + "次抽奖----------");
// 参加抽奖,第一步点击扣除积分
activity.debuctMoney();
// 第二步抽奖
activity.raffle();
}
}
}
- 实例,借贷平台
/**
* 借贷平台的订单,有审核-发布-抢单 等等 步骤,随着操作的不同,会改变订单的状态, 项目中的这个模块实现就会使用到状态模式
* 通常通过 if/else 判断订单的状态,从而实现不同的逻辑,伪代码如下
* 使用状态模式完成 借贷平台项目的审核模块 [设计 + 代码]
*/
abstract class AbstractState implements State {
protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");
//抽象类,默认实现了 State 接口的所有方法
//该类的所有方法,其子类(具体的状态类),可以有选择的进行重写
@Override
public void checkEvent(Context context) {
throw EXCEPTION;
}
@Override
public void checkFailEvent(Context context) {
throw EXCEPTION;
}
@Override
public void makePriceEvent(Context context) {
throw EXCEPTION;
}
@Override
public void acceptOrderEvent(Context context) {
throw EXCEPTION;
}
@Override
public void notPeopleAcceptEvent(Context context) {
throw EXCEPTION;
}
@Override
public void payOrderEvent(Context context) {
throw EXCEPTION;
}
@Override
public void orderFailureEvent(Context context) {
throw EXCEPTION;
}
@Override
public void feedBackEvent(Context context) {
throw EXCEPTION;
}
}
//各种具体状态类
class FeedBackState extends AbstractState {
@Override
public String getCurrentState() {
return StateEnum.FEED_BACKED.getValue();
}
}
class GenerateState extends AbstractState {
@Override
public void checkEvent(Context context) {
context.setState(new ReviewState());
}
@Override
public void checkFailEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.GENERATE.getValue();
}
}
class NotPayState extends AbstractState {
@Override
public void payOrderEvent(Context context) {
context.setState(new PaidState());
}
@Override
public void feedBackEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.NOT_PAY.getValue();
}
}
class PaidState extends AbstractState {
@Override
public void feedBackEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.PAID.getValue();
}
}
class PublishState extends AbstractState {
@Override
public void acceptOrderEvent(Context context) {
//把当前状态设置为 NotPayState。。。
//至于应该变成哪个状态,有流程图来决定
context.setState(new NotPayState());
}
@Override
public void notPeopleAcceptEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.PUBLISHED.getValue();
}
}
class ReviewState extends AbstractState {
@Override
public void makePriceEvent(Context context) {
context.setState(new PublishState());
}
@Override
public String getCurrentState() {
return StateEnum.REVIEWED.getValue();
}
}
/**
* 测试类
*/
class ClientTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建 context 对象
Context context = new Context();
//将当前状态设置为 PublishState
context.setState(new PublishState());
System.out.println(context.getCurrentState());
// //publish --> not pay
context.acceptOrderEvent(context);
// //not pay --> paid
context.payOrderEvent(context);
// // 失败, 检测失败时,会抛出异常
// try {
// context.checkFailEvent(context);
// System.out.println("流程正常..");
// } catch (Exception e) {
// // TODO: handle exception
// System.out.println(e.getMessage());
// }
}
}
//环境上下文
class Context extends AbstractState {
//当前的状态 state, 根据我们的业务流程处理,不停的变化
private State state;
@Override
public void checkEvent(Context context) {
state.checkEvent(this);
getCurrentState();
}
@Override
public void checkFailEvent(Context context) {
state.checkFailEvent(this);
getCurrentState();
}
@Override
public void makePriceEvent(Context context) {
state.makePriceEvent(this);
getCurrentState();
}
@Override
public void acceptOrderEvent(Context context) {
state.acceptOrderEvent(this);
getCurrentState();
}
@Override
public void notPeopleAcceptEvent(Context context) {
state.notPeopleAcceptEvent(this);
getCurrentState();
}
@Override
public void payOrderEvent(Context context) {
state.payOrderEvent(this);
getCurrentState();
}
@Override
public void orderFailureEvent(Context context) {
state.orderFailureEvent(this);
getCurrentState();
}
@Override
public void feedBackEvent(Context context) {
state.feedBackEvent(this);
getCurrentState();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Override
public String getCurrentState() {
System.out.println("当前状态 : " + state.getCurrentState());
return state.getCurrentState();
}
}
/**
* 状态接口
*
* @author Administrator
*/
interface State {
/**
* 电审
*/
void checkEvent(Context context);
/**
* 电审失败
*/
void checkFailEvent(Context context);
/**
* 定价发布
*/
void makePriceEvent(Context context);
/**
* 接单
* 尚硅谷 Java 设计模式
* 更多 Java –大数据 –前端 –python 人工智能 -区块链资料下载,可访问百度:尚硅谷官网 第 352页
*/
void acceptOrderEvent(Context context);
/**
* 无人接单失效
*/
void notPeopleAcceptEvent(Context context);
/**
* 付款
*/
void payOrderEvent(Context context);
/**
* 接单有人支付失效
*/
void orderFailureEvent(Context context);
/**
* 反馈
*/
void feedBackEvent(Context context);
String getCurrentState();
}
/**
* 状态枚举类
*
* @author Administrator
*/
enum StateEnum {
//订单生成
GENERATE(1, "GENERATE"),
//已审核
REVIEWED(2, "REVIEWED"),
//已发布
PUBLISHED(3, "PUBLISHED"),
//待付款
NOT_PAY(4, "NOT_PAY"),
//已付款
PAID(5, "PAID"),
//已完结
FEED_BACKED(6, "FEED_BACKED");
private int key;
private String value;
StateEnum(int key, String value) {
this.key = key;
this.value = value;
}
public int getKey() {
return key;
}
public String getValue() {
return value;
}
}
20.4、优缺点
- 优点
- 满足单一职责原则,特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,结构清晰。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
- 状态转换显示化,减少对象间的相互依赖。通过消除臃肿的状态机条件语句简化上下文代码。
- 缺点
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,增加系统的复杂度。
- 状态模式对开闭原则的支持并不太好,增加新的状态类和行为都需要修改源码。
20.5、注意事项
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
- 方便维护。将容易产生问题的
if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句,而且容易出错。 - 符合“开闭原则”。容易增删状态。
- 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度。
- 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。
20.6、对比
- 状态模式和责任链模式都能消除
if-else分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。- 状态模式强调的是一个对象内在状态的改变,各个状态对象知道自己要进入的下一个状态对象。
- 责任链模式强调的是外部节点对象间的改变,并不清楚其下一个节点处理对象,链式组装由客户端负责。
- 状态模式和策略模式的
UML类图架构几乎一样,但应用场景不一样。- 策略模式多种算法行为是独立的,任何一种都能满足业务,客户端可自行更换策略算法。
- 状态模式各个状态在一定条件下可以自动切换到其他状态,客户端只能设置初始状态,无法指定状态。
二十一、策略模式
21.1、概念
- 策略模式(
Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 - 这算法体现了几个设计原则。
- 把变化的代码从不变的代码中分离出来。
- 针对接口编程而不是具体类(定义了策略接口)。
- 多用组合/聚合,少用继承(客户通过组合方式使用策略)。
21.2、结构
- 上下文(
Context)角色:持有一个维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。 - 抽象策略(
Strategy)角色:通常由一个接口或抽象类实现。声明了一个上下文用于执行策略的方法。 - 具体策略(
ConcreteStrategy)角色:实现了上下文所用算法的各种不同变体。 - 客户端:创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
21.3、实现

interface QuackBehavior {
void quack();//子类实现
}
interface FlyBehavior {
void fly(); // 子类具体实现
}
class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println(" 飞翔技术高超 ~~~");
}
}
class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println(" 不会飞翔 ");
}
}
class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println(" 飞翔技术一般 ");
}
}
abstract class Duck {
//属性, 策略接口
FlyBehavior flyBehavior;
//其它属性<->策略接口
QuackBehavior quackBehavior;
public Duck() {
}
public abstract void display();//显示鸭子信息
public void quack() {
System.out.println("鸭子嘎嘎叫~~");
}
public void swim() {
System.out.println("鸭子会游泳~~");
}
public void fly() {
//改进
if (flyBehavior != null) {
flyBehavior.fly();
}
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
class PekingDuck extends Duck {
//假如北京鸭可以飞翔,但是飞翔技术一般
public PekingDuck() {
// TODO Auto-generated constructor stub
flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("~~北京鸭~~~");
}
}
class ToyDuck extends Duck {
public ToyDuck() {
// TODO Auto-generated constructor stub
flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("玩具鸭");
}
//需要重写父类的所有方法
public void quack() {
System.out.println("玩具鸭不能叫~~");
}
public void swim() {
System.out.println("玩具鸭不会游泳~~");
}
}
class WildDuck extends Duck {
//构造器,传入 FlyBehavor 的对象
public WildDuck() {
// TODO Auto-generated constructor stub
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println(" 这是野鸭 ");
}
}
24.4、优缺点
- 优点:
- 结构清晰,把策略分离成一个个单独的类,使得程序更清晰。
- 代码耦合度降低,安全性提高。
- 缺点:
- 客户端必须要知道所有的策略类,才能确定使用那个策略,所以策略模式适用于提前知道所有策略的情况下。
- 策略类数量增多,增加算法,需要新增策略类。
24.5、对比
- 策略模式和简单工厂模式的区别
- 策略模式和简单工厂模式都是通过多态来实现不同子类的选取。
- 在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。
- 简单工厂模式中只需要传递相应的条件就能得到想要的一个对象,然后通过这个对象实现算法的操作
- 策略模式使用时必须首先创建策略对象作为参数传递进去,通过该对象调用不同的算法。
21.6、注意事项
- 策略模式的关键是:分析项目中变化部分与不变部分。
- 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性。
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(
if..else if..else)。 - 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的
Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。 - 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大。
二十二、责任链模式
22.1、概念
- 职责链模式(
Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。 - 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
- 这种类型的设计模式属于行为型模式。
22.2、结构
- 抽象处理者(
Handler)角色:声明处理请求的所有接口,包含抽象处理方法,持有对下一个处理者的引用。 - 基础处理者(
Base Handler)角色:是一个可选的类,可以将所有处理者共用的样本代码放置在其中。- 当使用基础处理者时,抽象处理者只需要定义行为方法。持有对下一个处理者的引用下放到基础处理者。
- 可以在基础处理者实现一些具体处理者的公共方法,避免代码冗余。
- 具体处理者(
Concrete Handler)角色:实现抽象处理者的处理方法。判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。 - 客户类(
Client):创建处理链,并向链头的具体处理者对象提交请求。
22.3、实现

class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
//创建相关的审批人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");
//需要将各个审批级别的下一个设置好 (处理人构成环形: )
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
schoolMasterApprover.setApprover(departmentApprover);
departmentApprover.processRequest(purchaseRequest);
viceSchoolMasterApprover.processRequest(purchaseRequest);
}
}
//请求类
class PurchaseRequest {
private int type = 0; //请求类型
private float price = 0.0f; //请求金额
private int id = 0;
//构造器
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
abstract class ApprOver {
ApprOver approver; //下一个处理者
String name; // 名字
public ApprOver(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
//下一个处理者
public void setApprover(ApprOver approver) {
this.approver = approver;
}
//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
class CollegeApprover extends ApprOver {
public CollegeApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
class DepartmentApprover extends ApprOver {
public DepartmentApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
class SchoolMasterApprover extends ApprOver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
class ViceSchoolMasterApprover extends ApprOver {
public ViceSchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
22.4、优缺点
- 优点:
- 降低了对象之间的耦合度。责任链模式让发送者不需要知道对方的具体是哪个处理者处理请求。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,客户端可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用。
- 符合类的单一职责原则。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成。
- 缺点:
- 不能保证每个请求一定被处理。由于一个「请求没有明确的接收者」,该请求可能一直传到链的末端都得不到处理。
- 当责任链比较长的时候,请求的处理可能涉及多个处理对象,系统性能受影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会造成循环调用。
22.5、注意事项
- 将请求和处理分开,实现解耦,提高系统的灵活性。
- 简化了对象,使对象不需要知道链的结构。
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在
Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。 - 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、
Java Web中Tomcat对Encoding的处理、拦截器。

543

被折叠的 条评论
为什么被折叠?



