设计模式全解析

#设计模式全解析(持续更新中…):

概述:

  • java 语言面向对象的三大特征:封装、继承、多态。

    • 封装性
      • 封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义:
        • 把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位(即对象)。
        • 信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。
      • 封装的原则在软件上的反映是:要求使对象以外的部分不能随意存取对象的内部数据(属性),从而有效的避免了外部错误对它的”交叉感染”,使软件错误能够局部化,大大减少查错和排错的难度。
    • 继承性

      • 特殊类的对象拥有其一般类的全部属性与服务,称作特殊类对一般类的继承。一个类可以是多个一般类的特殊类,它从多个一般类中继承了属性与服务,这称为多继承。在java语言中,通常我们称一般类为父类(superclass,超类),特殊类为子类(subclass)。
    • 多态性

      • 对象的多态性是指在一般类中定义的属性或服务被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或服务在一般类及其各个特殊 类中具有不同的语义。例如:”几何图形”的”绘图”方法,”椭圆”和”多边形”都是”几何图”的子类,其”绘图”方法功能不同。
  • java 语言面向对象的六大原则

    • 开闭原则
    • 单一职责原则
    • 里氏替换原则
    • 依赖倒置原则
    • 接口隔离原则
    • 迪米特原则
  • 23种传统设计模式:(已经完成23种)

    • 1.单例设计模式 (完成)
    • 2.工厂方法模式 (完成)
    • 3.抽象工厂模式 (完成)
    • 4.模板方法模式 (完成)
    • 5.建造者模式 (完成)
    • 6.代理模式 (完成)
    • 7.原型模式(完成)
    • 8.中介者模式(完成)
    • 9.命令模式(完成)
    • 10.责任链模式(完成)
    • 11.装饰模式 (完成)
    • 12.策略模式 (完成)
    • 13.适配器模式(完成)
    • 14.迭代器模式(完成)
    • 15.组合模式(完成)
    • 16.观察者模式(完成)
    • 17.门面模式(完成)
    • 18.备忘录模式(完成)
    • 19.访问者模式(完成)
    • 20.状态模式(完成)
    • 21.解释器模式(完成)
    • 22.享元模式(完成)
    • 23.桥梁模式(完成)
  • 23种传统设计模式PK
    • 创建类模式包含工厂方法模式、建造者模式,抽象工厂模式,单例模式和原型模式(5种),他们都能提供对象的创建和管理职责。其中单例模式和原型模式比较容易理解,单例模式是要在内存中只有一个对象,原型模式是要通过复制的方式产生一个新的对象,这两个不容易混淆,剩下的就是工厂方法模式、抽象工厂模式和建造者模式了。这三个之间有较多的相似性。
    • 结构类模式包括适配器模式,桥梁模式,组合模式,装饰模式,门面模式,享元模式和代理模式(7种),为什么叫结构类模式呢,因为他们都是通过组合类或者对象产生更大结构以适应更高层次的逻辑需求的。
    • 行为类模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式(11种)。
  • 23种传统设计模式混编
    • 比如说:
      • 命令模式 + 责任链模式
      • 工厂方法模式 + 策略模式
      • 观察者模式 + 中介者模式
  • 新的设计模式
    • 规格模式、对象池模式、雇工模式、黑板模式、空对象模式…

设计模式各个击破

1.单例设计模式

  • 保证类在内存中只有一个对象。
  • 如何保证类在内存中只有一个对象呢?
    • 控制类的创建,不让其他类来创建本类的对象,也就是私有化构造;
    • 在本类中定义本类的对象;
    • 提供公共的访问方式;
  • 单例的几种写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
饿汉式:直接new出对象,这种就是以空间换时间
//饿汉式
class Singleton {
//1,私有构造函数
private Singleton(){}
//2,创建本类对象
private static Singleton s = new Singleton();
//3,对外提供公共的访问方法
public static Singleton getInstance() {
return s;
}
public static void print() {
System.out.println("饿汉式的方法");
}
}
(2)懒汉式 这种方式 多线程的问题?
//懒汉式,单例的延迟加载模式
class Singleton {
//1,私有构造函数
private Singleton(){}
//2,声明一个本类的引用
private static Singleton s;
//3,对外提供公共的访问方法
public static Singleton getInstance() {
if(s == null)
//线程1,线程2(可能某个对象在wait())
s = new Singleton();
return s;
}
public static void print() {
System.out.println("懒汉式的方法");
}
}
* 饿汉式和懒汉式的区别
* 1,饿汉式是空间换时间,懒汉式是时间换空间
* 2,在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象
*/
由于懒汉式存在线程安全的问题,所以我们可以这样做:
使用双重检测锁机制,先检测是否为空,如果为空再对该单例类加锁,再判断一次是否为空,这样既解决了线程的安全问题,也避免了每次使用单例都要加锁带来的系统资源的消耗。代码如下:
class Singleton {
//1,私有构造函数
private Singleton(){}
//2,声明一个本类的引用
private static volatile Singleton s;
//3,对外提供公共的访问方法
public static Singleton getInstance() {
if(s == null)
Synchronized(Singleton.class){
if(s == null) {
s = new Singleton();
}
}
return s;
}
public static void print() {
System.out.println("懒汉式的方法");
}
}
(3)第三种格式
class Singleton {
private Singleton() {}
public static final Singleton s = new Singleton();//final是最终的意思,被final修饰的变量不可以被更改
}

2.装饰设计模式

  • 顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Demo_Wrap {
/**
* @param args
* 装饰设计模式的好处是:
* 耦合性不强,被装饰的类的变化与装饰类的变化无关
*/
public static void main(String[] args) {
AdvanceStudent as = new AdvanceStudent(new Student());
as.code();
}
}
interface Coder {
public void code();
}
class Student implements Coder {
@Override
public void code() {
System.out.println("javase");
System.out.println("javaweb");
}
}
class AdvanceStudent implements Coder {
//1,获取被装饰类的引用
private Student s; //获取学生引用
//2,在构造方法中传入被装饰类的对象
public AdvanceStudent(Student s) {
this.s = s;
}
//3,对原有的功能进行升级
@Override
public void code() {
s.code();
System.out.println("ssh");
System.out.println("数据库");
System.out.println("大数据");
System.out.println("...");
}
}

3.简单工厂设计模式

  • 简单工厂模式概述
    • 又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
  • 优点
    • 客户端不需要在负责对象的创建,从而明确了各个类的职责
  • 缺点
    • 这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
  • 案例演示
    • 动物抽象类:public abstract Animal { public abstract void eat(); }
    • 具体狗类:public class Dog extends Animal {}
    • 具体猫类:public class Cat extends Animal {}
    • 开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      //动物抽象类
      public abstract class Animal {
      public abstract void eat();
      }
      //猫类
      public class Cat extends Animal {
      @Override
      public void eat() {
      System.out.println("猫吃鱼");
      }
      }
      //狗类
      public class Dog extends Animal {
      @Override
      public void eat() {
      System.out.println("狗吃肉");
      }
      }
      //动物工厂类
      public class AnimalFactory {
      /*public static Dog createDog() {
      return new Dog();
      }
      public static Cat createCat() {
      return new Cat();
      }*/
      //发现方法会定义很多,复用性太差
      //改进
      public static Animal createAnimal(String name) {
      if("dog".equals(name)) {
      return new Dog();
      }else if("cat".equals(name)) {
      return new Cat();
      }else {
      return null;
      }
      }
      }
      //测试类
      public class Test {
      /**
      * @param args
      */
      public static void main(String[] args) {
      //Dog d = AnimalFactory.createDog();
      Dog d = (Dog) AnimalFactory.createAnimal("dog");
      d.eat();
      Cat c = (Cat) AnimalFactory.createAnimal("cat");
      c.eat();
      }
      }

3.工厂方法设计模式

  • 工厂方法模式概述
    • 工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
  • 优点
    • 客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
  • 缺点
    • 需要额外的编写代码,增加了工作量
  • 案例演示
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    * 动物抽象类:public abstract Animal { public abstract void eat(); }
    * 工厂接口:public interface Factory {public abstract Animal createAnimal();}
    * 具体狗类:public class Dog extends Animal {}
    * 具体猫类:public class Cat extends Animal {}
    开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。发现每次修改代码太麻烦,用工厂方法改进,针对每一个具体的实现提供一个具体工厂。
    * 狗工厂:public class DogFactory implements Factory {
    public Animal createAnimal() {…}
    }
    * 猫工厂:public class CatFactory implements Factory {
    public Animal createAnimal() {…}
    }
    //动物抽象类
    public abstract class Animal {
    public abstract void eat();
    }
    //工厂接口
    public interface Factory {
    public Animal createAnimal();
    }
    //猫工厂类
    public class CatFactory implements Factory {
    @Override
    public Animal createAnimal() {
    return new Cat();
    }
    }
    //猫类
    public class Cat extends Animal {
    @Override
    public void eat() {
    System.out.println("猫吃鱼");
    }
    }
    //狗工厂类
    public class DogFactory implements Factory {
    @Override
    public Animal createAnimal() {
    return new Dog();
    }
    }
    //狗类
    public class Dog extends Animal {
    @Override
    public void eat() {
    System.out.println("狗吃肉");
    }
    }
    //测试类
    public class Test {
    /**
    * @param args
    */
    public static void main(String[] args) {
    DogFactory df = new DogFactory();
    Dog d = (Dog) df.createAnimal();
    d.eat();
    }
    }

4.抽象工厂设计模式

  • 定义:提供一个创建一系列相关或者相互依赖产品的接口, 而无需制定他们的具体类,一对多的关系。抽象工厂模式是 工厂方法模式的升级版。
  • 例子: 我们接着前边土豪的故事继续讲。话说这个土豪还有一个爱好,就是打猎。但是土豪打猎是有要求的(毕竟土豪嘛,要就就得高一点),他如果坐Audi车去打猎,那么他就一定要使用AK47这把枪(这是去打猎吗?);如果他坐Benz车去打猎那么他就一定要用M4A1这把枪,如果按照我们前边讲的工厂方法模式来编程,那么应该是建立一个Car的抽象工厂类CarFactory,然后Benz车的工厂继承自这个抽象的父类并实现生产Benz车的方法,Audi车的工厂继承自这个抽象的父类并实现生产Audi车的方法。并且还要有一个生产Gun的抽象工厂类,由它的具体子类工厂来实现生产AK47和M4A1。这样做是非常麻烦的,我们已经知道了如果土豪做Audi的话那他一定是使用AK47,所以我们可以使用一个工厂来同时生产Audi车和AK47,注意我说的前提是我们已经知道了土豪一定是Audi车和AK47一起使用的,如果不满足这个条件的话是不能使用抽象工厂模式来解决这个问题的。
  • 优点:即符合面向对象设计的“开闭原则”和“单一职责原 则”。又减少了系统中的类的数量,不用像工厂方法一样每生 产一个产品都要有一个具体的工厂类。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    /**
    * 仅仅定义生产同一族产品的两个不同等级结构的产品接口,具体的实现由子类工厂来实现
    * @Mr.Wu
    *
    */
    public abstract class AbstractFactory {
    public abstract Car getCar();
    public abstract Gun getGun();
    }
    /**
    * Audi车的工厂同时生产Audi车和配套的AK47
    * @Mr.Wu
    *
    */
    public class AudiFactory extends AbstractFactory{
    public Car getCar() {
    return new Audi();
    }
    public Gun getGun() {
    return new AK47();
    }
    }
    /**
    * Benz车的工厂同时生产Benz车和配套的M4A1
    * @Mr.Wu
    *
    */
    public class BenzFactory extends AbstractFactory{
    public Car getCar() {
    return new Benz();
    }
    public Gun getGun() {
    return new M4A1();
    }
    }
    //gun的抽象类
    public abstract class Gun {
    abstract void fire();
    }
    //M4A1的具体类
    public class M4A1 extends Gun{
    public M4A1(){
    System.out.println("Create an M4A1");
    }
    public void fire(){
    System.out.println("M4A1 start fire");
    }
    }
    //AK47的类
    public class AK47 extends Gun{
    public AK47(){
    System.out.println("Create an AK47");
    }
    public void fire(){
    System.out.println("AK47 start fire");
    }
    }
    //车的抽象类
    public abstract class Car {
    abstract void drive();
    }
    //奥迪车的类
    public class Audi extends Car{
    public Audi(){
    System.out.println("Create a Audi");
    }
    public void drive(){
    System.out.println("Audi start engine");
    }
    }
    //奔驰车的类
    public class Benz extends Car{
    public Benz(){
    System.out.println("Create a Benz");
    }
    public void drive(){
    System.out.println("Benz start engine");
    }
    }
    //测试类
    public class Main {
    public static void main(String[] args) throws Exception {
    //奔驰车司机
    AbstractFactory factory = new BenzFactory();
    //今天想做奥迪车
    Car car = factory.getCar();
    //开车
    car.drive();
    //获得开Benz时要用的枪
    Gun gun = factory.getGun();
    //开火
    gun.fire();
    }
    }

说明:当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。

5.享元设计模式

  • 解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象。如果有一个1M的文本,那么字母是何其的多,如果每个字母都定义一个对象那么内存早就爆了。那么如果要是每个字母都共享一个对象,那么就大大节约了资源。

  • 在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)来存放内部状态的对象。Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多,下面举个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
      先定义一个抽象的Flyweight类:
    package Flyweight;
    public abstract class Flyweight{
     public abstract void operation();
    }
    实现一个具体类:
    package Flyweight;
    public class ConcreteFlyweight extends Flyweight{
     private String string;
     public ConcreteFlyweight(String str){
      string = str;
     }
     public void operation()
     {
      System.out.println("Concrete---Flyweight : " + string);
     }
    }
    实现一个工厂方法类:
    package Flyweight;
    import java.util.Hashtable;
    public class FlyweightFactory{
     private Hashtable flyweights = new Hashtable();//----------------------------1
     public FlyweightFactory(){}
     public Flyweight getFlyWeight(Object obj){
      Flyweight flyweight = (Flyweight) flyweights.get(obj);//----------------2
      if(flyweight == null){//---------------------------------------------------3
       //产生新的ConcreteFlyweight
       flyweight = new ConcreteFlyweight((String)obj);
       flyweights.put(obj, flyweight);//--------------------------------------5
      }
      return flyweight;//---------------------------------------------------------6
     }
     public int getFlyweightSize(){
      return flyweights.size();
     }
    }
    这个工厂方法类非常关键,这里详细解释一下:
      在1处定义了一个Hashtable用来存储各个对象;在2处选出要实例化的对象,在6处将该对象返回,如果在Hashtable中没有要选择的对象,此时变量flyweight为null,产生一个新的flyweight存储在Hashtable中,并将该对象返回。
      最后看看Flyweight的调用:
    package Flyweight;
    import java.util.Hashtable;
    public class FlyweightPattern{
     FlyweightFactory factory = new FlyweightFactory();
     Flyweight fly1;
     Flyweight fly2;
     Flyweight fly3;
     Flyweight fly4;
     Flyweight fly5;
     Flyweight fly6;
     /** *//** Creates a new instance of FlyweightPattern */
     public FlyweightPattern(){
      fly1 = factory.getFlyWeight("Google");
      fly2 = factory.getFlyWeight("Qutr");
      fly3 = factory.getFlyWeight("Google");
      fly4 = factory.getFlyWeight("Google");
      fly5 = factory.getFlyWeight("Google");
      fly6 = factory.getFlyWeight("Google");
     }
     public void showFlyweight(){
      fly1.operation();
      fly2.operation();
      fly3.operation();
      fly4.operation();
      fly5.operation();
      fly6.operation();
      int objSize = factory.getFlyweightSize();
      System.out.println("objSize = " + objSize);
     }
     public static void main(String[] args){
      System.out.println("The FlyWeight Pattern!");
      FlyweightPattern fp = new FlyweightPattern();
      fp.showFlyweight();
     }
    }
    下面是运行结果:
    Concrete---Flyweight : Google
    Concrete---Flyweight : Qutr
    Concrete---Flyweight : Google
    Concrete---Flyweight : Google
    Concrete---Flyweight : Google
    Concrete---Flyweight : Google
    objSize = 2
     我们定义了6个对象,其中有5个是相同的,按照Flyweight模式的定义“Google”应该共享一个对象,在实际的对象数中我们可以看出实际的对象却是只有2个。
    总结:
      Flyweight(享元)模式是如此的重要,因为它能帮你在一个复杂的系统中大量的节省内存空间。在Java语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。
    熟悉java的应该知道下面这个例子:
    String a = "hello";
    String b = "hello";
    if(a == b)
     System.out.println("OK");
    else
     System.out.println("Error");
    输出结果是:OK。可以看出if条件比较的是两a和b的地址,也可以说是内存空间
    核心总结,可以共享的对象,也就是说返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。通常工厂类会有一个集合类型的成员变量来用以保存对象,如hashtable,vector等。在java中,数据库连接池,线程池等即是用享元模式的应用。

6.适配器设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Demo_Adapter {
/**
* @param args
* 适配器设计模式
* 鲁智深
*/
public static void main(String[] args) {
鲁智深 鲁智深 = new 鲁智深();
鲁智深.习武();
}
}
interface 和尚 {
public void 打坐();
public void 念经();
public void 撞钟();
public void 习武();
}
abstract class 天罡星 implements 和尚 { //声明成抽象的原因是,不想让其他类创建本类对象,因为创建也没有意义,方法都是空的
@Override
public void 打坐() {
}
@Override
public void 念经() {
}
@Override
public void 撞钟() {
}
@Override
public void 习武() {
}
}
class 鲁智深 extends 天罡星 {
public void 习武() {
System.out.println("倒拔垂杨柳");
System.out.println("拳打镇关西");
System.out.println("大闹野猪林");
System.out.println("......");
}
}

7.模板方法设计模式

  • 模版设计模式概述
    • 模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。
  • 优点和缺点
    • 优点:使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
    • 缺点:如果算法骨架有修改的话,则需要修改抽象类
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      public class Demo1_Template {
      /**
      * @param args
      */
      public static void main(String[] args) {
      /*long start = System.currentTimeMillis();
      for(int i = 0; i < 1000000; i++) {
      System.out.println("x");
      }
      long end = System.currentTimeMillis();
      System.out.println(end - start);*/
      Demo d = new Demo();
      System.out.println(d.getTime());
      }
      }
      abstract class GetTime {
      public final long getTime() {
      long start = System.currentTimeMillis();
      code();
      long end = System.currentTimeMillis();
      return end - start;
      } //final修饰,目的是不让被重写
      public abstract void code(); //由子类取实现想测试的代码
      }
      class Demo extends GetTime {
      @Override
      public void code() {
      int i = 0;
      while(i < 100000) {
      System.out.println("x");
      i++;
      }
      }
      }

8.观察者设计模式(有时又被称为发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)

  • 定义:又名发布—订阅模式,对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  • 组成部分:

    • 抽象目标(被观察者)角色(Subject):目标角色知道它的观察者,可以 有任意多个观察者观察同一个目标。并且提供注册和删除观 察者对象、通知观察者的方法。目标角色往往由抽象类或者 接口来实现。

    • 抽象观察者角色(Observer):为那些在目标发生改变时需
      要获得通知的对象定义一个更新接口,里面有一个更新数据 的方法。抽象观察者角色主要由抽象类或者接口来实现。

    • 具体目标(被观察者)角色(Concrete Subject):实现Subject,实现添 加、删除观察者、通知观察者的方法,当它的状态发生改变 时, 向它的各个观察者发出通知。
    • 具体观察者角色(Concrete Observer):实现Observer的更 新接口以使自身状态与目标的状态保持一致,获取通知进行 更新。
  • 观察者模式中,一个被观察者管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

  • 举例:珠宝商运送一批钻石,有黄金强盗准备抢劫,珠宝商雇佣了私人保镖,警察局也派人护送,于是当运输车上路的时候,强盗保镖警察都要观察运输车一举一动,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
抽象的观察者
public interface Watcher
{
public void update();
}
抽象的被观察者,在其中声明方法(添加、移除观察者,通知观察者):
public interface Watched
{
public void addWatcher(Watcher watcher);
public void removeWatcher(Watcher watcher);
public void notifyWatchers();
}
具体的观察者
保镖
public class Security implements Watcher
{
@Override
public void update()
{
System.out.println(“运输车有行动,保安贴身保护");
}
}
强盗
public class Thief implements Watcher
{
@Override
public void update()
{
System.out.println(“运输车有行动,强盗准备动手");
}
}
警察
public class Police implements Watcher
{
@Override
public void update()
{
System.out.println(“运输车有行动,警察护航");
}
}
具体的被观察者
public class Transporter implements Watched
{
private List<Watcher> list = new ArrayList<Watcher>();
@Override
public void addWatcher(Watcher watcher)
{
list.add(watcher);
}
@Override
public void removeWatcher(Watcher watcher)
{
list.remove(watcher);
}
@Override
public void notifyWatchers(String str)
{
for (Watcher watcher : list)
{
watcher.update();
}
}
}
测试类
public class Test
{
public static void main(String[] args)
{
Transporter transporter = new Transporter();
Police police = new Police();
Security security = new Security();
Thief thief = new Thief();
transporter.addWatcher(police);
transporter.addWatcher(security);
transporter.addWatcher(security);
transporter.notifyWatchers();
}
}
  • 我推你拉例子中没有关于数据和状态的变化通知,只是简单通知到各个观察者,告诉他们被观察者有行动。
    观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本。
    • 一种情况便是目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式——就是说变化的信息是观察者角色主动从目标角色中“拉”出来的。
    • 还有一种方法,那就是我目标角色“服务一条龙”,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是“推模式”——管你要不要,先给你啦。
  • 这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适啦。

9.代理设计模式

  • 什么是代理模式:代理模式的作用是,为其他对象提供一种代理以控制对这个对象的访问。
  • 好处:在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 代理模式一般涉及到的角色有:
    • 抽象角色:声明真实对象和代理对象的共同接口;
    • 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
    • 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
  • 应用场景举例:比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时是这样的体现的先说说这个场景中的要素:一种类型的女人,潘金莲,王婆,西门庆,后来扩展的贾氏也和西门庆勾上了,我们是假设的,然后西门庆找潘金莲happy,但潘金莲不好意思直接,就找个王婆代理呗。我们看看具体代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    先定义一种女人
    /**
    *
    * @author Mr.Wu
    *定义一种类型的女人,王婆和潘金莲都属于这个类型的女人
    */
    public interface KindWoman {
    //这种女人能做什么事情呢?
    public void makeEyesWithMan();//抛媚眼
    public void happyWithMan();//和男人那个....
    }
    一种类型嘛,那肯定是接口,定义个潘金莲
    /**
    *
    * @author Mr.Wu
    *定义一个潘金莲是什么样的人
    */
    public class PanJinLian implements KindWoman{
    @Override
    public void happyWithMan() {
    System.out.println("潘金莲和男人在做那个...");
    }
    @Override
    public void makeEyesWithMan() {
    System.out.println("潘金莲抛媚眼...");
    }
    }
    再定义个丑陋的王婆
    /**
    *
    * @author Mr.Wu
    *王婆这个人老聪明了,她太老了,是个男人都看不上她,
    *但是她有智慧经验呀,他作为一类女人的代理!
    */
    public class WangPo implements KindWoman {
    private KindWoman kindWoman;
    public WangPo(){
    //默认的话是潘金莲的代理
    this.kindWoman = new PanJinLian();
    }
    //她可以是KindWomam的任何一个女人的代理,只要你是这一类型
    public WangPo(KindWoman kindWoman){
    this.kindWoman = kindWoman;
    }
    @Override
    public void happyWithMan() {
    //自己老了,干不了了,但可以叫年轻的代替。
    this.kindWoman.happyWithMan();
    }
    @Override
    public void makeEyesWithMan() {
    //王婆年纪大了,谁看她抛媚眼啊
    this.kindWoman.makeEyesWithMan();
    }
    }
    两个女主角都上场了,该男主角了,定义个西门庆
    /**
    *
    * @author Mr.Wu
    *水浒传是这样写的:西门庆被潘金莲用竹竿敲了一下,西门庆看痴迷了,被王婆看到了,就开始撮合两人好事,王婆作为潘金莲的代理人收了不少好处费,那我们假设一下:
    *如果没有王婆在中间牵线,这两个不要脸的能成事吗?难说得很!
    */
    public class XiMenQing {
    /**
    * @param args
    */
    public static void main(String[] args) {
    WangPo wangPo;
    //把王婆叫出来
    wangPo = new WangPo();
    //然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏:
    wangPo.makeEyesWithMan();
    //看到没有表面是王婆在做,其实爽的是潘金莲
    wangPo.happyWithMan();
    }
    }
    那这就是活生生的一个例子,通过代理人实现了某种目的,如果真去了王婆这个中间环节,直接西门庆和潘金莲勾搭,估计很难成就武松杀嫂事件。
    那我们再考虑一下,水浒里面还有没有这类型的女人?有,卢俊义的老婆贾氏(就是和那个管家苟合的那个),这个名字起的:“贾氏”,那我们也让王婆做她的代理:
    /**
    *
    * @author Mr.Wu
    *定义一个贾氏是什么样的人
    */
    public class JiaShi implements KindWoman {
    @Override
    public void happyWithMan() {
    System.out.println("贾氏和男人在做那个...");
    }
    @Override
    public void makeEyesWithMan() {
    System.out.println("贾氏抛媚眼...");
    }
    }
    西门庆勾潘金莲又勾引贾氏
    /**
    *
    * @author Mr.Wu
    *水浒传是这样写的:西门庆被潘金莲用竹竿敲了一下,西门庆看痴迷了,被王婆看到了,就开始撮合两人好事,王婆作为潘金莲的代理人收了不少好处费,那我们假设一下:
    *如果没有王婆在中间牵线,这两个不要脸的能成事吗?难说得很!
    */
    public class XiMenQing {
    /**
    * @param args
    */
    public static void main(String[] args) {
    WangPo wangPo;
    //把王婆叫出来
    wangPo = new WangPo();
    //然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏:
    wangPo.makeEyesWithMan();
    //看到没有表面是王婆在做,其实爽的是潘金莲
    wangPo.happyWithMan();
    //西门庆勾引贾氏
    JiaShi jiaShi = new JiaShi();
    wangPo = new WangPo(jiaShi);
    wangPo.makeEyesWithMan();
    wangPo.happyWithMan();
    }
    }

    说完这个故事,那我总结一下,代理模式主要使用了java的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚得很,同样一个接口呗。

10.策略设计模式

  • 背景
    • 在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。
    • 这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。
  • 问题

    • 如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?
  • 方案

    • 把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。
    • 比如定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换,使得算法可独立于使用它的客户而变化。这就是策略模式。
  • 适用情况

    • 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
    • 当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。
    • 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
  • 优点
    • 1、可以动态的改变对象的行为
  • 缺点

    • 1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类
    • 2、策略模式将造成产生很多策略类
  • 组成

    • 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
    • 抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
    • 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
  • 应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    场景如下,刘备要到江东娶老婆了,走之前诸葛亮给赵云三个锦囊妙计,说是按天机拆开能解决棘手问题。场景中出现三个要素:三个妙计(具体策略类)、一个锦囊(环境类)、赵云(调用者)。
    抽象策略类(Strategy)
    public interface IStrategy {
    public void operate();
    }
    三个实现类(ConcreteStrategy):
    妙计一:初到吴国
    public class BackDoor implements IStrategy {
    @Override
    public void operate() {
    System.out.println("找乔国老帮忙,让吴国太给孙权施加压力,使孙权不能杀刘备");
    }
    }
    妙计二:求吴国太开绿灯放行
    public class GivenGreenLight implements IStrategy {
    @Override
    public void operate() {
    System.out.println("求吴国太开个绿灯,放行");
    }
    }
    妙计三:孙夫人断后,挡住追兵
    public class BlackEnemy implements IStrategy {
    @Override
    public void operate() {
    System.out.println("孙夫人断后,挡住追兵");
    }
    }
    环境类(Context)
    public class Context {
    private Strategy strategy;
    //构造函数,要你使用哪个妙计
    public Context(Strategy strategy){
    this.strategy = strategy;
    }
    public void setStrategy(Strategy strategy){
    this.strategy = strategy;
    }
    public void operate(){
    this.strategy.operate();
    }
    }
    下面就是使用的情况了
    public class Zhaoyun {
    public static void main(String[] args) {
    Context context;
    System.out.println("----------刚到吴国使用第一个锦囊---------------");
    context = new Context(new BackDoor());
    context.operate();
    System.out.println("\n");
    System.out.println("----------刘备乐不思蜀使用第二个锦囊---------------");
    context.setStrategy(new GivenGreenLight());
    context.operate();
    System.out.println("\n");
    System.out.println("----------孙权的追兵来了,使用第三个锦囊---------------");
    context.setStrategy(new BlackEnemy());
    context.operate();
    System.out.println("\n");
    }
    }
    }
    三招下来,搞得的周郎是“赔了夫人又折兵”。
    以上就是策略模式,多种不同解决方案动态切换,起到改变对象行为的效果。

11.建造者设计模式

  • 概念

    • 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 [构建与表示分离,同构建不同表示]
    • 与抽象工厂的区别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造模式可以强制实行一种分步骤进行的建造过程。
    • 建造模式是将复杂的内部创建封装在内部,对于外部调用的人来说,只需要传入建造者和建造工具,对于内部是如何建造成成品的,调用者无需关心。
    • 举个简单的例子,如汽车,有很多部件,车轮,方向盘,发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一部汽车,这个装配过程也很复杂(需要很好的组装技术), builder模式就是为了将部件和组装分开。
  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    public interface Builder {
        void buildPartA();
        void buildPartB();
        void buildPartC();
      
        Product getResult();
      }
    //具体建造工具
      public class ConcreteBuilder implements Builder {
        Part partA, partB, partC;
        public void buildPartA() {
          //这里是具体如何构建partA的代码
        };
        public void buildPartB() {
          //这里是具体如何构建partB的代码
        };
         public void buildPartC() {
          //这里是具体如何构建partB的代码
        };
         public Product getResult() {
          //返回最后组装成品结果
        };
      }
    //指导者
      public class Director {
        private Builder builder;
      
        public Director( Builder builder ) {
          this.builder = builder;
        }
        public void construct() {
          builder.buildPartA();
          builder.buildPartB();
          builder.buildPartC();
        }
      }
      public interface Product { }
      public interface Part { }
      
    下面是调用builder的方法:
      ConcreteBuilder builder = new ConcreteBuilder();
      Director director = new Director( builder );
      
      director.construct();
      Product product = builder.getResult();
  • 应用场景

    • 在Java的应用中JavaMail使用到了该模式,android中的Dialog使用到该模式

12.原型模式

  • 概念:原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:
    • 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
    • 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
    • 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。

-实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Prototype implements Cloneable {
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
for(int i=0; i< 10; i++){
ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
clonecp.show();
}
}
}

  • 优点:

    • 性能优良
      • 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一
      • 个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
    • 逃避构造函数的约束
      • 这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
  • 使用场景

    • 资源优化场景
    • 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
    • 性能和安全要求的场景
    • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
    • 一个对象多个修改者的场景
      • 一个对象需要提供给其他对象访问,丽且各个调用者可能都需要修改其值时,可以考虑 使用原型模式拷贝多个对象供调用者使用。
    • 在实际项日中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式己经与Java融为一体,大家可以随手拿来使用。
  • 注意事项
    • 使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
    • 深拷贝与浅拷贝。Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型byte,char,short,int,long,float,double,boolean),对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Prototype implements Cloneable {
private ArrayList list = new ArrayList();
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
prototype.list = (ArrayList) this.list.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
- 由于ArrayList不是基本类型,所以成员变量list,不会被拷贝,需要我们自己实现深拷贝,幸运的是Java提供的大部分的容器类都实现了Cloneable接口。所以实现深拷贝并不是特别困难。
- 关于深拷贝和浅拷贝,会发生深拷贝的是java 的 8种基本数据类型和他们的封装类,至于String这个类型需要注意,它是引用数据类型,所以是浅拷贝,

13.中介者模式

  • 定义

    • 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。
  • 角色

    • 抽象中介者:定义好同事类对象到中介者对象的接口,用于各个同事类之间的通信。一般包括一个或几个抽象的事件方法,并由子类去实现。
    • 中介者实现类:从抽象中介者继承而来,实现抽象中介者中定义的事件方法。从一个同事类接收消息,然后通过消息影响其他同时类。
    • 同事类:如果一个对象会影响其他的对象,同时也会被其他对象影响,那么这两个对象称为同事类。在类图中,同事类只有一个,这其实是现实的省略,在实际应用中,同事类一般由多个组成,他们之间相互影响,相互依赖。同事类越多,关系越复杂。并且,同事类也可以表现为继承了同一个抽象类的一组实现组成。在中介者模式中,同事类之间必须通过中介者才能进行消息传递。
  • 适用情况

    • 一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如有六个同事类对象,假如对象1发生变化,会有4个对象受到影响。如果对象2发生变化,那么会有5个对象受到影响。也就是说,同事类之间直接关联的设计是不好的。如果引入中介者模式,那么同事类之间的关系将变为星型结构,任何一个类的变动,只会影响的类本身,以及中介者,这样就减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为。
  • 应用

    • 我们使用一个例子来说明一下什么是同事类:有两个类A和B,类中各有一个数字,并且要保证类B中的数字永远是类A中数字的100倍。也就是说,当修改类A的数时,将这个数字乘以100赋给类B,而修改类B时,要将数除以100赋给类A。类A类B互相影响,就称为同事类。代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      ---常规写法开始---
      abstract class AbstractColleague {
      protected int number;
      public int getNumber() {
      return number;
      }
      public void setNumber(int number){
      this.number = number;
      }
      //抽象方法,修改数字时同时修改关联对象
      public abstract void setNumber(int number, AbstractColleague coll);
      }
      class ColleagueA extends AbstractColleague{
      public void setNumber(int number, AbstractColleague coll) {
      this.number = number;
      coll.setNumber(number*100);
      }
      }
      class ColleagueB extends AbstractColleague{
      public void setNumber(int number, AbstractColleague coll) {
      this.number = number;
      coll.setNumber(number/100);
      }
      }
      public class Client {
      public static void main(String[] args){
      AbstractColleague collA = new ColleagueA();
      AbstractColleague collB = new ColleagueB();
      System.out.println("==========设置A影响B==========");
      collA.setNumber(1288, collB);
      System.out.println("collA的number值:"+collA.getNumber());
      System.out.println("collB的number值:"+collB.getNumber());
      System.out.println("==========设置B影响A==========");
      collB.setNumber(87635, collA);
      System.out.println("collB的number值:"+collB.getNumber());
      System.out.println("collA的number值:"+collA.getNumber());
      }
      }
      ---常规写法结束---
      上面的代码中,类A类B通过直接的关联发生关系,假如我们要使用中介者模式,类A类B之间则不可以直接关联,他们之间必须要通过一个中介者来达到关联的目的。
      ---中介者模式开始---
      同事类
      abstract class AbstractColleague {
      protected int number;
      public int getNumber() {
      return number;
      }
      public void setNumber(int number){
      this.number = number;
      }
      //注意这里的参数不再是同事类,而是一个中介者
      public abstract void setNumber(int number, AbstractMediator am);
      }
      class ColleagueA extends AbstractColleague{
      public void setNumber(int number, AbstractMediator am) {
      this.number = number;
      am.AaffectB();
      }
      }
      class ColleagueB extends AbstractColleague{
      @Override
      public void setNumber(int number, AbstractMediator am) {
      this.number = number;
      am.BaffectA();
      }
      }
      抽象中介者类
      abstract class AbstractMediator {
      protected AbstractColleague A;
      protected AbstractColleague B;
      public AbstractMediator(AbstractColleague a, AbstractColleague b) {
      A = a;
      B = b;
      }
      public abstract void AaffectB();
      public abstract void BaffectA();
      }
      中介者类
      class Mediator extends AbstractMediator {
      public Mediator(AbstractColleague a, AbstractColleague b) {
      super(a, b);
      }
      //处理A对B的影响
      public void AaffectB() {
      int number = A.getNumber();
      B.setNumber(number*100);
      }
      //处理B对A的影响
      public void BaffectA() {
      int number = B.getNumber();
      A.setNumber(number/100);
      }
      }
      客户端
      public class Client {
      public static void main(String[] args){
      AbstractColleague collA = new ColleagueA();
      AbstractColleague collB = new ColleagueB();
      AbstractMediator am = new Mediator(collA, collB);
      System.out.println("==========通过设置A影响B==========");
      collA.setNumber(1000, am);
      System.out.println("collA的number值为:"+collA.getNumber());
      System.out.println("collB的number值为A的10倍:"+collB.getNumber());
      System.out.println("==========通过设置B影响A==========");
      collB.setNumber(1000, am);
      System.out.println("collB的number值为:"+collB.getNumber());
      System.out.println("collA的number值为B的0.1倍:"+collA.getNumber());
      }
      }
      ---中介者模式结束---
  • 优点

    • 适当地使用中介者模式可以避免同事类之间的过度耦合,使得各同事类之间可以相对独立地使用。
    • 使用中介者模式可以将对象间一对多的关联转变为一对一的关联,使对象间的关系易于理解和维护。
    • 使用中介者模式可以将对象的行为和协作进行抽象,能够比较灵活的处理对象间的相互作用。
  • 总结

    • 在面向对象编程中,一个类必然会与其他的类发生依赖关系,完全独立的类是没有意义的。一个类同时依赖多个类的情况也相当普遍,既然存在这样的情况,说明,一对多的依赖关系有它的合理性,适当的使用中介者模式可以使原本凌乱的对象关系清晰,但是如果滥用,则可能会带来反的效果。一般来说,只有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。可以将网状结构变为星状结构,使同事类之间的关系变的清晰一些。
    • 中介者模式是一种比较常用的模式,也是一种比较容易被滥用的模式。对于大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构,因此,大多数情况下,将对象间的依赖关系封装的同事类内部就可以的,没有必要非引入中介者模式。滥用中介者模式,只会让事情变的更复杂。

14.命令模式

  • 定义

    • 将来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化。用于“行为请求者”与“行为实现者”解耦,可实现二者之间的松耦合,以便适应变化。分离变化与不变的因素。
  • 角色

    • Command 定义命令的接口,声明执行的方法。
    • ConcreteCommand 命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
    • Receiver 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
    • Invoker 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
    • Client 创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
  • 优点

    • 1.降低对象之间的耦合度。
    • 2.新的命令可以很容易地加入到系统中。
    • 3.可以比较容易地设计一个组合命令。
    • 4.调用同一方法实现不同的功能
  • 缺点

    • 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
  • 适用情况

    • 1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
    • 2.系统需要在不同的时间指定请求、将请求排队和执行请求。
    • 3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
    • 4.系统需要将一组操作组合在一起,即支持宏命令。
  • 应用

    • 模拟对电视机的操作有开机、关机、换台命令。代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      //执行命令的接口
      public interface Command {
        void execute();
      }
      //命令接收者Receiver
      public class Tv {
        public int currentChannel = 0;
        public void turnOn() {
         System.out.println("The televisino is on.");
        }
        public void turnOff() {
         System.out.println("The television is off.");
        }
        public void changeChannel(int channel) {
         this.currentChannel = channel;
         System.out.println("Now TV channel is " + channel);
        }
      }
      //开机命令ConcreteCommand
      public class CommandOn implements Command {
        private Tv myTv;
        public CommandOn(Tv tv) {
         myTv = tv;
        }
        public void execute() {
         myTv.turnOn();
        }
      }
      //关机命令ConcreteCommand
      public class CommandOff implements Command {
        private Tv myTv;
        public CommandOff(Tv tv) {
         myTv = tv;
        }
        public void execute() {
         myTv.turnOff();
        }
      }
      //频道切换命令ConcreteCommand
      public class CommandChange implements Command {
        private Tv myTv;
        private int channel;
        public CommandChange(Tv tv, int channel) {
         myTv = tv;
         this.channel = channel;
        }
        public void execute() {
         myTv.changeChannel(channel);
        }
      }
      //可以看作是遥控器Invoker
      public class Control {
        private Command onCommand, offCommand, changeChannel;
        public Control(Command on, Command off, Command channel) {
         onCommand = on;
         offCommand = off;
         changeChannel = channel;
        }
        public void turnOn() {
         onCommand.execute();
        }
        public void turnOff() {
         offCommand.execute();
        }
        public void changeChannel() {
         changeChannel.execute();
        }
      }
      //测试类Client
      public class Client {
        public static void main(String[] args) {
         // 命令接收者Receiver
         Tv myTv = new Tv();
         // 开机命令ConcreteCommond
         CommandOn on = new CommandOn(myTv);
         // 关机命令ConcreteCommond
         CommandOff off = new CommandOff(myTv);
         // 频道切换命令ConcreteCommond
         CommandChange channel = new CommandChange(myTv, 2);
         // 命令控制对象Invoker
         Control control = new Control(on, off, channel);
         // 开机
         control.turnOn();
         // 切换频道
         control.changeChannel();
         // 关机
         control.turnOff();
        }
      }
      // 执行结果
      The televisino is on.
      Now TV channel is 2
      The television is off.
  • 总结

    • 1.命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
    • 2.每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
    • 3.命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
    • 4.命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
    • 5.命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

15.责任链模式

  • 什么是链

    • 1、链是一系列节点的集合。
    • 2.、链的各节点可灵活拆分再重组。
  • 职责链模式

    • 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,
    • 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
  • 角色

    • 抽象处理者角色(Handler):定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。
    • 具体处理者角色(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
  • 代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    抽象处理者角色
    public abstract class Handler {
    /**
    * 持有后继的责任对象
    */
    protected Handler successor;
    /**
    * 示意处理请求的方法,虽然这个示意方法是没有传入参数的
    * 但实际是可以传入参数的,根据具体需要来选择是否传递参数
    */
    public abstract void handleRequest();
    /**
    * 取值方法
    */
    public Handler getSuccessor() {
    return successor;
    }
    /**
    * 赋值方法,设置后继的责任对象
    */
    public void setSuccessor(Handler successor) {
    this.successor = successor;
    }
    }
    具体处理者角色
    public class ConcreteHandler extends Handler {
    /**
    * 处理方法,调用此方法处理请求
    */
    @Override
    public void handleRequest() {
    /**
    * 判断是否有后继的责任对象
    * 如果有,就转发请求给后继的责任对象
    * 如果没有,则处理请求
    */
    if(getSuccessor() != null)
    {
    System.out.println("放过请求");
    getSuccessor().handleRequest();
    }else
    {
    System.out.println("处理请求");
    }
    }
    }
    客户端类
    public class Client {
    public static void main(String[] args) {
    //组装责任链
    Handler handler1 = new ConcreteHandler();
    Handler handler2 = new ConcreteHandler();
    handler1.setSuccessor(handler2);
    //提交请求
    handler1.handleRequest();
    }
    }
    可以看出,客户端创建了两个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,而第二个处理者对象没有下家。然后客户端将请求传递给第一个处理者对象。
    由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理。
    因此,第一个处理者对象接到请求后,会将请求传递给第二个处理者对象。由于第二个处理者对象没有下家,于是自行处理请求。
  • 应用

    • 申请聚餐费用的管理,申请聚餐费用的大致流程一般是,由申请人先填写申请单,然后交给领导审批,如果申请批准下来,领导会通知申请人审批通过,然后申请人去财务领取费用,如果没有批准下来,领导会通知申请人审批未通过,此事也就此作罢。不同级别的领导,对于审批的额度是不一样的,比如,项目经理只能审批500元以内的申请;部门经理能审批1000元以内的申请;而总经理可以审核任意额度的申请。当某人提出聚餐费用申请的请求后,该请求会经由项目经理、部门经理、总经理之中的某一位领导来进行相应的处理,但是提出申请的人并不知道最终会由谁来处理他的请求,一般申请人是把自己的申请提交给项目经理,或许最后是由总经理来处理他的请求。申请人只要直接与项目经理交互就可以,其余的工作在黑盒中,究竟流程是怎样的,最后是由谁审批通过的,申请人无需关心。代码如下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      抽象处理者角色类
      public abstract class Handler {
      /**
      * 持有下一个处理请求的对象
      */
      protected Handler successor = null;
      /**
      * 取值方法
      */
      public Handler getSuccessor() {
      return successor;
      }
      /**
      * 设置下一个处理请求的对象
      */
      public void setSuccessor(Handler successor) {
      this.successor = successor;
      }
      /**
      * 处理聚餐费用的申请
      * @param user 申请人
      * @param fee 申请的钱数
      * @return 成功或失败的具体通知
      */
      public abstract String handleFeeRequest(String user , double fee);
      }
      具体处理者角色
      public class ProjectManager extends Handler {
      @Override
      public String handleFeeRequest(String user, double fee) {
      String str = "";
      //项目经理权限比较小,只能在500以内
      if(fee < 500)
      {
      //为了测试,简单点,只同意张三的请求
      if("张三".equals(user))
      {
      str = "成功:项目经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }else
      {
      //其他人一律不同意
      str = "失败:项目经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }
      }else
      {
      //超过500,继续传递给级别更高的人处理
      if(getSuccessor() != null)
      {
      return getSuccessor().handleFeeRequest(user, fee);
      }
      }
      return str;
      }
      }
      public class DeptManager extends Handler {
      @Override
      public String handleFeeRequest(String user, double fee) {
      String str = "";
      //部门经理的权限只能在1000以内
      if(fee < 1000)
      {
      //为了测试,简单点,只同意张三的请求
      if("张三".equals(user))
      {
      str = "成功:部门经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }else
      {
      //其他人一律不同意
      str = "失败:部门经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }
      }else
      {
      //超过1000,继续传递给级别更高的人处理
      if(getSuccessor() != null)
      {
      return getSuccessor().handleFeeRequest(user, fee);
      }
      }
      return str;
      }
      }
      public class GeneralManager extends Handler {
      @Override
      public String handleFeeRequest(String user, double fee) {
      String str = "";
      //总经理的权限很大,只要请求到了这里,他都可以处理
      if(fee >= 1000)
      {
      //为了测试,简单点,只同意张三的请求
      if("张三".equals(user))
      {
      str = "成功:总经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }else
      {
      //其他人一律不同意
      str = "失败:总经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
      }
      }else
      {
      //如果还有后继的处理对象,继续传递
      if(getSuccessor() != null)
      {
      return getSuccessor().handleFeeRequest(user, fee);
      }
      }
      return str;
      }
      }
      客户端类
      public class Client {
      public static void main(String[] args) {
      //先要组装责任链
      Handler h1 = new GeneralManager();
      Handler h2 = new DeptManager();
      Handler h3 = new ProjectManager();
      h3.setSuccessor(h2);
      h2.setSuccessor(h1);
      //开始测试
      String test1 = h3.handleFeeRequest("张三", 300);
      System.out.println("test1 = " + test1);
      String test2 = h3.handleFeeRequest("李四", 300);
      System.out.println("test2 = " + test2);
      System.out.println("---------------------------------------");
      String test3 = h3.handleFeeRequest("张三", 700);
      System.out.println("test3 = " + test3);
      String test4 = h3.handleFeeRequest("李四", 700);
      System.out.println("test4 = " + test4);
      System.out.println("---------------------------------------");
      String test5 = h3.handleFeeRequest("张三", 1500);
      System.out.println("test5 = " + test5);
      String test6 = h3.handleFeeRequest("李四", 1500);
      System.out.println("test6 = " + test6);
      }
      }
  • 职责链灵活在哪

      1. 改变内部的传递规则
        • 在内部,项目经理完全可以跳过人事部到那一关直接找到总经理。
        • 每个人都可以去动态地指定他的继任者。
      1. 可以从职责链任何一关开始。
        • 如果项目经理不在,可以直接去找部门经理,责任链还会继续,没有影响。
    • 3.用与不用的区别
      • 不用职责链的结构,我们需要和公司中的每一个层级都发生耦合关系。
      • 如果反映在代码上即使我们需要在一个类中去写上很多丑陋的if….else语句。
      • 如果用了职责链,相当于我们面对的是一个黑箱,我们只需要认识其中的一个部门,然后让黑箱内部去负责传递就好了
  • 纯的与不纯的责任链模式

    • 一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又 把责任向下传的情况。
    • 在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
    • 纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。

      16.迭代器模式

  • 定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
  • 类型:行为类模式
  • 如果要问Java中使用最多的一种模式,答案不是单例模式,也不是工厂模式,更不是策略模式,而是迭代器模式,先来看一段代码吧:
1
2
3
4
5
6
7
8
9
public static void print(Collection coll){
Iterator it = coll.iterator();
while(it.hasNext()){
String str = (String)it.next();
System.out.println(str);
}
}
这个方法的作用是循环打印一个字符串集合,里面就用到了迭代器模式,java语言已经完整地实现了迭代器模式,Iterator翻译成汉语就是迭代器的意思。提到迭代器,首先它是与集合相关的,集合也叫聚集、容器等,我们可以将集合看成是一个可以包容对象的容器,例如List,Set,Map,甚至数组都可以叫做集合,而迭代器的作用就是把容器中的对象一个一个地遍历出来。
  • 迭代器模式的结构
    • 抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
    • 具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
    • 抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),取得下一个元素的方法next(),判断是否遍历结束的方法isDone()(或者叫hasNext()),移出当前对象的方法remove(),
    • 迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。
  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    interface Iterator {
    public Object next();
    public boolean hasNext();
    }
    class ConcreteIterator implements Iterator{
    private List list = new ArrayList();
    private int cursor =0;
    public ConcreteIterator(List list){
    this.list = list;
    }
    public boolean hasNext() {
    if(cursor==list.size()){
    return false;
    }
    return true;
    }
    public Object next() {
    Object obj = null;
    if(this.hasNext()){
    obj = this.list.get(cursor++);
    }
    return obj;
    }
    }
    interface Aggregate {
    public void add(Object obj);
    public void remove(Object obj);
    public Iterator iterator();
    }
    class ConcreteAggregate implements Aggregate {
    private List list = new ArrayList();
    public void add(Object obj) {
    list.add(obj);
    }
    public Iterator iterator() {
    return new ConcreteIterator(list);
    }
    public void remove(Object obj) {
    list.remove(obj);
    }
    }
    public class Client {
    public static void main(String[] args){
    Aggregate ag = new ConcreteAggregate();
    ag.add("小明");
    ag.add("小红");
    ag.add("小刚");
    Iterator it = ag.iterator();
    while(it.hasNext()){
    String str = (String)it.next();
    System.out.println(str);
    }
    }
    }
    上面的代码中,Aggregate是容器类接口,大家可以想象一下Collection,List,Set等,Aggregate就是他们的简化版,容器类接口中主要有三个方法:添加对象方法add、删除对象方法remove、取得迭代器方法iterator。Iterator是迭代器接口,主要有两个方法:取得迭代对象方法next,判断是否迭代完成方法hasNext,大家可以对比java.util.List和java.util.Iterator两个接口自行思考。
  • 迭代器模式的优缺点

    • 迭代器模式的优点有:
      • 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
      • 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
      • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
    • 迭代器模式的缺点:
      • 对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。
  • 迭代器模式的适用场景

    • 迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了。

17.组合模式

  • 组合模式的定义吧:“将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。”就拿剪发办卡的事情来分析一下吧。首先,一张卡可以在总部,分店,加盟店使用,那么总部可以刷卡,分店也可以刷卡,加盟店也可以刷卡,这个属性结构的店面层级关系就明确啦。那么,总店刷卡消费与分店刷卡消费是一样的道理,那么总店与分店对会员卡的使用也具有一致性。

    • 组合模式的一个例子:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      import java.util.ArrayList;
      import java.util.List;
      public class ComponentDemo {
      public abstract class Component {
      String name;
      public abstract void add(Component c);
      public abstract void remove(Component c);
      public abstract void eachChild();
      }
      // 组合部件类
      public class Leaf extends Component {
      // 叶子节点不具备添加的能力,所以不实现
      @Override
      public void add(Component c) {
      // TODO Auto-generated method stub
      System.out.println("");
      }
      // 叶子节点不具备添加的能力必然也不能删除
      @Override
      public void remove(Component c) {
      // TODO Auto-generated method stub
      System.out.println("");
      }
      // 叶子节点没有子节点所以显示自己的执行结果
      @Override
      public void eachChild() {
      // TODO Auto-generated method stub
      System.out.println(name + "执行了");
      }
      }
      // 组合类
      public class Composite extends Component {
      // 用来保存节点的子节点
      List<Component> list = new ArrayList<Component>();
      // 添加节点 添加部件
      @Override
      public void add(Component c) {
      // TODO Auto-generated method stub
      list.add(c);
      }
      // 删除节点 删除部件
      @Override
      public void remove(Component c) {
      // TODO Auto-generated method stub
      list.remove(c);
      }
      // 遍历子节点
      @Override
      public void eachChild() {
      // TODO Auto-generated method stub
      System.out.println(name + "执行了");
      for (Component c : list) {
      c.eachChild();
      }
      }
      }
      public static void main(String[] args) {
      ComponentDemo demo = new ComponentDemo();
      // 构造根节点
      Composite rootComposite = demo.new Composite();
      rootComposite.name = "根节点";
      // 左节点
      Composite compositeLeft = demo.new Composite();
      compositeLeft.name = "左节点";
      // 构建右节点,添加两个叶子几点,也就是子部件
      Composite compositeRight = demo.new Composite();
      compositeRight.name = "右节点";
      Leaf leaf1 = demo.new Leaf();
      leaf1.name = "右-子节点1";
      Leaf leaf2 = demo.new Leaf();
      leaf2.name = "右-子节点2";
      compositeRight.add(leaf1);
      compositeRight.add(leaf2);
      // 左右节点加入 根节点
      rootComposite.add(compositeRight);
      rootComposite.add(compositeLeft);
      // 遍历组合部件
      rootComposite.eachChild();
      }
      }
      //执行结果如下:
      根节点执行了
      右节点执行了
      右-子节点1执行了
      右-子节点2执行了
      左节点执行了
  • 2.应用组合模式的会员卡消费

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    那么我们就根据我们会员卡的消费,来模拟一下组合模式的实现吧!let's Go!
    首先:
    1.我们的部件有,总店,分店,加盟店!
    2.我们的部件共有的行为是:刷会员卡
    3.部件之间的层次关系,也就是店面的层次关系是,总店下有分店、分店下可以拥有加盟店。
    有了我们这几个必要条件后,我的要求就是目前店面搞活动当我在总店刷卡后,就可以累积相当于在所有下级店面刷卡的积分总额,设计的代码如下:
    import java.util.ArrayList;
    import java.util.List;
    public class PayDemo {
    public abstract class Market {
    String name;
    public abstract void add(Market m);
    public abstract void remove(Market m);
    public abstract void PayByCard();
    }
    // 分店 下面可以有加盟店
    public class MarketBranch extends Market {
    // 加盟店列表
    List<Market> list = new ArrayList<PayDemo.Market>();
    public MarketBranch(String s) {
    this.name = s;
    }
    @Override
    public void add(Market m) {
    // TODO Auto-generated method stub
    list.add(m);
    }
    @Override
    public void remove(Market m) {
    // TODO Auto-generated method stub
    list.remove(m);
    }
    // 消费之后,该分店下的加盟店自动累加积分
    @Override
    public void PayByCard() {
    // TODO Auto-generated method stub
    System.out.println(name + "消费,积分已累加入该会员卡");
    for (Market m : list) {
    m.PayByCard();
    }
    }
    }
    // 加盟店 下面不在有分店和加盟店,最底层
    public class MarketJoin extends Market {
    public MarketJoin(String s) {
    this.name = s;
    }
    @Override
    public void add(Market m) {
    // TODO Auto-generated method stub
    }
    @Override
    public void remove(Market m) {
    // TODO Auto-generated method stub
    }
    @Override
    public void PayByCard() {
    // TODO Auto-generated method stub
    System.out.println(name + "消费,积分已累加入该会员卡");
    }
    }
    public static void main(String[] args) {
    PayDemo demo = new PayDemo();
    MarketBranch rootBranch = demo.new MarketBranch("总店");
    MarketBranch qhdBranch = demo.new MarketBranch("秦皇岛分店");
    MarketJoin hgqJoin = demo.new MarketJoin("秦皇岛分店一海港区加盟店");
    MarketJoin btlJoin = demo.new MarketJoin("秦皇岛分店二白塔岭加盟店");
    qhdBranch.add(hgqJoin);
    qhdBranch.add(btlJoin);
    rootBranch.add(qhdBranch);
    rootBranch.PayByCard();
    }
    }
    运行结果如下:
    总店消费,积分已累计加入该会员卡
    秦皇岛分店消费,积分已累加入该会员卡
    秦皇岛分店一海港区加盟店消费,积分已累加入该会员卡
    秦皇岛分店二白塔岭加盟店,积分已加入该会员卡
    这样在累积所有子店面积分的时候,就不需要去关心子店面的个数了,也不用关系是否是叶子节点还是组合节点了,也就是说不管是总店刷卡,还是加盟店刷卡,都可以正确有效的计算出活动积分。
  • 3.什么情况下使用组合模式

    • 引用大话设计模式的片段:“当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了。”

18.门面模式(外观模式)

-引言:医院的例子

- 现代的软件系统都是比较复杂的,设计师处理复杂系统的一个常见方法便是将其“分而治之”,把一个系统划分为几个较小的子系统。如果把医院作为一个子系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情。首先病人必须先挂号,然后门诊。如果医生要求化验,病人必须首先划价,然后缴费,才可以到化验部门做化验。化验后再回到门诊室。
- 解决这种不便的方法便是引进门面模式,医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。这个接待员就是门面模式的体现,病人只接触接待员,由接待员与各个部门打交道。
  • 门面模式的结构

    • 门面模式没有一个一般化的类图描述,最好的描述方法实际上就是以一个例子说明。
    • 由于门面模式的结构图过于抽象,因此把它稍稍具体点。假设子系统内有三个模块,分别是ModuleA、ModuleB和ModuleC,它们分别有一个示例方法,出现了两个角色:
      • 门面(Facade)角色 :客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
      • 子系统(SubSystem)角色 :可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合(如上面的子系统就是由ModuleA、ModuleB、ModuleC三个类组合而成)。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
    • 源代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      子系统角色中的类:
      public class ModuleA {
      //示意方法
      public void testA(){
      System.out.println("调用ModuleA中的testA方法");
      }
      }
      public class ModuleB {
      //示意方法
      public void testB(){
      System.out.println("调用ModuleB中的testB方法");
      }
      }
      public class ModuleC {
      //示意方法
      public void testC(){
      System.out.println("调用ModuleC中的testC方法");
      }
      }
      门面角色类:
      public class Facade {
      //示意方法,满足客户端需要的功能
      public void test(){
      ModuleA a = new ModuleA();
      a.testA();
      ModuleB b = new ModuleB();
      b.testB();
      ModuleC c = new ModuleC();
      c.testC();
      }
      }
      客户端角色类:
      public class Client {
      public static void main(String[] args) {
      Facade facade = new Facade();
      facade.test();
      }
      }
      Facade类其实相当于A、B、C模块的外观界面,有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。
  • 门面模式的实现

    • 使用门面模式还有一个附带的好处,就是能够有选择性地暴露方法。一个模块中定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部模块之间相互调用时使用的。有了Facade类,那么用于子系统内部模块之间相互调用的方法就不用暴露给子系统外部了。比如,定义如下A、B、C模块。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class Module {
/**
* 提供给子系统外部使用的方法
*/
public void a1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
private void a2(){};
private void a3(){};
}
public class ModuleB {
/**
* 提供给子系统外部使用的方法
*/
public void b1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
private void b2(){};
private void b3(){};
}
public class ModuleC {
/**
* 提供给子系统外部使用的方法
*/
public void c1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
private void c2(){};
private void c3(){};
}
public class ModuleFacade {
ModuleA a = new ModuleA();
ModuleB b = new ModuleB();
ModuleC c = new ModuleC();
/**
* 下面这些是A、B、C模块对子系统外部提供的方法
*/
public void a1(){
a.a1();
}
public void b1(){
b.b1();
}
public void c1(){
c.c1();
}
}
这样定义一个ModuleFacade类可以有效地屏蔽内部的细节,免得客户端去调用Module类时,发现一些不需要它知道的方法。比如a2()和a3()方法就不需要让客户端知道,否则既暴露了内部的细节,又让客户端迷惑。对客户端来说,他可能还要去思考a2()、a3()方法用来干什么呢?其实a2()和a3()方法是内部模块之间交互的,原本就不是对子系统外部的,所以干脆就不要让客户端知道。
  • 一个系统可以有几个门面类
      - 在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统都有一个门面类,整个系统可以有数个门面类。为子系统增加新行为
      - 初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。比如医院中的接待员并不是医护人员,接待员并不能为病人提供医疗服务。

  • 门面模式的优点:

    • 松散耦合

   - 门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。

  - 简单易用

   - 门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。

  - 更好的划分访问层次

   - 通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

19.备忘录模式

  • 定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态
    • 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态。比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回。这时我们便可以使用备忘录模式来实现。
  • 备忘录模式的结构
    • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
    • 备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
    • 管理角色:对备忘录进行管理,保存和提供备忘录。
  • 通用代码实现
    -单状态单备份备忘录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    class Originator {
    private String state = "";
    public String getState() {
    return state;
    }
    public void setState(String state) {
    this.state = state;
    }
    public Memento createMemento(){
    return new Memento(this.state);
    }
    public void restoreMemento(Memento memento){
    this.setState(memento.getState());
    }
    }
    class Memento {
    private String state = "";
    public Memento(String state){
    this.state = state;
    }
    public String getState() {
    return state;
    }
    public void setState(String state) {
    this.state = state;
    }
    }
    class Caretaker {
    private Memento memento;
    public Memento getMemento(){
    return memento;
    }
    public void setMemento(Memento memento){
    this.memento = memento;
    }
    }
    public class Client {
    public static void main(String[] args){
    Originator originator = new Originator();
    originator.setState("状态1");
    System.out.println("初始状态:"+originator.getState());
    Caretaker caretaker = new Caretaker();
    caretaker.setMemento(originator.createMemento());
    originator.setState("状态2");
    System.out.println("改变后状态:"+originator.getState());
    originator.restoreMemento(caretaker.getMemento());
    System.out.println("恢复后状态:"+originator.getState());
    }
    }
    代码演示了一个单状态单备份的例子,逻辑非常简单:Originator类中的state变量需要备份,以便在需要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的临时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。
    多状态多备份备忘录
    通用代码演示的例子中,Originator类只有一个state变量需要备份,而通常情况下,发起人角色通常是一个javaBean,对象中需要备份的变量不止一个,需要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法很多,备忘录模式有很多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。其实实现多状态多备份也很简单,最常用的方法是,我们在Memento中增加一个Map容器来存储所有的状态,在Caretaker类中同样使用一个Map容器才存储所有的备份。下面我们给出一个多状态多备份的例子:
    class Originator {
    private String state1 = "";
    private String state2 = "";
    private String state3 = "";
    public String getState1() {
    return state1;
    }
    public void setState1(String state1) {
    this.state1 = state1;
    }
    public String getState2() {
    return state2;
    }
    public void setState2(String state2) {
    this.state2 = state2;
    }
    public String getState3() {
    return state3;
    }
    public void setState3(String state3) {
    this.state3 = state3;
    }
    public Memento createMemento(){
    return new Memento(BeanUtils.backupProp(this));
    }
    public void restoreMemento(Memento memento){
    BeanUtils.restoreProp(this, memento.getStateMap());
    }
    public String toString(){
    return "state1="+state1+"state2="+state2+"state3="+state3;
    }
    }
    class Memento {
    private Map<String, Object> stateMap;
    public Memento(Map<String, Object> map){
    this.stateMap = map;
    }
    public Map<String, Object> getStateMap() {
    return stateMap;
    }
    public void setStateMap(Map<String, Object> stateMap) {
    this.stateMap = stateMap;
    }
    }
    class BeanUtils {
    public static Map<String, Object> backupProp(Object bean){
    Map<String, Object> result = new HashMap<String, Object>();
    try{
    BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
    PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
    for(PropertyDescriptor des: descriptors){
    String fieldName = des.getName();
    Method getter = des.getReadMethod();
    Object fieldValue = getter.invoke(bean, new Object[]{});
    if(!fieldName.equalsIgnoreCase("class")){
    result.put(fieldName, fieldValue);
    }
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    return result;
    }
    public static void restoreProp(Object bean, Map<String, Object> propMap){
    try {
    BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
    PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
    for(PropertyDescriptor des: descriptors){
    String fieldName = des.getName();
    if(propMap.containsKey(fieldName)){
    Method setter = des.getWriteMethod();
    setter.invoke(bean, new Object[]{propMap.get(fieldName)});
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    class Caretaker {
    private Map<String, Memento> memMap = new HashMap<String, Memento>();
    public Memento getMemento(String index){
    return memMap.get(index);
    }
    public void setMemento(String index, Memento memento){
    this.memMap.put(index, memento);
    }
    }
    class Client {
    public static void main(String[] args){
    Originator ori = new Originator();
    Caretaker caretaker = new Caretaker();
    ori.setState1("中国");
    ori.setState2("强盛");
    ori.setState3("繁荣");
    System.out.println("===初始化状态===\n"+ori);
    caretaker.setMemento("001",ori.createMemento());
    ori.setState1("软件");
    ori.setState2("架构");
    ori.setState3("优秀");
    System.out.println("===修改后状态===\n"+ori);
    ori.restoreMemento(caretaker.getMemento("001"));
    System.out.println("===恢复后状态===\n"+ori);
    }
    }
  • 备忘录模式的优缺点和适用场景

    • 备忘录模式的优点有:
      • 当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。
    • 备忘录模式的缺点:
      • 在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。
        -如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

20.访问者模式

  • 定义

    • 封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class A {
      public void method1(){
      System.out.println("我是A");
      }
      public void method2(B b){
      b.showA(this);
      }
      }
      class B {
      public void showA(A a){
      a.method1();
      }
      }
  • 看一下在类A中,方法method1和方法method2的区别在哪里,方法method1很简单,就是打印出一句“我是A”;方法method2稍微复杂一点,使用类B作为参数,并调用类B的showA方法。再来看一下类B的showA方法,showA方法使用类A作为参数,然后调用类A的method1方法,可以看到,method2方法绕来绕去,无非就是调用了一下自己的method1方法而已,它的运行结果应该也是“我是A”,分析完之后,我们来运行一下这两个方法,并看一下运行结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Test {
    public static void main(String[] args){
    A a = new A();
    a.method1();
    a.method2(new B());
    }
    }
    运行结果为:
    我是A
    我是A
  • 角色

    • 在例子中,对于类A来说,类B就是一个访问者。
    • 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
    • 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
    • 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
    • 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
    • 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      抽象元素类
      abstract class Element {
      public abstract void accept(IVisitor visitor);
      public abstract void doSomething();
      }
      元素类
      class ConcreteElement1 extends Element {
      public void doSomething(){
      System.out.println("这是元素1");
      }
      public void accept(IVisitor visitor) {
      visitor.visit(this);
      }
      }
      class ConcreteElement2 extends Element {
      public void doSomething(){
      System.out.println("这是元素2");
      }
      public void accept(IVisitor visitor) {
      visitor.visit(this);
      }
      }
      抽象访问者
      interface IVisitor {
      public void visit(ConcreteElement1 el1);
      public void visit(ConcreteElement2 el2);
      }
      访问者
      class Visitor implements IVisitor {
      public void visit(ConcreteElement1 el1) {
      el1.doSomething();
      }
      public void visit(ConcreteElement2 el2) {
      el2.doSomething();
      }
      }
      结构对象
      class ObjectStruture {
      public static List<Element> getList(){
      List<Element> list = new ArrayList<Element>();
      Random ran = new Random();
      for(int i=0; i<10; i++){
      int a = ran.nextInt(100);
      if(a>50){
      list.add(new ConcreteElement1());
      }else{
      list.add(new ConcreteElement2());
      }
      }
      return list;
      }
      }
      客户端
      public class Client {
      public static void main(String[] args){
      List<Element> list = ObjectStruture.getList();
      for(Element e: list){
      e.accept(new Visitor());
      }
      }
      }
  • 优点

    • 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
    • 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。
  • 缺点

    • 增加新的元素类比较困难。通过访问者模式的代码可以看到,在访问者类中,每一个元素类都有它对应的处理方法,也就是说,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,一个项目的基本功能已经确定下来,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。
  • 适用场景

    • 假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。
    • 假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。

21.状态模式

  • 状态(state)模式的定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类

  • 状态模式(State)适用性

    • 1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。

    • 2.一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。

      • 这个状态通常用一个或多个枚举常量表示。

      • 通常,有多个操作包含这一相同的条件结构。

      • State模式将每一个条件分支放入一个独立的类中。

      • 这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

  • 状态模式(State)的参与者

    • 1.Context

      • 定义客户感兴趣的接口。

      • 维护一个ConcreteState子类的实例,这个实例定义当前状态。

    • 2.State

      - 定义一个接口以封装与Context的一个特定状态相关的行为。
      
    • 3.ConcreteStatesubclasses

      - 每一子类实现一个与Context的一个状态相关的行为。
      

      

  • 具体代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    定义State
    //定义和Context中的状态相对应的行为
    public interface State {
    //获取天气情况
    String getState();
    }
    定义Context
    //定义当前的状态
    public class Context {
    private State state;
    public State getState() {
    return state;
    }
    public void setState(State state) {
    this.state = state;
    }
    public String stateMessage(){
    return state.getState();
    }
    }
    定义ConcreteStatesubclasses
    class Sunshine implements State{
    @Override
    public String getState() {
    return "晴天";
    }
    }
    class Rain implements State{
    @Override
    public String getState() {
    return "下雨";
    }
    }
    测试一下
    public class StateTest {
    public static void main(String args[]){
    Context context=new Context();
    context.setState(new Rain());
    System.out.println(context.stateMessage());
    context.setState(new Sunshine());
    System.out.println(context.stateMessage());
    }
    }
    运行结果:
    下雨
    晴天
  • 接下来我们用Java编程思想中的一个例子来讲解一下状态模式

    • 我们学习了多态,看起来似乎所有的东西都可以去继承,因为多态是一个如此巧妙的工具。事实上,当我们使用现成的类建立新类时,如果首先考虑使用继承技术,反倒会加重我们的设计负担,使得事情变得复杂起来。
    • 更好的设计思想是首先选择”组合”,尤其是不能十分确定应该使用哪一种方式的时候。组合不会强制我们的程序设计进入继承的层次结构中。而且,组合更加灵活,因为它可以动态选择类型(因此也就选择了行为),想法,继承在编译时就需要知道确定的类型,下面是具体代码体现:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      //相当于状态模式中的state
      class Actor {
      public void act(){}
      }
      //相当于状态模式中的ConcreteStateSubclassess
      class HappyActor extends Actor{
      public void act(){
      System.out.println("HappyActor");
      }
      }
      class SadActor extends Actor{
      public void act(){
      System.out.println("SadActor");
      }
      }
      //相当于状态模式中的Context
      class Stage{
      private Actor actor=new HappyActor();
      //改变引用actor的指向的具体类型
      public void change(){
      actor=new SadActor();
      }
      //根据状态的不同执行不同的行为
      public void performPlay(){
      actor.act();
      }
      }
      测试一下:
      public class Transmogrify {
      /**
      * @param args
      */
      public static void main(String[] args) {
      Stage stage=new Stage();
      stage.performPlay();
      stage.change();
      stage.performPlay();
      }
      }
      运行结果:
      HappyActor
      SadActor
  • 程序分析:在这里,Stage对象包含一个对Actor的引用,而Actor被初始化为HappyActor对象,这意味着performPlay()会产生某种特殊行为。既然引用在运行时可以和另一个不同的对象重新绑定起来,SadActor对象的引用可以在actor中被替换,然后由performPlay()产生的行为也随之改变,这样一来,我们在运行期间获得了动态灵活性。与此相反的是,我们不能在运行期间决定继承不同的对象,因为它要求在编译期间完全确定下来。

  • 大家是不是觉得状态模式和策略模式不是一样的吗?我们讲一下他们之间的区别和联系:

    • 状态模式和策略模式的区别和联系(取自知乎网友的回答,很精辟):
      • 区别:状态模式将各个状态所对应的操作分离开来,即对于不同的状态,由不同的子类实现具体操作,不同状态的切换由子类实现,当发现传入参数不是自己这个状态所对应的参数,则自己给Context类切换状态;而策略模式是直接依赖注入到Context类的参数进行选择策略,不存在切换状态的操作练习
      • 联系状态模式和策略模式都是为具有多种可能情形设计的模式,把不同的处理情形抽象为一个相同的接口,符合对扩展开放,对修改封闭的原则。还有就是,策略模式更具有一般性一些,在实践中,可以用策略模式来封装几乎任何类型的规则,只要在分析过程中听到需要在不同实践应用不同的业务规则,就可以考虑使用策略模式处理,在这点上策略模式是包含状态模式的功能的,策略模式是一个重要的设计模式。

22.解释器模式

  • 概述
    • 解释器模式是一种用得比较少的行为型模式.提供了一种解释语言的语法或表达式的方式.
    • 通过定义一个表达式接口,解释一个特定的上下文.
  • 定义

    • 给定一个语言,解释器模式可以定义出其文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子.

      1
      2
      3
      4
      5
      6
      7
      解释器中涉及到的文法,就是符合一定规则的语句结构.
      如 abcd…….cdef(ab开头,ef结尾,中间N个cd)中,根据N值的不同,可以得到不同的字符串如,abef,abcdef,abcdcdef….
      假设我们有如下推导式
      S ::= abA*ef
      A ::= cd
      其中 ::=表示推导,*表示闭包,意思是A可以有0~N个重复,S是初始符号,abef和cd是终结符号.
      像这样的从一个具体的符号出发,通过不断地应用一些产生式规则 从而生成一个字符串的集合,我们将描述这个集合的文法称为形式文法.
    • 给定一个语言(如由abcdef六个字符组成的字符串集合),定义它的文法的一种表示(S::=abA*ef,A::=cd)并定义一个解释器,解释器使用该表示来解释语言中的句子.

    • 其中的解释器类似一个翻译机
  • 使用场景

    • 某个简单的语言需要解释执行并且可以将该语言中的语句表示为一个抽象语法树的时候.
    • 在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,并构建解释器来解释该语句.
  • 其中涉及到的角色有:

    • AbstractExpression: 抽象表达式,声明一个抽象的解释操作父类,定义一个抽象的解释方法,具体的实现由子类解释器完成/
    • TerminalExpression: 终结符表达式,实现文法中与终结符有关的解释操作,文法中每一个终结符都有一个具体的终结表达式与之对应
    • NonterminalExpression: 非终结符表达式,实现文法中与非终结符有关的解释操作
    • Context: 上下文环境类,包含解释器之外的全局信息
    • Client: 客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.
  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    如下我们通过对算术表达式的解释来看一个解释器模式的实现,
    如表达式m+n+p中,如果我们使用解释器模式对该表达式进行解释,那么m,n,p代表的三个字母可以看成是终结符号,而+代表的运算符则可以看成是非终结符号
    首先建立抽象解释器表示数学运算
    public abstract class ArithmeticExpression {
    public abstract int interptet();
    }
    解释器中定义了interptet()方法,ArithmeticExpression有两个直接子类,NumExpression,和OperatorExpression
    建立NumExpression,对数字进行解释
    public class NumExpression extends ArithmeticExpression {
    private int num;
    public NumExpression(int _num) {
    num = _num;
    }
    @Override public int interptet() {
    return num;
    }
    }
    建立OperatorExpression,对运算符进行解释
    public abstract class OperatorExpression extends ArithmeticExpression {
    protected ArithmeticExpression mArithmeticExpression1,mArithmeticExpression2;
    public OperatorExpression(ArithmeticExpression _arithmeticExpression1,
    ArithmeticExpression _arithmeticExpression2) {
    mArithmeticExpression1 = _arithmeticExpression1;
    mArithmeticExpression2 = _arithmeticExpression2;
    }
    }
    AdditionExpression,OperatorExpression的直接子类,加法运算解释器
    public class AdditionExpression extends OperatorExpression {
    public AdditionExpression(ArithmeticExpression _arithmeticExpression1,
    ArithmeticExpression _arithmeticExpression2) {
    super(_arithmeticExpression1, _arithmeticExpression2);
    }
    @Override public int interptet() {
    return mArithmeticExpression1.interptet() + mArithmeticExpression2.interptet();
    }
    }
    新增业务逻辑处理类
    public class Calculator {
    protected Stack<ArithmeticExpression> mArithmeticExpressionStack = new Stack<>();
    public Calculator(String expression) {
    ArithmeticExpression arithmeticExpression1, arithmeticExpression2;
    String[] elements = expression.split(" ");
    for (int i = 0; i < elements.length; ++i) {
    switch (elements[i].charAt(0)) {
    case '+':
    arithmeticExpression1 = mArithmeticExpressionStack.pop();
    arithmeticExpression2 = new NumExpression(Integer.valueOf(elements[++i]));
    mArithmeticExpressionStack.push(
    new AdditionExpression(arithmeticExpression1, arithmeticExpression2));
    break;
    default:
    mArithmeticExpressionStack.push(new NumExpression(Integer.valueOf(elements[i])));
    break;
    }
    }
    }
    public int calculate() {
    return mArithmeticExpressionStack.pop().interptet();
    }
    }
    客户端调用
    // 解释计算123+124+125+126的运算结果
    Calculator calculator = new Calculator("123+124+125+126");
    Log.d(TAG, "setBtnClick: -->" + calculator.calculate());
  • 优缺点

    • 优点
      • 灵活性强,如上边的例子,当我们想对文法规则进行扩展延伸时,只需要增加相应的非终结符解释器,并在构建语法树的时候使用新增的解释器对象进行具体的解释即可.
    • 缺点
      • 因为每一条文法都可以对应至少一个解释器,会生成大量的类,导致后期维护困难,而且对应复杂的文法,构建语法树会显得异常繁琐.

实例代码

23.桥梁模式

  • 应用场景

    • 举个例子,铅笔和钢笔,都可以用来写字和画画。邮局可以邮寄信件和包裹,两者都有平邮和挂号邮寄两种功能。明白使用场景后,我们可以提取主要元素:事物和功能。
    • 可以这样定义:有一类事务的集合,{A1,A2,A3…},每个事物都具有功能F1,F2,F3。当然会有个功能的集合{F1,F2,F3…}。在计算机的世界里我们如何表示呢,代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class 信件{
      public void 平邮(){}
      public void 挂号(){}
      }
      public class 包裹{
      public void 平邮(){}
      public void 挂号(){}
      }
      每个事物是一个类,有m个事物,就有m个类,每个类有n个功能,一共下来有m*n个功能。显然,这种方法是不可取的。
  • 桥接模式概念

    • 桥接模式是关于怎么将抽象部分和它的实现部分分离,使它们都可以独立变化。像之前那种写法,会有大量的重复的代码。
  • 解决办法

    - 利用语义,对于平邮和挂号,它们是邮局发送的两种方式,所以一句话:邮局有发送功能,我们就可以先得到一个接口,代码如下:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package qj_1;
    public interface iPost {//邮局
    public void send();//发送功能
    }
    有了这个接口后,我们当然想着的是它的实现类了。
    //平邮实现类
    public class Pmail implements iPost{
    @Override
    public void send() {
    System.out.println("平邮发送。。");
    }
    }
    //挂号实现类
    public class Gmail implements iPost{
    @Override
    public void send() {
    System.out.println("挂号发送。。");
    }
    }
  • 以上我们解决了问题的一半:邮局有平邮和挂号两种功能。信件和包裹,是两种不同的事物,它们有相同的功能,当然也有不同的。对于它们共同的部分我们可以将其封装到一个类中,但是该类又不能代替一个具体的事物,所以我们可以将其定义为抽象类。

1
2
3
4
5
6
7
8
9
public abstract class AbstractThing {// 抽象类
private iPost post;// 多态成员
public AbstractThing(iPost obj) {
post = obj;
}
public void post() {// 平邮和挂号两种功能
post.send();
}
}
  • 该抽象类是桥接模式的核心。重点是在该类中定义了多态成员,发送的两种方式是信件和包裹共享的功能。接下的就很明确了,信件和包裹两个具体的事物去继承抽象类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //信件实现类
    public class Letter extends AbstractThing{
    public Letter(iPost obj) {
    super(obj);
    }
    //其他独有的变量和方法
    }
    //包裹实现类
    public class Parcel extends AbstractThing{
    public Parcel(iPost obj) {
    super(obj);
    }
    //其他独有的变量和方法
    }
    测试类代码如下:
    public class Test {
    public static void main(String[] str) {
    iPost post = new Gmail();
    Letter letter = new Letter(post);
    letter.post();
    }
    }
  • 分析总结

    • 从测试类中我们可以看出,先功能类中选择一种发送方式,然后再在事物类中选择一种具体的事物类,然后完成发送任务。所以可以总结,桥接模式完成的是一个多条件选择问题,假设有二维条件,分别有N1,N2个选择,桥接模式就是要求在一维的条件中选一个条件,再在二维的条件中选一个,完成有效的组合。这种情景在生活中很常见,比如,10件上衣10件裤子,你想搭配出一套衣服,你肯定会上衣摆出一排,在其中选一件,裤子摆出一排,在其中选一件。而不是衣服裤子混在一起挑。你从家里到长春,必须经过中转站北京,从家到北京好多路线和方式,从北京到长春同样很多路线,你肯定从家到北京的路线先选择一条,从北京到长春的路线选一条,完成从家到北京这个事。
  • 当需求发生变化时,桥接模式是如何满足的呢?

    - 当添加了新的事物,从抽象类中派生一个类即可。
    
    1
    2
    3
    4
    5
    6
    public class NewThing extends AbstractThing{
    public NewThing(iPost obj) {
    super(obj);
    }
    //其他独有的变量和方法
    }
  • 当添加了新的发送方式,从接口派生出一个类即可。

    1
    2
    3
    4
    5
    6
    public class NewMail implements iPost{
    @Override
    public void send() {
    System.out.println("新增的一种发送方式");
    }
    }

彩蛋:女孩儿与设计模式

形象的总结一下设计模式。

  • 1.SINGLETON 单例模式

    • 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

    • 俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)。

  • 2.FACTORY METHOD 工厂方法模式

    • 工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

    • 请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

  • 3.FACTORY 工厂模式

    • 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。

    • 追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory。

  • 4.BUILDER 建造模式

    • 建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

    • MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)

  • 5.PROTOTYPE 原型模式

    • 原型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

    • 跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。 原型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。

  • 6.ADAPTER 适配器模式

    • 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

    • 在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)。

  • 7.BRIDGE 桥梁模式

    • 桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

    • 早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了。

  • 8.COMPOSITE 合成模式

    • 合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

    • Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?

  • 9.DECORATOR 装饰模式

    • 装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。

    • Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

  • 10.FACADE 门面(外观)模式

    • 门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

    • 我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

  • 11.FLYWEIGHT 享元模式

    • 享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

    • 每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

  • 12.PROXY 代理模式

    • 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

    • 跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自己的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

  • 13.CHAIN OF RESPONSIBLEITY 责任链模式

    • 责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

    • 晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

  • 14.COMMAND 命令模式

    • 命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。

    • 俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”

  • 15.INTERPRETER 解释器模式

    • 解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

    • 俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。

  • 16.ITERATOR 迭代子模式

    • 迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

    • 我爱上了Mary,不顾一切的向她求婚。Mary:“想要我跟你结婚,得答应我的条件” 我:“什么条件我都答应,你说吧” Mary:“我看上了那个一克拉的钻石” 我:“我买,我买,还有吗?” Mary:“我看上了湖边的那栋别墅” 我:“我买,我买,还有吗?” Mary:“我看上那辆法拉利跑车” 我脑袋嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?” ……

  • 17.MEDIATOR 调停者模式

    • 调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

    • 四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

  • 18.MEMENTO 备忘录模式

    • 备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

    • 同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

  • 19.OBSERVER 观察者模式

    • 观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

    • 想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦。

  • 20.STATE 状态模式

    • 状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。

    • 跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

  • 21.STRATEGY 策略模式

    • 策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

    • 跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

  • 22.TEMPLATE METHOD 模板模式

    • 模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

    • 看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现)。

  • 23.VISITOR 访问者模式

    • 访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

    • 情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了。

坚持原创技术分享,您的支持将鼓励我继续创作!