《设计模式Java版》读书笔记
- 源码下载地址:https://github.com/quanke/design-pattern-java-source-code.git
面向对象设计原则
-
7种常用的面向对象设计原则
-
单一职责原则 (Single Responsibility Principle, SRP) 一个类只负责一个功能领域中的相应职责
-
开闭原则 (Open-Closed Principle, OCP) 软件实体应对扩展开放,而对修改关闭
-
里氏代换原则 (Liskov Substitution Principle, LSP)所有引用基类对象的地方能够透明地使用其子类的对象
-
依赖倒转原则 (Dependence Inversion Principle, DIP) 抽象不应该依赖于细节,细节应该依赖于抽象
-
接口隔离原则 (Interface Segregation Principle, ISP) 使用多个专门的接口,而不使用单一的总接口
-
合成复用原则 (Composite Reuse Principle, CRP) 尽量使用对象组合,而不是继承来达到复用的目的
-
迪米特法则 (Law of Demeter, LoD) 一个软件实体应当尽可能少地与其他实体发生相互作用
里氏代换原则
-
里氏代换原则
-
基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立
-
尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
依赖倒转原则
-
依赖倒转原则
-
实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中,依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种,分别是:构造注入,设值注入(Setter注入)和接口注入。
接口隔离原则
- 接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口
合成复用原则
-
合成复用原则
-
尽量使用对象组合,而不是继承来达到复用的目的。
-
复用时要尽量使用组合/聚合关系(关联关系),少用继承
-
首先应该考虑使用组合/聚合,组合/聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少
-
通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类
-
如果两个类之间是“Has-A”的关系应使用组合或聚合,如果是“Is-A”关系可使用继承。“Is-A"是严格的分类学意义上的定义,意思是一个类是另一个类的"一种”;而"Has-A"则不同,它表示某一个角色具有某一项责任。
迪米特法则
-
迪米特法则
-
一个软件实体应当尽可能少地与其他实体发生相互作用
-
迪米特法则还有几种定义形式,包括:不要和“陌生人”说话、只与你的直接朋友通信等
-
引入一个合理的第三者来降低现有对象之间的耦合度
工厂三兄弟之简单工厂模式
-
class Factory { //静态工厂方法 public static Product getProduct(String arg) { Product product = null; if (arg.equalsIgnoreCase("A")) { product = new ConcreteProductA(); //初始化设置product } else if (arg.equalsIgnoreCase("B")) { product = new ConcreteProductB(); //初始化设置product } return product; } }
工厂三兄弟之工厂方法模式
-
工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。
-
图3 日志记录器结构图
-
-
工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类
工厂三兄弟之抽象工厂模式
-
产品等级结构与产品族
-
抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图5所示:
-
-
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦
确保对象的唯一性——单例模式
-
确保对象的唯一性——单例模式
-
构造函数改为private修饰
-
为了保证成员变量的封装性,我们将TaskManager类型的tm对象的可见性设置为privat
-
getInstance()方法的修饰符,首先它应该是一个public方法,以便供外界其他对象使用,其次它使用了static关键字,即它是一个静态方法,在类外可以直接通过类名来访问,而无须创建TaskManager对象
对象的克隆——原型模式
-
对象的克隆——原型模式
-
所创建的对象是全新的对象,它们在内存中拥有新的地址
-
一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
不能, return this 的话, 那么就是返回的同一个对象, 原型是返回一个新的对象
-
思考能否将上述代码中的clone()方法写成:public Prototype clone() { return this;}?给出你的理由
-
一般而言,Java语言中的clone()方法满足:(1) 对任何对象x,都有x.clone() != x,即克隆对象与原型对象不是同一个对象;(2) 对任何对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原型对象的类型一样;(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。为了获取对象的一份拷贝,我们可以直接利用Object类的clone()方法,具体步骤如下:(1) 在派生类中覆盖基类的clone()方法,并声明为public;(2) 在派生类的clone()方法中,调用super.clone();(3)派生类需实现Cloneable接口。
-
浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制
-
在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制
-
通过覆盖Object类的clone()方法可以实现浅克隆
-
深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象
-
在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现
-
原型模式总结
-
主要优点
-
当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
-
可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
复杂对象的组装与创建——建造者模式
- 复杂对象的组装与创建——建造者模式
- 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式结构如图8-2所示:
不兼容结构的协调——适配器模式
-
不兼容结构的协调——适配器模式
-
适配器让那些由于接口不兼容而不能交互的类可以一起工作。
-
适配器模式和装饰模式的区别???
-
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)
-
我们通过增加一个新的适配器类来解决接口不兼容的问题
-
根据适配器类与适配者类的关系不同
-
可分为对象适配器和类适配器两种
-
对象适配器模式中,适配器与适配者之间是关联关系
-
类适配器模式中,适配器与适配者之间是继承(或实现)关系
-
在实际开发中,对象适配器的使用频率更高,对象适配器模式结构如图9-3所示:
-
-
类适配器模式中适配器和适配者是继承关系,类适配器模式结构如图9-5所示
-
-
主要优点
-
(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
处理多维度变化——桥接模式
-
我先跟大家谈谈两种常见文具的区别,它们是毛笔和蜡笔。假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3×12 = 36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响
-
Sunny软件公司欲开发一个跨平台图像浏览系统,要求该系统能够显示BMP、JPG、GIF、PNG等多种格式的文件,并且能够在Windows、Linux、Unix等多个操作系统上运行。系统首先将各种格式的文件解析为像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不同的操作系统中可以调用不同的绘制函数来绘制像素矩阵。系统需具有较好的扩展性以支持新的文件格式和操作系统。Sunny软件公司的开发人员针对上述要求,提出了一个初始设计方案,其基本结构如图10-1所示:
-
-
桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数
-
结构示意图如图10-4所示
-
-
Sunny公司开发人员使用桥接模式来重构跨平台图像浏览系统的设计,其基本结构如图10-5所示
-
-
适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中
-
如图10-6所示
-
重点就是这个。解决多层继承
- 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
树形结构的处理——组合模式
-
树形结构的处理——组合模式
-
Sunny软件公司欲开发一个杀毒(AntiVirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案。
-
组合模式为解决此类问题而诞生,它可以让叶子对象和容器对象的使用具有一致性
-
组合模式结构如图11-3所示
-
-
如图11-4所示
-
-
Sunny公司开发人员使用组合模式来进行杀毒软件的框架设计,其基本结构如图11-5所示
-
扩展系统功能——装饰模式
-
扩展系统功能——装饰模式
-
Sunny软件公司基于面向对象技术开发了一套图形界面构件库VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特效显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能
-
图12-2
-
-
为了让系统具有更好的灵活性和可扩展性,克服继承复用所带来的问题,Sunny公司开发人员使用装饰模式来重构图形界面构件库的设计,其中部分类的基本结构如图12-4所示:
-
-
装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系
-
-
完整代码如下所示:
-
//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化 abstract class Component { public abstract void display(); } //窗体类:具体构件类 class Window extends Component { public void display() { System.out.println("显示窗体!"); } } //文本框类:具体构件类 class TextBox extends Component { public void display() { System.out.println("显示文本框!"); } } //列表框类:具体构件类 class ListBox extends Component { public void display() { System.out.println("显示列表框!"); } } //构件装饰类:抽象装饰类 class ComponentDecorator extends Component { private Component component; //维持对抽象构件类型对象的引用 public ComponentDecorator(Component component) //注入抽象构件类型的对象 { this.component = component; } public void display() { component.display(); } } //滚动条装饰类:具体装饰类 class ScrollBarDecorator extends ComponentDecorator { public ScrollBarDecorator(Component component) { super(component); } public void display() { this.setScrollBar(); super.display(); } public void setScrollBar() { System.out.println("为构件增加滚动条!"); } } //黑色边框装饰类:具体装饰类 class BlackBorderDecorator extends ComponentDecorator { public BlackBorderDecorator(Component component) { super(component); } public void display() { this.setBlackBorder(); super.display(); } public void setBlackBorder() { System.out.println("为构件增加黑色边框!"); } } 编写如下客户端测试代码: class Client { public static void main(String args[]) { Component component,componentSB; //使用抽象构件定义 component = new Window(); //定义具体构件 componentSB = new ScrollBarDecorator(component); //定义装饰后的构件 componentSB.display(); } } 编译并运行程序,输出结果如下: 为构件增加滚动条! 显示窗体!
-
如果我们希望得到一个既有滚动条又有黑色边框的窗体,不需要对原有类库进行任何修改,只需将客户端代码修改为如下所示:
-
class Client { public static void main(String args[]) { Component component,componentSB,componentBB; //全部使用抽象构件定义 component = new Window(); componentSB = new ScrollBarDecorator(component); componentBB = new BlackBorderDecorator(componentSB); //将装饰了一次之后的对象继续注入到另一个装饰类中,进行第二次装饰 componentBB.display(); } }
深入浅出外观模式
-
图3所示的类图也可以作为描述外观模式的结构图
-
-
引入外观模式之后,增加新的子系统或者移除子系统都非常方便
-
实例结构图如图4所示
-
-
引入抽象外观类之后的文件加密模块结构图
-
为复杂的子系统调用提供一个统一的入口
-
它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
-
当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
设计模式之代理模式
- 代理模式
- 没啥好说, 静态代理和动态代理
请求的链式处理——职责链模式
-
职责链模式结构如图16-2所示:
-
-
具体处理者是抽象处理者的子类,它具有两大作用:第一是处理请求,不同的具体处理者以不同的形式实现抽象请求处理方法handleRequest();第二是转发请求,如果该请求超出了当前处理者类的权限,可以将该请求转发给下家。具体处理者类的典型代码如下
-
需要注意的是,职责链模式并不创建职责链,职责链的创建工作必须由系统的其他部分来完成,一般是在使用该职责链的客户端中创建职责链
思考: 用装饰模式是不是也可以实现职责链? 其实也是可以的
请求发送者与接收者解耦——命令模式
-
为了降低系统的耦合度,将请求的发送者和接收者解耦,我们可以使用一种被称之为命令模式的设计模式来设计系统
-
命令模式定义如下:
-
命令模式(Command Pattern):将一个请求封装为一个对象
-
命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式
命令模式我更想理解成委派模式, 通过一个抽象类去合成服用具体的实现。 这个抽象类就像是代理一样
-
命令模式的核心在于引入了命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法,其结构如图3所示:
-
-
在命令模式结构图中包含如下几个角色:● Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。● ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。● Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。● Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。
自定义语言的实现——解释器模式
- 表达式可分为终结符表达式和非终结符表达式
- 因此解释器模式的结构与组合模式的结构有些类似,但在解释器模式中包含更多的组成元素
- 它的结构如图18-3所示:
遍历聚合对象中的元素——迭代器模式
-
在软件开发中,我们经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据;二是遍历数据
-
可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中
-
更符合“单一职责原则”的要求。
-
图3 迭代器模式结构图
-
-
在迭代器模式结构图中包含如下几个角色:● Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。● ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。● Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。● ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
-
在具体迭代器中将实现抽象迭代器声明的遍历数据的方法,如下代码所示:
-
class ConcreteIterator implements Iterator { private ConcreteAggregate objects; //维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据 private int cursor; //定义一个游标,用于记录当前访问位置 public ConcreteIterator(ConcreteAggregate objects) { this.objects=objects; } public void first() { ...... } public void next() { ...... } public boolean hasNext() { ...... } public Object currentItem() { ...... } }
-
具体聚合类
-
代码如下所示
-
class ConcreteAggregate implements Aggregate { ...... public Iterator createIterator() { return new ConcreteIterator(this); } ...... }
这种方式更妙JDK 的ArrayList就是用的这种方式
- 除了使用关联关系外,为了能够让迭代器可以访问到聚合对象中的数据,我们还可以将迭代器类设计为聚合类的内部类,JDK中的迭代器类就是通过这种方法来实现的,如下AbstractList类代码片段所示
协调多个对象之间的交互——中介者模式
-
如果在一个系统中对象之间的联系呈现为网状结构,如图20-4所示。对象之间存在大量的多对多联系
-
-
将导致系统非常复杂,这些对象既会影响别的对象,也会被别的对象所影响
-
通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构,如图20-5所示
-
-
对象之间多对多的复杂关系就转化为相对简单的一对多关系
-
中介者模式是“迪米特法则”的一个典型应用
-
中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系
撤销功能的实现——备忘录模式
- 撤销功能的实现——备忘录模式
对象间的联动——观察者模式
-
“红灯停,绿灯行”,在日常生活中,交通信号灯装点着我们的城市,指挥着日益拥挤的城市交通。当红灯亮起,来往的汽车将停止;而绿灯亮起,汽车可以继续前行。在这个过程中,交通信号灯是汽车(更准确地说应该是汽车驾驶员)的观察目标,而汽车是观察者。随着交通信号灯的变化,汽车的行为也将随之而变化,一盏交通信号灯可以指挥多辆汽车
-
一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动
-
观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式
-
结构如图22-3所示:
-
-
在观察者模式结构图中包含如下几个角色:● Subject(目标):目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。● ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。● Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。● ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法
处理对象的多种状态及其相互转换——状态模式
-
很多事物都具有多种状态,而且在不同状态下会具有不同的行为,这些状态在特定条件下还将发生相互转换。就像水,它可以凝固成冰,也可以受热蒸发后变成水蒸汽,水可以流动,冰可以雕刻,蒸汽可以扩散。
-
当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式
-
将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化
-
在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如图3所示:
-
-
在状态模式结构图中包含如下几个角色:● Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。● State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。● ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
-
状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为
-
在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。
算法的封装与切换——策略模式
- 算法的封装与切换——策略模式
模板方法模式深度解析
-
模板方法模式
-
模板方法模式是一种基于继承的代码复用技术
-
在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果
-
其核心是抽象类和其中的模板方法的设计,其结构如图2所示:
-
-
(1) 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。在C#语言里一个抽象方法以abstract关键字标识。(2) 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承
-
钩子方法:一个钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展。通常在父类中给出的实现是一个空实现
-
并以该空实现作为方法的默认实现,当然钩子方法也可以提供一个非空的默认实现。
操作复杂对象结构——访问者模式
-
操作复杂对象结构——访问者模式
-
在医生开具处方单(药单)后,很多医院都存在如下处理流程:划价人员拿到处方单之后根据药品名称和数量计算总价,药房工作人员根据药品名称和数量准备药品,如图26-1所示:
-
-
我们可以将处方单看成一个药品信息的集合,里面包含了一种或多种不同类型的药品信息,不同类型的工作人员(如划价人员和药房工作人员)在操作同一个药品信息集合时将提供不同的处理方式,而且可能还会增加新类型的工作人员来操作处方单。
-
在软件开发中,有时候我们也需要处理像处方单这样的集合对象结构,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式,还有可能增加新的处理方式。在设计模式中,有一种模式可以满足上述要求,其模式动机就是以不同的方式操作复杂对象结构,该模式就是我们本章将要介绍的访问者模式
-
访问者模式的结构较为复杂,其结构如图26-2所示:
-
-
这种调用机制也称为**“双重分派”**,正因为使用了双重分派机制,使得增加新的访问者无须修改现有类库代码,
-
访问者模式中,对象结构是一个集合,它用于存储元素对象并接受访问者的访问,其典型代码如下所示:
-
class ObjectStructure { private ArrayList<Element> list = new ArrayList<Element>(); //定义一个集合用于存储元素对象 public void accept(Visitor visitor) { Iterator i=list.iterator(); while(i.hasNext()) { ((Element)i.next()).accept(visitor); //遍历访问集合中的每一个元素 } } public void addElement(Element element) { list.add(element); } public void removeElement(Element element) { list.remove(element); } }