什么是行为型设计模式
前面的几篇文章分别介绍了创建型设计模式和结构型设计模式,接下来的几篇文章将介绍行为型设计模式。
创建型模式(Creational Pattern)指的是对类的实例化过程进行了抽象,使得能够将软件模块中对象的创建和对象的使用分离。外界对于这些对象只需要知道它们共同的接口,而不用清楚其具体的实现细节,使得系统设计更符合单一职责原则,软件的结构也更加清晰。
结构型模式(Structural Pattern)则指的是如何将类或者对象结合在一起从而形成更大的结构,通过各种组合形式从而形成复杂的、功能更为强大的结构体系。
行为型模式(Behavioral Pattern)指的是对在不同的对象之间划分责任和算法的抽象化。
它不仅仅关注类和对象的结构,而且还重点关注它们之间的相互作用。通过行为型模式,可以更加清晰地划分类与对象的职责。
行为型模式分为类行为型模式和对象行为型模式两种:
类行为型模式:类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。
对象行为型模式:对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量
使用关联关系来取代继承关系,大部分行为型设计模式都属于对象行为型设计模式。
介绍的第一个行为型设计模式是----访问者模式!
定义
访问者模式指的是将某些作用于某种数据结构中各元素的操作分离出来封装成独立的类,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。它是一种类行为型设计模式!它也是行为类模式中最复杂的一种模式,也是使用较多的一种行为型设计模式!
组成部分
访问者模式主要包含以下几个部分:
1、抽象访问者(Visitor)这里主要用来定义一个访问具体元素的接口,并且为每个具体元素类对应一个访问操作方法,并且在该操作中的参数类型标识了被访问的具体元素。
2、具体访问者(ConcreteVisitor):它主要实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
3、抽象元素(Element):这里声明一个包含接受操作的接口,被接受的访问者对象作为 接受法的参数。
4、具体元素(ConcreteElement):实现抽象元素角色提供的接受操作,另外具体元素中可能还包含本身业务逻辑的相关操作。
5、对象结构(Object Structure):一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map这类的聚合类实现,但是在项目中一般很少抽象出这个角色。
栗子
首先来看一个简单的例子,先声明一个抽象访问者Visitor:
public interface Visitor { public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); } 复制代码
接着声明一个抽象元素Element两个具体元素ConcreteElement并且继承抽象元素Element:
public abstract class Element { public abstract void accept(Visitor visitor); public abstract void print(); } public class ConcreteElement1 extends Element{ @Override public void accept(Visitor visitor) { visitor.visit(this); } @Override public void print(){ System.out.println("这是具体元素ConcreteElement1"); } } public class ConcreteElement2 extends Element { @Override public void accept(Visitor visitor) { visitor.visit(this); } @Override public void print() { System.out.println("这是具体元素ConcreteElement2"); } } 复制代码
然后定义一个具体访问者ConcreteVisitor实现Visitor接口:
public class ConcreteVisitor implements Visitor { @Override public void visit(ConcreteElement1 el1) { el1.print(); } @Override public void visit(ConcreteElement2 el2) { el2.print(); } } 复制代码
最后一部分就是对象结构Object Structure,代码如下:
public class ObjectStruture { public static List<Element> getList(){ List<Element> list = new ArrayList<Element>(); Random ran = new Random(); for(int i=0; i<10; i++){ int a = ran.nextInt(100); if(a>50){ list.add(new ConcreteElement1()); }else{ list.add(new ConcreteElement2()); } } return list; } } 复制代码
最后的最后就是测试代码了:
public class VisitorPatternTest { public static void main(String[] args) { List<Element> list = ObjectStruture.getList(); for(Element e: list){ e.accept(new ConcreteVisitor()); } } } 复制代码
运行结果如下:
通过上面的例子,可以对访问者模式的特点(优缺点)进行总结了。
访问者模式的优点
1、前面提到了访问者模式是符合单一职责原则的。在访问者设计模式的代码中元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则。
2、通过上面代码可以发现访问者模式的扩展性良好,因为被封装的操作通常来说都是易变的,元素类可以通过接受不同的访问者来实现对不同操作的扩展。所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
3、访问者模式代码复用性好。因为可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
4、访问者模式灵活性好。因为访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
访问者模式的缺点
访问者模式的缺点也很明显:
1、访问者模式的代码中增加新的元素类困难。因为一旦增加一个新的元素类,都需要在每一个具体访问者类中去增加相应的具体操作,这很显然违背了“开闭原则”。
2、违反了依赖倒置原则。因为访问者模式依赖了具体类,而没有依赖抽象类。
适用场景
1、如果一个对象中存在着一些与本对象不相干的操作,为了避免这些操作影响这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。
2、或者在一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。
3、对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作
4、对象结构相对稳定,但其操作算法经常变化的系统
总结
以上就是关于行为型设计模式中的访问者设计模式的介绍。
访问者模式指的是将某些作用于某种数据结构中各元素的操作分离出来封装成独立的类,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。