设计模式介绍

news2025/1/11 21:43:11

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

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

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

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

A、创建模式(5种)
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

1 工厂模式
1.1 简单工厂模式
定义:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

举例:(我们举一个pizza工厂的例子)

pizza工厂一共生产三种类型的pizza:chesse,pepper,greak。通过工厂类(SimplePizzaFactory)实例化这三种类型的对象。类图如下:
在这里插入图片描述
工厂类的代码:

public class SimplePizzaFactory {
       public Pizza CreatePizza(String ordertype) {
              Pizza pizza = null;
              if (ordertype.equals("cheese")) {
                     pizza = new CheesePizza();
              } else if (ordertype.equals("greek")) {
                     pizza = new GreekPizza();
              } else if (ordertype.equals("pepper")) {
                     pizza = new PepperPizza();
              }
              return pizza;
       }
}

   
   

简单工厂存在的问题与解决方法: 简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以,从设计角度考虑,有一定的问题,如何解决?我们可以定义一个创建对象的抽象方法并创建多个不同的工厂类实现该抽象方法,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。这种方法也就是我们接下来要说的工厂方法模式。

1.2 工厂方法模式
定义:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

举例:(我们依然举pizza工厂的例子,不过这个例子中,pizza产地有两个:伦敦和纽约)。添加了一个新的产地,如果用简单工厂模式的的话,我们要去修改工厂代码,并且会增加一堆的if else语句。而工厂方法模式克服了简单工厂要修改代码的缺点,它会直接创建两个工厂,纽约工厂和伦敦工厂。类图如下:
在这里插入图片描述
OrderPizza中有个抽象的方法:

abstract Pizza createPizza();
两个工厂类继承OrderPizza并实现抽象方法:

public class LDOrderPizza extends OrderPizza {
       Pizza createPizza(String ordertype) {
              Pizza pizza = null;
              if (ordertype.equals("cheese")) {
                     pizza = new LDCheesePizza();
              } else if (ordertype.equals("pepper")) {
                     pizza = new LDPepperPizza();
              }
              return pizza;
       }
}
public class NYOrderPizza extends OrderPizza {
<span class="token return-type class-name">Pizza</span> <span class="token function">createPizza</span><span class="token punctuation">(</span><span class="token class-name">String</span> ordertype<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token class-name">Pizza</span> pizza <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

	<span class="token keyword">if</span> <span class="token punctuation">(</span>ordertype<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span><span class="token string">"cheese"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		pizza <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">NYCheesePizza</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ordertype<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span><span class="token string">"pepper"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		pizza <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">NYPepperPizza</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">return</span> pizza<span class="token punctuation">;</span>

<span class="token punctuation">}</span>

}

  • 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

、通过不同的工厂会得到不同的实例化的对象,PizzaStroe的代码如下:

public class PizzaStroe {
       public static void main(String[] args) {
              OrderPizza mOrderPizza;
              mOrderPizza = new NYOrderPizza();
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

解决了简单工厂模式的问题:增加一个新的pizza产地(北京),只要增加一个BJOrderPizza类:

public class BJOrderPizza extends OrderPizza {
       Pizza createPizza(String ordertype) {
              Pizza pizza = null;
              if (ordertype.equals("cheese")) {
                     pizza = new LDCheesePizza();
              } else if (ordertype.equals("pepper")) {
                     pizza = new LDPepperPizza();
              }
              return pizza;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

其实这个模式的好处就是,如果你现在想增加一个功能,只需做一个实现类就OK了,无需去改动现成的代码。这样做,拓展性较好!

工厂方法存在的问题与解决方法:客户端需要创建类的具体的实例。简单来说就是用户要订纽约工厂的披萨,他必须去纽约工厂,想订伦敦工厂的披萨,必须去伦敦工厂。 当伦敦工厂和纽约工厂发生变化了,用户也要跟着变化,这无疑就增加了用户的操作复杂性。为了解决这一问题,我们可以把工厂类抽象为接口,用户只需要去找默认的工厂提出自己的需求(传入参数),便能得到自己想要产品,而不用根据产品去寻找不同的工厂,方便用户操作。这也就是我们接下来要说的抽象工厂模式。

1.3 抽象工厂模式
定义:定义了一个接口用于创建相关或有依赖关系的对象族,而无需明确指定具体类。

举例:(我们依然举pizza工厂的例子,pizza工厂有两个:纽约工厂和伦敦工厂)。类图如下:
在这里插入图片描述
工厂的接口:

public interface AbsFactory {
       Pizza CreatePizza(String ordertype) ;
}

 
 
  • 1
  • 2
  • 3

工厂的实现:

public class LDFactory implements AbsFactory {
       @Override
       public Pizza CreatePizza(String ordertype) {
              Pizza pizza = null;
              if ("cheese".equals(ordertype)) {
                     pizza = new LDCheesePizza();
              } else if ("pepper".equals(ordertype)) {
                     pizza = new LDPepperPizza();
              }
              return pizza;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

PizzaStroe的代码如下:

public class PizzaStroe {
       public static void main(String[] args) {
              OrderPizza mOrderPizza;
              mOrderPizza = new OrderPizza("London");
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

解决了工厂方法模式的问题:在抽象工厂中PizzaStroe中只需要传入参数就可以实例化对象。

1.4 工厂模式适用的场合
大量的产品需要创建,并且这些产品具有共同的接口 。

1.5 三种工厂模式的使用选择
简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)

工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)

抽象工厂 :用来生产不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)

简单工厂的适用场合:只有伦敦工厂(只有这一个等级),并且这个工厂只生产三种类型的pizza:chesse,pepper,greak(固定产品)。

工厂方法的适用场合:现在不光有伦敦工厂,还增设了纽约工厂(仍然是同一等级结构,但是支持了产品的拓展),这两个工厂依然只生产三种类型的pizza:chesse,pepper,greak(固定产品)。

抽象工厂的适用场合:不光增设了纽约工厂(仍然是同一等级结构,但是支持了产品的拓展),这两个工厂还增加了一种新的类型的pizza:chinese pizza(增加产品族)。

所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。因此,我们可以用抽象工厂模式创建工厂,而用工厂方法模式创建生产线。比如,我们可以使用抽象工厂模式创建伦敦工厂和纽约工厂,使用工厂方法实现cheese pizza和greak pizza的生产。类图如下:
在这里插入图片描述
总结一下三种模式:

简单工厂模式就是建立一个实例化对象的类,在该类中对多个对象实例化。工厂方法模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类。这样做的好处是再有新的类型的对象需要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建对象族,而无需明确指定具体类。抽象工厂也是把对象的实例化交给了子类,即支持拓展。同时提供给客户端接口,避免了用户直接操作子类工厂。
2 单例模式
定义:确保一个类最多只有一个实例,并提供一个全局访问点

单例模式可以分为两种:预加载和懒加载

2.1 预加载
顾名思义,就是预先加载。再进一步解释就是还没有使用该单例对象,但是,该单例对象就已经被加载到内存了。

public class PreloadSingleton {
   <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">PreloadSingleton</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">PreloadSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

   <span class="token comment">//其他的类无法实例化单例类的对象</span>
   <span class="token keyword">private</span> <span class="token function">PreloadSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
   <span class="token punctuation">}</span><span class="token punctuation">;</span>
   
   <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">PreloadSingleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">return</span> instance<span class="token punctuation">;</span>
   <span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

很明显,没有使用该单例对象,该对象就被加载到了内存,会造成内存的浪费。

2.2 懒加载
为了避免内存的浪费,我们可以采用懒加载,即用到该单例对象的时候再创建。

public class Singleton {
   <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">;</span>
   
   <span class="token keyword">private</span> <span class="token function">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
   <span class="token punctuation">}</span><span class="token punctuation">;</span>
   
   <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">if</span><span class="token punctuation">(</span>instance<span class="token operator">==</span><span class="token keyword">null</span><span class="token punctuation">)</span>
          <span class="token punctuation">{<!-- --></span>
                 instance<span class="token operator">=</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
          <span class="token keyword">return</span> instance<span class="token punctuation">;</span>
          
   <span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.3 单例模式和线程安全
(1)预加载只有一条语句return instance,这显然可以保证线程安全。但是,我们知道预加载会造成内存的浪费。

(2)懒加载不浪费内存,但是无法保证线程的安全。首先,if判断以及其内存执行代码是非原子性的。其次,new Singleton()无法保证执行的顺序性。

不满足原子性或者顺序性,线程肯定是不安全的,这是基本的常识,不再赘述。我主要讲一下为什么new Singleton()无法保证顺序性。我们知道创建一个对象分三步:

memory=allocate();//1:初始化内存空间

ctorInstance(memory);//2:初始化对象

instance=memory();//3:设置instance指向刚分配的内存地址

  • 1
  • 2
  • 3
  • 4
  • 5

jvm为了提高程序执行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。我们用两个线程来说明线程是不安全的。线程A和线程B都创建对象。其中,A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象(线程不安全)。

在这里插入图片描述

2.4 保证懒加载的线程安全
我们首先想到的就是使用synchronized关键字。synchronized加载getInstace()函数上确实保证了线程的安全。但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。为了避免线程的上下文切换消耗大量时间,如果对象已经实例化了,我们没有必要再使用synchronized加锁,直接返回对象。

public class Singleton {
       private static Singleton instance = null;
       private Singleton() {
       };
       public static synchronized Singleton getInstance() {
              if (instance == null) {
                     instance = new Singleton();
              }
              return instance;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

我们把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁

public class Singleton {
       private static Singleton instance = null;
       private Singleton() {
       };
       public static synchronized Singleton getInstance() {
              if (instance == null) {
                     synchronized (Singleton.class) {
                           if (instance == null) {
                                  instance = new Singleton();
                           }
                     }
              }
              return instance;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我们经过2.3的讨论知道new一个对象的代码是无法保证顺序性的,因此,我们需要使用另一个关键字volatile保证对象实例化过程的顺序性。

public class Singleton {
       private static volatile Singleton instance = null;
       private Singleton() {
       };
       public static synchronized Singleton getInstance() {
              if (instance == null) {
                     synchronized (instance) {
                           if (instance == null) {
                                  instance = new Singleton();
                           }
                     }
              }
              return instance;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

到此,我们就保证了懒加载的线程安全。
3 生成器模式
定义:封装一个复杂对象构造过程,并允许按步骤构造。

定义解释: 我们可以将生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。

3.1 模式的结构和代码示例
生成器模式结构中包括四种角色:

(1)产品(Product):具体生产器要构造的复杂对象;

(2)抽象生成器(Bulider):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法之外,还要定义返回Product对象的方法(定义构造步骤);

(3)具体生产器(ConcreteBuilder):实现Builder接口的类,具体生成器将实现Builder接口所定义的方法(生产各个组件);

(4)指挥者(Director):指挥者是一个类,该类需要含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器类来构造用户所需要的Product对象,如果所请求的具体生成器成功地构造出Product对象,指挥者就可以让该具体生产器返回所构造的Product对象。(按照步骤组装部件,并返回Product)
在这里插入图片描述
举例(我们如果构建生成一台电脑,那么我们可能需要这么几个步骤(1)需要一个主机(2)需要一个显示器(3)需要一个键盘(4)需要一个鼠标)

虽然我们具体在构建一台主机的时候,每个对象的实际步骤是不一样的,比如,有的对象构建了i7cpu的主机,有的对象构建了i5cpu的主机,有的对象构建了普通键盘,有的对象构建了机械键盘等。但不管怎样,你总是需要经过一个步骤就是构建一台主机,一台键盘。对于这个例子,我们就可以使用生成器模式来生成一台电脑,他需要通过多个步骤来生成。类图如下:

在这里插入图片描述
ComputerBuilder类定义构造步骤:

public abstract class ComputerBuilder {
<span class="token keyword">protected</span> <span class="token class-name">Computer</span> computer<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token return-type class-name">Computer</span> <span class="token function">getComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">return</span> computer<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    computer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Computer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"生成了一台电脑!!!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildMaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildScreen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildKeyboard</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildMouse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">buildAudio</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

HPComputerBuilder定义各个组件:

public class HPComputerBuilder extends ComputerBuilder {
    @Override
    public void buildMaster() {
        // TODO Auto-generated method stub
        computer.setMaster("i7,16g,512SSD,1060");
        System.out.println("(i7,16g,512SSD,1060)的惠普主机");
    }
    @Override
    public void buildScreen() {
        // TODO Auto-generated method stub
        computer.setScreen("1080p");
        System.out.println("(1080p)的惠普显示屏");
    }
    @Override
    public void buildKeyboard() {
        // TODO Auto-generated method stub
        computer.setKeyboard("cherry 青轴机械键盘");
        System.out.println("(cherry 青轴机械键盘)的键盘");
    }
    @Override
    public void buildMouse() {
        // TODO Auto-generated method stub
        computer.setMouse("MI 鼠标");
        System.out.println("(MI 鼠标)的鼠标");
    }
    @Override
    public void buildAudio() {
        // TODO Auto-generated method stub
        computer.setAudio("飞利浦 音响");
        System.out.println("(飞利浦 音响)的音响");
    }
}

 
 
  • 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

Director类对组件进行组装并生成产品

public class Director {
<span class="token keyword">private</span> <span class="token class-name">ComputerBuilder</span> computerBuilder<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">setComputerBuilder</span><span class="token punctuation">(</span><span class="token class-name">ComputerBuilder</span> computerBuilder<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>computerBuilder <span class="token operator">=</span> computerBuilder<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name">Computer</span> <span class="token function">getComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">return</span> computerBuilder<span class="token punctuation">.</span><span class="token function">getComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">constructComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildComputer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildMaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildScreen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildKeyboard</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildMouse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    computerBuilder<span class="token punctuation">.</span><span class="token function">buildAudio</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.2 生成器模式的优缺点
优点
将一个对象分解为各个组件

将对象组件的构造封装起来

可以控制整个对象的生成过程

缺点
对不同类型的对象需要实现不同的具体构造器的类,这可能回答大大增加类的数量

3.3 生成器模式与工厂模式的不同
生成器模式构建对象的时候,对象通常构建的过程中需要多个步骤,就像我们例子中的先有主机,再有显示屏,再有鼠标等等,生成器模式的作用就是将这些复杂的构建过程封装起来。工厂模式构建对象的时候通常就只有一个步骤,调用一个工厂方法就可以生成一个对象。
4 原型模式
定义:通过复制现有实例来创建新的实例,无需知道相应类的信息。

简单地理解,其实就是当需要创建一个指定的对象时,我们刚好有一个这样的对象,但是又不能直接使用,我会clone一个一毛一样的新对象来使用;基本上这就是原型模式。关键字:Clone。

4.1 深拷贝和浅拷贝
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。clone明显是深复制,clone出来的对象是是不能去影响原型对象的

4.2 原型模式的结构和代码示例
在这里插入图片描述
Client:使用者

Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口

ConcretePrototype:具体的原型类

可以看出设计模式还是比较简单的,重点在于Prototype接口和Prototype接口的实现类ConcretePrototype。原型模式的具体实现:一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

public class Prototype implements Cloneable {  
     public Object clone() throws CloneNotSupportedException {  
         Prototype proto = (Prototype) super.clone();  
         return proto;  
     }  
}  

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

举例(银行发送大量邮件,使用clone和不使用clone的时间对比):我们模拟创建一个对象需要耗费比较长的时间,因此,在构造函数中我们让当前线程sleep一会

public Mail(EventTemplate et) {
              this.tail = et.geteventContent();
              this.subject = et.geteventSubject();
              try {
                     Thread.sleep(1000);
              } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
       }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

不使用clone,发送十个邮件

public static void main(String[] args) {
              int i = 0;
              int MAX_COUNT = 10;
              EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
              long start = System.currentTimeMillis();
              while (i < MAX_COUNT) {
                     // 以下是每封邮件不同的地方
                     Mail mail = new Mail(et);
                     mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..." + mail.getTail());
                     mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                     // 然后发送邮件
                     sendMail(mail);
                     i++;
              }
              long end = System.currentTimeMillis();
              System.out.println("用时:" + (end - start));
       }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

用时:10001

使用clone,发送十个邮件

    public static void main(String[] args) {
              int i = 0;
              int MAX_COUNT = 10;
              EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
              long start=System.currentTimeMillis();
              Mail mail = new Mail(et);         
              while (i < MAX_COUNT) {
                     Mail cloneMail = mail.clone();
                     mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..."
                                  + mail.getTail());
                     mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                     sendMail(cloneMail);
                     i++;
              }
              long end=System.currentTimeMillis();
              System.out.println("用时:"+(end-start));
       }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

用时:1001

4.3 总结
原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

B、结构模式(7种)
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

5 适配器模式
定义: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

5.1 类适配器模式
通过多重继承目标接口和被适配者类方式来实现适配

举例(将USB接口转为VGA接口),类图如下:
在这里插入图片描述
USBImpl的代码:

public class USBImpl implements USB{
       @Override
       public void showPPT() {
              // TODO Auto-generated method stub
              System.out.println("PPT内容演示");
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

AdatperUSB2VGA 首先继承USBImpl获取USB的功能,其次,实现VGA接口,表示该类的类型为VGA。

public class AdapterUSB2VGA extends USBImpl implements VGA {
       @Override
       public void projection() {
              super.showPPT();
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Projector将USB映射为VGA,只有VGA接口才可以连接上投影仪进行投影

public class Projector<T> {
       public void projection(T t) {
              if (t instanceof VGA) {
                     System.out.println("开始投影");
                     VGA v = new VGAImpl();
                     v = (VGA) t;
                     v.projection();
              } else {
                     System.out.println("接口不匹配,无法投影");
              }
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

test代码

       @Test
       public void test2(){
              //通过适配器创建一个VGA对象,这个适配器实际是使用的是USB的showPPT()方法
              VGA a=new AdapterUSB2VGA();
              //进行投影
              Projector p1=new Projector();
              p1.projection(a);
       } 

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.2 对象适配器模式
对象适配器和类适配器使用了不同的方法实现适配,对象适配器使用组合,类适配器使用继承。

举例(将USB接口转为VGA接口),类图如下:
在这里插入图片描述

public class AdapterUSB2VGA implements VGA {
       USB u = new USBImpl();
       @Override
       public void projection() {
              u.showPPT();
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

实现VGA接口,表示适配器类是VGA类型的,适配器方法中直接使用USB对象。

5.3 接口适配器模式
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。

举例(将USB接口转为VGA接口,VGA中的b()和c()不会被实现),类图如下:
在这里插入图片描述
AdapterUSB2VGA抽象类

public abstract class AdapterUSB2VGA implements VGA {
       USB u = new USBImpl();
       @Override
       public void projection() {
              u.showPPT();
       }
       @Override
       public void b() {
       };
       @Override
       public void c() {
       };
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

AdapterUSB2VGA实现,不用去实现b()和c()方法。

public class AdapterUSB2VGAImpl extends AdapterUSB2VGA {
       public void projection() {
              super.projection();
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

5.4 总结
总结一下三种适配器模式的应用场景:

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

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

接口适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

命名规则:

我个人理解,三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。

类适配器,以类给到,在Adapter里,就是将src当做类,继承,

对象适配器,以对象给到,在Adapter里,将src作为一个对象,持有。

接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现。

使用选择:

根据合成复用原则,组合大于继承。因此,类的适配器模式应该少用。
6 装饰者模式
定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。

6.1 装饰者模式结构图与代码示例
1.Component(被装饰对象的基类)

定义一个对象接口,可以给这些对象动态地添加职责。

2.ConcreteComponent(具体被装饰对象)

定义一个对象,可以给这个对象添加一些职责。

3.Decorator(装饰者抽象类)

维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。

4.ConcreteDecorator(具体装饰者)

具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。

被装饰对象和修饰者继承自同一个超类
在这里插入图片描述
举例(咖啡馆订单项目:1)、咖啡种类:Espresso、ShortBlack、LongBlack、Decaf2)、调料(装饰者):Milk、Soy、Chocolate),类图如下:在这里插入图片描述
被装饰的对象和装饰者都继承自同一个超类

public abstract class Drink {
       public String description="";
       private float price=0f;;
   <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">setDescription</span><span class="token punctuation">(</span><span class="token class-name">String</span> description<span class="token punctuation">)</span>
   <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">this</span><span class="token punctuation">.</span>description<span class="token operator">=</span>description<span class="token punctuation">;</span>
   <span class="token punctuation">}</span>
   
   <span class="token keyword">public</span> <span class="token return-type class-name">String</span> <span class="token function">getDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">return</span> description<span class="token operator">+</span><span class="token string">"-"</span><span class="token operator">+</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getPrice</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token punctuation">}</span>
   <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">float</span></span> <span class="token function">getPrice</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">return</span> price<span class="token punctuation">;</span>
   <span class="token punctuation">}</span>
   <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">setPrice</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">float</span></span> price<span class="token punctuation">)</span>
   <span class="token punctuation">{<!-- --></span>
          <span class="token keyword">this</span><span class="token punctuation">.</span>price<span class="token operator">=</span>price<span class="token punctuation">;</span>
   <span class="token punctuation">}</span>
   <span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">float</span></span> <span class="token function">cost</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 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 Coffee extends Drink {
       @Override
       public float cost() {
              // TODO Auto-generated method stub
              return super.getPrice();
       }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

coffee类的实现

public class Decaf extends Coffee {
       public Decaf()
       {
              super.setDescription("Decaf");
              super.setPrice(3.0f);
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

装饰者

装饰者不仅要考虑自身,还要考虑被它修饰的对象,它是在被修饰的对象上继续添加修饰。例如,咖啡里面加牛奶,再加巧克力。加糖后价格为coffee+milk。再加牛奶价格为coffee+milk+chocolate。

public class Decorator extends Drink {
       private Drink Obj;
       public Decorator(Drink Obj) {
              this.Obj = Obj;
       };
       @Override
       public float cost() {
              // TODO Auto-generated method stub
              return super.getPrice() + Obj.cost();
       }
       @Override
       public String getDescription() {
              return super.description + "-" + super.getPrice() + "&&" + Obj.getDescription();
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

装饰者实例化(加牛奶)。这里面要对被修饰的对象进行实例化。

public class Milk extends Decorator {
       public Milk(Drink Obj) {          
              super(Obj);
              // TODO Auto-generated constructor stub
              super.setDescription("Milk");
              super.setPrice(2.0f);
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

coffee店:初始化一个被修饰对象,修饰者实例需要对被修改者实例化,才能对具体的被修饰者进行修饰。

public class CoffeeBar {
       public static void main(String[] args) {
              Drink order;
              order = new Decaf();
              System.out.println("order1 price:" + order.cost());
              System.out.println("order1 desc:" + order.getDescription());
              System.out.println("****************");
              order = new LongBlack();
              order = new Milk(order);
              order = new Chocolate(order);
              order = new Chocolate(order);
              System.out.println("order2 price:" + order.cost());
              System.out.println("order2 desc:" + order.getDescription());
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6.2 总结

装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

7 代理模式
定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:
在这里插入图片描述
7.1 为什么要用代理模式?
中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理

7.2 静态代理

举例(买房),类图如下:
在这里插入图片描述
第一步:创建服务类接口

public interface BuyHouse {
    void buyHosue();
}

 
 
  • 1
  • 2
  • 3

第二步:实现服务接口

public class BuyHouseImpl implements BuyHouse {
       @Override
       public void buyHosue() {
              System.out.println("我要买房");
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第三步:创建代理类

public class BuyHouseProxy implements BuyHouse {
       private BuyHouse buyHouse;
       public BuyHouseProxy(final BuyHouse buyHouse) {
              this.buyHouse = buyHouse;
       }
       @Override
       public void buyHosue() {
              System.out.println("买房前准备");
              buyHouse.buyHosue();
              System.out.println("买房后装修");
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

总结:

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点: 代理对象与目标对象要实现相同的接口,我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

7.3 动态代理
动态代理有以下特点:

1.代理对象,不需要实现接口

2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

代理类不用再实现接口了。但是,要求被代理对象必须有接口。

动态代理实现:

Java.lang.reflect.Proxy类可以直接生成一个代理对象

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象

参数1:ClassLoader loader 代理对象的类加载器 一般使用被代理对象的类加载器

参数2:Class<?>[] interfaces 代理对象的要实现的接口 一般使用的被代理对象实现的接口

参数3:InvocationHandler h (接口)执行处理类

InvocationHandler中的invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行

参数3.1:代理对象(慎用)

参数3.2:当前执行的方法

参数3.3:当前执行的方法运行时传递过来的参数

第一步:编写动态处理器

public class DynamicProxyHandler implements InvocationHandler {
       private Object object;
       public DynamicProxyHandler(final Object object) {
              this.object = object;
       }
       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("买房前准备");
              Object result = method.invoke(object, args);
              System.out.println("买房后装修");
              return result;
       }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

第二步:编写测试类

public class DynamicProxyTest {
    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();
        BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
                Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
        proxyBuyHouse.buyHosue();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

动态代理总结:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏(我们要使用被代理的对象的接口),因为它的设计注定了这个遗憾。

7.4 CGLIB代理

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。

CGLIB的实现步骤:

第一步:建立拦截器

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"买房前准备"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token class-name">Object</span> result <span class="token operator">=</span> methodProxy<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span><span class="token keyword">object</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>

    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"买房后装修"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">return</span> result<span class="token punctuation">;</span>

<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

返回:从代理实例的方法调用返回的值。

其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

第二步: 生成动态代理类

public class CglibProxy implements MethodInterceptor {
    private Object target;
    public Object getInstance(final Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("买房前准备");
        Object result = methodProxy.invoke(object, args);
        System.out.println("买房后装修");
        return result;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

第三步:测试

public class CglibProxyTest {
    public static void main(String[] args){
        BuyHouse buyHouse = new BuyHouseImpl();
        CglibProxy cglibProxy = new CglibProxy();
        BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
        buyHouseCglibProxy.buyHosue();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

8 外观模式
定义: 隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。

8.1 模式结构和代码示例
在这里插入图片描述
简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,设计到3个角色。

1).门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。(客户调用,同时自身调用子系统功能)

2).子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。(实现具体功能)

3).客户角色:通过调用Facede来完成要实现的功能(调用门面角色)。

举例(每个Computer都有CPU、Memory、Disk。在Computer开启和关闭的时候,相应的部件也会开启和关闭),类图如下:
在这里插入图片描述
首先是子系统类:

public class CPU {
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"cpu is start..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"CPU is shutDown..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

public class Disk {
public void start() {
System.out.println(“Disk is start…”);
}

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Disk is shutDown..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

public class Memory {
public void start() {
System.out.println(“Memory is start…”);
}

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Memory is shutDown..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 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

然后是,门面类Facade

public class Computer {
<span class="token keyword">private</span> <span class="token class-name">CPU</span> cpu<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">Memory</span> memory<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">Disk</span> disk<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token function">Computer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	cpu <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">CPU</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	memory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Memory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	disk <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Disk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Computer start begin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	cpu<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	disk<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	memory<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Computer start end"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Computer shutDown begin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	cpu<span class="token punctuation">.</span><span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	disk<span class="token punctuation">.</span><span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	memory<span class="token punctuation">.</span><span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Computer shutDown end..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 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

最后为,客户角色

public class Client {
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String<span class="token punctuation">[</span><span class="token punctuation">]</span></span> args<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token class-name">Computer</span> computer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Computer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	computer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"================="</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	computer<span class="token punctuation">.</span><span class="token function">shutDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

8.2 优点
  - 松散耦合

使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;

- 简单易用

客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。

- 更好的划分访问层次

有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。

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

9.1 案例
看下图手机与手机软件的类图

在这里插入图片描述
增加一款新的手机软件,需要在所有手机品牌类下添加对应的手机软件类,当手机软件种类较多时,将导致类的个数急剧膨胀,难以维护

手机和手机中的软件是什么关系?

手机中的软件从本质上来说并不是一种手机,手机软件运行在手机中,是一种包含与被包含关系,而不是一种父与子或者说一般与特殊的关系,通过继承手机类实现手机软件类的设计是违反一般规律的。

如果Oppo手机实现了wifi功能,继承它的Oppo应用商城也会继承wifi功能,并且Oppo手机类的任何变动,都会影响其子类

换一种解决思路
在这里插入图片描述
从类图上看起来更像是手机软件类图,涉及到手机本身相关的功能,比如说:wifi功能,放到哪个类中实现呢?放到OppoAppStore中实现显然是不合适的

引起整个结构变化的元素有两个,一个是手机品牌,一个是手机软件,所以我们将这两个点抽出来,分别进行封装
在这里插入图片描述
9.2 桥接模式结构和代码示例
在这里插入图片描述
类图:
在这里插入图片描述
实现:

public interface Software {
	public void run();

}
public class AppStore implements Software {

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"run app store"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}
public class Camera implements Software {

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"run camera"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

抽象:

public abstract class Phone {
<span class="token keyword">protected</span> <span class="token class-name">Software</span> software<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">setSoftware</span><span class="token punctuation">(</span><span class="token class-name">Software</span> software<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>software <span class="token operator">=</span> software<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}
public class Oppo extends Phone {

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    software<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}
public class Vivo extends Phone {

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    software<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 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

对比最初的设计,将抽象部分(手机)与它的实现部分(手机软件类)分离,将实现部分抽象成单独的类,使它们都可以独立地变化。整个类图看起来像一座桥,所以称为桥接模式

继承是一种强耦合关系,子类的实现与它的父类有非常紧密的依赖关系,父类的任何变化 都会导致子类发生变化,因此继承或者说强耦合关系严重影响了类的灵活性,并最终限制了可复用性

从桥接模式的设计上我们可以看出聚合是一种比继承要弱的关联关系,手机类和软件类都可独立的进行变化,不会互相影响

9.3 适用场景
桥接模式通常适用于以下场景。

当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。

当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。

当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。

9.4 优缺点
优点:

(1)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。

(2)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。

缺点:

桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。

10 组合模式
定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。

关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

组合模式的主要优点有:

组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;

更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

其主要缺点是:

设计较复杂,客户端需要花更多时间理清类之间的层次关系;

不容易限制容器中的构件;

不容易用继承的方法来增加构件的新功能;
10.1 模式结构和代码示例
在这里插入图片描述
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。

树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。

树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法

举例(访问一颗树),类图如下:

在这里插入图片描述
1 组件

public interface Component {
    public void add(Component c);
    public void remove(Component c);
    public Component getChild(int i);
    public void operation();

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2 叶子

public class Leaf implements Component{
<span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>


<span class="token keyword">public</span> <span class="token function">Leaf</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">add</span><span class="token punctuation">(</span><span class="token class-name">Component</span> c<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">remove</span><span class="token punctuation">(</span><span class="token class-name">Component</span> c<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name">Component</span> <span class="token function">getChild</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> i<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	<span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">operation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	 System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"树叶"</span><span class="token operator">+</span>name<span class="token operator">+</span><span class="token string">":被访问!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span>

}

  • 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

3 树枝

public class Composite implements Component {
<span class="token keyword">private</span> <span class="token class-name">ArrayList<span class="token punctuation">&lt;</span>Component<span class="token punctuation">&gt;</span></span> children <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ArrayList<span class="token punctuation">&lt;</span>Component<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">add</span><span class="token punctuation">(</span><span class="token class-name">Component</span> c<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	children<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">remove</span><span class="token punctuation">(</span><span class="token class-name">Component</span> c<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	children<span class="token punctuation">.</span><span class="token keyword">remove</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name">Component</span> <span class="token function">getChild</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> i<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">return</span> children<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">operation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Object</span> obj <span class="token punctuation">:</span> children<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token punctuation">(</span><span class="token punctuation">(</span>Component<span class="token punctuation">)</span> obj<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">operation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

11 享元模式
定义:通过共享的方式高效的支持大量细粒度的对象。

主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。

如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。

关键代码:用 HashMap 存储这些对象。

应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。

优点:大大减少对象的创建,降低系统的内存,使效率提高。

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

简单来说,我们抽取出一个对象的外部状态(不能共享)和内部状态(可以共享)。然后根据外部状态的决定是否创建内部状态对象。内部状态对象是通过哈希表保存的,当外部状态相同的时候,不再重复的创建内部状态对象,从而减少要创建对象的数量。

11.1 享元模式的结构图和代码示例
在这里插入图片描述
1、Flyweight (享元抽象类):一般是接口或者抽象类,定义了享元类的公共方法。这些方法可以分享内部状态的数据,也可以调用这些方法修改外部状态。

2、ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类的方法,为享元对象开辟了内存空间来保存享元对象的内部数据,同时可以通过和单例模式结合只创建一个享元对象。

3、FlyweightFactory(享元工厂类):享元工厂类创建并且管理享元类,享元工厂类针对享元类来进行编程,通过提供一个享元池来进行享元对象的管理。一般享元池设计成键值对,或者其他的存储结构来存储。当客户端进行享元对象的请求时,如果享元池中有对应的享元对象则直接返回对应的对象,否则工厂类创建对应的享元对象并保存到享元池。

举例(JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面)。类图如下:
在这里插入图片描述
(1)创建享元对象接口

public interface IFlyweight {
    void print();
}

 
 
  • 1
  • 2
  • 3

(2)创建具体享元对象

public class Flyweight implements IFlyweight {
    private String id;
    public Flyweight(String id){
        this.id = id;
    }
    @Override
    public void print() {
        System.out.println("Flyweight.id = " + getId() + " ...");
    }
    public String getId() {
        return id;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(3)创建工厂,这里要特别注意,为了避免享元对象被重复创建,我们使用HashMap中的key值保证其唯一。

public class FlyweightFactory {
    private Map<String, IFlyweight> flyweightMap = new HashMap();
    public IFlyweight getFlyweight(String str){
        IFlyweight flyweight = flyweightMap.get(str);
        if(flyweight == null){
            flyweight = new Flyweight(str);
            flyweightMap.put(str, flyweight);
        }
        return  flyweight;
    }
    public int getFlyweightMapSize(){
        return flyweightMap.size();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(4)测试,我们创建三个字符串,但是只会产生两个享元对象

public class MainTest {
	public static void main(String[] args) {
        FlyweightFactory flyweightFactory = new FlyweightFactory();
        IFlyweight flyweight1 = flyweightFactory.getFlyweight("A");
        IFlyweight flyweight2 = flyweightFactory.getFlyweight("B");
        IFlyweight flyweight3 = flyweightFactory.getFlyweight("A");
        flyweight1.print();
        flyweight2.print();
        flyweight3.print();
        System.out.println(flyweightFactory.getFlyweightMapSize());
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
C、关系模式(11种)
先来张图,看看这11中模式的关系:

第一类:通过父类与子类的关系进行实现。

第二类:两个类之间。

第三类:类的状态。

第四类:通过中间类

在这里插入图片描述
12 策略模式
定义: 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

12.1 策略模式结构和示例代码
在这里插入图片描述
抽象策略角色: 这个是一个抽象的角色,通常情况下使用接口或者抽象类去实现。对比来说,就是我们的Comparator接口。

具体策略角色: 包装了具体的算法和行为。对比来说,就是实现了Comparator接口的实现一组实现类。

环境角色: 内部会持有一个抽象角色的引用,给客户端调用。

举例如下( 实现一个加减的功能),类图如下:
在这里插入图片描述
1、定义抽象策略角色

public interface Strategy {
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> num1<span class="token punctuation">,</span><span class="token class-name"><span class="token keyword">int</span></span> num2<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4

2、定义具体策略角色

public class AddStrategy implements Strategy {
@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> num1<span class="token punctuation">,</span> <span class="token class-name"><span class="token keyword">int</span></span> num2<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	<span class="token keyword">return</span> num1 <span class="token operator">+</span> num2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}
public class SubstractStrategy implements Strategy {

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> num1<span class="token punctuation">,</span> <span class="token class-name"><span class="token keyword">int</span></span> num2<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	<span class="token keyword">return</span> num1 <span class="token operator">-</span> num2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3、环境角色

public class Environment {
	private Strategy strategy;
<span class="token keyword">public</span> <span class="token function">Environment</span><span class="token punctuation">(</span><span class="token class-name">Strategy</span> strategy<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>strategy <span class="token operator">=</span> strategy<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">calculate</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> a<span class="token punctuation">,</span> <span class="token class-name"><span class="token keyword">int</span></span> b<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">return</span> strategy<span class="token punctuation">.</span><span class="token function">calc</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4、测试

public class MainTest {
	public static void main(String[] args) {
	<span class="token class-name">Environment</span> environment<span class="token operator">=</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">Environment</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">AddStrategy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name"><span class="token keyword">int</span></span> result<span class="token operator">=</span>environment<span class="token punctuation">.</span><span class="token function">calculate</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
	
	<span class="token class-name">Environment</span> environment1<span class="token operator">=</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">Environment</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token constructor-invocation class-name">SubstractStrategy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name"><span class="token keyword">int</span></span> result1<span class="token operator">=</span>environment1<span class="token punctuation">.</span><span class="token function">calculate</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

13.1 模式结构和代码示例
在这里插入图片描述
抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。

具体类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。

举例( 我们做菜可以分为三个步骤 (1)备料 (2)具体做菜 (3)盛菜端给客人享用,这三部就是算法的骨架 ;然而做不同菜需要的料,做的方法,以及如何盛装给客人享用都是不同的这个就是不同的实现细节。)。类图如下:
在这里插入图片描述
a. 先来写一个抽象的做菜父类:

public abstract class Dish {    
    /**
     * 具体的整个过程
     */
    protected void dodish(){
        this.preparation();
        this.doing();
        this.carriedDishes();
    }
    /**
     * 备料
     */
    public abstract void preparation();
    /**
     * 做菜
     */
    public abstract void doing();
    /**
     * 上菜
     */
    public abstract void carriedDishes ();
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

b. 下来做两个番茄炒蛋(EggsWithTomato)和红烧肉(Bouilli)实现父类中的抽象方法

public class EggsWithTomato extends Dish {
@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">preparation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"洗并切西红柿,打鸡蛋。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">doing</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"鸡蛋倒入锅里,然后倒入西红柿一起炒。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">carriedDishes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"将炒好的西红寺鸡蛋装入碟子里,端给客人吃。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}
public class Bouilli extends Dish{

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">preparation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"切猪肉和土豆。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">doing</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">carriedDishes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"将做好的红烧肉盛进碗里端给客人吃。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 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

c. 在测试类中我们来做菜:

public class MainTest {
	public static void main(String[] args) {
		Dish eggsWithTomato = new EggsWithTomato();
		eggsWithTomato.dodish();
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"-----------------------------"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token class-name">Dish</span> bouilli <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Bouilli</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	bouilli<span class="token punctuation">.</span><span class="token function">dodish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

13.2 模板模式的优点和缺点
优点:

(1)具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。

(2)代码复用的基本技术,在数据库设计中尤为重要。

(3)存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

缺点:
每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。

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

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个 ArrayList 存放观察者们。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

14.1 模式结构图和代码示例
在这里插入图片描述
抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。

具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

举例(有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。)类图如下:
在这里插入图片描述
1、定义一个抽象被观察者接口

public interface Subject {
  <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">registerObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">removeObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">notifyObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、定义一个抽象观察者接口

public interface Observer {
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5

3、定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。

public class WechatServer implements Subject {
<span class="token keyword">private</span> <span class="token class-name">List<span class="token punctuation">&lt;</span>Observer<span class="token punctuation">&gt;</span></span> list<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token function">WechatServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">ArrayList<span class="token punctuation">&lt;</span>Observer<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">registerObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> o<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	list<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">removeObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> o<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>list<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		list<span class="token punctuation">.</span><span class="token keyword">remove</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">notifyObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token comment">// TODO Auto-generated method stub</span>
	<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Observer</span> o <span class="token punctuation">:</span> list<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		o<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">setInfomation</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> s<span class="token punctuation">;</span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"微信服务更新消息: "</span> <span class="token operator">+</span> s<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">// 消息更新,通知所有观察者</span>
	<span class="token function">notifyObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 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

4、定义具体观察者,微信公众号的具体观察者为用户User

public class User implements Observer {
<span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@Override
<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> message<span class="token punctuation">;</span>
	<span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>name <span class="token operator">+</span> <span class="token string">" 收到推送消息: "</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

5、编写一个测试类

public class MainTest {
 <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String<span class="token punctuation">[</span><span class="token punctuation">]</span></span> args<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	 
        <span class="token class-name">WechatServer</span> server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">WechatServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token class-name">Observer</span> userZhang <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">User</span><span class="token punctuation">(</span><span class="token string">"ZhangSan"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Observer</span> userLi <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">User</span><span class="token punctuation">(</span><span class="token string">"LiSi"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Observer</span> userWang <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">User</span><span class="token punctuation">(</span><span class="token string">"WangWu"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        server<span class="token punctuation">.</span><span class="token function">registerObserver</span><span class="token punctuation">(</span>userZhang<span class="token punctuation">)</span><span class="token punctuation">;</span>
        server<span class="token punctuation">.</span><span class="token function">registerObserver</span><span class="token punctuation">(</span>userLi<span class="token punctuation">)</span><span class="token punctuation">;</span>
        server<span class="token punctuation">.</span><span class="token function">registerObserver</span><span class="token punctuation">(</span>userWang<span class="token punctuation">)</span><span class="token punctuation">;</span>
        server<span class="token punctuation">.</span><span class="token function">setInfomation</span><span class="token punctuation">(</span><span class="token string">"PHP是世界上最好用的语言!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        System<span class="token punctuation">.</span><span class="token keyword">out</span><span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"----------------------------------------------"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        server<span class="token punctuation">.</span><span class="token function">removeObserver</span><span class="token punctuation">(</span>userZhang<span class="token punctuation">)</span><span class="token punctuation">;</span>
        server<span class="token punctuation">.</span><span class="token function">setInfomation</span><span class="token punctuation">(</span><span class="token string">"JAVA是世界上最好用的语言!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        
    <span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

15 迭代器模式
定义:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

简单来说,不同种类的对象可能需要不同的遍历方式,我们对每一种类型的对象配一个迭代器,最后多个迭代器合成一个。

主要解决:不同的方式来遍历整个整合对象。

何时使用:遍历一个聚合对象。

如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

关键代码:定义接口:hasNext, next。

应用实例:JAVA 中的 iterator。

优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

15.1 模式结构和代码示例
在这里插入图片描述
(1)迭代器角色(Iterator):定义遍历元素所需要的方法,一般来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove(),

(2)具体迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成集合的迭代。

(3)容器角色(Aggregate): 一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等

(4)具体容器角色(ConcreteAggregate):就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。

举例(咖啡厅和中餐厅合并,他们两个餐厅的菜单一个是数组保存的,一个是ArrayList保存的。遍历方式不一样,使用迭代器聚合访问,只需要一种方式)
在这里插入图片描述
1 迭代器接口

public interface Iterator {
<span class="token keyword">public</span> <span class="token return-type class-name">boolean</span> <span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token return-type class-name">Object</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2 咖啡店菜单和咖啡店菜单遍历器


                

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/667698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM体系结构模块

1、类装载器ClassLoader &#xff08;1&#xff09;负责加载class文件&#xff0c;class文件在文件开头有特定的文件标示&#xff0c;并且ClassLoader只负责class文件的加载&#xff0c;至于它是否可以运行&#xff0c;则由Execution Engine决定 &#xff08;2&#xff09;加…

麻了。。。

同样的初始条件&#xff0c;一个每隔0.05s保存一步数据&#xff0c;一个每隔1.13s保存一步数据。 上面横轴代表时间&#xff0c;纵轴代表Nu。 可以看出其实是不太一样的&#xff0c;一个取平均是24.72&#xff0c;一个是25.34&#xff0c;差距其实有的。 我目前跑算例发现长时间…

【C++】类和对象超全超详细总结(万字详解)

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f681; 个人主页&#xff1a;不 良 &#x1f525; 系列专栏&#xff1a;&#x1f6f8;C &#x1f6f9;Linux &#x1f4d5; 学习格言&#xff1a;博观而约取&#xff0…

C#视觉检测-模板匹配

前几天一个学员在学习C#与视觉交互时,也不知道视觉可以用来做什么 。下面我们就详细讲讲C# 和视觉交互的相关知识。 C#和Halcon的视觉交互在工业生产和智能制造领域中得到了广泛应用。其中&#xff0c;模板匹配是一种简单但有效的图像处理技术&#xff0c;可以用于检测和定位物…

性能测试压测工具都有哪些?怎么选你知道吗?

目录 普遍存在的问题 工具选型和推荐 软件测试而非测试工具 总结&#xff1a; 普遍存在的问题 聊压测工具之前&#xff0c;先聊一下我面试候选人时问的问题以及在技术交流群经常遇到的一个情况。 面试候选人特别是性能测试岗位&#xff0c;我一般很少问测试工具的问题&…

53 最佳实践-安全最佳实践-虚拟机可信启动

文章目录 53 最佳实践-安全最佳实践-虚拟机可信启动53.1 概述53.2 配置vTPM设备&#xff0c;使能度量启动53.2.1 安装swtpm和libtpms软件53.2.2 虚拟机配置vTPM设备53.2.3 确认度量启动使能成功 53 最佳实践-安全最佳实践-虚拟机可信启动 53.1 概述 可信启动包含度量启动和远…

深入篇【Linux】学习必备:谈gcc/g++使用及原理 + 动静态库介绍 + debug与release模式

深入篇【Linux】学习必备&#xff1a;谈gcc/g是如何完成程序的翻译过程&&动静态库介绍&&debug与release模式 Ⅰ.gcc/g是如何完成程序的翻译过程的&#xff1f;①.预处理(宏替换)②.编译(生成汇编)③.汇编(生成机器可识别代码)④.链接(生成可执行文件) Ⅱ.动态库…

安装Nginx网站服务

目录 前言 一、Nginx简介 二、Nginx应用场景 三、Nginx与Apache优缺点 1&#xff09;Nginx 相对于 Apache 的优点 2&#xff09;Apache 相对于 Nginx 的优点 3&#xff09;Nginx和Apache的差异 四、阻塞与非阻塞、同步与异步 五、安装Nginx软件 1、在线安装 ①使用y…

半导体测试系统 芯片自动化测试软件 可定制测试方案ATECLOUD-IC

*测试产品&#xff1a;芯片半导体器件。纳米软件ATECLOUD-IC芯片自动化测试系统适用于二极管、三极管、绝缘栅型场效应管、结型场效应管、单向和双向可控硅、普通和高速光耦、整流桥、共阴共阳二极管及多阵列器件等各类半导体分立器件综合性能自动化测试。 *被测项目&#xff…

vue3 script setup

解决在使用vue 3 composition API(组合式API)时繁琐的问题&#xff0c;比如定义一个方法&#xff0c;模板需要使用该方法&#xff0c;就必须将方法返回&#xff0c;当组件中存在大量方法和属性时就很麻烦。 一、什么是script setup 二、script setup什么作用 1.自动注册子组件 …

flutter 主题色彩管理组件 flex_color_scheme

flutter 主题色彩管理组件 flex_color_scheme 前言 原文 https://ducafecat.com/blog/flutter-flex-color-scheme 平时我们做样式适配关心几个方面: 设计稿颜色 标记尺寸大小、比例 全局修改为主 快速可修改 今天将会介绍一个快速调整主题色彩样式的三方组件 flex_color_schem…

[RocketMQ] NameServer启动流程源码解析 (一)

文章目录 1.NameServer概述2.NamesrvStartup启动入口3.createNamesrvController创建NamesrvController3.1 创建NamesrvController 4.start启动NamesrvController4.1 初始化NettyServer4.1.1 创建NettyRemotingServer4.1.2 registerProcessor注册默认请求处理器4.1.3 启动定时任…

Google C++ Style文档及常用代码规范(一):命名约定、格式、注释

文章目录 Google C Style文档及常用代码规范&#xff08;一&#xff09;&#xff1a;命名约定、格式、注释命名约定通用命名规则文件命名类型命名变量命名常量命名函数命名命名空间命名枚举命名宏命名命名规则的特例 格式注释注释风格文件注释类注释函数注释变量注释类数据成员…

flutter getx nested navigation 嵌套路由

flutter getx nested navigation 嵌套路由 视频 https://youtu.be/SXER4JVBFps 前言 嵌套路由可以用在如购物确认向导界面切换。 使用 getx 实现嵌套路由&#xff0c;需要如下步骤&#xff1a; 通过 Navigator 组件的 key 属性 用 Get.nestedKey(1) 进行标记 onGenerateRoute…

delmia msd学习

在默认打开的结构树中添加一个产品tworobts TwoRobots是新建的一个空产品&#xff0c;并将其插入到resoourceslist下面通过 然后创建一个工位的工作区域 插入机器人 把机器人放在工作区域中,即其子物体 先选要移动的对象&#xff0c;然后选移动到什么地方 Keep positions的意思…

栈的概念和结构以及实现

1. 栈 1.1栈的概念及结构 栈:一种特殊的线性表&#xff0c;其只允许在 固定的一端 进行 插入和删除 元素操作。 进行数据插入和删除 操作的一端称为 栈顶 &#xff0c;另一端称为 栈底 。栈中的数据元素遵守 后进先出 LIFO (Last in First Out) 的原则。 压栈:栈的插入操作叫做…

【带你刷《剑指Offer》系列】【每天40分钟,跟我一起用50天刷完 (剑指Offer)】第一天

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

python:使用Scikit-image对单波段遥感影像进行形状特征提取(morphology)

作者:CSDN @ _养乐多_ 本文将介绍使用Scikit-image对单波段遥感影像做形状特征提取的方法和代码。包括:腐蚀(erosion),膨胀(dilation),开运算(opening),闭运算(closing),形态学梯度(morphological gradient),白帽变换(top hat),黑帽变换(black hat),形…

一、枚举类型——用EnumSet来代替标识

Set 是一种不允许有重复元素存在的集合。enum 要求每个内部成员都是唯一的&#xff0c;因此看起来很像 Set&#xff0c;但是由于无法添加或移除元素&#xff0c;它并不如 Set 那么好用。于是 EnumSet 被引入&#xff0c;用来配合 enum 的使用&#xff0c;以替代传统的基于 int …

计算机启动

按下主机上的 power 键后&#xff0c;第一个运行的软件是 BIOS,BIOS 全称叫 Base Input & Output System&#xff0c;即基本输入输出系统。 &#xff08;8086的1MB内存&#xff09; 地址 0&#xff5e;0x9FFFF 处是 DRAM&#xff0c;顶部的 0xF0000&#xff5e;0xFFFFF&am…