设计模式

设计模式
 第 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 [email protected]
 * I'm glad to share my knowledge with you all.
 * 首先定一个策略接口,这是诸葛亮老人家给赵云的三个锦囊妙计的接口
 *
 */
 public interface IStrategy {
 //每个锦囊妙计都是一个可执行的算法
 您的设计模式
 第 5 页
 public void operate();
 }
 然后再写三个实现类,有三个妙计嘛:
 package com.cbf4life.strategy;
 /**
 * @author cbf4Life [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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
 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 [email protected]
 * 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
 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 您的设计模式
 第 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 [email protected]
 * 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 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 [email protected]
 * 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 [email protected] I'm glad to share my knowledge
 with you
 * all.
 *
 */
 @SuppressWarnings("all")
 public class ClassUtils {
 //给一个接口,返回这个接口的所有实现类
 public static List getAllClassByInterface(Class c){
 List returnClassList = new ArrayList(); //返
 回结果
 //如果不是一个接口,则不做处理
 if(c.isInterface()){
 String packageName = c.getPackage().getName(); //获得当
 前的包名
 try {
 List allClass = getClasses(packageName); //
 获得当前包下以及子包下的所有类
 //判断是否是同一个接口
 for(int i=0;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 getClasses(String packageName)
 throws ClassNotFoundException, IOException {
 ClassLoader classLoader = Thread.currentThread()
 .getContextClassLoader();
 String path = packageName.replace('.', '/');
 Enumeration resources = classLoader.getResources(path);
 List dirs = new ArrayList();
 while (resources.hasMoreElements()) {
 URL resource = resources.nextElement();
 dirs.add(new File(resource.getFile()));
 }
 ArrayList classes = new ArrayList();
 for (File directory : dirs) {
 classes.addAll(findClasses(directory, packageName));
 }
 return classes;
 }
 private static List findClasses(File directory, String
 packageName) throws ClassNotFoundException {
 List classes = new ArrayList();
 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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 [email protected]
 * 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");

你可能感兴趣的:(设计模式,软件设计模式)