永不放弃的毅力,和对欲望的控制。
注意:要能够理解相类似的设计模式之间的区别和不同。可以把类比列举出来,加深记忆。
是否加入Spring容器中的标准是是否要用到Spring框架的方法或者功能特性,如事务,SpringMvc,与ibatis整合等相关的。如果不需要用到Spring,大可用java构造器去依赖即可,不需要加入Spring容器中。
记忆23+1种设计模式和六大原则
大话设计模式
head first 设计模式
加实践(临渊羡鱼,不如退而结网)
加思考
加实践
再看一遍UML
再看一遍两本书
分析总结设计模式在项目中的运用,框架中的运用
工作中运用设计模式
让学习作为大脑认为很重要的东西,这样学习的东西就容易被记住。让大脑觉得很重要!
慢方法是大量地重复。
快方法是尽一切可能让大脑活动起来,记忆力更深。
以慢为主,以快为辅,用快加速。
注意大脑活动
大脑有需要可以阅读以下9点帮助。
待处理
- 看下订单中心的简单工厂加反射的写法(用于获取实例,spring是通过容器管理,@Autowired 依赖进来得到实例的),模拟写一个。
- 企业设计模式?
使用模式最好的方式是:把模式装进脑子里(起码先要记忆住),然后在设计和已有的应用中,寻找何处可以使用它们。
分开变化和不变的部分。
最常用模式:
策略模式、观察者模式、装饰器模式、工厂模式(简单工厂、工厂方法、抽象工厂)、单例模式、命令模式、适配器模式、外观模式、模板方法、迭代器、组合、状态、代理、复合
案例分析:
生产不同种类的鸭子,真鸭子,假鸭子,唐老鸭,玩具鸭 。。。
前提分析:
- 使用继承有一些缺失, 因为改变鸭子的行为会影响所有种类的鸭子,而这并不恰当。
- F l y a b l e与Q u a c k a b l e接口一开始似乎还挺不错,但是J a v a 的接口不具有实现代码, 所以继
承接口无法达到代码的复用。
设计原则:找出应用中可能需要变化之处,把它们独立出来(封装起来变成组件), 不要和那些不需要变化的代码混在一起。
设计原则:针对接口编程, 而不是针对实现编程。
设计原则:多用组合,少用继承。
在设计系统时,预先考虑到有哪些地方未来可能需要变化,于是提前在代码中加入这些弹性。你会发现,原则与模式可以应用在软件开发生命周期的任何阶段。
设计是一种艺术,总是有许多取舍的地方。但是如果你能采用这些经过深思熟虑,且通过时间考验的设计模式,你就领先别人了。
建立可维护的O O 系统,要诀就在于随时想到系统以后可能需要的变化以及应付变化的原则。
『策略模式』
定义了算法族,分别封装起来,让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户。
策略模式用于算法的替换,算法代表鸭子能做的事(不同的叫法和飞行法),这样的作法也能用于用一群类计算不同州的销售税金。
观察者模式
案例:公司发布职位,求职者订阅该公司动态。观察者模式的代表人物——MVC
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都
会收到通知并自动更新。
一对多,松耦合,观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
发布/订阅模式
实现观察者模式的方法不只一种,但是以包含Subject与Observer接口的类设计的做法最常见。有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
设计原则:为了交互对象之间的松耦合设计而努力。
设计发布订阅气象站
java.util包(package)内包含最基本的Observer接口与Observable(“可观察者”(Observable))类,这和我们的Subject接口与Observer接口很相似。
如果你想“ 推” ( p u s h ) 数据给观察者, 你可以把数据当作数据对象传送给notifyObservers(arg)方法。否则,观察者就必须从可观察者对象中“拉”(pull)数据。拉的就要在主题类上提供拉取数据的方法,gettter();
setChanged()方法可以让你在更新观察者时,有更多的弹性,你可以更适当地通知观察者。比方说,如果没有setChanged()方法,我们的气象站测量是如此敏锐,以致于温度计读数每十分之一度就会更新,这会造成WeatherData对象持续不断地通知观察者,我们并不希望看到这样的事情发生。如果我们希望半度以上才更新,就可以在温度差距到达半度时,调用setChanged(),进行有效的更新。
装饰器模式
装饰器模式给爱用继承的人一个全新的设计眼界。一旦你熟悉了装饰的技巧,你将能够在不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。
不良的设计如下,不考虑事物后续极有可能的变化,没有封装好变化,达到开放关闭原则。
利用组合(composition)和委托(delegation)可以在运行时具有继承行为的效果。
既然没有改变现有代码,那么引进bug或产生意外副作用的机会将大幅度减少。
设计原则:类应该对扩展开放,对修改关闭。
许多模式是长期经验的实证,可通过提供扩展的方法来保护代码免于被修改。在本章,将看到使用装饰者模式的
一个好例子,完全遵循开放-关闭原则。
完全开放-关闭原则是不太可能的,把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。
哪些地方的改变是更重要呢?
这牵涉到设计OO系统的经验,和对你工作领域的了解。多看一些其他的例子可以帮你学习如何辨别设计中的变化
区。在选择需要被扩展的代码部分时要小心。每个地方都采用开放-关闭原则,是一种浪费,也没必要,还会导致代码变得复杂且难以理解。
装饰者模式定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,我们利用继承达到“类型匹配”,而不是利用继承获得“行为”。继承被装饰者的抽象类,是为了有正确的类型,而不是继承它的行为。行为来自装饰者和基础组件,或与其他装饰者之间的组合关系。
通常装饰者模式是采用抽象类,但是在Java中可以使用接口。尽管如此,通常我们都努力避免修改现有的代码,所以,如果抽象类运作得好好的,还是别去修改它。
装饰者通常是用其他类似于工厂或生成器这样的模式创建的。
真实世界的装饰者:J a v a I / O
是JavaAI/O也引出装饰者模式的一个“缺点”:利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。但是,现在你已经了解了装饰者的工作原理,以后当使用别人的大量装饰的API时,就可以很容易地辨别出他们的装饰者类是如何组织的,以方便用包装方式取得想要的行为。
注意:
in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("text.txt")));
//"text.txt" 指的是项目名下的路径如fcarfi下的路径
工厂模式
当使用 new 时,就是在实例化一个具体类,即使是用多态来编程也是,是面向实现而不是接口编程。(多态的话可以用工厂或者反射来创建具体的实现类,简单的写法是在代码里通过if/else判断出要实例化的具体类来用new多态创建实例)
静态工厂的优缺点:
优点是该工厂可以直接调用,不需要实例化工厂。
缺点是该工厂不能通过继承来改变创建方法的行为。
简单工厂UML:
工厂方法模式
定义:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
问题:利用字符串传入参数化的类型,字符串不对时怎么处理?
A:在编译时期就将参数上的错误挑出来,如创建代表参数类型的对象、使用静态变量、或者enum 枚举。
也可以在运行期把匹配不到的抛出异常来。
工厂方法的关键点
工厂方法的工厂和产品都是抽象的,不过是通过实现不同的工厂方法来实现生产不同的产品,所以叫工厂方法模式。
依赖倒置原则:
设计原则:要依赖抽象,不要依赖具体类。
不能让高层组件依赖低层组件(意思是高层组件不能依赖具体的低层组件,而应该依赖于低层的抽象的意思。否则高层不依赖于低层怎么实现其行为,应该依赖与接口、抽象,解耦合),而且,不管高层还是低层组件,两者都应该依赖于抽象。
高层组件指的是由其他低层组件定义其行为的类。如生产pizza店中:pizza店是高层,pizza是低层。
错误示范 175
依赖倒置倒置在低层组件现在竟然依赖于高层的抽象。如具体不同的pizza实现现在依赖于pizza抽象类,而现在高层组件pizza店也依赖于pizza这个相同的抽象类。错误示范中的依赖是从上而下的,现在却倒置了。
避免在设计中违反依赖倒置原则的三个指导方针如下:
- 变量不可以持有具体类的引用。(如果使用new ,就会持有具体类的引用。可以改用工厂来不可这样的做法。)
- 不要让类派生自具体类。(即不要在具体类上继承或者实现,应该在抽象类或者接口上实现)
- 不要覆盖基类中已实现的方法。(如果需要覆盖基类中已实现的方法,那么这个基类就不是一个真正适合被继承的抽象。基类已实现的方法,应该由所有的子类共享。——》里式替换原则)
注意:如果在设计中有一个不像会改变的类,则在代码中直接实例化具体类也没什么大碍,如填充东西insert时的pojo/ bean;字符串对象的实例化(字符串不能改变)
抽象工厂模式
定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
1
抽象工厂的方法经常以工厂方法的方式实现,即抽象工厂里的每一个抽象方法一般都是以工厂方法来实现。
pizza原料工厂实现
工厂方法通过继承创建对象,而抽象工厂通过组合创建。
抽象工厂缺点:在扩展新产品时可能因为没用到父类的这个工厂方法或者需要改变接口而很难受。
单例模式
应用场景:线程池、缓存、对话框、注册表对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。
定义:确保一个类只有一个实例,并提供一个全局访问点。
命令模式
案例:遥控器 ,队列请求、日志请求
定义:将 “请求” 封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销的操作。
撤销命令就是在具体的命令类上加上之前的状态属性值,方便回滚。当然撤销命令调用者(服务员)也要哟个撤销方法,对应实际命令的执行者(厨师)也要有个相反的行为方法。
如果想实现多层次撤销,可以用栈来保存值。
宏命令就是创建一个具体命令类,该类有一个可以接受一组命令组的容器属性值。这样当调用宏命令时,就能for循环调用里面的各个命令执行。
命令模式UML
适配器模式
双向适配器,用于实现适配新旧两边的接口,需要同时实现新旧两个接口。
定义:将一个类的接口,装换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
适配器和装饰器的区别是意图差异不同,适配器是为了接口的转换,而装饰器是为了扩展包装对象的行为或责任。而外观模式是为了让接口更简单,是为了提供子系统的一个简化接口。
UML图
外观模式
外观模式没有封装子系统的类,外观值提供简化的接口。如果客户觉得有必要,依然可以直接使用子系统的类。在提供简化的接口的同时,依然将系统完整的功能暴露出来,以供需要的人使用。
定义:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
设计原则:最少知识原则:只和你的密友谈话。
指导方针:
就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
- 该对象本身
- 被当做方法的参数而传递进来的对象
- 此方法锁创建或实例化的任何对象
- 对象的任何组件(实例变量所引用的任何对象)
最少知识原则缺点:虽然减少了对象之间的依赖,减少了软件的维护成本,但会导致更多的包装类被制造;导致复杂度和开发时间的增加,降低运行时的性能。
如下图
模板方法模式
案例:java排序(实现Comparable)
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。模板方法提供了一个框架,可以让其他实现该接口的对象插进来,新的实现职业实现自己的方法就可以了。模板方法专注于算法本身,而由子类提供完整的实现。
定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
当子类必须提供算法中某个方法或步骤的实现时,这时候使用抽象方法定义;如果算法的这个部分是可选的,这是要使用钩子。
注意:抽象方法的数量应该越少越好,毕竟子类都需要实现,如果是可选的步骤则最好用钩子方法来实现,而不是抽象方法。
在抽象类中写一个钩子方法,默认空实现或者默认实现的用途:
- 条件控制
- 对即将反生或刚发生的步骤做出反应
好莱坞原则:别调用(打电话给)我们,我们会调用你。这是高层组件对待低层组件的方式。
迭代器模式
多线程中(多个迭代器引用同一个对象集合)使用迭代器要特别小心,可能有并发问题。
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
使用迭代器使我们有一个统一的方法访问聚合中的每一个对象,这样我们就可以编写多态的代码和这些聚合搭配。
设计原则:一个类应该只有一个引起变化的原因。
组合模式
主要用于树形结构的设计模式。
定义:允许你将对象组合成树形结构来表现 “整体/部分” 层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。使用组合结构,我们能把相同的操作应用在组合和个别对象上。即一般情况下,我们可以忽略对象组合和个别对象之间的差别。
组合模式以单一职责设计原则换取透明性。透明性指通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。即一个元素究竟是组合还是叶节点,对客户是透明的。组合中的Component类同时拥有两种类型(组合和叶节点)的操作,因此客户有机会对一个元素做不恰当或者没有意义的操作(如试图把菜单添加到菜单项),失去些安全性;但如果把接口操作区分开来,则客户的代码将必须用条件语句和instanceof操作处理不同类型的节点,失去了透明性。
返回空迭代器,客户端代码便不需要判断返回值是否为null。
状态模式
定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
策略模式和状态模式几乎相同,他们的区别在于他们的”意图 “不同。与策略模式改变行为不同,状态模式的状态改变都是确定,定义好的,状态模式随着状态变化改变行为。状态模式拥有一组定义良好的状态转换
客户端不会直接与状态交互,而是通过Context来调用,交互是由Context完成。
如果有默认的错误响应行为我们可以实现状态接口来创建一个抽象类,让默认行为得到继承。
一般状态的实例会放在静态的实例变量中共享。
代理模式
目的:控制和管理访问。
客户对象做远程方法调用时,其实只是调用本地堆中的“代理”对象上的方法,再由代理处理所以网络通信的底层细节。
RMI将客户辅助对象称为stub,服务辅助对象称为skeleton。
定义:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
我们也将代理描述成另一个对象的“代表”。被代理的对象可以是1、远程的对象;2、创建开销大的对象(如电商网站页面的图片展示中图片未加载完成前的代理占位区域对象。);3、需要安全控制的对象。
代理对象Proxy在被代理对象RealSubject出现的地方取代它。客户与被代理对象RealSubject交互都必须通过Proxy。
虚拟代理(Virtual Proxy)
用于创建开销大的对象,当对象在创建前和创建中时,有虚拟代理来扮演对象的替身,如果RealSubject已经创建了,则代理吧请求委托给RealSubject。
远程代理
远程代理可以作为另一个JVM上的对象的本地代表。
使用方法:我们常提供一个工厂,实例化并返回主题,因为这个是在工厂方法里面放生的,因此我们可以用代理包装主题再返回,这样就可以让客户使用代理,而不是真正的对象了。
代理和装饰器的区别:
目的不同。
装饰器是包装对象,提供新的方法和行为;而代理如远程代理,所代表的对象在远程服务器上,并不是对对象的包装;而虚拟代理,一开始并没有被代理的主题对象,而是由代理来创建对象。
保护代理
动态代理:实际的代理类是在运行时创建的.
三步骤:
- 创建InvocationHandler
- 创建动态代理,Proxy.newProxyInstance();
选择合适的代理包装任何想被代理的对象
public Person getOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new OwnerInvocationHandler(person));
}
其他代理:
- 防火墙代理 :控制网络资源的访问。(常用于公司的防火墙系统。)
- 智能引用代理 : 当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数。()
- 缓存代理 : 为开销大的运算结果提供暂时存储:它也允许多个客户共享结果,以减少计算或网络延迟。(常用于Web服务器代理,以及内容管理与出版系统。)
- 同步代理 : 在多线程的情况下为主题提供安全的访问。 (常用于Java Spaces。为分散式环境内的潜在对象集合提供同步访问控制。)
- 复制隐藏代理 : 用来隐藏一个类的复制即的复杂度,并进行控制访问,也称外观代理。
- 写入时复制代理 : 用来控制对象的复制,方法是延迟对象的复制,知道对象真的需要为止,如CopyOnWriteArrayList。虚拟代理的变体。
复合模式 —— 模式的模式
复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。
MVC
M:模型 V: 视图 C :控制器
Q:控制器存在的作用及为什么不合并到视图层?
A: 首先这样会让视图代码变得复杂,导致视图拥有两个责任,不满足单一职责原则。其次,这样做会导致模型与视图紧耦合,导致视图无法复用处理其他模型。
MVC用到的设计模式:
观察者模式 :模型(主题)利用观察者让控制器和视图可以随最新的状态改变而更新。
策略模式 : 视图和控制器实现策略模式,控制器是视图的行为,不同的行为可以直接更换不同的控制器。控制器提供策略。
组合模式 : 视图内部使用组合模式来管理窗口、按钮以及其他显示组件。
571 实践MVC 先跳过吧。
真实世界的设计模式
模式定义:模式是在某情境下,针对某问题的某种解决方案。
- 情境就是应用某个模式的情况。这应该是会不断出现的情况。即普遍,或者说被运用非常多次。
- 问题就是你想在某情境下达到的目标,但也可以是某情境下的约束。
- 解决方案就是你追求的:一个通用的设计,用来解决约束、达到目标。
当我们改变模式的结构以符合设计时,最好能够在文档中注明它与经典的设计模式有何差异,方便后续维护。
模式分类
- 创建型:涉及到将对象实例化,这类模式都提供一个方法,将客户从需要实例化的对象中解耦。
- 结构型: 可以让你把类或对象组合到更大的结构中。
- 行为型:只要是行为型模式,都涉及到类和对象如何交互及分配职责。
模式的意图决定所属类型,如装饰器虽然是在增加行为,但其焦点是在于如何动态地组合对象以获取功能,而不是行为类的目的——对象之间的沟通和交互。
设计时,尽可能地用最简单的方式解决问题。是否选择使用模式时应做到保持简单的设计。加入模式是要应对可能发生的实际改变,而不是假想的改变。如果现在不需要,就别做,要有实际的需要。
当系统变得非常复杂,而且并不需要预留任何弹性的时候,就不要用模式。
不要急切于使用模式,而是致力于用最能解决问题的简单方案。
在代码注释和命名习惯上把加入的模式体现出来。
模式和描述
- 装饰者:包装一个对象,以提供新的行为。
- 状态 :封装了基于状态的行为,并使用委托在行为之间切换。
- 迭代器 :在对象的集合之中游走,而不暴露集合的实现。
- 外观 :简化一群类的接口。
- 策略 :封装可以互换的行为,并使用委托来决定要使用哪一个。
- 代理 :包装对象,以控制对此对象的访问。
- 工厂方法 :由子类决定要创建的具体类是哪一个。
- 适配器 :封装对象,并提供不同的接口,用于适配。
- 观察者 :让对象能够在状态改变时被通知(发布/订阅)。
- 模板方法 :由子类决定如何实现一个算法中的步骤。
- 组合 :让客户端可以用一直的方法处理对象集合和单个对象。
- 单例 :确保有且只有一个对象被创建。
- 抽象工厂 :允许客户创建对象的家族,而无需指定他们的具体类。
- 命令 :封装请求(命令)成为对象。
桥接模式
定义:使用桥接不只改变你的实现,也改变你的抽象。
桥接的优点:
- 将实现解耦,让它和界面直接不再永久绑定。
- 抽象和实现可以独立扩展,不会影响到对方。
- 对于 “具体的抽象类” 所做的改变,不会影响到客户。
用途与缺点:
- 适用于在需要扩月多个平台的图形和窗口系统上。
- 当需要用不同的方式改变接口和实现时使用。
- 缺点是增加了复制度。
建造者模式
定义:封装一个产品的构造过程,并允许按步骤构造。
建造者模式的优点:
- 将一个复杂对象的建造过程封装起来。
- 允许对象通过多个步骤来创建,并且可以改变过程。(与工厂模式不同)
- 向客户端因此产品内部的表现。
- 产品的实现可以被替换,因为客户是调用抽象的接口及其方法。
建造者模式的用途和缺点:
- 经常被用来创建组合结构。
- 缺点是与工厂相比,才有建造者模式创建对象的客户,需要具备更多的领域知识(知道的太多,过多)
责任链
定义:当你想让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。
案例:邮件的处理(为某个请求创建一个对象链)。比如邮件有3种分类,一种是工作邀约,一种是私人邮件,一种是垃圾邮件。因此如果我们想针对性处理每一封进来的邮件的话,用职责链会很好的解决。如果是工作邀约则分发给经理人处理,私人邮件则转发到手机上,垃圾邮件的话直接删除。
责任链的优点:
- 将请求的发送者和接受者解耦。
- 可以简化你的对象,因为它不需要知道链的结构。
- 通过改变链内的成员或调动它们的次序,允许你动态地新增或者删除责任。
责任链用途和缺点:
- 经常被使用在窗口系统中,处理鼠标和键盘之类的事件。
- 缺点1:并不能保证请求一定会被执行,因为如果结果没有任何对象处理它的话。(有时候也是优点)
- 缺点2:不容易观察运行时的特征,有碍于除错。
享元模式
定义:如果想让某个类的一个实例能用来提供许多“虚拟实例”,就使用享元。
案例:大量需要实例化的相同的对象,只是外部有一点点区别。如下面的景观树对象,是种植的坐标和树的年龄不同,这时候可以用享元来共享实例。
享元模式的优点:
- 减少运行时对象实例的个数,节省内存。
- 将许多“虚拟”对象的状态集中管理
享元模式的用途和缺点:
- 当一个类有许多的实例,而这些实例能被同一方法控制时,使用享元。
- 缺点:一旦使用享元,那么单个的逻辑实例将无法拥有独立而不同的行为。
解释器
定义:使用解释器模式为语言创建解释器。
解释器模式的优点:
- 将每一个语法规则表示成一个类,方便于实现语言。
- 可以轻易地扩展该语言,因为语法由类表示。
- 通过在类结构中加入新的方法,可以在解释的同时增加新的行为。例如格式美化等。
解释器的用途和缺点:
- 当需要实现一个简单的语言时,使用解释器。
- 有个简单的语法,且简单比效率更重要时,使用解释器。
- 可以处理脚本语言和编程语言。
- 缺点:语法规则数目太大时,模式会变得非常繁杂,这种情况下,使用解释器/编译器的产生器更合适。
中介者模式
定义:使用中介者模式来集中相关对象之间复杂的沟通和控制方式。
- 每个对象都会在自己的状态改变时,告诉中介者。
- 每个对象都会对中介者所发出的请求做出回应。
- 有了中介者后对象之间彻底解耦。
中介者的优点:
- 通过将对象彼此解耦,可以增加对象的复用性。
- 通过将控制逻辑集中,可以简化系统维护。
- 可以让对象之间所传递的消息变得简单而且大幅减少。
中介者的用途和缺点:
- 中介者常用来协调相关的GUI组件。
- 缺点:如果设计不当,中介者本身会变得过于复杂。(毕竟所有控制逻辑都在中介者类上)
备忘录模式
定义:当你需要让对象返回之前的状态时(如用户请求撤销,游戏存储进度,读取存档),就使用备忘录模式。
备忘录的两个目标:
- 存储系统关键对象的重要状态。
- 维护关键对象的封装。
备忘录模式的优点:关键对象就是要备份的对象。
- 将被存储的状态放在外面,不和关键对象混在一起,有助于维护内聚。 ?
- 保持关键对象的数据封装。
- 提供了容易实现的恢复能力。
备忘录的用途和缺点:
- 备忘录模式用于存储状态。
- 缺点:存储和恢复状态的过程可能相当耗时。(备份对象过于庞大时)
- 替换备忘录模式:Java中可以使用序列化机制来存储系统的状态。
原型模式
案例:在Java中其实就是clone或者反序列化,与备忘录模式中的序列化存储相对应。
定义:当创建给定类的实例化的过程很昂贵或者复杂时,就使用原型模式。
原型模式的优点:
- 向客户隐藏制造新实例的复杂性,即封装复杂性,最小知道原则。
- 提供让客户能够产生位置类型对象的选项。?
- 在某些环境下,复杂对象比创建新对象更有效。?
用途和缺点:
- 在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,可以使用原型。
- 缺点:对象的复制可能相当复杂。(深复制)
访问者模式
待确认,与大话模式不同?P667
定义:当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式。
优点:
- 允许你对组合结构加入新的操作,而无需改变结构本身。
- 想要加入新的操作,相对容易。
- 访问者锁进行的操作,其代码是集中在一起的。?
用途和缺点:
- 采用访问者模式会打破组合类的封装。 ?
- 缺点:对组合结构的改变比较困难,也是使用该模式要避免的。