设计模式
第 2 页
目 录
1. 策略模式【Strategy Pattern】 .......................................................... 4
2. 代理模式【Proxy Pattern】 .............................................................. 8
3. 单例模式【Singleton Pattern】 ...................................................... 12
4. 多例模式【Multition Pattern】 ...................................................... 17
5. 工厂方法【Factory Method Pattern】 ............................................ 20
6. 抽象工厂模式【Abstract Factory Pattern】 ................................... 32
7. 门面模式【Facade Pattern】 .......................................................... 46
8. 适配器模式【Adapter Pattern】 .................................................... 53
9. 模板方法模式【Template Method Pattern】 ................................. 65
10. 建造者模式【Builder Pattern】 ...................................................... 84
11. 桥梁模式【Bridge Pattern】 ........................................................... 99
12. 命令模式【Command Pattern】 ................................................... 115
13. 装饰模式【Decorator Pattern】 ................................................... 129
14. 迭代器模式【Iterator Pattern】 ................................................... 141
15. 组合模式【Composite Pattern】 .................................................. 151
16. 观察者模式【Observer Pattern】 ................................................. 180
17. 访问者模式【Visitor Pattern】 ..................................................... 181
18. 状态模式【State Pattern】 ........................................................... 182
19. 责任链模式【Chain of Responsibility Pattern】 ........................... 183
您的设计模式
第 3 页
20. 原型模式【Prototype Pattern】 ................................................... 184
21. 中介者模式【Mediator Pattern】 ................................................ 185
22. 解释器模式【Interpreter Pattern】 .............................................. 186
23. 亨元模式【Flyweight Pattern】 .................................................... 187
24. 备忘录模式【Memento Pattern】 ............................................... 188
25. 模式大PK ...................................................................................... 189
26. 混编模式讲解 ............................................................................... 190
27. 更新记录: ................................................................................... 191
28. 相关说明 ....................................................................................... 192
29. 后序 ............................................................................................... 193
您的设计模式
第 4 页
1. 策略模式【Strategy Pattern】
刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开
解决棘手问题,嘿,还别说,真是解决了大问题,搞到最后是周瑜陪了夫人又折兵呀,那咱
们先看看这个场景是什么样子的。
先说这个场景中的要素:三个妙计,一个锦囊,一个赵云,妙计是小亮同志给的,妙计
是放置在锦囊里,俗称就是锦囊妙计嘛,那赵云就是一个干活的人,从锦囊中取出妙计,执
行,然后获胜,用JAVA 程序怎么表现这个呢?我们先看类图:
三个妙计是同一类型的东东,那咱就写个接口:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 首先定一个策略接口,这是诸葛亮老人家给赵云的三个锦囊妙计的接口
*
*/
public interface IStrategy {
//每个锦囊妙计都是一个可执行的算法
您的设计模式
第 5 页
public void operate();
}
然后再写三个实现类,有三个妙计嘛:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 找乔国老帮忙,使孙权不能杀刘备
*/
public class BackDoor implements IStrategy {
public void operate() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
}
}
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 求吴国太开个绿灯
*/
public class GivenGreenLight implements IStrategy {
public void operate() {
System.out.println("求吴国太开个绿灯,放行!");
}
}
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 孙夫人断后,挡住追兵
*/
您的设计模式
第 6 页
public class BlockEnemy implements IStrategy {
public void operate() {
System.out.println("孙夫人断后,挡住追兵");
}
}
好了,大家看看,三个妙计是有了,那需要有个地方放这些妙计呀,放锦囊呀:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 计谋有了,那还要有锦囊
*/
public class Context {
//构造函数,你要使用那个妙计
private IStrategy straegy;
public Context(IStrategy strategy){
this.straegy = strategy;
}
//使用计谋了,看我出招了
public void operate(){
this.straegy.operate();
}
}
然后就是赵云雄赳赳的揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的、
色迷迷的刘老爷子去入赘了,嗨,还别说,小亮的三个妙计还真是不错,瞅瞅:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ZhaoYun {
/**
* 赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
您的设计模式
第 7 页
*/
public static void main(String[] args) {
Context context;
//刚刚到吴国的时候拆第一个
System.out.println("-----------刚刚到吴国的时候拆第一个
-------------");
context = new Context(new BackDoor()); //拿到妙计
context.operate(); //拆开执行
System.out.println("\n\n\n\n\n\n\n\n");
//刘备乐不思蜀了,拆第二个了
System.out.println("-----------刘备乐不思蜀了,拆第二个了
-------------");
context = new Context(new GivenGreenLight());
context.operate(); //执行了第二个锦囊了
System.out.println("\n\n\n\n\n\n\n\n");
//孙权的小兵追了,咋办?拆第三个
System.out.println("-----------孙权的小兵追了,咋办?拆第三个
-------------");
context = new Context(new BlockEnemy());
context.operate(); //孙夫人退兵
System.out.println("\n\n\n\n\n\n\n\n");
/*
*问题来了:赵云实际不知道是那个策略呀,他只知道拆第一个锦囊,
*而不知道是BackDoor这个妙计,咋办? 似乎这个策略模式已经把计谋名称
写出来了
*
* 错!BackDoor、GivenGreenLight、BlockEnemy只是一个代码,你写成
first、second、third,没人会说你错!
*
* 策略模式的好处就是:体现了高内聚低耦合的特性呀,缺点嘛,这个那个,我
回去再查查
*/
}
}
就这三招,搞的周郎是“陪了夫人又折兵”呀!这就是策略模式,高内聚低耦合的特点
也表现出来了,还有一个就是扩展性,也就是OCP 原则,策略类可以继续增加下去,只要修
改Context.java 就可以了,这个不多说了,自己领会吧。
您的设计模式
第 8 页
2. 代理模式【Proxy Pattern】
什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代
理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代
理人虽然不能干活,但是被代理的人能干活呀。
比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程
序上时这样的:
先定义一种类型的女人:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一种类型的女人,王婆和潘金莲都属于这个类型的女人
*/
public interface KindWomen {
//这种类型的女人能做什么事情呢?
public void makeEyesWithMan(); //抛媚眼
public void happyWithMan(); //happy what? You know that!
}
一种类型嘛,那肯定是接口,然后定义潘金莲:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定一个潘金莲是什么样的人
*/
public class PanJinLian implements KindWomen {
您的设计模式
第 9 页
public void happyWithMan() {
System.out.println("潘金莲在和男人做那个.....");
}
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼");
}
}
再定一个丑陋的王婆:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 王婆这个人老聪明了,她太老了,是个男人都看不上,
* 但是她有智慧有经验呀,她作为一类女人的代理!
*/
public class WangPo implements KindWomen {
private KindWomen kindWomen;
public WangPo(){ //默认的话,是潘金莲的代理
this.kindWomen = new PanJinLian();
}
//她可以是KindWomen的任何一个女人的代理,只要你是这一类型
public WangPo(KindWomen kindWomen){
this.kindWomen = kindWomen;
}
public void happyWithMan() {
this.kindWomen.happyWithMan(); //自己老了,干不了,可以让年轻的
代替
}
public void makeEyesWithMan() {
this.kindWomen.makeEyesWithMan(); //王婆这么大年龄了,谁看她抛
媚眼?!
}
您的设计模式
第 10 页
}
两个女主角都上场了,男主角也该出现了:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个西门庆,这人色中饿鬼
*/
public class XiMenQing {
/*
* 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,
* 被王婆看到了, 就开始撮合两人好事,王婆作为潘金莲的代理人
* 收了不少好处费,那我们假设一下:
* 如果没有王婆在中间牵线,这两个不要脸的能成吗?难说的很!
*/
public static void main(String[] args) {
//把王婆叫出来
WangPo wangPo = new WangPo();
//然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那
出戏:
wangPo.makeEyesWithMan(); //看到没,虽然表面上时王婆在做,实际上
爽的是潘金莲
wangPo.happyWithMan(); }
}
那这就是活生生的一个例子,通过代理人实现了某种目的,如果真去掉王婆这个中间环
节,直接是西门庆和潘金莲勾搭,估计很难成就武松杀嫂事件。
那我们再考虑一下,水浒里还有没有这类型的女人?有,卢俊义的老婆贾氏(就是和那
个固管家苟合的那个),这名字起的:“假使”,那我们也让王婆做她的代理:
把贾氏素描出来:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
您的设计模式
第 11 页
*/
public class JiaShi implements KindWomen {
public void happyWithMan() {
System.out.println("贾氏正在Happy中......");
}
public void makeEyesWithMan() {
System.out.println("贾氏抛媚眼");
}
}
西门庆勾贾氏:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个西门庆,这人色中饿鬼
*/
public class XiMenQing {
public static void main(String[] args) {
//改编一下历史,贾氏被西门庆勾走:
JiaShi jiaShi = new JiaShi();
WangPo wangPo = new WangPo(jiaShi); //让王婆作为贾氏的代理人
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
说完这个故事,那额总结一下,代理模式主要使用了Java 的多态,干活的是被代理类,
代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代
理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚的很,同一个接口
呗。
您的设计模式
第 12 页
3. 单例模式【Singleton Pattern】
这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如
此的有人缘,单例就是单一、独苗的意思,那什么是独一份呢?你的思维是独一份,除此之
外还有什么不能山寨的呢?我们举个比较难复制的对象:皇帝
中国的历史上很少出现两个皇帝并存的时期,是有,但不多,那我们就认为皇帝是个单
例模式,在这个场景中,有皇帝,有大臣,大臣是天天要上朝参见皇帝的,今天参拜的皇帝
应该和昨天、前天的一样(过渡期的不考虑,别找茬哦),大臣磕完头,抬头一看,嗨,还
是昨天那个皇帝,单例模式,绝对的单例模式,先看类图:
然后我们看程序实现,先定一个皇帝:
package com.cbf4life.singleton1;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 中国的历史上一般都是一个朝代一个皇帝,有两个皇帝的话,必然要PK出一个皇帝出来
*/
public class Emperor {
private static Emperor emperor = null; //定义一个皇帝放在那里,然
后给这个皇帝名字
private Emperor(){
//世俗和道德约束你,目的就是不让你产生第二个皇帝
}
您的设计模式
第 13 页
public static Emperor getInstance(){
if(emperor == null){ //如果皇帝还没有定义,那就定一个
emperor = new Emperor();
}
return emperor;
}
//皇帝叫什么名字呀
public static void emperorInfo(){
System.out.println("我就是皇帝某某某....");
}
}
然后定义大臣:
package com.cbf4life.singleton1;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 大臣是天天要面见皇帝,今天见的皇帝和昨天的,前天不一样那就出问题了!
*/
@SuppressWarnings("all")
public class Minister {
/**
* @param args
*/
public static void main(String[] args) {
//第一天
Emperor emperor1=Emperor.getInstance();
emperor1.emperorInfo(); //第一天见的皇帝叫什么名字呢?
//第二天
Emperor emperor2=Emperor.getInstance();
Emperor.emperorInfo();
//第三天
Emperor emperor3=Emperor.getInstance();
emperor2.emperorInfo();
//三天见的皇帝都是同一个人,荣幸吧!
}
您的设计模式
第 14 页
}
看到没,大臣天天见到的都是同一个皇帝,不会产生错乱情况,反正都是一个皇帝,是
好是坏就这一个,只要提到皇帝,大家都知道指的是谁,清晰,而又明确。问题是这是通常
情况,还有个例的,如同一个时期同一个朝代有两个皇帝,怎么办?
单例模式很简单,就是在构造函数中多了加一个构造函数,访问权限是private 的就可
以了,这个模式是简单,但是简单中透着风险,风险?什么风险?在一个B/S 项目中,每个
HTTP Request 请求到J2EE 的容器上后都创建了一个线程,每个线程都要创建同一个单例对
象,怎么办?,好,我们写一个通用的单例程序,然后分析一下:
package com.cbf4life.singleton3;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 通用单例模式
*/
@SuppressWarnings("all")
public class SingletonPattern {
private static SingletonPattern singletonPattern= null;
//限制住不能直接产生一个实例
private SingletonPattern(){
}
public SingletonPattern getInstance(){
if(this.singletonPattern == null){ //如果还没有实例,则创建一个
this.singletonPattern = new SingletonPattern();
}
return this.singletonPattern;
}
}
我们来看黄色的那一部分,假如现在有两个线程A 和线程B,线程A 执行到
this.singletonPattern = new SingletonPattern(),正在申请内存分配,可能需要0.001
您的设计模式
第 15 页
微秒,就在这0.001 微秒之内,线程B 执行到if(this.singletonPattern == null),你说
这个时候这个判断条件是true 还是false?是true,那然后呢?线程B 也往下走,于是乎
就在内存中就有两个SingletonPattern 的实例了,看看是不是出问题了?如果你这个单例
是去拿一个序列号或者创建一个信号资源的时候,会怎么样?业务逻辑混乱!数据一致性校
验失败!最重要的是你从代码上还看不出什么问题,这才是最要命的!因为这种情况基本上
你是重现不了的,不寒而栗吧,那怎么修改?有很多种方案,我就说一种,能简单的、彻底
解决问题的方案:
package com.cbf4life.singleton3;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 通用单例模式
*/
@SuppressWarnings("all")
public class SingletonPattern {
private static final SingletonPattern singletonPattern= new
SingletonPattern();
//限制住不能直接产生一个实例
private SingletonPattern(){
}
public synchronized static SingletonPattern getInstance(){
return singletonPattern;
}
}
直接new 一个对象传递给类的成员变量singletonpattern,你要的时候getInstance
()直接返回给你,解决问题!
您的设计模式
第 16 页
您的设计模式
第 17 页
4. 多例模式【Multition Pattern】
这种情况有没有?有!大点声,有没有? 有!,是,确实有,就出现在明朝,那三国期
间的算不算,不算,各自称帝,各有各的地盘,国号不同。大家还记得那首诗《石灰吟》吗?
作者是谁?于谦,他是被谁杀死的?明英宗朱祁镇,对,就是那个在土木堡之变中被瓦刺俘
虏的皇帝,被俘虏后,他弟弟朱祁钰当上了皇帝,就是明景帝,估计当上皇帝后乐疯了,忘
记把老哥朱祁镇削为太上皇了,我Shit,在中国的历史上就这个时期是有2 个皇帝,你说
这期间的大臣多郁闷,两个皇帝耶,两个精神依附对象呀。
这个场景放到我们设计模式中就是叫有上限的多例模式(没上限的多例模式太容易了,
和你直接new 一个对象没啥差别,不讨论)怎么实现呢,看我出招,先看类图:
然后看程序,先把两个皇帝定义出来:
package com.cbf4life.singleton2;
import java.util.ArrayList;
import java.util.Random;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 中国的历史上一般都是一个朝代一个皇帝,有两个皇帝的话,必然要PK出一个皇帝出来。
* 问题出来了:如果真在一个时间,中国出现了两个皇帝怎么办?比如明朝土木堡之变后,
* 明英宗被俘虏,明景帝即位,但是明景帝当上皇帝后乐疯了,竟然忘记把他老哥明英宗
您的设计模式
第 18 页
削为太上皇,
* 也就是在这一个多月的时间内,中国竟然有两个皇帝!
*
*/
@SuppressWarnings("all")
public class Emperor {
private static int maxNumOfEmperor = 2; //最多只能有连个皇帝
private static ArrayList emperorInfoList=new
ArrayList(maxNumOfEmperor); //皇帝叫什么名字
private static ArrayList emperorList=new
ArrayList(maxNumOfEmperor); //装皇帝的列表;
private static int countNumOfEmperor =0; //正在被人尊称的是那个皇
帝
//先把2个皇帝产生出来
static{
//把所有的皇帝都产生出来
for(int i=0;i<maxNumOfEmperor;i++){
emperorList.add(new Emperor("皇"+(i+1)+"帝"));
}
}
//就这么多皇帝了,不允许再推举一个皇帝(new 一个皇帝)
private Emperor(){
//世俗和道德约束你,目的就是不让你产生第二个皇帝
}
private Emperor(String info){
emperorInfoList.add(info);
}
public static Emperor getInstance(){
Random random = new Random();
countNumOfEmperor = random.nextInt(maxNumOfEmperor); //随机
拉出一个皇帝,只要是个精神领袖就成
return (Emperor)emperorList.get(countNumOfEmperor);
}
//皇帝叫什么名字呀
public static void emperorInfo(){
System.out.println(emperorInfoList.get(countNumOfEmperor));
}
}
您的设计模式
第 19 页
那大臣是比较悲惨了,两个皇帝呀,两个老子呀,怎么拜呀,不管了,只要是个皇帝就
成:
package com.cbf4life.singleton2;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 大臣们悲惨了,一个皇帝都伺候不过来了,现在还来了两个个皇帝
* TND,不管了,找到个皇帝,磕头,请按就成了!
*/
@SuppressWarnings("all")
public class Minister {
/**
* @param args
*/
public static void main(String[] args) {
int ministerNum =10; //10个大臣
for(int i=0;i<ministerNum;i++){
Emperor emperor = Emperor.getInstance();
System.out.print("第"+(i+1)+"个大臣参拜的是:");
emperor.emperorInfo();
}
}
}
那各位看官就可能会不屑了:有的大臣可是有骨气,只拜一个真神,你怎么处理?这个
问题太简单,懒的详细回答你,getInstance(param)是不是就解决了这个问题?!自己思考,
太Easy 了。
您的设计模式
第 20 页
5. 工厂方法【Factory Method Pattern】
女娲补天的故事大家都听说过吧,今天不说这个,说女娲创造人的故事,可不是“造人”
的工作,这个词被现代人滥用了。这个故事是说,女娲在补了天后,下到凡间一看,哇塞,
风景太优美了,天空是湛蓝的,水是清澈的,空气是清新的,太美丽了,然后就待时间长了
就有点寂寞了,没有动物,这些看的到都是静态的东西呀,怎么办?
别忘了是神仙呀,没有办不到的事情,于是女娲就架起了八卦炉(技术术语:建立工厂)
开始创建人,具体过程是这样的:先是泥巴捏,然后放八卦炉里烤,再扔到地上成长,但是
意外总是会产生的:
第一次烤泥人,兹兹兹兹~~,感觉应该熟了,往地上一扔,biu~,一个白人诞生了,没
烤熟!
第二次烤泥人,兹兹兹兹兹兹兹兹~~,上次都没烤熟,这次多烤会儿,往地上一扔,嘿,
熟过头了,黑人哪!
第三次烤泥人,兹~兹~兹~,一边烤一边看着,嘿,正正好,Perfect,优品,黄色人种!
【备注:RB 人不再此列】
这个过程还是比较有意思的,先看看类图:(之前在论坛上有兄弟建议加类图和源文件,
以后的模式都会加上去,之前的会一个一个的补充,目的是让大家看着舒服,看着愉悦,看
着就想要,就像是看小说一样,目标,目标而已,能不能实现就看大家给我的信心了)
您的设计模式
第 21 页
那这个过程我们就用程序来表现,首先定义一个人类的总称:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个人类的统称
*/
public interface Human {
//首先定义什么是人类
//人是愉快的,会笑的,本来是想用smile表示,想了一下laugh更合适,好长时间
没有大笑了;
public void laugh();
//人类还会哭,代表痛苦
public void cry();
//人类会说话
public void talk();
}
然后定义具体的人种:
您的设计模式
第 22 页
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黄色人种,这个翻译的不准确,将就点吧
*/
public class YellowHuman implements Human {
public void cry() {
System.out.println("黄色人种会哭");
}
public void laugh() {
System.out.println("黄色人种会大笑,幸福呀!");
}
public void talk() {
System.out.println("黄色人种会说话,一般说的都是双字节");
}
}
白色人种:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 白色人种
*/
public class WhiteHuman implements Human {
public void cry() {
System.out.println("白色人种会哭");
}
public void laugh() {
System.out.println("白色人种会大笑,侵略的笑声");
您的设计模式
第 23 页
}
public void talk() {
System.out.println("白色人种会说话,一般都是但是单字节!");
}
}
黑色人种:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黑色人种,记得中学学英语,老师说black man是侮辱人的意思,不懂,没跟老外说
话
*/
public class BlackHuman implements Human {
public void cry() {
System.out.println("黑人会哭");
}
public void laugh() {
System.out.println("黑人会笑");
}
public void talk() {
System.out.println("黑人可以说话,一般人听不懂");
}
}
人种也定义完毕了,那我们把八卦炉定义出来:
package com.cbf4life;
import java.util.List;
import java.util.Random;
/**
* @author cbf4Life cbf4life@126.com
您的设计模式
第 24 页
* I'm glad to share my knowledge with you all.
* 今天讲女娲造人的故事,这个故事梗概是这样的:
* 很久很久以前,盘古开辟了天地,用身躯造出日月星辰、山川草木,天地一片繁华
* One day,女娲下界走了一遭,哎!太寂寞,太孤独了,没个会笑的、会哭的、会说话
的东东
* 那怎么办呢?不用愁,女娲,神仙呀,造出来呀,然后捏泥巴,放八卦炉(后来这个成
了太白金星的宝贝)中烤,于是就有了人:
* 我们把这个生产人的过程用Java程序表现出来:
*/
public class HumanFactory {
//定一个烤箱,泥巴塞进去,人就出来,这个太先进了
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
human = (Human)Class.forName(c.getName()).newInstance();
//产生一个人种
} catch (InstantiationException e) {//你要是不说个人种颜色的话,
没法烤,要白的黑,你说话了才好烤
System.out.println("必须指定人种的颜色");
} catch (IllegalAccessException e) { //定义的人种有问题,那就烤
不出来了,这是...
System.out.println("人种定义错误!");
} catch (ClassNotFoundException e) { //你随便说个人种,我到哪里
给你制造去?!
System.out.println("混蛋,你指定的人种找不到!");
}
return human;
}
}
然后我们再把女娲声明出来:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
您的设计模式
第 25 页
* 首先定义女娲,这真是额的神呀
*/
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
System.out.println("------------造出的第一批人是这样的:白人
-----------------");
Human whiteHuman =
HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
//女娲第二次造人,火候加足点,然后又出了个次品,黑人
System.out.println("\n\n------------造出的第二批人是这样的:黑人
-----------------");
Human blackHuman =
HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,这次火候掌握的正好,黄色人种(不写黄人,免得引起歧义),
备注:RB人不属于此列
System.out.println("\n\n------------造出的第三批人是这样的:黄色
人种-----------------");
Human yellowHuman =
HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk()
}
}
这样这个世界就热闹起来了,人也有了,但是这样创建太累了,神仙也会累的,那怎么
办?神仙就想了:我塞进去一团泥巴,随机出来一群人,管他是黑人、白人、黄人,只要是
人就成(你看看,神仙都偷懒,何况是我们人),先修改类图:
您的设计模式
第 26 页
然后看我们的程序修改,先修改HumanFactory.java,增加了createHuman()方法:
package com.cbf4life;
import java.util.List;
import java.util.Random;
public class HumanFactory {
//定一个烤箱,泥巴塞进去,人就出来,这个太先进了
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
human = (Human)Class.forName(c.getName()).newInstance();
//产生一个人种
} catch (InstantiationException e) {//你要是不说个人种颜色的话,
没法烤,要白的黑,你说话了才好烤
System.out.println("必须指定人种的颜色");
} catch (IllegalAccessException e) { //定义的人种有问题,那就烤
不出来了,这是...
System.out.println("人种定义错误!");
} catch (ClassNotFoundException e) { //你随便说个人种,我到哪里
给你制造去?!
您的设计模式
第 27 页
System.out.println("混蛋,你指定的人种找不到!");
}
return human;
}
//女娲生气了,把一团泥巴塞到八卦炉,哎产生啥人种就啥人种
public static Human createHuman(){
Human human=null; //定义一个类型的人类
//首先是获得有多少个实现类,多少个人种
List<Class> concreteHumanList =
ClassUtils.getAllClassByInterface(Human.class); //定义了多少人种
//八卦炉自己开始想烧出什么人就什么人
Random random = new Random();
int rand = random.nextInt(concreteHumanList.size());
human = createHuman(concreteHumanList.get(rand));
return human;
}
}
然后看女娲是如何做的:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 首先定义女娲,这真是额的神呀
*/
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
System.out.println("------------造出的第一批人是这样的:白人
-----------------");
Human whiteHuman =
HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
您的设计模式
第 28 页
//女娲第二次造人,火候加足点,然后又出了个次品,黑人
System.out.println("\n\n------------造出的第二批人是这样的:黑人
-----------------");
Human blackHuman =
HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,这次火候掌握的正好,黄色人种(不写黄人,免得引起歧义),
备注:RB人不属于此列
System.out.println("\n\n------------造出的第三批人是这样的:黄色
人种-----------------");
Human yellowHuman =
HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk();
//女娲烦躁了,爱是啥人种就是啥人种,烧吧
for(int i=0;i<10000000000;i++){
System.out.println("\n\n------------随机产生人种了
-----------------" + i);
Human human = HumanFactory.createHuman();
human.cry();
human.laugh();
human.talk();
}
}
}
哇,这个世界热闹了!,不过还没有完毕,这个程序你跑不起来,还要有这个类:
package com.cbf4life;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
您的设计模式
第 29 页
import java.util.List;
/**
* @author cbf4Life cbf4life@126.com I'm glad to share my knowledge
with you
* all.
*
*/
@SuppressWarnings("all")
public class ClassUtils {
//给一个接口,返回这个接口的所有实现类
public static List<Class> getAllClassByInterface(Class c){
List<Class> returnClassList = new ArrayList<Class>(); //返
回结果
//如果不是一个接口,则不做处理
if(c.isInterface()){
String packageName = c.getPackage().getName(); //获得当
前的包名
try {
List<Class> allClass = getClasses(packageName); //
获得当前包下以及子包下的所有类
//判断是否是同一个接口
for(int i=0;i<allClass.size();i++){
if(c.isAssignableFrom(allClass.get(i))){ //判断
是不是一个接口
if(!c.equals(allClass.get(i))){ //本身不加进
去
returnClassList.add(allClass.get(i));
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return returnClassList;
}
您的设计模式
第 30 页
//从一个包中查找出所有的类,在jar包中不能查找
private static List<Class> getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes;
}
private static List<Class> findClasses(File directory, String
packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." +
file.getName()));
} else if (file.getName().endsWith(".class")) {
classes.add(Class.forName(packageName + '.' +
file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
}
告诉你了,这个ClassUtils 可是个宝,用处可大了去了,可以由一个接口查找到所有
您的设计模式
第 31 页
的实现类,也可以由父类查找到所有的子类,这个要自己修改一下,动脑筋想下,简单的很!
完整的类图如下:
我们来总结一下,特别是增加了createHuman()后,是不是这个工厂的扩展性更好了?
你看你要再加一个人种,只要你继续集成Human 接口成了,然后啥都不用修改就可以生产了,
具体产多少,那要八卦炉说了算,简单工厂模式就是这么简单,那我们再引入一个问题:人
是有性别的呀,有男有女,你这怎么没区别,别急,这个且听下回分解!
您的设计模式
第 32 页
6. 抽象工厂模式【Abstract Factory Pattern】
好了,我们继续上一节课,上一节讲到女娲造人,人是造出来了,世界时热闹了,可是
低头一看,都是清一色的类型,缺少关爱、仇恨、喜怒哀乐等情绪,人类的生命太平淡了,
女娲一想,猛然一拍脑袋,Shit!忘记给人类定义性别了,那怎么办?抹掉重来,然后就把
人类重新洗牌,准备重新开始制造人类。
由于先前的工作已经花费了很大的精力做为铺垫,也不想从头开始了,那先说人类
(Product 产品类)怎么改吧,好,有了,给每个人类都加一个性别,然后再重新制造,这
个问题解决了,那八卦炉怎么办?只有一个呀,要么生产出全都是男性,要不都是女性,那
不行呀,有了,把已经有了一条生产线——八卦炉(工厂模式中的Concrete Factory)拆
开,于是女娲就使用了“八卦拷贝术”,把原先的八卦炉一个变两个,并且略加修改,就成
了女性八卦炉(只生产女性,一个具体工厂的实现类)和男性八卦炉(只生产男性,又一个
具体工厂的实现类),这个过程的类图如下:
先看人类(也就是产品)的类图:
这个类图也比较简单,Java 的典型类图,一个接口,几个抽象类,然后是几个实现类,
没啥多说的,其中三个抽象类在抽象工厂模式中是叫做产品等级,六个实现类是叫做产品族,
这个也比较好理解,实现类嘛是真实的产品,一个叫产品,多了就叫产品族,然后再看工厂
类:
您的设计模式
第 33 页
其中抽象工厂只实现了一个createHuman 的方法,目的是简化实现类的代码工作量,这
个在讲代码的时候会说。这里还使用了Jdk 1.5 的一个新特性Enum 类型,其实这个完全可
以类的静态变量来实现,但我想既然是学习就应该学有所获得,即使你对这个模式非常了解,
也可能没用过Enum 类型,也算是一个不同的知识点吧,我希望给大家讲解,每次都有新的
技术点提出来,每个人都会有一点的收获就足够了,然后在具体的项目中使用时,知道有这
个技术点,然后上baidu 狗狗一下就能解决问题。话题扯远了,我们继续类图,完整的类图
如下,这个看不大清楚,其实就是上面那两个类图加起来,大家可以看源码中的那个类图文
件:
然后类图讲解完毕,我们来看程序实现:
您的设计模式
第 34 页
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个人类的统称,问题出来了,刚刚定义的时候忘记定义性别了
* 这个重要的问题非修改不可,否则这个世界上太多太多的东西不存在了
*/
public interface Human {
//首先定义什么是人类
//人是愉快的,会笑的,本来是想用smile表示,想了一下laugh更合适,好长时间
没有大笑了;
public void laugh();
//人类还会哭,代表痛苦
public void cry();
//人类会说话
public void talk();
//定义性别
public void sex();
}
人类的接口定义好,然后根据接口创建三个抽象类,也就是三个产品等级,实现
laugh()、cry()、talk()三个方法,以AbstractYellowHuman 为例:
package com.cbf4life.yellowHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 为什么要修改成抽象类呢?要定义性别呀
*/
public abstract class AbstractYellowHuman implements Human {
public void cry() {
System.out.println("黄色人种会哭");
您的设计模式
第 35 页
}
public void laugh() {
System.out.println("黄色人种会大笑,幸福呀!");
}
public void talk() {
System.out.println("黄色人种会说话,一般说的都是双字节");
}
}
其他的两个抽象类AbstractWhiteHuman 和AbstractgBlackHuman 与此类似的事项方法,
不再通篇拷贝代码,大家可以看一下源代码。算了,还是拷贝,反正是电子档的,不想看就
往下翻页,也成就了部分“懒人”,不用启动Eclipse,还要把源码拷贝进来:
白种人的抽象类:
package com.cbf4life.whiteHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 白色人人种
* 为了代码整洁,新建一个包,这里是白种人的天下了
*/
public abstract class AbstractWhiteHuman implements Human {
public void cry() {
System.out.println("白色人种会哭");
}
public void laugh() {
System.out.println("白色人种会大笑,侵略的笑声");
您的设计模式
第 36 页
}
public void talk() {
System.out.println("白色人种会说话,一般都是但是单字节!");
}
}
黑种人的抽象类:
package com.cbf4life.blackHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黑色人种,记得中学学英语,老师说black man是侮辱人的意思,不懂,没跟老外说
话
*/
public abstract class AbstractBlackHuman implements Human {
public void cry() {
System.out.println("黑人会哭");
}
public void laugh() {
System.out.println("黑人会笑");
}
public void talk() {
System.out.println("黑人可以说话,一般人听不懂");
}
}
三个抽象类都实现完毕了,然后就是些实现类了。其实,你说抽象类放这里有什么意义
吗?就是不允许你new 出来一个抽象的对象呗,使用非抽象类完全就可以代替,呵呵,杀猪
杀尾巴,各有各的杀法,不过既然进了Java 这个门就要遵守Java 这个规矩,我们看实现类:
您的设计模式
第 37 页
女性黄种人的实现类:
package com.cbf4life.yellowHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女性黄种人
*/
public class YellowFemaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为女...");
}
}
男性黄种人的实现类:
package com.cbf4life.yellowHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性黄种人
*/
public class YellowMaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为男....");
}
}
女性白种人的实现类:
package com.cbf4life.whiteHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.\
* 女性白种人
*/
public class WhiteFemaleHuman extends AbstractWhiteHuman {
您的设计模式
第 38 页
public void sex() {
System.out.println("该白种人的性别为女....");
}
}
男性白种人的实现类:
package com.cbf4life.whiteHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性白种人
*/
public class WhiteMaleHuman extends AbstractWhiteHuman {
public void sex() {
System.out.println("该白种人的性别为男....");
}
}
女性黑种人的实现类:
package com.cbf4life.blackHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女性黑种人
*/
public class BlackFemaleHuman extends AbstractBlackHuman {
public void sex() {
System.out.println("该黑种人的性别为女...");
}
}
您的设计模式
第 39 页
男性黑种人的实现类:
package com.cbf4life.blackHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性黑种人
*/
public class BlackMaleHuman extends AbstractBlackHuman {
public void sex() {
System.out.println("该黑种人的性别为男...");
}
}
抽象工厂模式下的产品等级和产品族都已经完成,也就是人类以及产生出的人类是什么
样子的都已经定义好了,下一步就等着工厂开工创建了,那我们来看工厂类。
在看工厂类之前我们先看那个枚举类型,这个是很有意思的:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 世界上有哪些类型的人,列出来
* JDK 1.5开始引入enum类型也是目的的,吸引C程序员转过来
*/
public enum HumanEnum {
//把世界上所有人类型都定义出来
YelloMaleHuman("com.cbf4life.yellowHuman.YellowMaleHuman"),
YelloFemaleHuman("com.cbf4life.yellowHuman.YellowFemaleHuman")
,
WhiteFemaleHuman("com.cbf4life.whiteHuman.WhiteFemaleHuman"),
WhiteMaleHuman("com.cbf4life.whiteHuman.WhiteMaleHuman"),
BlackFemaleHuman("com.cbf4life.blackHuman.BlackFemaleHuman"),
您的设计模式
第 40 页
BlackMaleHuman("com.cbf4life.blackHuman.BlackMaleHuman");
private String value = "";
//定义构造函数,目的是Data(value)类型的相匹配
private HumanEnum(String value){
this.value = value;
}
public String getValue(){
return this.value;
}
/*
* java enum类型尽量简单使用,尽量不要使用多态、继承等方法
* 毕竟用Clas完全可以代替enum
*/
}
我之所以引入Enum 这个类型,是想让大家在看这本书的时候能够随时随地的学到点什
么,你如果看不懂设计模式,你可以从我的程序中学到一些新的技术点,不用像我以前报着
砖头似的书在那里啃,看一遍不懂,再看第二遍,然后翻了英文原本才知道,哦~,原来是
这样滴,只能说有些翻译家实在不懂技术。我在讲解技术的时候,尽量少用专业术语,尽量
使用大部分人类都能理解的语言。
Enum 以前我也很少用,最近在一个项目中偶然使用上了,然后才发觉它的好处,Enum
类型作为一个参数传递到一个方法中时,在Junit 进行单元测试的时候,不用判断输入参数
是否为空、长度为0 的边界异常条件,如果方法传入的参数不是Enum 类型的话,根本就传
递不进来,你说定义一个类,定义一堆的静态变量,这也可以呀,这个不和你抬杠,上面的
代码我解释一下,构造函数没啥好说的,然后是getValue()方法,就是获得枚举类型中一
个元素的值,枚举类型中的元素也是有名称和值的,这个和HashMap 有点类似。
然后,我们看我们的工厂类,先看接口:
package com.cbf4life;
您的设计模式
第 41 页
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 这次定一个接口,应该要造不同性别的人,需要不同的生产线
* 那这个八卦炉必须可以制造男人和女人
*/
public interface HumanFactory {
//制造黄色人种
public Human createYellowHuman();
//制造一个白色人种
public Human createWhiteHuman();
//制造一个黑色人种
public Human createBlackHuman();
}
然后看抽象类:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
import com.cbf4life.HumanFactory;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 编写一个抽象类,根据enum创建一个人类出来
*/
public abstract class AbstractHumanFactory implements HumanFactory
{
/*
* 给定一个性别人种,创建一个人类出来专业术语是产生产品等级
*/
protected Human createHuman(HumanEnum humanEnum) {
Human human = null;
//如果传递进来不是一个Enum中具体的一个Element的话,则不处理
if (!humanEnum.getValue().equals("")) {
try {
//直接产生一个实例
您的设计模式
第 42 页
human = (Human)
Class.forName(humanEnum.getValue()).newInstance();
} catch (Exception e) {
//因为使用了enum,这个种异常情况不会产生了,除非你的enum有问
题;
e.printStackTrace();
}
}
return human;
}
}
看到没,这就是引入enum 的好处,createHuman(HumanEnum humanEnum)这个方法定义
了输入参数必须是HumanEnum 类型,然后直接使用humanEnum.getValue()方法就能获得具
体传递进来的值,这个不多说了,大家自己看程序领会,没多大难度,这个抽象类的目的就
是减少下边实现类的代码量,我们看实现类:
男性工厂,只创建男性:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性创建工厂
*/
public class MaleHumanFactory extends AbstractHumanFactory {
//创建一个男性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackMaleHuman);
}
//创建一个男性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteMaleHuman);
}
//创建一个男性黄种人
您的设计模式
第 43 页
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloMaleHuman);
}
}
女性工厂,只创建女性:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.\
* 女性创建工厂
*/
public class FemaleHumanFactory extends AbstractHumanFactory {
//创建一个女性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackFemaleHuman);
}
//创建一个女性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteFemaleHuman);
}
//创建一个女性黄种人
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloFemaleHuman);
}
}
产品定义好了,工厂也定义好了,万事俱备只欠东风,那咱就开始造吧,哦,不对,女
娲开始造人了:
package com.cbf4life;
您的设计模式
第 44 页
import com.cbf4life.humanFactory.FemaleHumanFactory;
import com.cbf4life.humanFactory.MaleHumanFactory;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女娲建立起了两条生产线,分别是:
* 男性生产线
* 女性生产线
*/
public class NvWa {
public static void main(String[] args) {
//第一条生产线,男性生产线
HumanFactory maleHumanFactory = new MaleHumanFactory();
//第二条生产线,女性生产线
HumanFactory femaleHumanFactory = new FemaleHumanFactory();
//生产线建立完毕,开始生产人了:
Human maleYellowHuman =
maleHumanFactory.createYellowHuman();
Human femaleYellowHuman =
femaleHumanFactory.createYellowHuman();
maleYellowHuman.cry();
maleYellowHuman.laugh();
femaleYellowHuman.sex();
/*
* .....
* 后面你可以续了
*/
}
}
两个八卦炉,一个造女的,一个造男的,开足马力,一直造到这个世界到现在这个模式
为止。
抽象工厂模式讲完了,那我们再思考一些问题:工厂模式有哪些优缺点?先说优点,我
这人一般先看人优点,非常重要的有点就是,工厂模式符合OCP 原则,也就是开闭原则,怎
么说呢,比如就性别的问题,这个世界上还存在双性人,是男也是女的人,那这个就是要在
您的设计模式
第 45 页
我们的产品族中增加一类产品,同时再增加一个工厂就可以解决这个问题,不需要我再来实
现了吧,很简单的大家自己画下类图,然后实现下。
那还有没有其他好处呢?抽象工厂模式,还有一个非常大的有点,高内聚,低耦合,在
一个较大的项目组,产品是由一批人定义开发的,但是提供其他成员访问的时候,只有工厂
方法和产品的接口,也就是说只需要提供Product Interface 和Concrete Factory 就可以
产生自己需要的对象和方法,Java 的高内聚低耦合的特性表现的一览无遗,哈哈。
您的设计模式
第 46 页
7. 门面模式【Facade Pattern】
好,我们继续讲课。大家都是高智商的人,都写过纸质的信件吧,比如给女朋友写情书
什么的,写信的过程大家都还记得吧,先写信的内容,然后写信封,然后把信放到信封中,
封好,投递到信箱中进行邮递,这个过程还是比较简单的,虽然简单,这四个步骤都是要跑
的呀,信多了还是麻烦,比如到了情人节,为了大海捞针,给十个女孩子发情书,都要这样
跑一遍,你不要累死,更别说你要发个广告信啥的,一下子发1 千万封邮件,那不就完蛋了?
那怎么办呢?还好,现在邮局开发了一个新业务,你只要把信件的必要信息高速我,我给你
发,我来做这四个过程,你就不要管了,只要把信件交给我就成了。
我们的类图还是从最原始的状态开始:
在这中环境下,最累的是写信的人,为了发送一封信出去要有四个步骤,而且这四个步
骤还不能颠倒,你不可能没写信就把信放到信封吧,写信的人要知道这四个步骤,而且还要
知道这四个步骤的顺序,恐怖吧,我们先看看这个过程如何表现出来的:
先看写信的过程接口,定义了写信的四个步骤:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个写信的过程
您的设计模式
第 47 页
*/
public interface LetterProcess {
//首先要写信的内容
public void writeContext(String context);
//其次写信封
public void fillEnvelope(String address);
//把信放到信封里
public void letterInotoEnvelope();
//然后邮递
public void sendLetter();
}
写信过程的具体实现:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 写信的具体实现了
*/
public class LetterProcessImpl implements LetterProcess {
//写信
public void writeContext(String context) {
System.out.println("填写信的内容...." + context);
}
//在信封上填写必要的信息
public void fillEnvelope(String address) {
System.out.println("填写收件人地址及姓名...." + address);
}
//把信放到信封中,并封好
public void letterInotoEnvelope() {
System.out.println("把信放到信封中....");
}
//塞到邮箱中,邮递
您的设计模式
第 48 页
public void sendLetter() {
System.out.println("邮递信件...");
}
}
然后就有人开始用这个过程写信了:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 我开始给朋友写信了
*/
public class Client {
public static void main(String[] args) {
//创建一个处理信件的过程
LetterProcess letterProcess = new LetterProcessImpl();
//开始写信
letterProcess.writeContext("Hello,It's me,do you know who I
am? I'm your old lover. I'd like to....");
//开始写信封
letterProcess.fillEnvelope("Happy Road No. 666,God
Province,Heaven");
//把信放到信封里,并封装好
letterProcess.letterInotoEnvelope();
//跑到邮局把信塞到邮箱,投递
letterProcess.sendLetter();
}
}
那这个过程与高内聚的要求相差甚远,你想,你要知道这四个步骤,而且还要知道这四
个步骤的顺序,一旦出错,信就不可能邮寄出去,那我们如何来改进呢?先看类图:
您的设计模式
第 49 页
这就是门面模式,还是比较简单的,Sub System 比较复杂,为了让调用者更方便的调
用,就对Sub System 进行了封装,增加了一个门面,Client 调用时,直接调用门面的方法
就可以了,不用了解具体的实现方法以及相关的业务顺序,我们来看程序的改变,
LetterProcess 接口和实现类都没有改变,只是增加了一个ModenPostOffice 类,我们这个
java 程序清单如下:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ModenPostOffice {
private LetterProcess letterProcess = new LetterProcessImpl();
//写信,封装,投递,一体化了
public void sendLetter(String context,String address){
//帮你写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//把信放到信封中
letterProcess.letterInotoEnvelope();
您的设计模式
第 50 页
//邮递信件
letterProcess.sendLetter();
}
}
这个类是什么意思呢,就是说现在又一个叫Hell Road PostOffice(地狱路邮局)提
供了一种新型的服务,客户只要把信的内容以及收信地址给他们,他们就会把信写好,封好,
并发送出去,这种服务提出时大受欢迎呀,这简单呀,客户减少了很多工作,那我们看看客
户是怎么调用的,Client.java 的程序清单如下:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 我开始给朋友写信了
*/
public class Client {
public static void main(String[] args) {
//现代化的邮局,有这项服务,邮局名称叫Hell Road
ModenPostOffice hellRoadPostOffice = new ModenPostOffice();
//你只要把信的内容和收信人地址给他,他会帮你完成一系列的工作;
String address = "Happy Road No. 666,God Province,Heaven"; //
定义一个地址
String context = "Hello,It's me,do you know who I am? I'm your
old lover. I'd like to....";
hellRoadPostOffice.sendLetter(context, address);
}
}
看到没,客户简单了很多,提供这种模式后,系统的扩展性也有了很大的提高,突然一
个非常时期,寄往God Province(上帝省)的邮件都必须进行安全检查,那我们这个就很
好处理了,看类图:
您的设计模式
第 51 页
看这个红色的框,只增加了这一部分,其他部分在类图上都不需要改动,那我们来看源
码:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ModenPostOffice {
private LetterProcess letterProcess = new LetterProcessImpl();
private Police letterPolice = new Police();
//写信,封装,投递,一体化了
public void sendLetter(String context,String address){
//帮你写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//警察要检查信件了
letterPolice.checkLetter(letterProcess);
//把信放到信封中
letterProcess.letterInotoEnvelope();
您的设计模式
第 52 页
//邮递信件
letterProcess.sendLetter();
}
}
只是增加了一个letterPolice 变量的声明以及一个方法的调用,那这个写信的过程就
变成了这样:先写信,然后写信封,然后警察开始检查,然后才把信放到信封,然后发送出
去,那这个变更对客户来说,是透明的,他根本就看不到有人在检查他的邮件,他也不用了
解,反正现代化的邮件都帮他做了,这也是他乐意的地方。
门面模式讲解完毕,这是一个很好的封装方法,一个子系统比较复杂的实话,比如算法
或者业务比较复杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常
好。还有,在一个较大项目中的时候,为了避免人员带来的风险,也可以使用这个模式,技
术水平比较差的成员,尽量安排独立的模块(Sub System),然后把他写的程序封装到一个
门面里,尽量让其他项目成员不用看到这些烂人的代码,看也看不懂,我也遇到过一个“高
人”写的代码,private 方法、构造函数、常量基本都不用,你要一个public 方法,好,
一个类里就一个public 方法,所有代码都在里面,然后你就看吧,一大坨的程序,看着能
把人逼疯,使用门面模式后,对门面进行单元测试,约束项目成员的代码质量,对项目整体
质量的提升也是一个比较好的帮助。
您的设计模式
第 53 页
8. 适配器模式【Adapter Pattern】
好,请安静,后排聊天的同学别吵醒前排睡觉的同学了,大家要相互理解嘛。今天讲适
配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你
在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是220V,日本是110V,
但是这个适配器能够把这些不同的电压转换为你需要的36V 电压,保证你的笔记本能够正常
运行,那我们在设计模式中引入这个适配器模式是不是也是这个意思呢?是的,一样的作用,
两个不同接口,有不同的实现,但是某一天突然上帝命令你把B 接口转换为A 接口,怎么办?
继承,能解决,但是比较傻,而且还违背了OCP 原则,怎么办?好在我们还有适配器模式。
适配器的通用类图是这个样子滴:
首先声明,这个不是我画的,这是从Rational Rose 的帮助文件中截取的,这个类图也
很容易理解,Target 是一个类(接口),Adaptee 是一个接口,通过Adapter 把Adaptee 包
装成Target 的一个子类(实现类),注意了这里用了个名词包装(Wrapper),那其实这个模
式也叫做包装模式(Wrapper),但是包装模式可不知一个,还包括了以后要讲的装饰模式。
我来讲个自己的一个经验,让大家可以更容易了解这个适配器模式,否则纯讲技术太枯燥了,
技术也可以有娱乐的嘛。
我在2004 年的时候带了一个项目,做一个人力资源管理,该项目是我们总公司发起的
项目,公司一共有700 多号人,包括子公司,这个项目还是比较简单的,分为三大模块:人
员信息管理,薪酬管理,职位管理,其中人员管理这块就用到了适配器模式,是怎么回事呢?
当时开发时明确的指明:人员信息简管理的对象是所有员工的所有信息,然后我们就这样设
您的设计模式
第 54 页
计了一个类图:
还是比较简单的,有一个对象UserInfo 存储用户的所有信息(实际系统上还有很多子
类,不多说了),也就是BO(Business Object),这个对象设计为贫血对象(Thin Business
Object),不需要存储状态以及相关的关系,而且我也是反对使用充血对象(Rich Business
Object),这里说了两个名词贫血对象和充血对象,这两个名词很简单,在领域模型中分别
叫做贫血领域模型和充血领域模型,有什么区别呢?在一个对象中不存储实体状态以及对象
之间的关系的就叫做贫血对象,上升到领域模型中就是贫血领域模型,有实体状态和对象关
系的模型的就是充血领域模型,是不是太技术化了?这个看不懂没关系,都是糊弄人的东西,
属于专用名词,这本书写完了,我再折腾本领域模型的文章,揭露领域模型中糊弄人的专有
名词,这个绝对是专有名词的堆砌,呵呵。扯远了,我们继续说适配器模式,这个UserInfo
对象,在系统中很多地方使用,你可以查看自己的信息,也可以做修改,当然这个对象是有
setter 方法的,我们这里用不到就隐藏掉了。
这个项目是04 年年底投产的,运行到05 年年底还是比较平稳的,中间修修补补也很正
常,05 年年底不知道是那股风吹的,很多公司开始使用借聘人员的方式招聘人员,我们公
司也不例外,从一个人力资源公司借用了一大批的低技术、低工资的人员,分配到各个子公
司,总共有将近200 号人,然后就找我们部门老大谈判,说要增加一个功能借用人员管理,
老大一看有钱赚呀,一拍大腿,做!
我带人过去一调研,不是这么简单,人力资源公司有一套自己的人员管理系统,我们公
您的设计模式
第 55 页
司需要把我们使用到的人员信息传输到我们的系统中,系统之间的传输使用RMI(Remote
Method Invocation,远程对象调用)的方式,但是有一个问题人力资源公司的人员对象和
我们系统的对象不相同呀,他们的对象是这样的:
人员资源公司是把人的信息分为了三部分:基本信息,办公信息和个人家庭信息,并且
都放到了HashMap 中,比如人员的姓名放到BaseInfo 信息中,家庭地址放到HomeInfo 中,
这咱不好说他们系统设计的不好,那问题是咱的系统要和他们系统有交互,怎么办?使用适
配器模式,类图如下:
您的设计模式
第 56 页
大家可能会问,这两个对象都不在一个系统中,你如何使用呢?简单!RMI 已经帮我们
做了这件事情,只要有接口,就可以把远程的对象当成本地的对象使用,这个大家有时间可
以去看一下RMI 文档,不多说了。通过适配器,把OuterUser 伪装成我们系统中一个
IUserInfo 对象,这样,我们的系统基本不用修改什么程序员,所有的人员查询、调用跟本
地一样样的,说的口干舌燥,那下边我们来看具体的代码实现:
首先看IUserInfo.java 的代码:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 用户信息对象
*/
public interface IUserInfo {
//获得用户姓名
public String getUserName();
//获得家庭地址
public String getHomeAddress();
//手机号码,这个太重要,手机泛滥呀
public String getMobileNumber();
//办公电话,一般式座机
public String getOfficeTelNumber();
//这个人的职位是啥
public String getJobPosition();
//获得家庭电话,这个有点缺德,我是不喜欢打家庭电话讨论工作
public String getHomeTelNumber();
}
然后看这个接口的实现类:
package com.cbf4life;
您的设计模式
第 57 页
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class UserInfo implements IUserInfo {
/*
* 获得家庭地址,下属送礼也可以找到地方
*/
public String getHomeAddress() {
System.out.println("这里是员工的家庭地址....");
return null;
}
/*
* 获得家庭电话号码
*/
public String getHomeTelNumber() {
System.out.println("员工的家庭电话是....");
return null;
}
/*
* 员工的职位,是部门经理还是小兵
*/
public String getJobPosition() {
System.out.println("这个人的职位是BOSS....");
return null;
}
/*
* 手机号码
*/
public String getMobileNumber() {
System.out.println("这个人的手机号码是0000....");
return null;
}
/*
* 办公室电话,烦躁的时候最好“不小心”把电话线踢掉,我经常这么干,对己对人都
有好处
*/
public String getOfficeTelNumber() {
System.out.println("办公室电话是....");
您的设计模式
第 58 页
return null;
}
/*
* 姓名了,这个老重要了
*/
public String getUserName() {
System.out.println("姓名叫做...");
return null;
}
}
可能有人要问了,为什么要把电话号码、手机号码都设置成String 类型,而不是int
类型,大家觉的呢?题外话,这个绝对应该是String 类型,包括数据库也应该是varchar
类型的,手机号码有小灵通带区号的,比如02100001,这个你用数字怎么表示?有些人要
在手机号码前加上0086 后再保存,比如我们公司的印度阿三就是这样,喜欢在手机号码前
0086 保存下来,呵呵,我是想到啥就说啥,啰嗦了点。继续看我们的代码,下面看我们系
统的应用如何调用UserInfo 的信息:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 这就是我们具体的应用了,比如老板要查所有的20-30的女性信息
*/
public class App {
public static void main(String[] args) {
//没有与外系统连接的时候,是这样写的
IUserInfo youngGirl = new UserInfo();
//从数据库中查到101个
for(int i=0;i<101;i++){
youngGirl.getMobileNumber();
}
}
您的设计模式
第 59 页
}
这老板,比较那个,为什么是101,是男生都应该知道吧,111 代表男生,101 代表女
生,呵呵,是不是比较色呀。从数据库中生成了101 个UserInfo 对象,直接打印出来就成
了。那然后增加了外系统的人员信息,怎么处理呢?下面是IOuterUser.java 的源代码:
package com.cbf4life;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 外系统的人员信息
*/
@SuppressWarnings("all")
public interface IOuterUser {
//基本信息,比如名称,性别,手机号码了等
public Map getUserBaseInfo();
//工作区域信息
public Map getUserOfficeInfo();
//用户的家庭信息
public Map getUserHomeInfo();
}
我们再看看外系统的用户信息的具体实现类:
package com.cbf4life;
import java.util.HashMap;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 外系统的用户信息的实现类
*/
@SuppressWarnings("all")
您的设计模式
第 60 页
public class OuterUser implements IOuterUser {
/*
* 用户的基本信息
*/
public Map getUserBaseInfo() {
HashMap baseInfoMap = new HashMap();
baseInfoMap.put("userName", "这个员工叫混世魔王....");
baseInfoMap.put("mobileNumber", "这个员工电话是....");
return baseInfoMap;
}
/*
* 员工的家庭信息
*/
public Map getUserHomeInfo() {
HashMap homeInfo = new HashMap();
homeInfo.put("homeTelNumbner", "员工的家庭电话是....");
homeInfo.put("homeAddress", "员工的家庭地址是....");
return homeInfo;
}
/*
* 员工的工作信息,比如职位了等
*/
public Map getUserOfficeInfo() {
HashMap officeInfo = new HashMap();
officeInfo.put("jobPosition","这个人的职位是BOSS...");
officeInfo.put("officeTelNumber", "员工的办公电话是....");
return officeInfo;
}
}
那怎么把外系统的用户信息包装成我们公司的人员信息呢?看下面的OuterUserInfo
类源码,也就是我们的适配器:
您的设计模式
第 61 页
package com.cbf4life;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 把OuterUser包装成UserInfo
*/
@SuppressWarnings("all")
public class OuterUserInfo extends OuterUser implements IUserInfo {
private Map baseInfo = super.getUserBaseInfo(); //员工的基本信息
private Map homeInfo = super.getUserHomeInfo(); //员工的家庭信息
private Map officeInfo = super.getUserOfficeInfo(); //工作信息
/*
* 家庭地址
*/
public String getHomeAddress() {
String homeAddress =
(String)this.homeInfo.get("homeAddress");
System.out.println(homeAddress);
return homeAddress;
}
/*
* 家庭电话号码
*/
public String getHomeTelNumber() {
String homeTelNumber =
(String)this.homeInfo.get("homeTelNumber");
System.out.println(homeTelNumber);
return homeTelNumber;
}
/*
*职位信息
*/
public String getJobPosition() {
String jobPosition =
(String)this.officeInfo.get("jobPosition");
System.out.println(jobPosition);
return jobPosition;
您的设计模式
第 62 页
}
/*
* 手机号码
*/
public String getMobileNumber() {
String mobileNumber =
(String)this.baseInfo.get("mobileNumber");
System.out.println(mobileNumber);
return mobileNumber;
}
/*
* 办公电话
*/
public String getOfficeTelNumber() {
String officeTelNumber =
(String)this.officeInfo.get("officeTelNumber");
第 2 页
目 录
1. 策略模式【Strategy Pattern】 .......................................................... 4
2. 代理模式【Proxy Pattern】 .............................................................. 8
3. 单例模式【Singleton Pattern】 ...................................................... 12
4. 多例模式【Multition Pattern】 ...................................................... 17
5. 工厂方法【Factory Method Pattern】 ............................................ 20
6. 抽象工厂模式【Abstract Factory Pattern】 ................................... 32
7. 门面模式【Facade Pattern】 .......................................................... 46
8. 适配器模式【Adapter Pattern】 .................................................... 53
9. 模板方法模式【Template Method Pattern】 ................................. 65
10. 建造者模式【Builder Pattern】 ...................................................... 84
11. 桥梁模式【Bridge Pattern】 ........................................................... 99
12. 命令模式【Command Pattern】 ................................................... 115
13. 装饰模式【Decorator Pattern】 ................................................... 129
14. 迭代器模式【Iterator Pattern】 ................................................... 141
15. 组合模式【Composite Pattern】 .................................................. 151
16. 观察者模式【Observer Pattern】 ................................................. 180
17. 访问者模式【Visitor Pattern】 ..................................................... 181
18. 状态模式【State Pattern】 ........................................................... 182
19. 责任链模式【Chain of Responsibility Pattern】 ........................... 183
您的设计模式
第 3 页
20. 原型模式【Prototype Pattern】 ................................................... 184
21. 中介者模式【Mediator Pattern】 ................................................ 185
22. 解释器模式【Interpreter Pattern】 .............................................. 186
23. 亨元模式【Flyweight Pattern】 .................................................... 187
24. 备忘录模式【Memento Pattern】 ............................................... 188
25. 模式大PK ...................................................................................... 189
26. 混编模式讲解 ............................................................................... 190
27. 更新记录: ................................................................................... 191
28. 相关说明 ....................................................................................... 192
29. 后序 ............................................................................................... 193
您的设计模式
第 4 页
1. 策略模式【Strategy Pattern】
刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开
解决棘手问题,嘿,还别说,真是解决了大问题,搞到最后是周瑜陪了夫人又折兵呀,那咱
们先看看这个场景是什么样子的。
先说这个场景中的要素:三个妙计,一个锦囊,一个赵云,妙计是小亮同志给的,妙计
是放置在锦囊里,俗称就是锦囊妙计嘛,那赵云就是一个干活的人,从锦囊中取出妙计,执
行,然后获胜,用JAVA 程序怎么表现这个呢?我们先看类图:
三个妙计是同一类型的东东,那咱就写个接口:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 首先定一个策略接口,这是诸葛亮老人家给赵云的三个锦囊妙计的接口
*
*/
public interface IStrategy {
//每个锦囊妙计都是一个可执行的算法
您的设计模式
第 5 页
public void operate();
}
然后再写三个实现类,有三个妙计嘛:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 找乔国老帮忙,使孙权不能杀刘备
*/
public class BackDoor implements IStrategy {
public void operate() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
}
}
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 求吴国太开个绿灯
*/
public class GivenGreenLight implements IStrategy {
public void operate() {
System.out.println("求吴国太开个绿灯,放行!");
}
}
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 孙夫人断后,挡住追兵
*/
您的设计模式
第 6 页
public class BlockEnemy implements IStrategy {
public void operate() {
System.out.println("孙夫人断后,挡住追兵");
}
}
好了,大家看看,三个妙计是有了,那需要有个地方放这些妙计呀,放锦囊呀:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 计谋有了,那还要有锦囊
*/
public class Context {
//构造函数,你要使用那个妙计
private IStrategy straegy;
public Context(IStrategy strategy){
this.straegy = strategy;
}
//使用计谋了,看我出招了
public void operate(){
this.straegy.operate();
}
}
然后就是赵云雄赳赳的揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的、
色迷迷的刘老爷子去入赘了,嗨,还别说,小亮的三个妙计还真是不错,瞅瞅:
package com.cbf4life.strategy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ZhaoYun {
/**
* 赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
您的设计模式
第 7 页
*/
public static void main(String[] args) {
Context context;
//刚刚到吴国的时候拆第一个
System.out.println("-----------刚刚到吴国的时候拆第一个
-------------");
context = new Context(new BackDoor()); //拿到妙计
context.operate(); //拆开执行
System.out.println("\n\n\n\n\n\n\n\n");
//刘备乐不思蜀了,拆第二个了
System.out.println("-----------刘备乐不思蜀了,拆第二个了
-------------");
context = new Context(new GivenGreenLight());
context.operate(); //执行了第二个锦囊了
System.out.println("\n\n\n\n\n\n\n\n");
//孙权的小兵追了,咋办?拆第三个
System.out.println("-----------孙权的小兵追了,咋办?拆第三个
-------------");
context = new Context(new BlockEnemy());
context.operate(); //孙夫人退兵
System.out.println("\n\n\n\n\n\n\n\n");
/*
*问题来了:赵云实际不知道是那个策略呀,他只知道拆第一个锦囊,
*而不知道是BackDoor这个妙计,咋办? 似乎这个策略模式已经把计谋名称
写出来了
*
* 错!BackDoor、GivenGreenLight、BlockEnemy只是一个代码,你写成
first、second、third,没人会说你错!
*
* 策略模式的好处就是:体现了高内聚低耦合的特性呀,缺点嘛,这个那个,我
回去再查查
*/
}
}
就这三招,搞的周郎是“陪了夫人又折兵”呀!这就是策略模式,高内聚低耦合的特点
也表现出来了,还有一个就是扩展性,也就是OCP 原则,策略类可以继续增加下去,只要修
改Context.java 就可以了,这个不多说了,自己领会吧。
您的设计模式
第 8 页
2. 代理模式【Proxy Pattern】
什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代
理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代
理人虽然不能干活,但是被代理的人能干活呀。
比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程
序上时这样的:
先定义一种类型的女人:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一种类型的女人,王婆和潘金莲都属于这个类型的女人
*/
public interface KindWomen {
//这种类型的女人能做什么事情呢?
public void makeEyesWithMan(); //抛媚眼
public void happyWithMan(); //happy what? You know that!
}
一种类型嘛,那肯定是接口,然后定义潘金莲:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定一个潘金莲是什么样的人
*/
public class PanJinLian implements KindWomen {
您的设计模式
第 9 页
public void happyWithMan() {
System.out.println("潘金莲在和男人做那个.....");
}
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼");
}
}
再定一个丑陋的王婆:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 王婆这个人老聪明了,她太老了,是个男人都看不上,
* 但是她有智慧有经验呀,她作为一类女人的代理!
*/
public class WangPo implements KindWomen {
private KindWomen kindWomen;
public WangPo(){ //默认的话,是潘金莲的代理
this.kindWomen = new PanJinLian();
}
//她可以是KindWomen的任何一个女人的代理,只要你是这一类型
public WangPo(KindWomen kindWomen){
this.kindWomen = kindWomen;
}
public void happyWithMan() {
this.kindWomen.happyWithMan(); //自己老了,干不了,可以让年轻的
代替
}
public void makeEyesWithMan() {
this.kindWomen.makeEyesWithMan(); //王婆这么大年龄了,谁看她抛
媚眼?!
}
您的设计模式
第 10 页
}
两个女主角都上场了,男主角也该出现了:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个西门庆,这人色中饿鬼
*/
public class XiMenQing {
/*
* 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,
* 被王婆看到了, 就开始撮合两人好事,王婆作为潘金莲的代理人
* 收了不少好处费,那我们假设一下:
* 如果没有王婆在中间牵线,这两个不要脸的能成吗?难说的很!
*/
public static void main(String[] args) {
//把王婆叫出来
WangPo wangPo = new WangPo();
//然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那
出戏:
wangPo.makeEyesWithMan(); //看到没,虽然表面上时王婆在做,实际上
爽的是潘金莲
wangPo.happyWithMan(); }
}
那这就是活生生的一个例子,通过代理人实现了某种目的,如果真去掉王婆这个中间环
节,直接是西门庆和潘金莲勾搭,估计很难成就武松杀嫂事件。
那我们再考虑一下,水浒里还有没有这类型的女人?有,卢俊义的老婆贾氏(就是和那
个固管家苟合的那个),这名字起的:“假使”,那我们也让王婆做她的代理:
把贾氏素描出来:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
您的设计模式
第 11 页
*/
public class JiaShi implements KindWomen {
public void happyWithMan() {
System.out.println("贾氏正在Happy中......");
}
public void makeEyesWithMan() {
System.out.println("贾氏抛媚眼");
}
}
西门庆勾贾氏:
package com.cbf4life.proxy;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个西门庆,这人色中饿鬼
*/
public class XiMenQing {
public static void main(String[] args) {
//改编一下历史,贾氏被西门庆勾走:
JiaShi jiaShi = new JiaShi();
WangPo wangPo = new WangPo(jiaShi); //让王婆作为贾氏的代理人
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
说完这个故事,那额总结一下,代理模式主要使用了Java 的多态,干活的是被代理类,
代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代
理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚的很,同一个接口
呗。
您的设计模式
第 12 页
3. 单例模式【Singleton Pattern】
这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如
此的有人缘,单例就是单一、独苗的意思,那什么是独一份呢?你的思维是独一份,除此之
外还有什么不能山寨的呢?我们举个比较难复制的对象:皇帝
中国的历史上很少出现两个皇帝并存的时期,是有,但不多,那我们就认为皇帝是个单
例模式,在这个场景中,有皇帝,有大臣,大臣是天天要上朝参见皇帝的,今天参拜的皇帝
应该和昨天、前天的一样(过渡期的不考虑,别找茬哦),大臣磕完头,抬头一看,嗨,还
是昨天那个皇帝,单例模式,绝对的单例模式,先看类图:
然后我们看程序实现,先定一个皇帝:
package com.cbf4life.singleton1;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 中国的历史上一般都是一个朝代一个皇帝,有两个皇帝的话,必然要PK出一个皇帝出来
*/
public class Emperor {
private static Emperor emperor = null; //定义一个皇帝放在那里,然
后给这个皇帝名字
private Emperor(){
//世俗和道德约束你,目的就是不让你产生第二个皇帝
}
您的设计模式
第 13 页
public static Emperor getInstance(){
if(emperor == null){ //如果皇帝还没有定义,那就定一个
emperor = new Emperor();
}
return emperor;
}
//皇帝叫什么名字呀
public static void emperorInfo(){
System.out.println("我就是皇帝某某某....");
}
}
然后定义大臣:
package com.cbf4life.singleton1;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 大臣是天天要面见皇帝,今天见的皇帝和昨天的,前天不一样那就出问题了!
*/
@SuppressWarnings("all")
public class Minister {
/**
* @param args
*/
public static void main(String[] args) {
//第一天
Emperor emperor1=Emperor.getInstance();
emperor1.emperorInfo(); //第一天见的皇帝叫什么名字呢?
//第二天
Emperor emperor2=Emperor.getInstance();
Emperor.emperorInfo();
//第三天
Emperor emperor3=Emperor.getInstance();
emperor2.emperorInfo();
//三天见的皇帝都是同一个人,荣幸吧!
}
您的设计模式
第 14 页
}
看到没,大臣天天见到的都是同一个皇帝,不会产生错乱情况,反正都是一个皇帝,是
好是坏就这一个,只要提到皇帝,大家都知道指的是谁,清晰,而又明确。问题是这是通常
情况,还有个例的,如同一个时期同一个朝代有两个皇帝,怎么办?
单例模式很简单,就是在构造函数中多了加一个构造函数,访问权限是private 的就可
以了,这个模式是简单,但是简单中透着风险,风险?什么风险?在一个B/S 项目中,每个
HTTP Request 请求到J2EE 的容器上后都创建了一个线程,每个线程都要创建同一个单例对
象,怎么办?,好,我们写一个通用的单例程序,然后分析一下:
package com.cbf4life.singleton3;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 通用单例模式
*/
@SuppressWarnings("all")
public class SingletonPattern {
private static SingletonPattern singletonPattern= null;
//限制住不能直接产生一个实例
private SingletonPattern(){
}
public SingletonPattern getInstance(){
if(this.singletonPattern == null){ //如果还没有实例,则创建一个
this.singletonPattern = new SingletonPattern();
}
return this.singletonPattern;
}
}
我们来看黄色的那一部分,假如现在有两个线程A 和线程B,线程A 执行到
this.singletonPattern = new SingletonPattern(),正在申请内存分配,可能需要0.001
您的设计模式
第 15 页
微秒,就在这0.001 微秒之内,线程B 执行到if(this.singletonPattern == null),你说
这个时候这个判断条件是true 还是false?是true,那然后呢?线程B 也往下走,于是乎
就在内存中就有两个SingletonPattern 的实例了,看看是不是出问题了?如果你这个单例
是去拿一个序列号或者创建一个信号资源的时候,会怎么样?业务逻辑混乱!数据一致性校
验失败!最重要的是你从代码上还看不出什么问题,这才是最要命的!因为这种情况基本上
你是重现不了的,不寒而栗吧,那怎么修改?有很多种方案,我就说一种,能简单的、彻底
解决问题的方案:
package com.cbf4life.singleton3;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 通用单例模式
*/
@SuppressWarnings("all")
public class SingletonPattern {
private static final SingletonPattern singletonPattern= new
SingletonPattern();
//限制住不能直接产生一个实例
private SingletonPattern(){
}
public synchronized static SingletonPattern getInstance(){
return singletonPattern;
}
}
直接new 一个对象传递给类的成员变量singletonpattern,你要的时候getInstance
()直接返回给你,解决问题!
您的设计模式
第 16 页
您的设计模式
第 17 页
4. 多例模式【Multition Pattern】
这种情况有没有?有!大点声,有没有? 有!,是,确实有,就出现在明朝,那三国期
间的算不算,不算,各自称帝,各有各的地盘,国号不同。大家还记得那首诗《石灰吟》吗?
作者是谁?于谦,他是被谁杀死的?明英宗朱祁镇,对,就是那个在土木堡之变中被瓦刺俘
虏的皇帝,被俘虏后,他弟弟朱祁钰当上了皇帝,就是明景帝,估计当上皇帝后乐疯了,忘
记把老哥朱祁镇削为太上皇了,我Shit,在中国的历史上就这个时期是有2 个皇帝,你说
这期间的大臣多郁闷,两个皇帝耶,两个精神依附对象呀。
这个场景放到我们设计模式中就是叫有上限的多例模式(没上限的多例模式太容易了,
和你直接new 一个对象没啥差别,不讨论)怎么实现呢,看我出招,先看类图:
然后看程序,先把两个皇帝定义出来:
package com.cbf4life.singleton2;
import java.util.ArrayList;
import java.util.Random;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 中国的历史上一般都是一个朝代一个皇帝,有两个皇帝的话,必然要PK出一个皇帝出来。
* 问题出来了:如果真在一个时间,中国出现了两个皇帝怎么办?比如明朝土木堡之变后,
* 明英宗被俘虏,明景帝即位,但是明景帝当上皇帝后乐疯了,竟然忘记把他老哥明英宗
您的设计模式
第 18 页
削为太上皇,
* 也就是在这一个多月的时间内,中国竟然有两个皇帝!
*
*/
@SuppressWarnings("all")
public class Emperor {
private static int maxNumOfEmperor = 2; //最多只能有连个皇帝
private static ArrayList emperorInfoList=new
ArrayList(maxNumOfEmperor); //皇帝叫什么名字
private static ArrayList emperorList=new
ArrayList(maxNumOfEmperor); //装皇帝的列表;
private static int countNumOfEmperor =0; //正在被人尊称的是那个皇
帝
//先把2个皇帝产生出来
static{
//把所有的皇帝都产生出来
for(int i=0;i<maxNumOfEmperor;i++){
emperorList.add(new Emperor("皇"+(i+1)+"帝"));
}
}
//就这么多皇帝了,不允许再推举一个皇帝(new 一个皇帝)
private Emperor(){
//世俗和道德约束你,目的就是不让你产生第二个皇帝
}
private Emperor(String info){
emperorInfoList.add(info);
}
public static Emperor getInstance(){
Random random = new Random();
countNumOfEmperor = random.nextInt(maxNumOfEmperor); //随机
拉出一个皇帝,只要是个精神领袖就成
return (Emperor)emperorList.get(countNumOfEmperor);
}
//皇帝叫什么名字呀
public static void emperorInfo(){
System.out.println(emperorInfoList.get(countNumOfEmperor));
}
}
您的设计模式
第 19 页
那大臣是比较悲惨了,两个皇帝呀,两个老子呀,怎么拜呀,不管了,只要是个皇帝就
成:
package com.cbf4life.singleton2;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 大臣们悲惨了,一个皇帝都伺候不过来了,现在还来了两个个皇帝
* TND,不管了,找到个皇帝,磕头,请按就成了!
*/
@SuppressWarnings("all")
public class Minister {
/**
* @param args
*/
public static void main(String[] args) {
int ministerNum =10; //10个大臣
for(int i=0;i<ministerNum;i++){
Emperor emperor = Emperor.getInstance();
System.out.print("第"+(i+1)+"个大臣参拜的是:");
emperor.emperorInfo();
}
}
}
那各位看官就可能会不屑了:有的大臣可是有骨气,只拜一个真神,你怎么处理?这个
问题太简单,懒的详细回答你,getInstance(param)是不是就解决了这个问题?!自己思考,
太Easy 了。
您的设计模式
第 20 页
5. 工厂方法【Factory Method Pattern】
女娲补天的故事大家都听说过吧,今天不说这个,说女娲创造人的故事,可不是“造人”
的工作,这个词被现代人滥用了。这个故事是说,女娲在补了天后,下到凡间一看,哇塞,
风景太优美了,天空是湛蓝的,水是清澈的,空气是清新的,太美丽了,然后就待时间长了
就有点寂寞了,没有动物,这些看的到都是静态的东西呀,怎么办?
别忘了是神仙呀,没有办不到的事情,于是女娲就架起了八卦炉(技术术语:建立工厂)
开始创建人,具体过程是这样的:先是泥巴捏,然后放八卦炉里烤,再扔到地上成长,但是
意外总是会产生的:
第一次烤泥人,兹兹兹兹~~,感觉应该熟了,往地上一扔,biu~,一个白人诞生了,没
烤熟!
第二次烤泥人,兹兹兹兹兹兹兹兹~~,上次都没烤熟,这次多烤会儿,往地上一扔,嘿,
熟过头了,黑人哪!
第三次烤泥人,兹~兹~兹~,一边烤一边看着,嘿,正正好,Perfect,优品,黄色人种!
【备注:RB 人不再此列】
这个过程还是比较有意思的,先看看类图:(之前在论坛上有兄弟建议加类图和源文件,
以后的模式都会加上去,之前的会一个一个的补充,目的是让大家看着舒服,看着愉悦,看
着就想要,就像是看小说一样,目标,目标而已,能不能实现就看大家给我的信心了)
您的设计模式
第 21 页
那这个过程我们就用程序来表现,首先定义一个人类的总称:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个人类的统称
*/
public interface Human {
//首先定义什么是人类
//人是愉快的,会笑的,本来是想用smile表示,想了一下laugh更合适,好长时间
没有大笑了;
public void laugh();
//人类还会哭,代表痛苦
public void cry();
//人类会说话
public void talk();
}
然后定义具体的人种:
您的设计模式
第 22 页
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黄色人种,这个翻译的不准确,将就点吧
*/
public class YellowHuman implements Human {
public void cry() {
System.out.println("黄色人种会哭");
}
public void laugh() {
System.out.println("黄色人种会大笑,幸福呀!");
}
public void talk() {
System.out.println("黄色人种会说话,一般说的都是双字节");
}
}
白色人种:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 白色人种
*/
public class WhiteHuman implements Human {
public void cry() {
System.out.println("白色人种会哭");
}
public void laugh() {
System.out.println("白色人种会大笑,侵略的笑声");
您的设计模式
第 23 页
}
public void talk() {
System.out.println("白色人种会说话,一般都是但是单字节!");
}
}
黑色人种:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黑色人种,记得中学学英语,老师说black man是侮辱人的意思,不懂,没跟老外说
话
*/
public class BlackHuman implements Human {
public void cry() {
System.out.println("黑人会哭");
}
public void laugh() {
System.out.println("黑人会笑");
}
public void talk() {
System.out.println("黑人可以说话,一般人听不懂");
}
}
人种也定义完毕了,那我们把八卦炉定义出来:
package com.cbf4life;
import java.util.List;
import java.util.Random;
/**
* @author cbf4Life cbf4life@126.com
您的设计模式
第 24 页
* I'm glad to share my knowledge with you all.
* 今天讲女娲造人的故事,这个故事梗概是这样的:
* 很久很久以前,盘古开辟了天地,用身躯造出日月星辰、山川草木,天地一片繁华
* One day,女娲下界走了一遭,哎!太寂寞,太孤独了,没个会笑的、会哭的、会说话
的东东
* 那怎么办呢?不用愁,女娲,神仙呀,造出来呀,然后捏泥巴,放八卦炉(后来这个成
了太白金星的宝贝)中烤,于是就有了人:
* 我们把这个生产人的过程用Java程序表现出来:
*/
public class HumanFactory {
//定一个烤箱,泥巴塞进去,人就出来,这个太先进了
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
human = (Human)Class.forName(c.getName()).newInstance();
//产生一个人种
} catch (InstantiationException e) {//你要是不说个人种颜色的话,
没法烤,要白的黑,你说话了才好烤
System.out.println("必须指定人种的颜色");
} catch (IllegalAccessException e) { //定义的人种有问题,那就烤
不出来了,这是...
System.out.println("人种定义错误!");
} catch (ClassNotFoundException e) { //你随便说个人种,我到哪里
给你制造去?!
System.out.println("混蛋,你指定的人种找不到!");
}
return human;
}
}
然后我们再把女娲声明出来:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
您的设计模式
第 25 页
* 首先定义女娲,这真是额的神呀
*/
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
System.out.println("------------造出的第一批人是这样的:白人
-----------------");
Human whiteHuman =
HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
//女娲第二次造人,火候加足点,然后又出了个次品,黑人
System.out.println("\n\n------------造出的第二批人是这样的:黑人
-----------------");
Human blackHuman =
HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,这次火候掌握的正好,黄色人种(不写黄人,免得引起歧义),
备注:RB人不属于此列
System.out.println("\n\n------------造出的第三批人是这样的:黄色
人种-----------------");
Human yellowHuman =
HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk()
}
}
这样这个世界就热闹起来了,人也有了,但是这样创建太累了,神仙也会累的,那怎么
办?神仙就想了:我塞进去一团泥巴,随机出来一群人,管他是黑人、白人、黄人,只要是
人就成(你看看,神仙都偷懒,何况是我们人),先修改类图:
您的设计模式
第 26 页
然后看我们的程序修改,先修改HumanFactory.java,增加了createHuman()方法:
package com.cbf4life;
import java.util.List;
import java.util.Random;
public class HumanFactory {
//定一个烤箱,泥巴塞进去,人就出来,这个太先进了
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
human = (Human)Class.forName(c.getName()).newInstance();
//产生一个人种
} catch (InstantiationException e) {//你要是不说个人种颜色的话,
没法烤,要白的黑,你说话了才好烤
System.out.println("必须指定人种的颜色");
} catch (IllegalAccessException e) { //定义的人种有问题,那就烤
不出来了,这是...
System.out.println("人种定义错误!");
} catch (ClassNotFoundException e) { //你随便说个人种,我到哪里
给你制造去?!
您的设计模式
第 27 页
System.out.println("混蛋,你指定的人种找不到!");
}
return human;
}
//女娲生气了,把一团泥巴塞到八卦炉,哎产生啥人种就啥人种
public static Human createHuman(){
Human human=null; //定义一个类型的人类
//首先是获得有多少个实现类,多少个人种
List<Class> concreteHumanList =
ClassUtils.getAllClassByInterface(Human.class); //定义了多少人种
//八卦炉自己开始想烧出什么人就什么人
Random random = new Random();
int rand = random.nextInt(concreteHumanList.size());
human = createHuman(concreteHumanList.get(rand));
return human;
}
}
然后看女娲是如何做的:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 首先定义女娲,这真是额的神呀
*/
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
System.out.println("------------造出的第一批人是这样的:白人
-----------------");
Human whiteHuman =
HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
您的设计模式
第 28 页
//女娲第二次造人,火候加足点,然后又出了个次品,黑人
System.out.println("\n\n------------造出的第二批人是这样的:黑人
-----------------");
Human blackHuman =
HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,这次火候掌握的正好,黄色人种(不写黄人,免得引起歧义),
备注:RB人不属于此列
System.out.println("\n\n------------造出的第三批人是这样的:黄色
人种-----------------");
Human yellowHuman =
HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk();
//女娲烦躁了,爱是啥人种就是啥人种,烧吧
for(int i=0;i<10000000000;i++){
System.out.println("\n\n------------随机产生人种了
-----------------" + i);
Human human = HumanFactory.createHuman();
human.cry();
human.laugh();
human.talk();
}
}
}
哇,这个世界热闹了!,不过还没有完毕,这个程序你跑不起来,还要有这个类:
package com.cbf4life;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
您的设计模式
第 29 页
import java.util.List;
/**
* @author cbf4Life cbf4life@126.com I'm glad to share my knowledge
with you
* all.
*
*/
@SuppressWarnings("all")
public class ClassUtils {
//给一个接口,返回这个接口的所有实现类
public static List<Class> getAllClassByInterface(Class c){
List<Class> returnClassList = new ArrayList<Class>(); //返
回结果
//如果不是一个接口,则不做处理
if(c.isInterface()){
String packageName = c.getPackage().getName(); //获得当
前的包名
try {
List<Class> allClass = getClasses(packageName); //
获得当前包下以及子包下的所有类
//判断是否是同一个接口
for(int i=0;i<allClass.size();i++){
if(c.isAssignableFrom(allClass.get(i))){ //判断
是不是一个接口
if(!c.equals(allClass.get(i))){ //本身不加进
去
returnClassList.add(allClass.get(i));
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return returnClassList;
}
您的设计模式
第 30 页
//从一个包中查找出所有的类,在jar包中不能查找
private static List<Class> getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes;
}
private static List<Class> findClasses(File directory, String
packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." +
file.getName()));
} else if (file.getName().endsWith(".class")) {
classes.add(Class.forName(packageName + '.' +
file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
}
告诉你了,这个ClassUtils 可是个宝,用处可大了去了,可以由一个接口查找到所有
您的设计模式
第 31 页
的实现类,也可以由父类查找到所有的子类,这个要自己修改一下,动脑筋想下,简单的很!
完整的类图如下:
我们来总结一下,特别是增加了createHuman()后,是不是这个工厂的扩展性更好了?
你看你要再加一个人种,只要你继续集成Human 接口成了,然后啥都不用修改就可以生产了,
具体产多少,那要八卦炉说了算,简单工厂模式就是这么简单,那我们再引入一个问题:人
是有性别的呀,有男有女,你这怎么没区别,别急,这个且听下回分解!
您的设计模式
第 32 页
6. 抽象工厂模式【Abstract Factory Pattern】
好了,我们继续上一节课,上一节讲到女娲造人,人是造出来了,世界时热闹了,可是
低头一看,都是清一色的类型,缺少关爱、仇恨、喜怒哀乐等情绪,人类的生命太平淡了,
女娲一想,猛然一拍脑袋,Shit!忘记给人类定义性别了,那怎么办?抹掉重来,然后就把
人类重新洗牌,准备重新开始制造人类。
由于先前的工作已经花费了很大的精力做为铺垫,也不想从头开始了,那先说人类
(Product 产品类)怎么改吧,好,有了,给每个人类都加一个性别,然后再重新制造,这
个问题解决了,那八卦炉怎么办?只有一个呀,要么生产出全都是男性,要不都是女性,那
不行呀,有了,把已经有了一条生产线——八卦炉(工厂模式中的Concrete Factory)拆
开,于是女娲就使用了“八卦拷贝术”,把原先的八卦炉一个变两个,并且略加修改,就成
了女性八卦炉(只生产女性,一个具体工厂的实现类)和男性八卦炉(只生产男性,又一个
具体工厂的实现类),这个过程的类图如下:
先看人类(也就是产品)的类图:
这个类图也比较简单,Java 的典型类图,一个接口,几个抽象类,然后是几个实现类,
没啥多说的,其中三个抽象类在抽象工厂模式中是叫做产品等级,六个实现类是叫做产品族,
这个也比较好理解,实现类嘛是真实的产品,一个叫产品,多了就叫产品族,然后再看工厂
类:
您的设计模式
第 33 页
其中抽象工厂只实现了一个createHuman 的方法,目的是简化实现类的代码工作量,这
个在讲代码的时候会说。这里还使用了Jdk 1.5 的一个新特性Enum 类型,其实这个完全可
以类的静态变量来实现,但我想既然是学习就应该学有所获得,即使你对这个模式非常了解,
也可能没用过Enum 类型,也算是一个不同的知识点吧,我希望给大家讲解,每次都有新的
技术点提出来,每个人都会有一点的收获就足够了,然后在具体的项目中使用时,知道有这
个技术点,然后上baidu 狗狗一下就能解决问题。话题扯远了,我们继续类图,完整的类图
如下,这个看不大清楚,其实就是上面那两个类图加起来,大家可以看源码中的那个类图文
件:
然后类图讲解完毕,我们来看程序实现:
您的设计模式
第 34 页
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个人类的统称,问题出来了,刚刚定义的时候忘记定义性别了
* 这个重要的问题非修改不可,否则这个世界上太多太多的东西不存在了
*/
public interface Human {
//首先定义什么是人类
//人是愉快的,会笑的,本来是想用smile表示,想了一下laugh更合适,好长时间
没有大笑了;
public void laugh();
//人类还会哭,代表痛苦
public void cry();
//人类会说话
public void talk();
//定义性别
public void sex();
}
人类的接口定义好,然后根据接口创建三个抽象类,也就是三个产品等级,实现
laugh()、cry()、talk()三个方法,以AbstractYellowHuman 为例:
package com.cbf4life.yellowHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 为什么要修改成抽象类呢?要定义性别呀
*/
public abstract class AbstractYellowHuman implements Human {
public void cry() {
System.out.println("黄色人种会哭");
您的设计模式
第 35 页
}
public void laugh() {
System.out.println("黄色人种会大笑,幸福呀!");
}
public void talk() {
System.out.println("黄色人种会说话,一般说的都是双字节");
}
}
其他的两个抽象类AbstractWhiteHuman 和AbstractgBlackHuman 与此类似的事项方法,
不再通篇拷贝代码,大家可以看一下源代码。算了,还是拷贝,反正是电子档的,不想看就
往下翻页,也成就了部分“懒人”,不用启动Eclipse,还要把源码拷贝进来:
白种人的抽象类:
package com.cbf4life.whiteHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 白色人人种
* 为了代码整洁,新建一个包,这里是白种人的天下了
*/
public abstract class AbstractWhiteHuman implements Human {
public void cry() {
System.out.println("白色人种会哭");
}
public void laugh() {
System.out.println("白色人种会大笑,侵略的笑声");
您的设计模式
第 36 页
}
public void talk() {
System.out.println("白色人种会说话,一般都是但是单字节!");
}
}
黑种人的抽象类:
package com.cbf4life.blackHuman;
import com.cbf4life.Human;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 黑色人种,记得中学学英语,老师说black man是侮辱人的意思,不懂,没跟老外说
话
*/
public abstract class AbstractBlackHuman implements Human {
public void cry() {
System.out.println("黑人会哭");
}
public void laugh() {
System.out.println("黑人会笑");
}
public void talk() {
System.out.println("黑人可以说话,一般人听不懂");
}
}
三个抽象类都实现完毕了,然后就是些实现类了。其实,你说抽象类放这里有什么意义
吗?就是不允许你new 出来一个抽象的对象呗,使用非抽象类完全就可以代替,呵呵,杀猪
杀尾巴,各有各的杀法,不过既然进了Java 这个门就要遵守Java 这个规矩,我们看实现类:
您的设计模式
第 37 页
女性黄种人的实现类:
package com.cbf4life.yellowHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女性黄种人
*/
public class YellowFemaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为女...");
}
}
男性黄种人的实现类:
package com.cbf4life.yellowHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性黄种人
*/
public class YellowMaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为男....");
}
}
女性白种人的实现类:
package com.cbf4life.whiteHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.\
* 女性白种人
*/
public class WhiteFemaleHuman extends AbstractWhiteHuman {
您的设计模式
第 38 页
public void sex() {
System.out.println("该白种人的性别为女....");
}
}
男性白种人的实现类:
package com.cbf4life.whiteHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性白种人
*/
public class WhiteMaleHuman extends AbstractWhiteHuman {
public void sex() {
System.out.println("该白种人的性别为男....");
}
}
女性黑种人的实现类:
package com.cbf4life.blackHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女性黑种人
*/
public class BlackFemaleHuman extends AbstractBlackHuman {
public void sex() {
System.out.println("该黑种人的性别为女...");
}
}
您的设计模式
第 39 页
男性黑种人的实现类:
package com.cbf4life.blackHuman;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性黑种人
*/
public class BlackMaleHuman extends AbstractBlackHuman {
public void sex() {
System.out.println("该黑种人的性别为男...");
}
}
抽象工厂模式下的产品等级和产品族都已经完成,也就是人类以及产生出的人类是什么
样子的都已经定义好了,下一步就等着工厂开工创建了,那我们来看工厂类。
在看工厂类之前我们先看那个枚举类型,这个是很有意思的:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 世界上有哪些类型的人,列出来
* JDK 1.5开始引入enum类型也是目的的,吸引C程序员转过来
*/
public enum HumanEnum {
//把世界上所有人类型都定义出来
YelloMaleHuman("com.cbf4life.yellowHuman.YellowMaleHuman"),
YelloFemaleHuman("com.cbf4life.yellowHuman.YellowFemaleHuman")
,
WhiteFemaleHuman("com.cbf4life.whiteHuman.WhiteFemaleHuman"),
WhiteMaleHuman("com.cbf4life.whiteHuman.WhiteMaleHuman"),
BlackFemaleHuman("com.cbf4life.blackHuman.BlackFemaleHuman"),
您的设计模式
第 40 页
BlackMaleHuman("com.cbf4life.blackHuman.BlackMaleHuman");
private String value = "";
//定义构造函数,目的是Data(value)类型的相匹配
private HumanEnum(String value){
this.value = value;
}
public String getValue(){
return this.value;
}
/*
* java enum类型尽量简单使用,尽量不要使用多态、继承等方法
* 毕竟用Clas完全可以代替enum
*/
}
我之所以引入Enum 这个类型,是想让大家在看这本书的时候能够随时随地的学到点什
么,你如果看不懂设计模式,你可以从我的程序中学到一些新的技术点,不用像我以前报着
砖头似的书在那里啃,看一遍不懂,再看第二遍,然后翻了英文原本才知道,哦~,原来是
这样滴,只能说有些翻译家实在不懂技术。我在讲解技术的时候,尽量少用专业术语,尽量
使用大部分人类都能理解的语言。
Enum 以前我也很少用,最近在一个项目中偶然使用上了,然后才发觉它的好处,Enum
类型作为一个参数传递到一个方法中时,在Junit 进行单元测试的时候,不用判断输入参数
是否为空、长度为0 的边界异常条件,如果方法传入的参数不是Enum 类型的话,根本就传
递不进来,你说定义一个类,定义一堆的静态变量,这也可以呀,这个不和你抬杠,上面的
代码我解释一下,构造函数没啥好说的,然后是getValue()方法,就是获得枚举类型中一
个元素的值,枚举类型中的元素也是有名称和值的,这个和HashMap 有点类似。
然后,我们看我们的工厂类,先看接口:
package com.cbf4life;
您的设计模式
第 41 页
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 这次定一个接口,应该要造不同性别的人,需要不同的生产线
* 那这个八卦炉必须可以制造男人和女人
*/
public interface HumanFactory {
//制造黄色人种
public Human createYellowHuman();
//制造一个白色人种
public Human createWhiteHuman();
//制造一个黑色人种
public Human createBlackHuman();
}
然后看抽象类:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
import com.cbf4life.HumanFactory;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 编写一个抽象类,根据enum创建一个人类出来
*/
public abstract class AbstractHumanFactory implements HumanFactory
{
/*
* 给定一个性别人种,创建一个人类出来专业术语是产生产品等级
*/
protected Human createHuman(HumanEnum humanEnum) {
Human human = null;
//如果传递进来不是一个Enum中具体的一个Element的话,则不处理
if (!humanEnum.getValue().equals("")) {
try {
//直接产生一个实例
您的设计模式
第 42 页
human = (Human)
Class.forName(humanEnum.getValue()).newInstance();
} catch (Exception e) {
//因为使用了enum,这个种异常情况不会产生了,除非你的enum有问
题;
e.printStackTrace();
}
}
return human;
}
}
看到没,这就是引入enum 的好处,createHuman(HumanEnum humanEnum)这个方法定义
了输入参数必须是HumanEnum 类型,然后直接使用humanEnum.getValue()方法就能获得具
体传递进来的值,这个不多说了,大家自己看程序领会,没多大难度,这个抽象类的目的就
是减少下边实现类的代码量,我们看实现类:
男性工厂,只创建男性:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 男性创建工厂
*/
public class MaleHumanFactory extends AbstractHumanFactory {
//创建一个男性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackMaleHuman);
}
//创建一个男性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteMaleHuman);
}
//创建一个男性黄种人
您的设计模式
第 43 页
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloMaleHuman);
}
}
女性工厂,只创建女性:
package com.cbf4life.humanFactory;
import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.\
* 女性创建工厂
*/
public class FemaleHumanFactory extends AbstractHumanFactory {
//创建一个女性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackFemaleHuman);
}
//创建一个女性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteFemaleHuman);
}
//创建一个女性黄种人
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloFemaleHuman);
}
}
产品定义好了,工厂也定义好了,万事俱备只欠东风,那咱就开始造吧,哦,不对,女
娲开始造人了:
package com.cbf4life;
您的设计模式
第 44 页
import com.cbf4life.humanFactory.FemaleHumanFactory;
import com.cbf4life.humanFactory.MaleHumanFactory;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 女娲建立起了两条生产线,分别是:
* 男性生产线
* 女性生产线
*/
public class NvWa {
public static void main(String[] args) {
//第一条生产线,男性生产线
HumanFactory maleHumanFactory = new MaleHumanFactory();
//第二条生产线,女性生产线
HumanFactory femaleHumanFactory = new FemaleHumanFactory();
//生产线建立完毕,开始生产人了:
Human maleYellowHuman =
maleHumanFactory.createYellowHuman();
Human femaleYellowHuman =
femaleHumanFactory.createYellowHuman();
maleYellowHuman.cry();
maleYellowHuman.laugh();
femaleYellowHuman.sex();
/*
* .....
* 后面你可以续了
*/
}
}
两个八卦炉,一个造女的,一个造男的,开足马力,一直造到这个世界到现在这个模式
为止。
抽象工厂模式讲完了,那我们再思考一些问题:工厂模式有哪些优缺点?先说优点,我
这人一般先看人优点,非常重要的有点就是,工厂模式符合OCP 原则,也就是开闭原则,怎
么说呢,比如就性别的问题,这个世界上还存在双性人,是男也是女的人,那这个就是要在
您的设计模式
第 45 页
我们的产品族中增加一类产品,同时再增加一个工厂就可以解决这个问题,不需要我再来实
现了吧,很简单的大家自己画下类图,然后实现下。
那还有没有其他好处呢?抽象工厂模式,还有一个非常大的有点,高内聚,低耦合,在
一个较大的项目组,产品是由一批人定义开发的,但是提供其他成员访问的时候,只有工厂
方法和产品的接口,也就是说只需要提供Product Interface 和Concrete Factory 就可以
产生自己需要的对象和方法,Java 的高内聚低耦合的特性表现的一览无遗,哈哈。
您的设计模式
第 46 页
7. 门面模式【Facade Pattern】
好,我们继续讲课。大家都是高智商的人,都写过纸质的信件吧,比如给女朋友写情书
什么的,写信的过程大家都还记得吧,先写信的内容,然后写信封,然后把信放到信封中,
封好,投递到信箱中进行邮递,这个过程还是比较简单的,虽然简单,这四个步骤都是要跑
的呀,信多了还是麻烦,比如到了情人节,为了大海捞针,给十个女孩子发情书,都要这样
跑一遍,你不要累死,更别说你要发个广告信啥的,一下子发1 千万封邮件,那不就完蛋了?
那怎么办呢?还好,现在邮局开发了一个新业务,你只要把信件的必要信息高速我,我给你
发,我来做这四个过程,你就不要管了,只要把信件交给我就成了。
我们的类图还是从最原始的状态开始:
在这中环境下,最累的是写信的人,为了发送一封信出去要有四个步骤,而且这四个步
骤还不能颠倒,你不可能没写信就把信放到信封吧,写信的人要知道这四个步骤,而且还要
知道这四个步骤的顺序,恐怖吧,我们先看看这个过程如何表现出来的:
先看写信的过程接口,定义了写信的四个步骤:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 定义一个写信的过程
您的设计模式
第 47 页
*/
public interface LetterProcess {
//首先要写信的内容
public void writeContext(String context);
//其次写信封
public void fillEnvelope(String address);
//把信放到信封里
public void letterInotoEnvelope();
//然后邮递
public void sendLetter();
}
写信过程的具体实现:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 写信的具体实现了
*/
public class LetterProcessImpl implements LetterProcess {
//写信
public void writeContext(String context) {
System.out.println("填写信的内容...." + context);
}
//在信封上填写必要的信息
public void fillEnvelope(String address) {
System.out.println("填写收件人地址及姓名...." + address);
}
//把信放到信封中,并封好
public void letterInotoEnvelope() {
System.out.println("把信放到信封中....");
}
//塞到邮箱中,邮递
您的设计模式
第 48 页
public void sendLetter() {
System.out.println("邮递信件...");
}
}
然后就有人开始用这个过程写信了:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 我开始给朋友写信了
*/
public class Client {
public static void main(String[] args) {
//创建一个处理信件的过程
LetterProcess letterProcess = new LetterProcessImpl();
//开始写信
letterProcess.writeContext("Hello,It's me,do you know who I
am? I'm your old lover. I'd like to....");
//开始写信封
letterProcess.fillEnvelope("Happy Road No. 666,God
Province,Heaven");
//把信放到信封里,并封装好
letterProcess.letterInotoEnvelope();
//跑到邮局把信塞到邮箱,投递
letterProcess.sendLetter();
}
}
那这个过程与高内聚的要求相差甚远,你想,你要知道这四个步骤,而且还要知道这四
个步骤的顺序,一旦出错,信就不可能邮寄出去,那我们如何来改进呢?先看类图:
您的设计模式
第 49 页
这就是门面模式,还是比较简单的,Sub System 比较复杂,为了让调用者更方便的调
用,就对Sub System 进行了封装,增加了一个门面,Client 调用时,直接调用门面的方法
就可以了,不用了解具体的实现方法以及相关的业务顺序,我们来看程序的改变,
LetterProcess 接口和实现类都没有改变,只是增加了一个ModenPostOffice 类,我们这个
java 程序清单如下:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ModenPostOffice {
private LetterProcess letterProcess = new LetterProcessImpl();
//写信,封装,投递,一体化了
public void sendLetter(String context,String address){
//帮你写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//把信放到信封中
letterProcess.letterInotoEnvelope();
您的设计模式
第 50 页
//邮递信件
letterProcess.sendLetter();
}
}
这个类是什么意思呢,就是说现在又一个叫Hell Road PostOffice(地狱路邮局)提
供了一种新型的服务,客户只要把信的内容以及收信地址给他们,他们就会把信写好,封好,
并发送出去,这种服务提出时大受欢迎呀,这简单呀,客户减少了很多工作,那我们看看客
户是怎么调用的,Client.java 的程序清单如下:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 我开始给朋友写信了
*/
public class Client {
public static void main(String[] args) {
//现代化的邮局,有这项服务,邮局名称叫Hell Road
ModenPostOffice hellRoadPostOffice = new ModenPostOffice();
//你只要把信的内容和收信人地址给他,他会帮你完成一系列的工作;
String address = "Happy Road No. 666,God Province,Heaven"; //
定义一个地址
String context = "Hello,It's me,do you know who I am? I'm your
old lover. I'd like to....";
hellRoadPostOffice.sendLetter(context, address);
}
}
看到没,客户简单了很多,提供这种模式后,系统的扩展性也有了很大的提高,突然一
个非常时期,寄往God Province(上帝省)的邮件都必须进行安全检查,那我们这个就很
好处理了,看类图:
您的设计模式
第 51 页
看这个红色的框,只增加了这一部分,其他部分在类图上都不需要改动,那我们来看源
码:
package com.cbf4life.facade;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class ModenPostOffice {
private LetterProcess letterProcess = new LetterProcessImpl();
private Police letterPolice = new Police();
//写信,封装,投递,一体化了
public void sendLetter(String context,String address){
//帮你写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//警察要检查信件了
letterPolice.checkLetter(letterProcess);
//把信放到信封中
letterProcess.letterInotoEnvelope();
您的设计模式
第 52 页
//邮递信件
letterProcess.sendLetter();
}
}
只是增加了一个letterPolice 变量的声明以及一个方法的调用,那这个写信的过程就
变成了这样:先写信,然后写信封,然后警察开始检查,然后才把信放到信封,然后发送出
去,那这个变更对客户来说,是透明的,他根本就看不到有人在检查他的邮件,他也不用了
解,反正现代化的邮件都帮他做了,这也是他乐意的地方。
门面模式讲解完毕,这是一个很好的封装方法,一个子系统比较复杂的实话,比如算法
或者业务比较复杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常
好。还有,在一个较大项目中的时候,为了避免人员带来的风险,也可以使用这个模式,技
术水平比较差的成员,尽量安排独立的模块(Sub System),然后把他写的程序封装到一个
门面里,尽量让其他项目成员不用看到这些烂人的代码,看也看不懂,我也遇到过一个“高
人”写的代码,private 方法、构造函数、常量基本都不用,你要一个public 方法,好,
一个类里就一个public 方法,所有代码都在里面,然后你就看吧,一大坨的程序,看着能
把人逼疯,使用门面模式后,对门面进行单元测试,约束项目成员的代码质量,对项目整体
质量的提升也是一个比较好的帮助。
您的设计模式
第 53 页
8. 适配器模式【Adapter Pattern】
好,请安静,后排聊天的同学别吵醒前排睡觉的同学了,大家要相互理解嘛。今天讲适
配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你
在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是220V,日本是110V,
但是这个适配器能够把这些不同的电压转换为你需要的36V 电压,保证你的笔记本能够正常
运行,那我们在设计模式中引入这个适配器模式是不是也是这个意思呢?是的,一样的作用,
两个不同接口,有不同的实现,但是某一天突然上帝命令你把B 接口转换为A 接口,怎么办?
继承,能解决,但是比较傻,而且还违背了OCP 原则,怎么办?好在我们还有适配器模式。
适配器的通用类图是这个样子滴:
首先声明,这个不是我画的,这是从Rational Rose 的帮助文件中截取的,这个类图也
很容易理解,Target 是一个类(接口),Adaptee 是一个接口,通过Adapter 把Adaptee 包
装成Target 的一个子类(实现类),注意了这里用了个名词包装(Wrapper),那其实这个模
式也叫做包装模式(Wrapper),但是包装模式可不知一个,还包括了以后要讲的装饰模式。
我来讲个自己的一个经验,让大家可以更容易了解这个适配器模式,否则纯讲技术太枯燥了,
技术也可以有娱乐的嘛。
我在2004 年的时候带了一个项目,做一个人力资源管理,该项目是我们总公司发起的
项目,公司一共有700 多号人,包括子公司,这个项目还是比较简单的,分为三大模块:人
员信息管理,薪酬管理,职位管理,其中人员管理这块就用到了适配器模式,是怎么回事呢?
当时开发时明确的指明:人员信息简管理的对象是所有员工的所有信息,然后我们就这样设
您的设计模式
第 54 页
计了一个类图:
还是比较简单的,有一个对象UserInfo 存储用户的所有信息(实际系统上还有很多子
类,不多说了),也就是BO(Business Object),这个对象设计为贫血对象(Thin Business
Object),不需要存储状态以及相关的关系,而且我也是反对使用充血对象(Rich Business
Object),这里说了两个名词贫血对象和充血对象,这两个名词很简单,在领域模型中分别
叫做贫血领域模型和充血领域模型,有什么区别呢?在一个对象中不存储实体状态以及对象
之间的关系的就叫做贫血对象,上升到领域模型中就是贫血领域模型,有实体状态和对象关
系的模型的就是充血领域模型,是不是太技术化了?这个看不懂没关系,都是糊弄人的东西,
属于专用名词,这本书写完了,我再折腾本领域模型的文章,揭露领域模型中糊弄人的专有
名词,这个绝对是专有名词的堆砌,呵呵。扯远了,我们继续说适配器模式,这个UserInfo
对象,在系统中很多地方使用,你可以查看自己的信息,也可以做修改,当然这个对象是有
setter 方法的,我们这里用不到就隐藏掉了。
这个项目是04 年年底投产的,运行到05 年年底还是比较平稳的,中间修修补补也很正
常,05 年年底不知道是那股风吹的,很多公司开始使用借聘人员的方式招聘人员,我们公
司也不例外,从一个人力资源公司借用了一大批的低技术、低工资的人员,分配到各个子公
司,总共有将近200 号人,然后就找我们部门老大谈判,说要增加一个功能借用人员管理,
老大一看有钱赚呀,一拍大腿,做!
我带人过去一调研,不是这么简单,人力资源公司有一套自己的人员管理系统,我们公
您的设计模式
第 55 页
司需要把我们使用到的人员信息传输到我们的系统中,系统之间的传输使用RMI(Remote
Method Invocation,远程对象调用)的方式,但是有一个问题人力资源公司的人员对象和
我们系统的对象不相同呀,他们的对象是这样的:
人员资源公司是把人的信息分为了三部分:基本信息,办公信息和个人家庭信息,并且
都放到了HashMap 中,比如人员的姓名放到BaseInfo 信息中,家庭地址放到HomeInfo 中,
这咱不好说他们系统设计的不好,那问题是咱的系统要和他们系统有交互,怎么办?使用适
配器模式,类图如下:
您的设计模式
第 56 页
大家可能会问,这两个对象都不在一个系统中,你如何使用呢?简单!RMI 已经帮我们
做了这件事情,只要有接口,就可以把远程的对象当成本地的对象使用,这个大家有时间可
以去看一下RMI 文档,不多说了。通过适配器,把OuterUser 伪装成我们系统中一个
IUserInfo 对象,这样,我们的系统基本不用修改什么程序员,所有的人员查询、调用跟本
地一样样的,说的口干舌燥,那下边我们来看具体的代码实现:
首先看IUserInfo.java 的代码:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 用户信息对象
*/
public interface IUserInfo {
//获得用户姓名
public String getUserName();
//获得家庭地址
public String getHomeAddress();
//手机号码,这个太重要,手机泛滥呀
public String getMobileNumber();
//办公电话,一般式座机
public String getOfficeTelNumber();
//这个人的职位是啥
public String getJobPosition();
//获得家庭电话,这个有点缺德,我是不喜欢打家庭电话讨论工作
public String getHomeTelNumber();
}
然后看这个接口的实现类:
package com.cbf4life;
您的设计模式
第 57 页
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
*/
public class UserInfo implements IUserInfo {
/*
* 获得家庭地址,下属送礼也可以找到地方
*/
public String getHomeAddress() {
System.out.println("这里是员工的家庭地址....");
return null;
}
/*
* 获得家庭电话号码
*/
public String getHomeTelNumber() {
System.out.println("员工的家庭电话是....");
return null;
}
/*
* 员工的职位,是部门经理还是小兵
*/
public String getJobPosition() {
System.out.println("这个人的职位是BOSS....");
return null;
}
/*
* 手机号码
*/
public String getMobileNumber() {
System.out.println("这个人的手机号码是0000....");
return null;
}
/*
* 办公室电话,烦躁的时候最好“不小心”把电话线踢掉,我经常这么干,对己对人都
有好处
*/
public String getOfficeTelNumber() {
System.out.println("办公室电话是....");
您的设计模式
第 58 页
return null;
}
/*
* 姓名了,这个老重要了
*/
public String getUserName() {
System.out.println("姓名叫做...");
return null;
}
}
可能有人要问了,为什么要把电话号码、手机号码都设置成String 类型,而不是int
类型,大家觉的呢?题外话,这个绝对应该是String 类型,包括数据库也应该是varchar
类型的,手机号码有小灵通带区号的,比如02100001,这个你用数字怎么表示?有些人要
在手机号码前加上0086 后再保存,比如我们公司的印度阿三就是这样,喜欢在手机号码前
0086 保存下来,呵呵,我是想到啥就说啥,啰嗦了点。继续看我们的代码,下面看我们系
统的应用如何调用UserInfo 的信息:
package com.cbf4life;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 这就是我们具体的应用了,比如老板要查所有的20-30的女性信息
*/
public class App {
public static void main(String[] args) {
//没有与外系统连接的时候,是这样写的
IUserInfo youngGirl = new UserInfo();
//从数据库中查到101个
for(int i=0;i<101;i++){
youngGirl.getMobileNumber();
}
}
您的设计模式
第 59 页
}
这老板,比较那个,为什么是101,是男生都应该知道吧,111 代表男生,101 代表女
生,呵呵,是不是比较色呀。从数据库中生成了101 个UserInfo 对象,直接打印出来就成
了。那然后增加了外系统的人员信息,怎么处理呢?下面是IOuterUser.java 的源代码:
package com.cbf4life;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 外系统的人员信息
*/
@SuppressWarnings("all")
public interface IOuterUser {
//基本信息,比如名称,性别,手机号码了等
public Map getUserBaseInfo();
//工作区域信息
public Map getUserOfficeInfo();
//用户的家庭信息
public Map getUserHomeInfo();
}
我们再看看外系统的用户信息的具体实现类:
package com.cbf4life;
import java.util.HashMap;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 外系统的用户信息的实现类
*/
@SuppressWarnings("all")
您的设计模式
第 60 页
public class OuterUser implements IOuterUser {
/*
* 用户的基本信息
*/
public Map getUserBaseInfo() {
HashMap baseInfoMap = new HashMap();
baseInfoMap.put("userName", "这个员工叫混世魔王....");
baseInfoMap.put("mobileNumber", "这个员工电话是....");
return baseInfoMap;
}
/*
* 员工的家庭信息
*/
public Map getUserHomeInfo() {
HashMap homeInfo = new HashMap();
homeInfo.put("homeTelNumbner", "员工的家庭电话是....");
homeInfo.put("homeAddress", "员工的家庭地址是....");
return homeInfo;
}
/*
* 员工的工作信息,比如职位了等
*/
public Map getUserOfficeInfo() {
HashMap officeInfo = new HashMap();
officeInfo.put("jobPosition","这个人的职位是BOSS...");
officeInfo.put("officeTelNumber", "员工的办公电话是....");
return officeInfo;
}
}
那怎么把外系统的用户信息包装成我们公司的人员信息呢?看下面的OuterUserInfo
类源码,也就是我们的适配器:
您的设计模式
第 61 页
package com.cbf4life;
import java.util.Map;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 把OuterUser包装成UserInfo
*/
@SuppressWarnings("all")
public class OuterUserInfo extends OuterUser implements IUserInfo {
private Map baseInfo = super.getUserBaseInfo(); //员工的基本信息
private Map homeInfo = super.getUserHomeInfo(); //员工的家庭信息
private Map officeInfo = super.getUserOfficeInfo(); //工作信息
/*
* 家庭地址
*/
public String getHomeAddress() {
String homeAddress =
(String)this.homeInfo.get("homeAddress");
System.out.println(homeAddress);
return homeAddress;
}
/*
* 家庭电话号码
*/
public String getHomeTelNumber() {
String homeTelNumber =
(String)this.homeInfo.get("homeTelNumber");
System.out.println(homeTelNumber);
return homeTelNumber;
}
/*
*职位信息
*/
public String getJobPosition() {
String jobPosition =
(String)this.officeInfo.get("jobPosition");
System.out.println(jobPosition);
return jobPosition;
您的设计模式
第 62 页
}
/*
* 手机号码
*/
public String getMobileNumber() {
String mobileNumber =
(String)this.baseInfo.get("mobileNumber");
System.out.println(mobileNumber);
return mobileNumber;
}
/*
* 办公电话
*/
public String getOfficeTelNumber() {
String officeTelNumber =
(String)this.officeInfo.get("officeTelNumber");