总结
基础
抽象
封装
多态
继承
原则
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的松耦合设计而努力
对扩展开放,对修改关闭
依赖抽象,不要依赖具体类
只和朋友交流
别找我,我会找你
类应该只有一个改变的理由
模式
策略模式
策略模式,定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
观察者模式
在对象之间定义一对多的依赖,这样一来,当这个对象改变状态,依赖它的对象都会受到通知,并自动更新。代表MVC
装饰者模式
动态将责任附加到对象上,想要扩展功能,装饰者提供有别于继承的另一种选择。代表JAVA中IO(INPUTStream系列)
抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
工厂方法模式
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
单例模式
确保一个类只有一个实例,并提供全局访问点。
命令模式
将请求封装成对象,这可以让你使用不同的请求、队列、或者日志请求来参数化其对象。命令模式也可以支持撤销操作
适配器模式
将一个类的接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无间
外观模式(门面模式)
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更加容易使用。
模板方法模式(Template)
在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使的子类可以在不改变算法的结构的情况下,重新定义算法中的某一些步骤。
迭代器模式(Iterator)
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露其内部的表示
组合模式(Composite)
运行你将对象组成树形结构表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合。
状态模式(STATE)
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
代理模式(proxy)
为另一个对象提供一个替身或占位符以访问这个对象
复合模式
复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。
设计模式入门
使用模式对的最好方式:把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用他们。
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
- 针对接口编程,而不是针对实现编程。针对接口编程,就是针对超类型编程。
- 变量的声明应该是超类型:比如抽象类或者一个接口。
- 有一个(组合)可能比是一个更好
- 多用组合,少用继承.类的行为不是通过继承得到而是通过组合得到的。
策略模式(Strategy)
定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
类的行为就可以看做是算法
观察者模式(Observe)让你的对象知悉现况
类比报纸、杂志的订阅
观察者模式
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
Swing大量使用观察者模式,许多GUI框架也是如此。
java内置观察者模式
JAVA API有内置的观察者模式。java.util包内包含了最基本的Observer接口与Observable类,甚至可以使用推拉模式。
可观察者如何送出通知
- 先调用setChanged()方法,标记状态已经改变的事实
- 然后调用两种notifyObservers()方法中的一个:notifyObservers()或notifyObservers(Object arg),这个版本可以传送任何数据对象给每一个观察者
观察者如何接受通知
观察者实现了更新方法,但是方法的签名不一样。
update(Observable o,Object arg)
1. Observable:主题本身当做第一个变量,好让观察者知道是哪个主题通知它的。
2. arg这正是传入notifyObservers()的数据,如果没有说明则为空
setChanged()方法用来标记状态已经改变的事实,好让notifyObservers()知道当它被调用时应该更新观察者。如果调用notifyObservers()之前没有先调用setchanged(),观察者就不会被通知。
java.util.Observable的暗黑面
Observable是以类,不是接口,必须被继承。
setChanged()方法是protect的,只有继承才能调用。
装饰者模式(Decorator)
类应该对拓展开放,对修改关闭
举例:以饮料为主体,然后运行时以调料来“装饰”饮料。
- 装饰者和被装饰者对象有相同的超类型。
- 你可以用一个或多个装饰者包装一个对象
- 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为
- 对象可以在任何时候被装饰
装饰者模式
动态地将责任附加到对象上,若有拓展功能,装饰者提供了比继承更有弹性的替代方案。
真实世界的装饰者:java I/O
装饰者模式缺点
有大量的小类,数量比较多
原则
对拓展开放,对修改关闭
工厂模式(Factory)
利用静态方法定义一个简单的工厂,这是很常见的技巧,常被称为静态工厂。为何使用静态方法?因为不需要使用创建对象的方法来实例化对象。但是有一个缺点,不能通过继承来改变创建方法的行为
定义简单工厂
简单工厂其实不是一个设计模式,应该算是一种编程习惯。
设计模式中,所谓“实现一个接口”并不一定表是写一个类,并利用implement关键字来实现某个Java接口。“实现一个接口”泛指实现某个超类型(可以试类或接口)
所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
定义工厂方法模式
工厂方法模式:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
简单工厂把全部的事情,在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实心。简单工厂的做法,是将对象的创建封装起来,简单工厂不具备工厂方法的弹性。
要依赖抽象,不要依赖具体类
多依赖抽象类,而不依赖具体类。
依赖倒置:是底层组件依赖于抽象高层的抽象。
定义抽象工厂模式
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
总结
依赖抽象,不要依赖具体类
- 所有的工厂都是用来封装对象的创建
- 简单工厂,虽然不是真正的涉及模式,但是也是一个简单的方法,实现将客户程序从具体类解耦
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
- 抽象工厂使用对象的组合:对象的创建被实现在工厂接口所暴露出来的方法中
- 抽象工厂创建相关的对象家族,而不需要依赖它们具体类。
- 简单理解就是抽象工厂由多个工厂方法组成
单例模式(singleton)
用来创建独一无二的,只能有一个实例的对象。比如线程池、内存、配置表。
单例模式也提供一个全局访问点,和全局变量。但是不是程序一开始就创建对象。
JVM实现:在用到的时候才创建对象
经典的单间模式实现
public class Singleton{
private static Singleton uniqueInstance;//利用静态变量记录唯一实例
private Singleton(){//私有构造器声明为私有,只有Singleton类才可以调用构造器
}
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
如果我们不需要这个实例,它就永远不会产生
单例模式
确保一个类只有一个实例,并提供一个全局访问点
处理多线程问题
把getInstance()变成同步(synchronized)方法,就可以解决多线程
public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqeuInstance;
}
}
诚然,这种方式会影响性能。可以有如下选择:
- 如果getInstance()的性能对应用程序不是很关键,就什么都别做。这样做简单高效
- 如果创建单例,对程序负担不重,就不用延迟实例化的做法
pulbic static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
- 用双重检查加锁,在getInstance()中减少使用同步
利用双重检查加锁,首先检查是否创建了,未创建,才进行同步,只有一次同步机会。
public class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
synchronized(Singleton.class){
if(uniqueInstance==null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
注意:volatile关键字确保:当uniqueInstance变量被初始化,多个线程能处理uniqueInstance变量。必须在1.5及以上才能使用。
关于多个类加载器可能有机会各自创建自己的单价实例
每个类加载器都定义了一个命名空间。如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次。如果是单例模式的类,就会产生多个单例并存。解决办法:自行指定类加载器,并指定同一个类加载器。
java1.2以后单例模式不会被垃圾回收器回收。放心使用
总结
- 单例模式确保程序中一个类最多只有一个实例
- 单例模式也是提供访问这个实例的全局点
- 在java实现单例模式需要私有构造器、一个静态方法和一个静态变量
- 确定在性能和资源的限制上,小心选择适当的方案来实现单例。
- 如果不是采用java 5以上,双重检查会失效。
- 使用JVM1.2或之前的版本,必须建立单件注册表,以免垃圾收集器将单件回收
命令模式(command)——封装调用
将方法调用(method invocation)封装起来
通过封装方法调用哦,可以记录日志、或者实现撤销undo
命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦
命令模式
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销操作。
在许多设计模式中都会看到空对象的使用。甚至有些时候,空对象本身也被视为是一种设计模式
总结
- 命令模式将发出请求的对象和执行请求的对象解耦
- 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接受者的一个或一组动作
- 调用者通过调用命令对象的excute()发出请求,这会使得接受者的动作被调用
- 调用者可以接受命令当做参数,甚至在运行时动态地进行
- 命令可以支持撤销,做法是实现一个undo()方法来回到excute()被执行前的状态。
- 宏命令是命令的一种简单延伸,允许调用多个命令。宏方法也可以支持撤销
- 实际操作时,很常见使用命令对象,也可以直接实现请求,而不是将工作委托给接受者。
- 命令可以用来实现日志和事物系统。
适配器模式(Adapter)与外观模式(Facade,也叫门面模式)
装饰者模式将对象包装起来,并赋予它们新的职责。而现在是将某些对象包装起来,将类的接口转换成自己想要的接口
需要让一个适配器包装多个被适配者,这就是外观模式
定义适配器模式
将一个类的接口,转换成客户期望的另一接口。适配器让原本接口不兼容的类可以合作无间。
1. 类适配器使用多重继承,对象适配器使用组合
2. 对象适配器使用组合,可以适配该类的任何子类
3.
装饰者和适配器
- 当事情涉及到装饰者,就表示一些新的行为或责任要加入到你的设计中。
外观模式和适配器模式的差异不在于它们包装了几个类,而是在于它们的意图,适配器模式的意图是“改变”接口符合客户的期望;而外观模式的意图是提供子系统的一个简化接口。
定义外观模式
提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
最少知识原则
告诉我们要减少对象之间的交互,只留下几个密友
总结
- 当需要使用一个现有的类而其接口不符合我的需求时,就使用适配器
- 当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观
- 适配器改变接口以符合客户的期望
- 外观将客户从一个复杂的子系统中解耦
- 实现一个适配器可能需要一番功夫,与目标接口的大小和复杂程度有关
- 实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统来执行
- 适配器模式有两种形式:对象适配器和类适配器。类适配器需要用到多重继承
- 你可以为一个子系统实现一个以上的外观
- 适配器将一个对象包装起来以改变其接口;装饰者将一个对象包装起来以增加新的行为和责任,而外观将一群对象“包装”起来以简化接口。
模板方法模式 封装算法(Template)
直到目前,我们议题都绕着封装转;我们已经封装了对象的创建、方法的调用、复杂接口。接下来我们要封装算法块
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现
定义模板方法模式
在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使的子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
1. 当你的子类必须提供算法中的某个方法或步骤的实现时,就使用抽象方法。如果算法的这个部分是可选的,就使用钩子,如果是钩子的话,子类可以选择实现这个钩子
2. 好莱坞法则:别调用高层,让高层选择调用
3. applet大量使用了模板方法模式
总结
- 模板方法定义了算法的步骤,把这些步骤的实现延迟到了子类
- 模板方法模式为我们提供了一种代码复用的重要技巧。
- 模板方法的抽象类可以定义具体方法、抽象方法和钩子
- 抽象方法由子类实现
- 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它
- 为了防止子类改变模板方法中的算法,可以将模板方法声明为final
- 好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用底层模块
- 策略模式和模板方法模式都封装算法,一个用组合一个用继承
- 工厂方法是模板方法的一种特殊版本
管理良好的集合 迭代器(Iterator)与组合模式(Composite)
封装变化的部分
1. 如果不允许一个方法,可以抛出一个java.lang.UnsupportedOperationException运行时异常
定义迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
迭代器模式把元素之间的游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面
迭代器意味着没有次序。只是取出所有的元素,并不表示取出元素的先后就代表元素的大小次序。对于迭代器来说,数据结构可以有次序或是没有次序,甚至数据是可以重复的。除非某个集合文件有特别说明,否则不可以对迭代器所取出的元素大小顺序做出假设
单一责任
类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。这个原则告诉我们,尽量让每个类保持单一责任
#### java5的迭代器和集合
java5中包含了一种新形式的for语句,称为for/in.这可以让你在一个集合或者一个数组中遍历,而且不需要显式创建迭代器
```
for(Object:collection){
}
```
定义组合模式(composite)
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合
使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别
总结
- 迭代器允许访问聚合的元素,而不需要暴露它的内部结构
- 迭代器将遍历聚合的工作封装进一个对象中。
- 当使用迭代器的时候,我们依赖聚合提供遍历
- 迭代器提供了一个通用的接口,让我们遍历聚合的项时,就可以使用多态机制
- 组合模式提供一个结构,可同时包容个别对象和组合对象
- 组合模式允许客户对个别对象以及组合对象一视同仁
- 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点
状态模式(事物的状态)state
状态通过改变对象内部的状态来帮助对象控制自己的行为
定义状态模式
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
将状态封装称为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变
总结
- 状态模式允许一个对象基于内部状态而拥有不同的行为
- 和程序状态机PSM不同,状态模式用类代表状态
- Context会将行为委托给当前状态对象
- 通过将每个状态封装进一个类,我们把以后需要做的任何改变发局部化了
- 状态模式和策略模式有相同的类图,但是它们的意图不同
- 策略模式通常会用行为或算法来配置Context类
- 状态模式允许Context随着状态的改变而改变行为
- 状态转换可以由STATE类或Context类控制
- 使用状态模式通常会导致设计中类的数目大量增加
- 状态类可以被过个Context实例共享
代理模式(Proxy)控制对象的访问
代理要做的就是:控制和管理访问
远程对象:是一种对象,活在不同java虚拟机(JVM)堆中或者说,在不同的地址空间运行的远程对象
制作远程服务
- 制作远程接口 远程接口定义出可以让客户远程调用的方法
- 制作远程实现 为远程接口中定义远程方法提供真正的实现
- 利用rmic产生的stub和skeleton。这是客户和服务的辅助类,不需要创建这些类
- 启动RMI registry 可以从中查找代理的位置
- 开始远程服务 服务实现类去实例化一个服务的实例,并将这个服务注册到RMI registry
制作远程接口
- 拓展java.rmi.Remote remote不具有方法,只是一个标记
- 声明所有的方法都会抛出RemoteException
- 确定变量和返回值属于原语类型或者序列化类型。远程方法的变量和返回值,必须属于原语类型或Serializable类型。这不难理解。远程方法的变量必须被打包并通过网络运送,这要靠序列化来完成。如果你使用原语类型、字符串和许多API中内定的类型,都不会有问题。如果你传送自己定义的类,就必须保证你的类实现了Serializable
制作远程实现
- 实现远程接口 你的服务必须实现远程接口,也就是客户将要调用的方法接口
- 拓展UnicastRemoteObject 为了要成为远程服务对象,你的对象需要某些“远程”功能。最简单的就是拓展UnicastRemoteObject,让超类帮你做这些工作
- 设计一个不带变量的构造器,并声明RemoteException
- 用RMI REgistry注册此服务 当注册这个实现对象时,RMI系统其实注册的是stub,因为这是客户真正需要的。注册服务使用了java.rmi.Naming类的静态rebind()方法
产生Stub和Skeleton
在远程实现类上执行rmic
rmic 是JDK内一个工具,用来为一个服务类产生stub和skeleton。
执行remiregistry
开启一个终端,启动remiregistry
启动服务
开启另一个终端,启动服务
工作方式
- 客户到RMI registry中寻找
Naming.lookup("rmi://127.0.0.1/RemoteHello")
- RMI registry返回Stub对象 作为lookup方法的返回值,然后RMI会自动对Stub反序列化。你在客户端必须有stub类,否则stub就无法被反序列化
- 客户调用stub的方法,就像stub就是真正的服务对象一样
注意:
1. 忘了在启动远程服务之前先启动rmiregistry
2. 忘了让变量和返回值的类型成为可序列化的类型
3. 忘了给客户提供stub类
transient关键字,这样就告诉JVM不要序列化这个字段
定义代理模式
为另一个对象提供一个替身或占位符以控制对这个对象的访问。
控制访问是客户端不知道如何访问真正的对象。
几种代理控制访问的方式
1. 远程代理控制访问远程对象
2. 虚拟代理控制访问创建开销大的资源
3. 保护代理基于权限控制对资源的访问
虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
使用JAVA API代理创建保护代理
java在java.lang.reflect包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。动态代理,需要执行方法放在InvocationHandler中。
要点
- 代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有许多种
- 远程代理管理客户和远程对象之间的交互
- 虚拟代理控制访问示例化开销大的对象
- 保护代理基于调用者控制对对象方法的访问
- 代理模式有许多变体,例如:缓存代理、同步代理、防火墙代理和写入时复制代理
- 代理在结构上类似装饰者
- 装饰者模式为对象加上行为,而代理则是控制访问
- java内置的代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器
- 和其他的包装者(wrapper)一样,代理会造成你的设计中类的数目增加
复合模式 模式的模式
由不同模式构成模式。复合模式代表是MVC
复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。
MVC是数个模式的结合
MVC模式
- 模型利用观察者让控制器和视图可以随最新的状态改变而更新。控制器是视图的行为
- 视图和控制器实现了经典的策略模式:视图是一个对象,可以被调整使用不同的策略,而控制器提供了策略。
Model 2是MVC在WEB上的调整
总结
- mvc是复合模式,结合了观察者模式、策略模式和组合模式
- 模型使用了观察者模式,以便观察者更新,同时保持两者之间的解耦
- 控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为
- 视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮
- 这些模式携手合作,把MVC模型的三层解耦,保持设计干净又有弹性
- 适配器模式用来将新的模型适配成已有的视图和控制器
- model2是MVC在web上的应用,在model2中,控制器实现成Servlet,而JSP/html是视图
复合模式
复合模式结合两个及两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。
与设计模式相处——真实世界中的模式
模式
在某情景下,针对某问题的某种解决方案
- 情景就是应用某个模式的情况,这应该是会不断出现的情况
- 问题就是你想在某情景下达到的目标,但也可以是某情景下的约束
- 解决方案就是你所追求的,一个通用的设计。
创建型模式
创建型模式涉及到将对象实例化,这类模式都提供一个方法,将客户从所需要实例化的对象中解耦
行为型模式
都涉及到类和对象如何交互及分配职责
结构型模式
可以让你把类或对象组合到更大的结构中
保持简单
设计时,尽可能用最简单的方式解决问题,你的目标应该是简单
重构的时间就是模式的时间
重构就是通过改变你的代码来改进它的组织方式的过程。目标是要改善其结构而不是其行为。应该将思绪集中在设计本身,而不是模式上,只有真正需要时才使用模式,有些时候简单的方式就行得通,那么就不要用模式
反模式
告诉你如何采用一个不好的解决方案解决一个问题。反模式看起来总像是一个好的解决方案,但是当它真正被采用后,就会带来麻烦。通过将反模式归档,我们能够帮助其他人在实现它们之前,分辨出不好的解决方案。
反模式有它自己的诱惑,但是在长远来看会造成不好的影响。反模式除了告诉你不好之外,也给你提出建议的解决方案。
- 模式能够带来最大的好处之一是:让你的团队拥有共享词汇。
其它模式
桥接模式(Bridge)
使用桥接模式不只改变你的实现,也改变你的抽象
责任链模式(Chain of Responsibility Pattern)
当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式
享元模式(蝇量模式 Flyweight Pattern)
如想让某个类的一个实例能用来提供许多“虚拟实例”,就使用享元模式。
享元模式的用途和缺点
- 当一个类有许多的实例,而这些实例能被同一方法控制的时候,我们就可以使用享元模式
- 享元模式的缺点在于,一旦你实现了它,那么单个逻辑实例将无法拥有独立而不同的行为。
中介者 (Mediator pattern)
使用中介者模式(Mediator Pattern)来集中相关对象之间的复杂的沟通和控制方式。
中介者的优点
1. 通过将对象彼此解耦,可以增加对象的复用性
2. 通过将控制逻辑集中,可以简化系统维护
3. 可以让对象之间所传递的消息变得简单而且大幅减少
中介者的用途和缺点
1. 中介者常常被用来协调相关的GUI组件
2. 中介者模式的缺点是,如果设计不当,中介者对象本身会变的过于复杂
备忘录(Memento Pattern)
当你需要让对象返回之前的状态时(例如,你的用户请求“撤销”),就使用备忘录模式
备忘录的用途和缺点
1. 备忘录用于存储状态
2. 使用备忘录的缺点:存储和恢复状态的过程可能相当耗时
3. 在java系统中,其实可以考虑使用序列化机制存储系统的状态
原型模式(prototype)
当创建给定类的实例的过程很昂贵或很复杂时,就使用原型模式
访问者模式
当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式(Visitor Pattern)
访问者的优点
1. 允许你对组合结构加入新的操作,而无需改变结构本身。
2. 想要加入新的操作,相对容易
3. 访问者所进行的操作,其代码是集中在一起的
访问者的用途和缺点
1. 当采用访问者模式的时候,就会打破组合类的封装
2. 因为游走的功能牵涉其中,所以对组合结构的改变就更加困难