<设计模式:java语言中的应用>一书已经学完,现做个摘要、总结。
创建模式(Creational Patterns)
Abstract Factory Builder
Factory Method Prototype
Singleton
结构模式(Structural Patterns)
Adapter Bridge
Composite Decorator
Facade Flyweight
Proxy
行为模式(Behavioral Pattern)
Chain of Responsibility Command
Interpreter Iterator
Mediator Memento
Observer State
Strategy Template Method
Visitor
一、创建模式(Creational Patterns)
1.Abstract Factory(抽象工厂)—把相关零件组合成产品
Abstract Factory Pattern 是把各种抽象零件组合成抽象产品。换句话说,处理的重点是在接口(API)而不是零件的具体实现。只利用接口(API)就能把零件组合成产品.
程序示例:
--Main.java 测试用的类
|-factory
| |-------Factory.java 表示抽象工厂的类(产生Link,Tray,Page)
| |-------Itme.java 用来统一处理Link和Tray的类
| |-------Link.java 抽象零件:表示HTML连接的类
| |-------Tray.java 抽象零件:抽取Link和Tray的类
| |-------Page.java 抽象零件:表示HTML网页的类
|
|-listfactory
|-------listFactory.java 表示具体工厂的类(产生ListLink,ListTray,ListPage)
|-------listLink.java 具体零件:表示HTML连接的类
|-------listTray.java 具体零件:抽取Link和Tray的类
|-------listPage.java 具体零件:表示HTML网页的类
步骤:定义抽象零件->用抽象零件定义抽象工厂->定义具体零件(继承实现抽象零件)->定义具体工厂(继承实现抽象工厂,制造实际产品)
2.Factory Method
Factory Method Pattern 在父类规定对象的创建方法,但并没有深入到较具体的类名.所有具体的完整内容都放在子类.根据这个原则,我们可以大致分成产生对象实例的大纲(框架)和实际产生对象实例的类两方面.
程序示例:
--Main.java 测试用的类
|-framework
| |-------Product.java 仅定义抽象方法use的抽象类
| |-------Factory.java 规定createProduct,registerProduct,实现create的抽象类(类似模板方法)
|
|-idcard
|-------IDCard.java 实现方法use的具体类
|-------IDCardFactory.java 实现方法createProduct,registerProduct的类
步骤:定义抽象产品->根据抽象产品定义抽象工厂->定义具体产品(继承实现抽象产品)->定义具体工厂(继承实现抽象工厂,制造实际产品)
3.Singleton(单件)-唯一的对象实例
Singleton Pattern 是针对一个类而言. Singleton类只会产生1个对象实例.Singleton类把singleton定义为static字段(类变量),再以Singleton类的对象实例进行初始化.这个初始化的操作仅在加载Singleton类时进行一次.
Singleton类的构造函数是private的,主要是为了禁止从非Singleton类调用构造函数.即使下面这个表达式不在此类之内,编译时仍然会出现错误.
程序示例:
|--Main.java 测试用的类
|--Singleton.java 只有1个对象实例的类
步骤:定义一个该类类型的static字段,同时实例化->该类的构造方法设为private->定义一个static的getInstance()方法,返回已经实例化的static字段.
4.Builder(生成器)-组合复杂的对象实例
Builder Pattern 是采用循序渐进的方式组合较复杂对象实例的.
程序示例:
|--Main.java 测试用的类
|--Builder.java 规定建立文件时的方法的抽象类
|--Director.java 产生1个文件的类
|--TextBuilder.java 产生plaintext格式(一般文本格式)的类
|--HTMLBuilder.java 产生HTML格式的类
步骤:定义建立文件时的通用方法(Builder.java)->根据通用方法组织建立文件(Director.java)->根据不同需求实现建立文件的通用方法(TextBuilder.java,HTMLBuilder.java)
5.Prototype(原型)-复制建立对象
Prototype Pattern 不是利用类产生对象实例,而是从一个对象实例产生出另一个新对象实例.
程序示例:
|--Main.java 测试用的类
|--MessageBox.java 把字符串框起来use的类.实现use和createClone
|--UnderlinePen.java 把字符串加上下划线的类.实现use和createCone
|--framework
|-------Product.java 已声明抽象方法use和createClone的接口
|-------Manager.java 利用createClone复制对象实例的类
步骤:规定可复制产品的接口(Product.java,继承Cloneable接口)->保存可复制的产品(以Product类型存以哈西表中),并提供复制产品的方法create(调用产品的复制方法,复制工作在具体产品类中执行)(Manager.java)->定义可复制的具体产品(UnderlinePen.java,MessageBox.java,实现复制产品方法)
二、结构模式(Structural Patterns)
1.Adapter(适配器)-换个包装再度利用
Adapter Pattern 把既有的无法直接利用的内容转换成必要的类型后再使用.具有填平"既有内容"和"需要结果"两者间"落差"的功能.
Adapter Pattern 有继承和委托两种形式.
程序示例:
|--Main.java 测试用的类
|--Banner.java 具有原始功能showWithParen,showWithAster的类
|--Print.java 所需新功能printWeak,printStrong的接口或抽象类
|--PrintBanner.java 把原始功能转换成新功能的类
步骤:
(继承)构造具有原始功能的类(Banner.java)->定义具有新功能的接口(Print.java)->转换(PrintBanner.java,继承Banner实现Print接口,即继承旧方法实现新功能)
(委托)构造具有原始功能的类(Banner.java)->定义具有新功能的抽象类(Print.java)->转换(PrintBanner.java,继承具有新功能的Print类.定义委托对象,即原始功能类.构造时传入原始功能实例对象,新功能的实现利用委托对象的原始功能.)
2.Bridge(桥接)-分成功能层次和实现层次
Bridge Pattern 沟通着"功能的类层次"和"实现的类层次"
功能的类层次:给父类增加不同的功能
实现的类层次:给父类以不同的实现
Bridge Pattern 本质上是通过功能类(最上层的功能类)中的一个实现类(最上层的实现类,一般是抽象类)字段来桥接两个类层次的.
程序示例:
|--Main.java 测试用的类
|--Display.java 功能类层次的最上层类
|--CountDisplay.java 功能类层次的新增功能类
|--DisplayImpl.java 实现类层次的最上层类
|--StringDisplayImpl.java 实现类层次的实现类
步骤:定义实现类层次的最上层类(DisplayImpl.java)->定义功能类层次的最上层类(Display.java,使用Adapter Pattern的委托方式把DisplayImpl.java的原始功能转换成Display.java的新功能)->定义功能类层次的新增功能类(CountDisplay.java)->定义实现类层次的实现类(StringDisplayImpl.java)
3.Composite(组成)-对容器和内容一视同仁
有时候把容器和内容当作是同类来处理会比较好下手。容器里面可以是内容,也可以是更小一号的容器;而这个小一号的容器里还可以再放更小一号的容器,可以建立出像这样大套小的结构和递归结构的Pattern就是Composite Pattern
使用Composite Pattern,容器和内容必须有一定的共性.
程序示例:
|--Main.java 测试用的类
|--File.java 表示文件的类
|--Directory.java 表示目录的类
|--Entry.java 对File和Directory一视同仁的抽象类
|--FileTreatmentException.java 欲在文件内新增Entry时所发生的异常类
步骤:定义异常类(FileTreatmentException.java)->定义进入点类,即将容器和内容一视同仁的抽象类(Entry.java,容器和内容都含有共同的方法)->定义容器类和内容类(File.java,Directory.java,继承Entry,实现通用方法)
4.Decorator(装饰)-对装饰和内容一视同仁
先建立一个核心对象,再一层层加上装饰用的功能,就可以完成符合所需的对象.可以看成是多个通用的适配器.
程序示例:
|--Main.java 测试用的类
|--Display.java 打印字符串用的抽象类
|--StringDisplay.java 只有1行的打印字符串用的类
|--Border.java 表示"装饰外框"的抽象类
|--SideBorder.java 只在左右加上装饰外框的类
|--FullBorder.java 在上下左右加上装饰外框的类
步骤:定义核心对象的抽象类(Display.java)->定义核心对象类(StringDisplay.java)->定义装饰类的抽象类(Border.java,继承核心对象的抽象类Display.java,以便装饰和内容一视同仁.装饰类中继承自核心对象抽象类的方法委托给传入的核心对象)->定义其它装饰类(SideBorder.java,FullBorder.java,继承Border.java)
5.Facade(外观)-单一窗口
Facade Pattern 能整理错综复杂的来龙去脉,提供较高级的接口(API).Facade参与者让系统外部看到较简单的接口(API).而且Facade参与者还会兼顾系统内部各类功能和互动关系,以最正确的顺序利用类.
Facade Pattern 把业务逻辑封装起来,只提供一个简单的接口给外部调用.
程序示例:
|--Main.java 测试用的类
|--maildata.txt 邮件列表文件
|--pagemaker
|-------Database.java 从邮件信箱取得用户名称的类
|-------HtmlWriter.java 产生HTML文件的类
|-------PageMaker.java 根据邮件信箱产生用户网页的类
步骤:定义业务逻辑需要的相关类(Database.java,HtmlWriter.java)->定义外部接口类(PageMaker.java)
6.Flyweight(享元)-有相同的部分就共享,采用精简政策
"尽量共享对象实例,不做无谓的new".不是一需要对象实例就马上new,如果可以利用其他现有的对象实例,就让它们共享.这就是Flyweigth Pattern的核心概念.
Flyweight Pattern 实质是把创建的占用内存量大的对象存储起来(一般用hashtable存储),后续使用时,再从hashtable取出.
程序示例:
|--Main.java 测试用的类
|--BigChar.java 表示"大型字符"的类
|--BigCharFactory.java 共享并产生BigChar的对象实例的类
|--BigString.java 表示多个BigChar所产生的"大型文本"的类
步骤:定义占用内存量大,需要共享的类(Display.java)->定义共享实例的类(BigCharFactory.java,共享处理在此进行,将产生的共享对象存储在哈希表中,第二次使用时从表中取出即可,不需要new)->定义共享对象组合使用类(BigString.java)
7.Proxy(代理)-需要再建立
代理就是那个代替本来应该自己动手做事的本人的人.
由于代理纯粹只是代理工作而已,因此能力范围也有限.如果遇到超出代理能力所及的范围,代理就应该去找本人商量才对.
程序示例:
|--Main.java 测试用的类
|--Printer.java 表示命名的打印机的类(本人)
|--Printable.java Printer和PrinterProxy共享的接口
|--PrinterProxy.java 表示命名的打印机的类(代理)
步骤:定义本人和代理都能处理的问题的接口(Printable.java)->建立本人类(Printer.java,实现Printable.java接口)->建立代理类(PrinterProxy.java,定义本人字段,把代理无法处理的问题交给本人)
三、行为模式(Behavioral Pattern)
1.Chain of Responsibility(职责链)-责任转送
先对人产生一个要求,如果这个人有处理的能力就处理掉;如果不能处理的话,就把要求转送给"第二个人".同样的,如果第二个人有处理的能力时就处理掉,不能处理的话,就继续转送给"第三个人",依此类推.这就是Chain of Responsiblility Pattern.
Chain of Responsibility Pattern 的关键在于定义转送字段(next)和定义职责链.
程序示例:
|--Main.java 建立Support的连锁,产生问题的测试用类
|--Trouble.java 表示发生问题的类.内有问题编号.
|--Support.java 解决问题的抽象类.内有转送字段和处理方法.
|--NoSupport.java 解决问题的具体类(永远"不处理")
|--LimitSupport.java 解决问题的具体类(解决小于指定号码的问题)
|--OddSupport.java 解决问题的具体类(解决奇数号码的问题)
|--SpecialSupport.java 解决问题的具体类(解决特殊号码的问题)
步骤:建立问题类(Trouble.java)->建立解决问题的抽象类(Support.java,定义了转送字段next,设置转送字段的方法setNext和处理问题的方法support)->建立解决问题的具体类(NoSupport.java,LimitSupport.java,OddSupport.java,SpecialSupport.java,继承Support.java)->产生处理问题的对象,建立职责链
2.Command(命令)-将命令写成类
用一个"表示命令的类的对象实例"来代表欲执行的操作,而不需采用"调用方法"的类的动态处理.如欲管理相关纪录,只需管理该对象实例的集合即可.而若预先将命令的集合存储起来,还可再执行同一命令;或者是把多个命令结合成一个新命令供再利用.
Command Pattern 重点在于存储/使用命令
程序示例:
--Main.java 测试用的类
|-command
| |-------Command.java 表示"命令"的接口
| |-------MacroCommand.java 表示"结合多个命名的命令"的类
|
|-drawer
|-------DrawCommand.java 表示"点的绘制命令"的类
|-------Drawable.java 表示"绘制对象"的接口
|-------DrawCanvas.java 表示"绘制对象"的类
步骤:建立命令接口(Command.java)->建立命令结合类(MacroCommand.java,将各个命令存储到一个Stack类型的字段)->建立绘制命令类(DrawCommand.java,定义绘制对象字段drawable,实现命令接口)->建立绘制对象接口(Drawable.java)->建立绘制对象类(DrawCanvas.java,实现绘制对象接口,定义命令集合字段history)->测试
3.Interpreter(解释器)-以类来表达语法规则
Interpreter Pattern 是用简单的"迷你语言"来表现程序要解决的问题,以迷你语言写成"迷你程序"而表现具体的问题.迷你程序本身无法独自启动,必须先用java语言另外写一个负责"解释(interpreter)"的程序.解释程序能分析迷你语言,并解释/执行迷你程序.这个解释程序也称为解释器.当应解决的问题发生变化时,要修改迷你程序来对应处理.而不是修改用java语言写成的程序.
迷你语言语法:
程序示例:
|--Main.java 测试用的类
|--Node.java 树状剖析中"节点"的类
|--ProgramNode.java 对应
|--CommandListNode.java 对应
|--CommandNode.java 对应
|--RepeatCommandNode.java 对应
|--PrimitiveCommandNode.java 对应
|--Context.java 表示语法解析之前后关系的类
|--ParseException.java 语法解析中的例外类
步骤:确定迷你语言的语法->建立语法解析类(Context.java,使用java.util.StringTokenizer类)->建立解析异常类(ParseException.java)->建立语法节点抽象类(Node.java,定义parse解析方法)->建立各语法节点对应的语法类(ProgramNode.java,CommandListNode.java,CommandNode.java,RepeatCommandNode.java,PrimitiveCommand.java,继承语法节点Node.java类)
4.Iterator-迭代器
Iterator Pattern 是指依序遍历并处理多个数字或变量.
程序示例:
|--Main.java 测试用的类
|--Aggregate.java 表示已聚合的类
|--Iterator.java 执行递增/遍历的接口
|--Book.java 表示书籍的类
|--BookShelf.java 表示书架的类
|--BookShelfIterator.java 扫描书架的类
步骤:定义聚合接口(Aggregate.java)->定义遍历接口(Iterator.java)->建立具体的遍历对象类(Book.java)->建立具体的聚合类(BookShelf.java,实现聚合接口)->建立具体的遍历类(BookShelfIterator.java,实现遍历接口)
5.Mediator(中介者)-只要面对一个顾问
每个成员都只对顾问提出报告,也只有顾问会发出指示给各个成员;成员们彼此也不会去探问目前状况如何,或乱发指示给其他成员.
程序示例:
|--Main.java 测试用的类
|--Mediator.java 决定"顾问"接口(API)的接口
|--Colleague.java 决定"成员"接口(API)的接口
|--ColleagueButton.java 实现Colleagues接口.表示按键的类
|--ColleagueTextField.java 实现Colleagues接口.输入文本的类
|--ColleagueCheckbox.java 实现Colleagues接口.表示选择项目(在此为选择按钮)的类
|--LoginFrame.java 实现Mediator接口.表示登录对话框的类
步骤:定义顾问接口(Mediator.java)->定义成员接口(Colleague.java)->建立具体的成员类(ColleagueButton.java,ColleagueTextField.java,ColleagueCheckbox.java,实现成员接口)->建立具体的顾问类(LoginFrame.java,实现顾问接口)
6.Memento(备忘录)-存储状态
Memento Pattern 会把某个时间点的对象实例状态记录存储起来,等到以后再让对象实例复原到当时的状态.
程序示例:
|--Main.java 进行游戏的类.先把Memento的对象实例存储起来,如有必要时再复原Gamer的状态
|--game
|-------Gamer.java 玩游戏的主人翁的类
|-------Memento.java 表示Gamer状态的类.产生Memento的对象实例
步骤:建立需要存储状态的类(Gamer.java)->建立状态类(Memento.java,状态类与需要存储状态的类Gamer.java应具有相同的必要字段)
7.Observer(观察者)-通知状态变化
当被Observer Pattern 列入观察名单的状态发生变化,就会通知观察者.在写一些跟状态变化有关的处理时,Observer Pattern是很好用的工具.
程序示例:
|--Main.java 测试用的类
|--Observer.java 表示观察者的接口
|--NumberGenerator.java 表示产生数值对象的抽象类
|--RandomNumberGenerator.java 产生随机数的类
|--DigitObserver.java 以数字表示数值的类
|--GraphObserver.java 以简易长条图表示数值的类
步骤:定义观察者接口(Observer.java)->建立被观察的类(NumberGenerator.java,RandomNumberGenerator.java,定义观察者结合字段将观察者存储起来)->建立具体的观察者类(DigitObserver.java,GraphObserver.java,实现观察者接口)
8.State(状态)-以类表示状态
以类来表示状态之后,只要切换类就能表现“状态变化”,而且在必须新增其他状态时,也很清楚该编写哪个部分。
程序示例:
|--Main.java 测试用的类
|--State.java 表示金库状态的接口
|--DayState.java 实现State的类。表示白天的状态
|--NightState.java 实现State的类。表示夜间的状态
|--Context.java 管理金库的状态变化,跟保安中心联络的接口
|--SafeFrame.java 实现Context的类。含有按钮、画面显示等的用户接口
步骤:定义状态接口(State.java,将使用State Pattern之前各种行为方法抽象出来)->建立具体的状态类(DayState.java,NightState.java,实现状态接口,状态变化的具体动作在这里执行)->定义管理状态变化的接口(Context.java,规定状态变化及相关的调用方法)->建立状态管理类(SafeFrame.java,实现状态管理接口)
9.Strategy(策略)-把算法整个换掉
在Strategy Pattern之下,可以更换实现算法的部分而且不留痕迹。切换整个算法,简化改为采用其他方法来解决同样的问题。
程序示例:
|--Main.java 测试用的类
|--Hand.java 表示猜拳“手势”的类
|--Strategy.java 表示猜拳“战略”的接口
|--WinningStrategy.java 表示猜赢之后继续出同样招式的战略的类
|--ProbStrategy.java 表示从上一次出的招式,以概率分配方式求出下一个招式机率的类
|--Player.java 表示玩猜拳的游戏者的类
步骤:定义策略接口(Strategy.java)->建立具体的策略类(WinningStrategy.java,ProbStrategy.java,实现策略接口)->建立使用策略的类(Player.java,定义策略字段,以便使用切换策略)->建立其它类(Main.java,Hand.java)
10.Template Method(模板方法)-实际处理交给子类
在父类指定处理大纲、在子类规定具体内容的Design Pattern就称为Template Method Pattern
程序示例:
|--Main.java 测试用的类
|--AbstractDisplay.java 只实现方法display的抽象类
|--CharDisplay.java 实现方法open,print,close的类
|--StringDisplay.java 实现方法open,print,close的类
步骤:定义模板类(AbstractDisplay.java,实现dispaly方法,即制作了模板)->建立具体内容类(CharDisplay.java,StringDisplay.java,继承模板类,实现模板类没有实现的方法)
11.Visitor(访问者)-在结构中穿梭还同时做事
Visitor Pattern 把数据结构和处理两者分开,另外写一个表示在数据结构内穿梭来去的主体“访客”的类,然后把处理交给这个类来进行。如此一来,如果想追加新的处理行为时,只要再建立一个新的“访客”即可。而在数据结构这边,也只要能接受来敲门的“访客”就能完成动作。
在父类指定处理大纲、在子类规定具体内容的Design Pattern就称为Template Method Pattern
程序示例:
|--Main.java 测试用的类
|--Visitor.java 表示访问文件或目录的访客的抽象类
|--Acceptor.java 表示接受Visitor类的对象实例的数据结构的接口
|--ListVisitor.java Visitor类的子类,打印文件和目录信息的类
|--Entry.java File和Directory的父类的抽象类(实现Acceptor接口)
|--File.java 表示文件的类
|--Directory.java 表示目录的类
|--FileTreatmentException.java 发生在对File进行add时的例外类
步骤:定义访问者的抽象类(Visitor.java,定义访问方法)->定义受访者接口(Acceptor.java,定义接受访问的方法)->建立具体的访问者类(ListVisitor.java,继承访问者抽象类,实现访问方法)->建立具体的受访者类(Entry.java,File.java,Directory.java,实现受访者接口)->编写异常类(FileTreatmentException.java)