[关闭]
@myecho 2019-03-27T11:20:13.000000Z 字数 13920 阅读 1159

设计模式

Java


UML基本知识

类图
+代表public
-代表private
#代表protected

一般化关系
关联关系
基数表
聚合关系
合成关系
依赖关系

六大原则

  1. 开闭原则(Open Close Principle)
    开闭原则就是说对扩展开放、对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。但是我们在后边可以看到很多的设计模式实际上没有实现这一点要求,比如简单工厂模式。
  2. 里氏代换原则(Liskov Substitution Principle)
    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。在代理模式、合成模式中均有体现。
  3. 依赖倒转原则(Dependence Inversion Principle)
    这个是开闭原则的基础,依赖于抽象而不依赖于具体,要依赖于接口编程而不针对实现编程。比如模板方法模式与迭代子模式(返回的是一个抽象的Iterator类型)
  4. 接口隔离原则(Interface Segregation Principle)
    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。比如迭代子模式中,聚集对象提供给迭代子对象是一个宽接口,另一个提供给客户端是窄接口
  5. 迪米特法则(最少知道原则)(Demeter Principle)
    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。除非绝对需要,不然不要与外界通信。即使必须进行通信,也应当尽量限制通信的广度和深度。如门面模式、调停者模式就是这一法则的典型应用。 在类的设计上尽量降低类的访问权限以及成员的访问权限也是迪米特法则的体现。
  6. 合成复用原则(Composite Reuse Principle)
    原则是尽量使用合成/聚合的方式,而不是使用继承。

工厂模式

简单工厂模式

核心代码就是

pulic static FruitFactory(String which) {
    if (which.equalsIgnoreCase("apple")) {
        return new Apple();
    } else if (which.equalsIgnoreCase("strawberry")) {
        return new StrawBerry();
    } else {
        throw new BadFruitException("Bad fruit request.");
    }
}

工厂方法模式(多态性工厂模式)

工厂方法与产品的对应层次
实际上有一个接口工厂类,然后有一个产品就产生一个对应的产品工厂实现类。
在Java中的应用
1. 所有的聚集对象都会有返回一个Iterator抽象接口的iterator方法,这个方法就是一个工厂方法,然后不同的聚集对象去实现自己的工厂逻辑
2. 所有继承了List接口的实现中,还包含了一个listIterator方法,也是一个工厂方法。

抽象工厂模式

这种工厂方法的基础上是产品族的概念,是指在不同的产品登记结构中,功能相关联的产品构成的家族。一般而言,有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。
对应层次

单例模式

private static Singleton _instance;

public static Singleton getInstance() {
        if (_instance == null) { //1
            _instance = new Singleton(); //2
        }
        return _instance; //3
}

但上边这种情况不适用于多线程的情况,反例如下所示,
假设两个线程并发调用 getInstance() 方法并且按以下顺序执行调用:
1. 线程 1 调用 getInstance() 方法并决定 instance 在 //1 处为 null 。
2. 线程 1 进入 if 代码块,但在执行 //2 处的代码行时被线程 2 预占。
3. 线程 2 调用 getInstance() 方法并在 //1 处决定 instance 为 null 。
4. 线程 2 进入 if 代码块并创建一个新的 Singleton 对象并在 //2 处将变量 instance 分配给这个新对象。
5. 线程 2 在 //3 处返回 Singleton 对象引用。
6. 线程 2 被线程 1 预占。
7. 线程 1 在它停止的地方启动,并执行 //2 代码行,这导致创建另一个 Singleton 对象。
8. 线程 1 在 //3 处返回这个对象。

朴素的多线程版本,但由于每次都要加锁,因此效率不高。

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

适用于多线程的双重检测锁:

private static Singleton _instance;
public static Singleton getInstanceDC() {
        if (_instance == null) {  // Single Checked
            synchronized (Singleton.class) {
                if (_instance == null) {  // Double checked
                    _instance = new Singleton();
                }
            }
        }
        return _instance;
}

似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B在single checked的位置进行判断,由于instance此时不是null,因此它马上离开了函数并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

可以通过将_instance实例设置为volatile变量来阻止指令重排,会在编译的字节码中加入内存屏障(读也加、写也加),jdk1.5之后有效。

synchronized只能保证代码块内的变量改变能够及时的写回去。
http://www.cnblogs.com/cookiezhi/p/5774583.html

另外一种解决当前指令重排问题的方式:(用另一个方法调用来保证不会发生指令重排的现象)

public class SingletonTest {

    private static SingletonTest instance = null;  

    private SingletonTest() {  
    }  

    private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }
    }

    public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
    }  
} 

预先加载方法

class S1 {   
    private S1() {
        System.out.println("ok1");   
    }   

    private static S1 instance = new S1(); 
    public static S1 getInstance() {
        return 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;  
    }  

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

C++中的单例模式:https://blog.csdn.net/qq_35280514/article/details/70211845

建造者模式

建造者通常需要完成产品的组装过程(组装可能仅是set一下或者有更复杂的处理逻辑),而不是仅仅像工厂方法一样直接返回产品类。

建造者UML图
其实在上图中,我们可以去掉导演者的角色,然后将Builder类中的组装逻辑return this来支持链式调用,导演者其实也就是完成了链式调用的这个过程并最后返回了Student类的任务而已。

Student.builder()
        .stuName("chenxuxu")
        .stuAge(22)
        .stuGrade("13级")
        .stuMajor("软件工程")
        .stuNo("123456789");

Student stu = new Student();
        stu.setAge(22);
        stu.setName("chenxuxu");
        stu.setGrade("13级");
        stu.setNo("123456789");
        stu.setMajor("软件工程");

public class Student {
    /**
     * 不能通过new初始化
     */
    private Student(){}
    private String name;
    private int age;
    private String no;
    private String grade;
    private String major;
    public static Builder builder(){
        return new Builder();
    }
    //public get and set functions ...
}

class Builder {
    private Student stu;
    private Builder(){} //只能通过Student.builder()获得
    public Builder stuName(String name){
        stu.name = name; // call set function
        return this;
    }
    public Builder stuAge(int age){
        stu.age = age;
        return this;
    }
    public Builder stuNo(String no){
        stu.no = no;
        return this;
    }
    public Builder stuGrade(String grade){
        stu.grade = grade;
        return this;
    }
    public Builder stuMajor(String major){
        stu.major = major;
        return this;
    }
    public Student create(){ //最后调用,作为链式调用的终点
        return stu;
    }
}

原型模式

原型模式源代码
实现clone()方法要注意以下几点:
1. 对任何的对象x,都有:x.clone() != x
2. 对任何的对象x,都有x.clone().getClass() == x.getClass()
3. 如果对象x的equals方法定义恰当的话,那么x.clone().equals(x)应当是成立的

同时要注意clone()方法的深复制与浅复制的问题,
利用串行化来做深复制
而有一些对象,比如线程thread对象或者Socket对象,是不能简单复制或者共享的。不管是使用深复制还是浅复制,只要涉及这样的对象就必须把对象设置为transient而不予复制;或者由程序自行创建相同同种的对象,权当做复制件使用。

原型模式的优点
  原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
假设产品类具有一定的等级结构,如果采用工厂模式,则工厂类也必须有一个相应的等级结构,而产品类的等级结构一旦发生变化,则工厂类的等级结构也要发生变化。如果采用原型模式,则给每一个产品类都配备一个克隆方法(大多数的时候只需要给产品类等级结构的根类配备一个克隆方法),便可以避免使用工厂模式所带来的具有固定等级结构的工厂类。
原型模式的缺点
  原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。
  
  
结构模式描述如何将类或者对象结合在一起形成更大的结构。结构模式描述两种不同的东西:类与类的实例,根据这一不同,结构模式可以分为类的结构模式和对象的结构模式。

适配器模式

几个应用实例:
1. 将Itetator适配成老java编译器所支持的Enumeration接口,使用对象的适配器模式
2. 将某个电脑游戏的Linux桌面适配成Windows桌面

装饰模式

一个纯粹的装饰类必须与Component对象在接口上的完全相同,并增强后者的功能。
装饰模式源代码
所有实现了Sourceable接口的类都可以被Decorator类来修饰。
如果需要很多不同类型的修饰类的,都可以在继承Decorator类的基础上重写这些方法。

JAVA I/O库中的设计模式的使用

主要是运用了适配器模式和装饰模式。

InputStream类型中的装饰模式

其子类被分为了原始流处理器和链接流处理器,而实际上这些链接流处理器就是作为原始流类的装饰类而存在的。修饰了InputStream的内部工作方式。

适配器模式的使用

  1. InputStream类型的原始流处理器是适配器模式的使用。比如StringBufferInputStream,其中持有一个对String对象的引用。这是一个将String对象适配成InputStream对象形式的适配器模式。
  2. InputStreamReader是从byte输入流到char输入流的一个适配器。

代理模式

代理模式源代码
我们可以看到与装饰模式的不同:代理模式在创建时就已经明确知道了要代理的是哪个具体类了,而装饰模式则不知道其要装饰的实现了接口的哪个具体类,因此代理模式也不需要传入实例。

合成模式

合成模式把整体和部分的关系用树结构表示出来。
合成模式UML图
分类
举例:一幅图画中所拥有的长方形、正方形、或者内嵌的图画构成了一种树形结构,建议使用合成模式。
实例:http://www.cnblogs.com/java-my-life/archive/2012/04/17/2453861.html

享元模式

在Java语言中,String类型就使用了享元模式。String是不可变对象,一旦创建出来就不可改变。在JVM内部,String对象都是共享的。
享元对象能做到共享的关键是区分内蕴状态和外蕴状态。一个享元对象拥有内蕴状态并可以被共享。
内蕴状态在创建之后不能够被改变。如果一个享元对象有外蕴状态的话,所有的外部状态都必须储存在客户端,与内蕴状态相互隔离。
享元对象必须通过享元工厂对象获得(一般做成单例模式),然后判断拥有此状态的享元对象是否存在,不存在时才创建并返回。

复合享元模式指的是享元对象有可能是一种复合状态,这样的复合享元对象本身不能共享,但是它们可以分解成单纯的享元对象。
复合享元对象

桥梁模式

将抽象化与实现化脱耦,使得二者可以独立的变化----定义。
桥梁模式的用意是把实现和它的接口分开,以便它们可以独立的变化。要体会与适配器模式的不同。

桥梁模式具体的解释
这里要着重注意的就是上图中提到的 实现化角色与抽象化角色的接口可以非常不一样,因为实现化相当于是具体的实现,而抽象化角色是高层的抽象。抽象化角色会向实现化角色委派任务。
典型的应用就是JDBC驱动库的实现。
JDBC实现示意图
实例:http://www.cnblogs.com/java-my-life/archive/2012/05/07/2480938.html

门面模式

外部系统与一个子系统的通信必须通过一个统一的门面对象进行,这就是门面模式。门面模式提供一个高层次的接口,使得子系统更易于使用。
* 门面(Facade)角色 :客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
* 子系统(SubSystem)角色 :可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合(如上面的子系统就是由ModuleA、ModuleB、ModuleC三个类组合而成)。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
在Tomcat中的Request以及Response都存在与之对应的Facade类,这样做的原因是Request对象中的很多方法都是内部组件之间相互交互时使用的,比如setComet、setRequestedSessionId等方法。这些方法并不对外部公开,但是又必须设置为public,因为还需要跟内部组件之间交互使用。最好的解决方法就是通过使用一个Facade类,将与内部组件之间交互使用的方法屏蔽掉,只提供给外部程序感兴趣的方法。

行为模式是在不同的对象之间划分责任和算法的抽象化。行为模式不仅仅是关于类和对象的,而且是关于它们之间的相互作用的。
行为模式分为类的行为模式和对象的行为模式两种:
* 类的行为模式 类的行为模式使用继承关系在几个类之间分配行为。
* 对象的行为模式 对象的行为模式则使用对象的聚合来分配行为

不变模式

策略模式

策略模式其用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以互相替换。
经常可以见到的是,所有的具体的策略类都有一些公共的行为。这时候,就应当这些公共的行为放到共同的抽象策略角色Strategy类里面。
排序系统实例
模板方法模式与策略模式不同在于,策略模式使用委派的方法提供不同的算法行为,而模板方法模式使用继承的方法提供不同的算法行为。而策略模式可行的基础就是里氏代换规则,同时策略模式也复合"开-闭"规则。

模板方法模式

模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。
实例代码
HttpServlet类中的service方法就是一个模板方法,这个方法根据http methond的不同区调用七个do方法中的一个或者几个。子类可以重写do*()方法来实现不同的剩余逻辑。

观察者模式

观察者模式是对象的行为模式,又叫做发布-订阅模式或者源-监听器模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象发生变化时,会通知所有的观察者对象,使得它们能够自动更新。
UML图1
另外一种观察者模式的实现方式:
UML图二
Java语言本身提供了对观察者模式的支持:
在java.util库里面,提供了一个Observable类以及一个Observer接口,构成对观察者模式的支持。
其中被观察者类都是Observable类的子类
Observable类图
Observer只有一个update方法,提供更新操作。

迭代子模式

接口实现

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);  
    }
}  

以上的实现方式叫做白箱聚集与外禀迭代子。

一个黑箱聚集不向外部提供遍历自己元素对象的接口,因此,这些元素对象只可以被聚集内部成员访问。由于内禀迭代子恰好是聚集内部的成员子类,因此,内禀迭代子对象是可以访问聚集的元素的。
主要区别在于以下:
黑箱模式的实现
其中Aggregate接口只定义了一个createIterator()方法。Iterator接口与白箱模式相同。
使用外禀迭代子的重要理由是它可以被几个不同的聚集一起使用,只要他们有相同的抽象接口。而内禀迭代子的优点是它不破坏对象聚集的封装(可以保证在迭代过程中不会受到用户调用遍历元素接口的影响)。

在java中的实现要注意以下几点:
1. 虽然在java中的聚集中提供了对外访问数据的接口,但是java仍然采用的内禀迭代子的实现,有的是内部类、甚至是匿名类的实现。
2. java中的迭代器也不只一种,例如listIterator\ArrayDeque的descendingIterator等,在使用时最好打开源码看一下。
3. Fail fast机制,当聚集在迭代时,如果发现其他线程修改了此集合,立即抛出ConcurrentModificationException异常,其判断机制是通过modCount来记录改变集合结构的操作次数而实现的。当expectedModCount != modCount时,则抛出异常。

责任链模式

责任链模式是对象的一种行为模式。在责任链中,很多对象由每一个对象对其下家的引用而链接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
责任链模式角色
其中Handler是一个抽象类。源码可定义如下:

public abstract class Handler
{
    protected Handler successor;
    public abstract void handleRquest();

    public void setSuccessor(Handler successor){
        this.successor = successor;
    }

    public Handler getSuccessor() {
        return successor;
    }
}

命令模式

命令模式属于对象的行为模式,命令模式又称行动模式或交易模式。命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
具有以下优点:
(1) 使得新的命令很容易的被加入系统中
(2) 允许接受请求的一方决定是否要否决请求
(3) 能比较容易的设计一个命令队列
(4) 可以容易的实现对请求的Undo和Redo
(5) 在需要的情况下,可以较容易的将命令记入日志

命令模式UML示意图
如果需要支持undo和redo操作的话,需要保存以下状态信息 (1)(2)用于redo (3)用于undo
(1) 接受者对象实际上请求所代表的操作
(2) 对接受者对象所作操作所需要的参数
(3) 接受者类最初的状态
与备忘录模式的关系:
如果命令需要撤销或者恢复操作的话,备忘录模式可以用来储存关于命令的状态效果信息。

备忘录模式

备忘录模式又叫做快照模式,是对象的行为模式。
备忘录对象是一个用来存储另外一个对象内部状态的快照。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以再将来合适的时候把这个对象还原到存储起来的状态。
我们把存储这些快照的备忘录对象叫做对象的历史,某一个快照所处的位置叫做检查点。

备忘录模式的白箱模式,此时备忘录对象对于负责人对象来说是透明的,可以设置备忘录的对象。

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();  
    }  
}
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;  
    }  
}
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;  
    }  
} 

备忘录对象的黑箱模式,此时备忘录对象对发起人和负责人来说提供了不同的使用接口,相当于提供了双重接口。
1
2
3

状态模式

TCP应用状态模式的例子
这里的Context环境类就是TcpConnection类。
如何区分状态模式与策略模式?
考察环境角色是否有明显的状态的改变。如果环境角色只有一个状态,那么就使用策略模式,策略模式的特点是一旦环境选定一个具体策略类,那么整个环境类的生命周期内都不会改变这个具体的策略类。而状态模式则适用于环境角色中状态不断发生改变的情况。

访问者模式

访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于数据结构上的操作之间的耦合解开,使得操作集合可以相对自由的变化。
访问者模式UML结构图

public interface Visitor {
    /**
     * 对应于NodeA的访问操作
     */
    public void visit(NodeA node);
    /**
     * 对应于NodeB的访问操作
     */
    public void visit(NodeB node);
}
public class VisitorA implements Visitor {
/**
 * 对应于NodeA的访问操作
 */
@Override
public void visit(NodeA node) {
    System.out.println(node.operationA());
}
/**
 * 对应于NodeB的访问操作
 */
@Override
public void visit(NodeB node) {
    System.out.println(node.operationB());
}

}
public class VisitorB implements Visitor {
/**
 * 对应于NodeA的访问操作
 */
@Override
public void visit(NodeA node) {
    System.out.println(node.operationA());
}
/**
 * 对应于NodeB的访问操作
 */
@Override
public void visit(NodeB node) {
    System.out.println(node.operationB());
}

}
public abstract class Node {
/**
 * 接受操作
 */
public abstract void accept(Visitor visitor);
}
public class NodeA extends Node{
/**
 * 接受操作
 */
@Override
public void accept(Visitor visitor) {
    visitor.visit(this);
}
/**
 * NodeA特有的方法
 */
public String operationA(){
    return "NodeA";
}

}
public class NodeB extends Node{
/**
 * 接受方法
 */
@Override
public void accept(Visitor visitor) {
    visitor.visit(this);
}
/**
 * NodeB特有的方法
 */
public String operationB(){
    return "NodeB";
}
}
//结构对象角色类,这个结构对象角色持有一个聚集,并向外界提供add()方法作为对聚集的管理操作。通过调用这个方法,可以动态地增加一个新的节点。
public class ObjectStructure {

private List<Node> nodes = new ArrayList<Node>();

/**
 * 执行方法操作
 */
public void action(Visitor visitor){

    for(Node node : nodes)
    {
        node.accept(visitor);
    }
}
/**
 * 添加一个新元素
 */
public void add(Node node){
    nodes.add(node);
}
}

我们可以看到在实现访问模式过程中实现了两次分派过程,第一次是node.accept(Visitor)是一次分派,第二次是accept方法内部,visitor.visit(this)又是一次分派的过程。

先进行一次静态分派,再根据invokevirtual指令进行动态分派。

解释器模式

解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一种解释器。客户端可以使用这个解释器来解释这个语言中的句子。

调停者模式

调停者模式是对象的行为模式。调停者模式包装了一系列对象相互作用的方式,使得这些对象之间不必互相明显引用,从而使它们可以较松散的耦合。 是迪米特法则体现比较明显的一个设计模式。
调停者模式UML图
调停者在创建时就确定好了要调停的是哪几个同事。setColleagueChanged(Colleague)是当某个同事发生变化时,其他同事应当采用的action操作。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注