建议大家学习编程的时候务必看看算法,看看底层如何实现,无论学习任何一门语言,我相信设计模式是必须掌握熟知的,此篇文章以​Java​语言为代表,讲解了23种设计模式,并有例子。祝大家学习进步,QQ交流群127591054.

设计模式建议书籍:Head First

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。 

项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

一、设计模式的分类

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

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

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

二、Java的23中设计模式

1、工厂方法模式(Factory Method)

    工厂方法模式分为三种:普通工厂模式   多个工厂方法模式   静态工厂方法模式

1.1、普通工厂模式,就是建立一个工厂类,对实现了同一接口的产品类进行实例的创建

例子:
//发送短信和邮件的接口
public interface Sender {
public void Send();
}

//发送邮件的实现类
public class MailSender implements Sender {
public void Send() {
System.out.println("发送邮件!");
}
}
//发送短信的实现类
public class SmsSender implements Sender {
public void Send() {
System.out.println("发送短信!");
}
}

//创建工厂类
public class SendFactory {
//工厂方法
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}

//测试类
public class FactoryTest {

public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produce("sms");
sender.Send();
}
}

1.2、多个工厂方法模式 是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

//将上面的代码做下修改,改动下SendFactory类就行
//这个就不用根据用户传的字符串类创建对象了
public class SendFactory {

public Sender produceMail(){
return new MailSender();
}

public Sender produceSms(){
return new SmsSender();
}
}

//测试类
public class FactoryTest {

public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}

1.3、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

public class SendFactory {  

public static Sender produceMail(){
return new MailSender();
}

public static Sender produceSms(){
return new SmsSender();
}
}

//测试类
public class FactoryTest {

public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}

2、抽象工厂模式(Abstract Factory) 

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

    例子:

//发送短信和邮件的接口
public interface Sender {
public void Send();
}

//发送邮件的实现类
public class MailSender implements Sender {
public void Send() {
System.out.println("发送邮件!");
}
}
//发送短信的实现类
public class SmsSender implements Sender {
public void Send() {
System.out.println("发送短信!");
}
}

//给工厂类一个接口
public interface Provider {
public Sender produce();
}
//两个工厂的实现类
public class SendMailFactory implements Provider {
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
public Sender produce() {
return new SmsSender();
}
}

//测试类
public class Test {

public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}

注:这个模式的好处就是,如果你现在想增加一个功能:发送及时信息,则只需做一个实现类实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好

3、单例模式(Singleton) 

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处: 

1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。 

2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。 

3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。

例子:
//简单的单例类 饿汉模式
public class Singleton {

/* 持有私有静态实例,防止被引用*/
private static Singleton instance = new Singleton();

/* 私有构造方法,防止被实例化 */
private Singleton() {
}

/* 静态工程方法,返回Singleton实例 */
public static Singleton getInstance() {
return instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
private Object readResolve() {
return instance;
}
}

这个类是可以实现单例模式的,但是存在不少问题,比如在类中不管用户是否要使用该类的对象,就先创建好了一个实例放在内存中。

//简单的单例类 懒汉模式
public class Singleton {

/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;

/* 私有构造方法,防止被实例化 */
private Singleton() {
}

/* 静态工程方法,创建实例 */
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
private Object readResolve() {
return instance;
}
}

这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:

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

但是,synchronized作为修饰符在方法上使用,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:

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

似乎解决了之前提到的问题,将synchronized关键字加在了方法内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的。

看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句并非是一个原子操作,在 JVM 中这句代码大概做了下面 3 件事情:

    1给 new的对象 分配内存
2调用 Singleton 的构造函数来初始化成员变量
3将引用instance指向分配的内存空间(执行完这步 instance 就为非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,另外一个线程B抢夺到了CPU的执行权,这时instance已经是非null了(但却没有初始化),所以线程B会直接返回 instance,然后使用,结果就会出现问题了(因为对象还没有初始化)。

还有另外一种解决方案:使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的(就是加载完毕后别的线程才能使用)。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。例如:

public class Singleton {  

/* 私有构造方法,防止被实例化 */
private Singleton() {
}

/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}

/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
private Object readResolve() {
return getInstance();
}
}

但是如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。

同时,我们还可以使用反射去创建这个类的对象,即使它的构造器是私有的,我们也是可以调用到的。那么这个时候我们就需要再次修改代码去访问别人反射调用构造器。

例子:
//在构造器中控制一下,构造器只允许调用一次,之后在调用就抛出异常
public class Singleton {
private static boolean flag;
/* 私有构造方法,防止被实例化 */
private Singleton() {
if(!flag){
flag = false;
}else{
throw new RuntimeException("不能多次创建单例对象");
}
}

/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}

/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
private Object readResolve() {
return getInstance();
}
}

反射的问题处理完了之后,这里还有一个问题,就是如果把单例对象进行序列化然后再反序列化,那么内存中就会出现俩个一样的单例对象,只是内存地址不同。这种情况我们可以使用readResolve方法来防止。

private Object readResolve(){.....}
ObjectInputStream 会检查对象的class是否定义了readResolve方法。如果定义了,将由readResolve方法指定返回的对象。返回对象的类型一定要是兼容的,否则会抛出ClassCastException 。
例子:
public abstract class Singleton8 implements Serializable{

private static final long serialVersionUID = 7863921642928237696L;

/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
@SuppressWarnings("serial")
private static Singleton8 instance = new Singleton8(){};
}

//测试方式,把单例对象序列化后再反序列化从而获得一个新的对象 就相当于复制了一个单例对象
public Singleton8 copy() throws Exception{
(ByteArrayOutputStream在溜关闭之后,还会被调用,并且不会产生任何exception)
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(Singleton8.getInstance());

InputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
Singleton8 obj = (Singleton8)ois.readObject();
return obj;
}

/* 获取实例 */
public static Singleton8 getInstance() {
return SingletonFactory.instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
/* 把这个方法注释前和注释后来运行测试代码观察结果 */
private Object readResolve() {
return getInstance();
}
}

最后再思考一个问题 : 是否可以使用枚举来实现单例模式?

4、建造者模式(Builder) 

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。因此, 建造者模式主要用来解决“对象部分”的需求变化。 这样可以对对象构造的过程进行更加精细的控制。 

例子: 

//CPU接口 

public interface CPU {

}
//Inter的cup
class IntelCPU implements CPU{

}
//AMD的cpu
class AMDCPU implements CPU{

}

//内存接口
public interface Memory {

}
//金士顿内存
class KingstonMemory implements Memory{

}
//三星内存
class SamsungMemory implements Memory{

}

//主板内存
public interface Mainboard {

}
//华硕主板
class AsusMainboard implements Mainboard{

}
//技嘉主板
class GaMainboard implements Mainboard{

}

//计算机
public class Computer {
private CPU cpu;
private Memory memory;
private Mainboard mainboard;
get/set
}

//计算机的builder的接口
public interface ComputerBuilder {
public void buildCPU();
public void buildMemory();
public void buildMainboard();
public Computer getComputer();
}


//联想电脑的builder
public class LenoveComputerBuilder implements ComputerBuilder {
private Computer lenoveComputer;
public LenoveComputerBuilder(){
lenoveComputer = new Computer();
}
public void buildCPU() {
lenoveComputer.setCpu(new IntelCPU());
}
public void buildMemory() {
lenoveComputer.setMemory(new KingstonMemory());
}
public void buildMainboard() {
lenoveComputer.setMainboard(new AsusMainboard());
}
public Computer getComputer() {
return lenoveComputer;
}
}

//惠普电脑的builder
public class HPComputerBuilder implements ComputerBuilder {
private Computer HPComputer;

public HPComputerBuilder(){
HPComputer = new Computer();
}
public void buildCPU() {
HPComputer.setCpu(new AMDCPU());
}
public void buildMemory() {
HPComputer.setMemory(new SamsungMemory());
}
public void buildMainboard() {
HPComputer.setMainboard(new GaMainboard());
}
public Computer getComputer() {
return HPComputer;
}
}

//Director类(导演)
//指导如何具体的创造电脑
public class Director {
private ComputerBuilder builder;
public Director(ComputerBuilder builder) {
this.builder = builder;
}
//用户自定义的自造顺序 具体指导各种builder如何创建电脑
public void construct() {

builder.buildCPU();
builder.buildMemory();
builder.buildMainboard();
}
}

//测试类
public class Test {
public static void main(String[] args) {
Computer lenoveComputer = null;
ComputerBuilder lenoveComputerBuilder = new LenoveComputerBuilder();
Director director = new Director(lenoveComputerBuilder);
director.construct();
lenoveComputer = lenoveComputerBuilder.getComputer();
System.out.println(lenoveComputer);
}
}

从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建适合对象的多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。 

例如一个Person类是由头、身体、脚三个对象组成,那么我们在建造者模式中就要先分别创造出这三个部分然后再把他们组装成一个Person对象。

5、原型模式(Prototype) 

原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。在Java中,复制对象是通过clone()实现的,先创建一个原型类:

public class Prototype implements Cloneable {  

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

很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,说明这个方法实现并不是使用java语言。

在这里先认识俩个概念 : 浅复制 深复制,浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

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

public class Prototype implements Cloneable, Serializable {  

private static final long serialVersionUID = 1L;
private String string;
//这个是在下面声明的一个类
private SerializableObject obj;

/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}

/* 深复制 */
public Object deepClone() throws IOException, ClassNotFoundException {

/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}

public String getString() {
return string;
}

public void setString(String string) {
this.string = string;
}

public SerializableObject getObj() {
return obj;
}

public void setObj(SerializableObject obj) {
this.obj = obj;
}

}

class SerializableObject implements Serializable {
private static final long serialVersionUID = 1L;
}

上面是五种是创建型模式,接下来看一下7种结构型模式 : 适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式

6、适配器模式(Adapter) 

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

类的适配器模式: 

核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里

        例子:
public class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {

/* 与原类中的方法相同 */
public void method1();

/* 新的方法 */
public void method2();
}

//类Source和接口Targetable因为不兼容,导致不能在一起工作
//适配器Adapter则可以在不改变源代码的基础上解决这个问题
//这样Targetable接口的实现类Adapter的对象即使Targetable类型,也能访问到Source中的方法
public class Adapter extends Source implements Targetable {
public void method2() {
System.out.println("this is the targetable method!");
}
}

//测试类 这样Targetable接口的实现类就具有了Source类的功能。
public class AdapterTest {
public static void main(String[] args) {
Targetable target = new Adapter();
target.method1();
target.method2();
}
}

对象的适配器模式 

基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题

    例子:
//只需要修改Adapter类的源码即可:
public class Wrapper implements Targetable {

private Source source;

public Wrapper(Source source){
this.source = source;
}
public void method2() {
System.out.println("this is the targetable method!");
}
public void method1() {
source.method1();
}
}

//测试类 输出与第一种情况一样,只是使用的适配方法不同而已。
public class AdapterTest {

public static void main(String[] args) {
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
}
}

接口的适配器模式 

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。 

我们这GUI这个章节应该是见过不少的监听器接口的适配器类:XxxxAdapter

例子:
public interface Sourceable {
public void method1();
public void method2();
}
//抽象类
public abstract class Wrapper implements Sourceable{
public void method1(){}
public void method2(){}
}

之后在我们写的子类中需要什么方法去重写什么方法就可以了,就不需要把接口中的所有方法都实现了

三种情况适配器模式的总结: 

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

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

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

7、装饰模式(Decorator) 

顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是【动态】的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例 

这里的动态指的是用户可以根据自己的需求把之前定好的功能任意组合。 

JDK中的IO流部分就是典型的使用了装饰模式,回忆一下BufferedReader对象的是如何创建的

  例子:
//功能接口
public interface Action {
public void go();
}
//被装饰的类 就是需要我们装饰的目标
public class Person implements Action{
public void go() {
System.out.println("我在走路");
}
}

//抽象的装饰类
public abstract class Decorator implements Action{
private Action action;
public Decorator(Action action) {
this.action = action;
}
public void go() {
this.action.go();
}
}

//具体的装饰类 可以添加一个听音乐的功能
public class ListenDecorator extends Decorator{
public ListenDecorator(Action action) {
super(action);
}
public void go() {
listen();//可以在go方法【前】添加一个听音乐的功能
super.go();
}
public void listen(){
System.out.println("我在听音乐");
}

}

//具体的装饰类 可以添加一个休息的功能
public class RelaxDecorator extends Decorator{
public RelaxDecorator(Action action) {
super(action);
}
public void go() {
super.go();
relax();//可以在go方法【后】添加一个休息的功能
}
public void relax(){
System.out.println("我在休息");
}
}

//测试类
public class Test {
/*用户可以根据需求 任意给go方法添加听音乐或者休息的功能*/
//Action a = new Person();
//Action a = new ListenDecorator(new Person());
//Action a = new RelaxDecorator(new Person());
//Action a = new RelaxDecorator(new ListenDecorator(new Person()));
Action a = new ListenDecorator(new RelaxDecorator(new Person()));
a.go();
}

装饰器模式的应用场景: 

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

2、动态的为一个对象增加功能,而且还能动态撤销。 

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

8、代理模式(Proxy) 

其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

    例子:
//公共接口
public interface Sourceable {
public void method();
}
//目标类/被代理类
public class Source implements Sourceable {

public void method() {
System.out.println("the original method!");
}
}
//代理类
public class Proxy implements Sourceable {

private Source source;
public Proxy(Source source){
this.source = source;
}
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}

//测试类
public class ProxyTest {
public static void main(String[] args) {
Source target = new Source();
Sourceable proxy = new Proxy(target);
proxy.method();
}
}

代理模式的应用场景: 

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法: 

1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。 

2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。 

使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

注意: 装饰模式和代理模式在很多情况下,大部分代码都是类似的,但是这俩种设计的意图是不一样的,装饰模式是增强被包装对象的功能,代理模式是控制被代理对象的行为

例如一块代码,如果被描述为使用了装饰模式,那么我们就知道设计的意图是增加被包装对象的功能,如果被描述为使用了代理模式,那么我们就知道设计的意图是控制被代理对象的行为,虽然这俩种情况下他们的代码结构基本相同.

1、装饰器模式:能动态的新增或组合对象的行为。 

2、代理模式 :为目标对象提供一种代理以便控制对这个对象的访问. 

3、装饰模式是“新增行为”,而代理模式是“控制访问”。

1.装饰模式:对被装饰的对象增加额外的行为 

如:杯子生产线,杯子必须可以装水,在生产线上可以给杯子涂颜色,加杯盖,但要保证杯子可以装水。 

2.代理模式:对被代理的对象提供访问控制。 

如:客户网上商城订购商品,网上商城是厂家的代理,网上商城可以帮客户完成订购商品的任务,但是商城可以对商品进行控制,不交钱不给商品,人不在不给商品,也可以赠送你额外的礼品,代金券。

对代理模式的一些重要扩展 

用户tom—买—>商品 

由于各种原因导致不是很方便购买,所以就找代购 

用户tom—找—>代购者zs—买—>商品 

那么在代理模式中,用户tom就是目标对象,代购者zs就是代理对象 

创建目标对象的类叫目标类或者被代理类 

创建代理对象的类加代理类

建时期,代理类可分为两种: 

1、静态代理类: 

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 

2、动态代理类:在程序运行时,运用反射机制动态创建而成。 

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包下面的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

CGLib代理(第三方类库)
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为目标对象创建一个子类对象,并在子类对象中拦截所有父类方法的调用,然后在方法调用前后调用后都可以加入自己想要执行的代码。
需要这种方法只是需要俩个第三方jar包: cglib-3.2.1.jar和asm-5.0.4.jar
同时很多框架已经把这些jar包整合到一起了,比如spring框架的spring-core-3.2.4.RELEASE.jar,这一个jar包就包括上述俩个jar包的大多数功能

静态代理: staticProxy

        例子:
//公共接口
public interface HelloService {
void sayHello();
}
//委托类
public class HelloServiceImpl implements HelloService{
public void sayHello() {
System.out.println("hello world");
}
}
//代理类
public class HelloServiceProxy implements HelloService{
private HelloService target;
public HelloServiceProxy(HelloService target) {
this.target = target;
}
public void sayHello() {
System.out.println("log:sayHello马上要执行了...");
target.sayHello();
}
}
//测试类
public class Test {
public static void main(String[] args) {
//目标对象
HelloService target = new HelloServiceImpl();
//代理对象
HelloService proxy = new HelloServiceProxy(target);
proxy.sayHello();
}

}

JDK的动态代理: dynamicProxy

    例子:
//Student类
public class Student {
private long id;
private String name;
private int age;
get/set
}
//日志类
public class StudentLogger {
public void log(String msg){
System.out.println("log: "+msg);
}
}
//Service接口 处理学生的相关业务
public interface IStudentService {
void save(Student s);
void delete(long id);
Student find(long id);
}
//接口的一个简单实现
public class StudentServiceImpl implements IStudentService {
public void delete(long id) {
System.out.println("student is deleted...");
}
public Student find(long id) {
System.out.println("student is found...");
return null;
}
public void save(Student s) {
System.out.println("student is saved...");
}
}

//InvocationHandler接口的实现类
//JDK动态代理中必须用到的接口实现
public class MyHandler implements InvocationHandler{
private Object target;
private StudentLogger logger = new StudentLogger();

public MyHandler(Object target, StudentLogger logger) {
this.target = target;
this.logger = logger;
}

public MyHandler(Object target) {
this.target = target;
}

//参数1 proxy 将来给目标对象所动态产生的代理对象
//参数2 method 将来你所调用的目标对象中的方法的镜像
//参数3 args 将来你所调用方法的时候所传的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
String msg = method.getName()+"方法被调用了...";
logger.log(msg);
Object o = method.invoke(target, args);
return o;
}
}

//测试类
public class DProxyTest {
public static void main(String[] args) {
IStudentService target = new StudentServiceImpl();

ClassLoader loader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h = new MyHandler(target);

//参数1 loader 目标对象的类加载器
//参数2 interfaces 目标对象所实现的接口
//参数3 h InvocationHandler接口的实现类对象
IStudentService proxy = (IStudentService)Proxy.newProxyInstance(loader, interfaces, h);

proxy.delete(1);
proxy.save(null);
proxy.find(1);

System.out.println(proxy.toString());
System.out.println(proxy.getClass());
System.out.println(target.getClass());
}

}

第三方jar包提供的动态代理(cglib) CglibProxy

例子:
//目标的对象 没有实现接口
public class BookService {
public void addBook() {
System.out.println("添加书籍成功");
}
}

//产生代理对象的工厂类
public class MyCglibProxyFactory implements MethodInterceptor {

public Object getInstance(Class<?> c) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(c);
enhancer.setCallback(this);
return enhancer.create();
}

public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {

System.out.println("开始执行方法");
//这句代码最终会执行到我们目标对象中的方法
proxy.invokeSuper(obj, args);
System.out.println("方法执行结束");

return null;
}
}
//测试类
public class TestCglibProxy {
public static void main(String[] args) {
MyCglibProxyFactory cglib=new MyCglibProxyFactory();
BookService bookCglib=
(BookService)cglib.getInstance(new BookService().getClass());

bookCglib.addBook();
System.out.println(bookCglib.getClass());
}
}

9、外观模式(Facade) 

外观模式也可以叫做门面模式 

为子系统或者模块中的一组接口提供一个一致的访问方式,此模式定义了一个高层接口,这个接口使得各个子系统/模块中的功能更加容易使用。 

实际应用中,我们在对付一些老旧的代码或者即便不是老旧code,但涉及多个子系统时,除了重写全部代码,我们还可能采用这样一种策略:重新进行类的设计,将原来分散在源码中的类/结构及方法重新组合,形成新的、统一的接口,供上层应用使用,同时也隐藏了子系统或者子模块中功能实现的复杂性

    例子:
//模块A中的类
public class ServiceA {
public void start(){
System.out.println("模块A中的start方法");
}
}
//模块B中的类
public class ServiceB {
public void run(){
System.out.println("模块B中的run方法");
}
}

//模块C中的类
public class ServiceC {
public void end(){
System.out.println("模块C中的end方法");
}
}

//外观类/门面类
public class Facade {
private ServiceA a;
private ServiceB b;
private ServiceC c;
public Facade() {
a = new ServiceA();
b = new ServiceB();
c = new ServiceC();
}

public void start(){
a.start();
}
public void run(){
b.run();
}
public void end(){
c.end();
}

public void service(){
a.start();
b.run();
c.end();
}

}

//测试类
public class Test {

public static void main(String[] args) {
Facade f = new Facade();
f.start();
f.run();
f.end();

f.service();
}
}

Facade是我们的外观类/门面类,用户可以通过这个类使用到系统中不同模块中的不同方法,同时也对用户隐藏了系统中对这些功能的实现细节。给用户提供了一个统一的访问方式。

10、桥接模式(Bridge) 

桥接模式(也叫桥梁模式)就是将抽象部分和实现部分分离,使它们都可以独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接​​数据库​​的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。

    例子:
//公共的驱动接口
public interface Driver {
public void getConnection();
}
//第一个实现类 mysql驱动类
public class MysqlDriver implements Driver{
public void getConnection() {
System.out.println("mysql 数据库连接");
}
}
//第二个实现类 oracle驱动类
public class OracleDriver implements Driver {
public void getConnection() {
System.out.println("oracle数据库连接");
}
}
//抽象的管理器 Bridge
public abstract class Manager {
private Driver driver;
public void getConnection(){
driver.getConnection();
}
public void setDriver(Driver driver) {
this.driver = driver;
}
}
//具体的驱动管理器 Bridge
public class DriverManager extends Manager {
public DriverManager(Driver driver){
setDriver(driver);
}
public void getConnection() {
super.getConnection();
}
}


//测试类 注意我们的抽象和具体实现是分开的,无论他们如何变化都不会影响到我们bridge中的功能执行
//JDBC中,我们使用的就是一系列javaAPI提供的接口,而且数据公司商则给我们提供接口的实现
public class Test {
public static void main(String[] args) {

DriverManager manager = new DriverManager(new MysqlDriver());
manager.getConnection();

manager = new DriverManager(new OracleDriver());
manager.getConnection();
}
}

11、组合模式(Composite) 

组合模式有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便

    例子:
//节点类
public class TreeNode {

private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();

public TreeNode(String name){
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public TreeNode getParent() {
return parent;
}

public void setParent(TreeNode parent) {
this.parent = parent;
}

//添加孩子节点
public void add(TreeNode node){
children.add(node);
}

//删除孩子节点
public void remove(TreeNode node){
children.remove(node);
}

//取得孩子节点
public Enumeration<TreeNode> getChildren(){
return children.elements();
}
}
//表示一个树状结构
public class Tree {

TreeNode root = null;

public Tree(String name) {
root = new TreeNode(name);
}
}

//测试类
public class Test{

public static void main(String[] args) {
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");

nodeB.add(nodeC);
tree.root.add(nodeB);
System.out.println("build the tree finished!");
}
}

12、享元模式(Flyweight) 

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

    例子:
//数据库连接池
public class ConnectionPool {

private Vector<Connection> pool;

/*公有属性*/
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";

private int poolSize = 100;
//这里对instance可以使用一个单例模式
private static ConnectionPool instance = null;
Connection conn = null;

/*构造方法,做一些初始化工作*/
private ConnectionPool() {
pool = new Vector<Connection>(poolSize);

for (int i = 0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

/* 把连接对象返回到连接池 */
public synchronized void release() {
pool.add(conn);
}

/* 返回连接池中的一个数据库连接 */
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
} else {
return null;
}
}
}




接下来我们再看剩余的11种行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

13、策略模式(Strategy) 

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

    例子:
//统一的接口
public interface ICalculator {
public int calculate(String exp);
}

//抽象类,作为辅助类,可以提供一些你认为需要的方法
public abstract class AbstractCalculator {
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}

//接口的三个实现类:
public class Plus extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"[+]");
return arrayInt[0]+arrayInt[1];
}
}

public class Minus extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}

public class Multiply extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"[*]");
return arrayInt[0]*arrayInt[1];
}
}

//测试类
public class Test {

public static void main(String[] args) {
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
}
}

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
我们之前在学校TreeSet排序的时候,有一种叫做资客户化排序的方式,就是给TreeSet传一个比较器对象,这个其实就是使用了策略模式

14、模板方法模式(Template Method) 

模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类的方法,实现对子类的调用 

其实就是我们之前所说的: 

子类重新/实现父类中的方法,那么调用该方法的时候则是调用到了子类中重写之后的方法

    例子:
//父类
public abstract class AbstractCalculator {

/*实现对本类其它方法的调用*/
public final int calculate(String exp,String opt){
int array[] = split(exp,opt);
return calculate(array[0],array[1]);
}

/*被子类重写的方法*/
abstract public int calculate(int num1,int num2);

public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}

//子类
public class Plus extends AbstractCalculator {
public int calculate(int num1,int num2) {
return num1 + num2;
}
}

//测试类
public class Test {

public static void main(String[] args) {
String exp = "8+8";
AbstractCalculator cal = new Plus();
int result = cal.calculate(exp, "\\+");
System.out.println(result);
}
}

15、观察者模式(Observer) 

观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。 

我们在GUI那一章学习的事件监听机制就是可以这种设置模式来构建的代码

    例子:
//观察者接口
public interface Observer {
public void update();
}

//观察者1
public class Observer1 implements Observer {
public void update() {
System.out.println("observer1 has received!");
}
}

//观察者2
public class Observer2 implements Observer {
public void update() {
System.out.println("observer2 has received!");
}
}

//被观察者接口
public interface Subject {

/*增加观察者*/
public void add(Observer observer);

/*删除观察者*/
public void del(Observer observer);

/*通知所有的观察者*/
public void notifyObservers();

/*自身的操作*/
public void operation();
}

//被观察者的一个抽象实现 提供基本的实现
public abstract class AbstractSubject implements Subject {

private Vector<Observer> vector = new Vector<Observer>();
public void add(Observer observer) {
vector.add(observer);
}

public void del(Observer observer) {
vector.remove(observer);
}

public void notifyObservers() {
Iterator<Observer> it = vector.iterator();
while(it.hasNext()){
Observer next = it.next();
next.update();
}
}
}

//我们自己的一个被观察者实现 里面可以有我们自己的各种属性和方法
public class MySubject extends AbstractSubject {
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}

//测试类
public class Test {

public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
}
}

16、迭代器模式(Iterator) 

顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。 

例子:

public interface Collection {  
public Iterator iterator();

/*取得集合元素*/
public Object get(int i);

/*取得集合大小*/
public int size();
}

public interface Iterator {
//前移 上一个元素
public Object previous();

//后移 下一个元素
public Object next();
public boolean hasNext();

//取得第一个元素
public Object first();
}

public class MyCollection implements Collection {
//假设这个集合内部是由数组实现
public String string[] = {"A","B","C","D","E"};

public Iterator iterator() {
return new MyIterator(this);
}
public Object get(int i) {
return string[i];
}
public int size() {
return string.length;
}
}

//这个地方其实一般会设计为内部类
public class MyIterator implements Iterator {

private Collection collection;
private int pos = -1;

public MyIterator(Collection collection){
this.collection = collection;
}
public Object previous() {
if(pos > 0){
pos--;
}
return collection.get(pos);
}
public Object next() {
if(pos<collection.size()-1){
pos++;
}
return collection.get(pos);
}
public boolean hasNext() {
if(pos<collection.size()-1){
return true;
}else{
return false;
}
}
public Object first() {
pos = 0;
return collection.get(pos);
}
}

//测试类
public class Test {

public static void main(String[] args) {
Collection collection = new MyCollection();
Iterator it = collection.iterator();

while(it.hasNext()){
System.out.println(it.next());
}
}
}

17、责任链模式(Chain of Responsibility) 

责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。 

(web应该中学习到的Filter其实就是一个责任链设计模式)

    例子:
public interface Handler {
public void operator();
}

public class MyHandler implements Handler {

private String name;
private Handler handler;

public MyHandler(String name) {
this.name = name;
}

public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}

public void operator() {
System.out.println("name = "+name);
if(getHandler()!=null){
getHandler().operator();
}
}
}

//测试类
public class Test {

public static void main(String[] args) {
MyHandler h1 = new MyHandler("h1");
MyHandler h2 = new MyHandler("h2");
MyHandler h3 = new MyHandler("h3");

h1.setHandler(h2);
h2.setHandler(h3);

h1.operator();
}
}

18、命令模式(Command) 

命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者(司令、命令、士兵)相互解耦,任何一方都不用去依赖其他人的具体实现,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。

    例子:
//命令执行的接口
public interface Command {
public void exe();
}
//具体实现的命令
public class MyCommand implements Command {

private Receiver receiver;

public MyCommand(Receiver receiver) {
this.receiver = receiver;
}
public void exe() {
receiver.action();
}
}

//被调用者(士兵)
public class Receiver {
public void action(){
System.out.println("command received!");
}
}
//调用者(司令员)
public class Invoker {

private Command command;

public Invoker(Command command) {
this.command = command;
}

public void action(){
command.exe();
}
}
//测试类
public class Test {

public static void main(String[] args) {
Receiver receiver = new Receiver();
Command cmd = new MyCommand(receiver);
Invoker invoker = new Invoker(cmd);
invoker.action();
}
}

这个很好理解,命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开。
对于大多数请求-响应模式的功能,比较适合使用命令模式。

19、备忘录模式(Memento) 

也可以叫备份模式,主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。

    例子:
//原始类,里面有需要保存的属性value
public class Original {

private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public Original(String value) {
this.value = value;
}

//创建备忘录对象用来存储属性值
public Memento createMemento(){
return new Memento(value);
}

//还原属性值
public void restoreMemento(Memento memento){
this.value = memento.getValue();
}
}

//备忘录类,用来保存value值
public class Memento {

private String value;

public Memento(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}

//存储备忘录的类,持有Memento类的实例
public class Storage {

private Memento memento;

public Storage(Memento memento) {
this.memento = memento;
}

public Memento getMemento() {
return memento;
}

public void setMemento(Memento memento) {
this.memento = memento;
}
}

//测试类
public class Test {

public static void main(String[] args) {

// 创建原始类
Original origi = new Original("egg");

// 创建备忘录
Storage storage = new Storage(origi.createMemento());

// 修改原始类的状态
System.out.println("初始化状态为:" + origi.getValue());
origi.setValue("niu");
System.out.println("修改后的状态为:" + origi.getValue());

// 回复原始类的状态
origi.restoreMemento(storage.getMemento());
System.out.println("恢复后的状态为:" + origi.getValue());
}
}

20、状态模式(State) 

核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作。再比如交通灯,有红黄绿三种状态,每种状态下操作也是不一样的

    例子:
//状态类
public class State {

private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public void method1(){
System.out.println("execute the first opt!");
}

public void method2(){
System.out.println("execute the second opt!");
}
}

//Context类可以实现切换状态
public class Context {

private State state;

public Context(State state) {
this.state = state;
}

public State getState() {
return state;
}

public void setState(State state) {
this.state = state;
}

public void method() {
if (state.getValue().equals("state1")) {
state.method1();
} else if (state.getValue().equals("state2")) {
state.method2();
}
}
}

//测试类
public class Test {

public static void main(String[] args) {

State state = new State();
Context context = new Context(state);

//设置第一种状态
state.setValue("state1");
context.method();

//设置第二种状态
state.setValue("state2");
context.method();
}
}

21、访问者模式(Visitor) 

访问者模式把​​数据结构​​和作用于结构上的操作解耦合,使得对数据操作可相对自由地演化。访问者模式适用于数据结构相对稳定,算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。 

简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。

    例子:
//访问者接口
public interface Visitor {
public void visit(Subject sub);
}
//访问者的一个具体实现
public class MyVisitor implements Visitor {
public void visit(Subject sub) {
System.out.println("visit the subject:"+sub.getSubject());
}
}

//被访问者接口
public interface Subject {
public void accept(Visitor visitor);
public String getSubject();
}
//被访问者的一个具体实现
public class MySubject implements Subject {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getSubject() {
return "love";
}
}
//测试类
public class Test {

public static void main(String[] args) {

Visitor visitor = new MyVisitor();
Subject sub = new MySubject();
sub.accept(visitor);
}
}

该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添加?3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦

22、中介者模式(Mediator) 

介者模式也是用来降低类和类之间的耦合的,因为如果类和类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行

    例子:
//中间者接口
public interface Mediator {
public void createMediator();
public void workAll();
}
//中介者的一个具体实现
public class MyMediator implements Mediator {

private User user1;
private User user2;

public User getUser1() {
return user1;
}

public User getUser2() {
return user2;
}
public void createMediator() {
user1 = new User1(this);
user2 = new User2(this);
}
public void workAll() {
user1.work();
user2.work();
}
}
//抽象类
public abstract class User {

private Mediator mediator;

public Mediator getMediator(){
return mediator;
}

public User(Mediator mediator) {
this.mediator = mediator;
}
public abstract void work();
}
//User1
public class User1 extends User {
public User1(Mediator mediator){
super(mediator);
}
public void work() {
System.out.println("user1 exe!");
}
}
//User2
public class User2 extends User {
public User2(Mediator mediator){
super(mediator);
}
public void work() {
System.out.println("user2 exe!");
}
}
//测试类
public class Test {

public static void main(String[] args) {
Mediator mediator = new MyMediator();
mediator.createMediator();
mediator.workAll();
}
}

适用场景 

在面向对象编程中,一个类必然会与其他的类发生依赖关系,完全独立的类是没有意义的。一个类同时依赖多个类的情况也相当普遍,既然存在这样的情况,说明,一对多的依赖关系有它的合理性,适当的使用中介者模式可以使原本凌乱的对象关系清晰,但是如果滥用,则可能会带来反的效果。一般来说,只有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。可以将网状结构变为星状结构,使同事类之间的关系变的清晰一些。

23、解释器模式(Interpreter)

例子:解释器接口(这里的是专门解析数学运算表达式)

public interface Expression {  
public int interpret(Context context);
}
//加法
public class Plus implements Expression {
public int interpret(Context context) {
return context.getNum1()+context.getNum2();
}
}
//减法
public class Minus implements Expression {
public int interpret(Context context) {
return context.getNum1()-context.getNum2();
}
}

//Context类是一个上下文环境类 持有运行中所需的数据
public class Context {

private int num1;
private int num2;

public Context(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}

public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
}

//测试类
public class Test {

public static void main(String[] args) {

// 计算9+2-8的值
int result = new Minus().interpret(new Context(
new Plus().interpret(new Context(9, 2)), 8));
//相当于:new Minus().interpret(new Context(11, 8));
System.out.println(result);
}
}

在以下情况下可以使用解释器模式:

    有一个简单的语法规则,比如一个sql语句,如果我们需要根据sql语句进行其他语言的转换,就可以使用解释器模式来对语句进行解释。
一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。

END