1、论文 【装饰模式应用场景举例】 比如在玩“极品飞车”这款游戏,游戏中有对汽车进行喷涂鸦的功能,而且这个喷涂鸦是可以覆盖的,并且覆盖的顺序也影响到最后车身的显示效果,假设现在喷涂鸦具有2种样式:(1)红色火焰 (2)紫色霞光如果使用“继承父类”设计这样的功能,那么类图就像如下的这样: 从图中可以看到使用继承来实现这种功能,并且是2种涂鸦样式,就需要创建4个子类,如果喷涂鸦有3种,4种呢?这种情况就是典型中学课程学习过的“排列与组合”,那简直就是“Head First设计模式”书中讲的“类爆炸”。 显然继承“奥迪汽车类”的这个办法是无效,而且是非常徒劳,繁琐的。
2、 那么如何才能以“灵活”,“顺序敏感”这样的需求来实现这样的功能呢? 【装饰模式解释】 类型:结构模式 动态的对一个对象进行功能上的扩展,也可以对其子类进行功能上的扩展。 【装饰模式UML图】 【装饰模式-JAVA代码实现】 新建一个抽象汽车父类: package car_package; public abstract class car_parent { // 汽车抽象父类 private String make_address; private int speed; publ
3、ic String getMake_address() { return make_address; } public void setMake_address(String make_address) { this.make_address = make_address; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed;
4、 } public abstract void print_face(); } 然后新建一个奥迪汽车子类 package car_package; public class audi_sub extends car_parent { // 奥迪汽车子类 @Override public void print_face() { System.out.println("audi车默认的颜色为 黑色"); } } 然后再新建一个装饰者父类: package decorator_package;
5、 import car_package.car_parent; public abstract class decorator_parent extends car_parent { // 装饰者父类 protected car_parent car_parent_ref; public void setCar_parent_ref(car_parent car_parent_ref) { this.car_parent_ref = car_parent_ref; } @Override public void p
6、rint_face() { car_parent_ref.print_face(); } } 然后再新建装饰者子类:红色火焰装饰者类: package decorator_package; public class decorator_audi_red extends decorator_parent { @Override public void print_face() { super.print_face(); System.out.println("给 奥迪 喷涂鸦 - 颜色为 红色火焰
7、"); } } 然后再新建装饰者子类:紫色霞光装饰者类: package decorator_package; public class decorator_audi_purple extends decorator_parent { @Override public void print_face() { super.print_face(); System.out.println("给 奥迪 喷涂鸦 - 颜色为 紫色霞光"); } } 新建一个运行类 package main_run;
8、 import car_package.audi_sub; import decorator_package.decorator_audi_purple; import decorator_package.decorator_audi_red; public class main_run { public static void main(String[] args) { audi_sub audi_sub_ref = new audi_sub(); audi_sub_ref.setMake_address("北京市朝阳区"
9、); audi_sub_ref.setSpeed(200); decorator_audi_red decorator_audi_red_ref = new decorator_audi_red(); decorator_audi_red_ref.setCar_parent_ref(audi_sub_ref); decorator_audi_purple decorator_audi_purple_ref = new decorator_audi_purple(); decorator_audi_
10、purple_ref.setCar_parent_ref(decorator_audi_red_ref); decorator_audi_purple_ref.print_face(); } } 程序运行结果如下: audi车默认的颜色为 黑色 给 奥迪 喷涂鸦 - 颜色为 红色火焰 给 奥迪 喷涂鸦 - 颜色为 紫色霞光 从程序结构中可以看到,完全符合了前面我们的要求:“灵活”,“顺序敏感”。 【策略模式应用场景举例】 比如在玩“极品飞车”这款游戏,那么游戏对车的轮胎是可以更换的,不同的轮胎在高速转弯时有不同的痕迹样式,
11、那么针对“汽车”的配件“轮胎”就要可以变化,而且轮胎和轮胎之间是可以相互替换的,这就是典型的要应用“策略模式”的场景!从程序结构中可以看到,完全符合了前面我们的要求:“灵活”,“顺序敏感”。 【策略模式解释】 类型:行为模式 定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。策略模式使这些算法在客户端调用它们的时候能够互不影响地变化。 【策略模式UML图】 【策略模式-JAVA代码实现】 从策略模式UML图中可以看到Context与接口Strategy是组合关系,即强引用关系。 新建一个轮胎接口: packa
12、ge strategy_interface; public interface tyre_interface { // tyre 轮胎 public void print_tyre_line();// 显示出轮胎的痕迹 } 新建2个轮胎接口的实现类: package strategy_implement; import strategy_interface.tyre_interface; //长痕迹轮胎类 public class tyre_long_implement implements tyre_interface { public v
13、oid print_tyre_line() { System.out.println("在路面上显示一个长轮胎痕迹"); } } package strategy_implement; import strategy_interface.tyre_interface; //短痕迹轮胎类 public class tyre_short_implement implements tyre_interface { public void print_tyre_line() { System.out.println("在路面上显示一
14、个短轮胎痕迹"); } } 基于一个轮胎接口来实现不同样式的轮胎样式。 组装一个Car车类: package car_package; import strategy_interface.tyre_interface; public class Car { private String make_address;// 制造地 private int death_year;// 报废年限 private int speed;// 速度 private tyre_interface tyre_interface_ref;// 轮
15、胎的样式 public String getMake_address() { return make_address; } public void setMake_address(String make_address) { this.make_address = make_address; } public int getDeath_year() { return death_year; } public void setDeath_year(int death_yea
16、r) { this.death_year = death_year; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public tyre_interface getTyre_interface_ref() { return tyre_interface_ref; } public void se
17、tTyre_interface_ref(tyre_interface tyre_interface_ref) { this.tyre_interface_ref = tyre_interface_ref; } public void start() { System.out.println("车的基本信息为:"); System.out.println("制造地make_address:" + this.getMake_address()); System.out.println("报废年限death_
18、year:" + this.getDeath_year()); System.out.println("速度speed:" + this.getSpeed()); System.out.println("Car 起动了!"); System.out.println("Car高速行驶,遇到一个大转弯,路面显示:"); this.getTyre_interface_ref().print_tyre_line(); } } 让车跑起来,并且具有更换轮胎样式的功能: package main_run
19、 import strategy_implement.tyre_long_implement; import strategy_implement.tyre_short_implement; import car_package.Car; public class run_main { public static void main(String[] args) { tyre_long_implement tyre_long_implement = new tyre_long_implement(); tyre_short_impleme
20、nt tyre_short_implement = new tyre_short_implement(); Car car = new Car(); car.setDeath_year(8); car.setMake_address("北京朝阳区"); car.setSpeed(200); car.setTyre_interface_ref(tyre_long_implement); car.start(); } } 控制台打印出: 车的基本信息为: 制造地ma
21、ke_address:北京朝阳区 报废年限death_year:8 速度speed:200 Car 起动了! Car高速行驶,遇到一个大转弯,路面显示: 在路面上显示一个长轮胎痕迹 是一个长轮胎痕迹,但在程序中可以使用代码:car.setTyre_interface_ref(tyre_long_implement);来对轮胎的样式进行不同的替换,可以替换成短轮胎痕迹的汽车轮胎,这样在不更改Car类的前题下进行了不同轮胎样式的改变,轮胎和轮胎之间可以互相替换,这就是策略模式。 策略模式strategy示例代码 【代理模式应用场景举例】 比如在玩“极品飞车”这
22、款游戏,如果游戏者手中的金钱达到了一定的数量就可以到车店买一部性能更高的赛车,那么这个卖车的“车店”就是一个典型的“汽车厂家”的“代理”,他为汽车厂家“提供卖车的服务”给有需求的人士。从面向对象的方面考虑,“销售汽车的代理”也是一个对象,那么这个对象也具有一定的状态,在软件项目中这个对象也具有管理财务进销存的基本功能,那么在设计时就要以面向OOP编程的思想来考虑软件的类结构,这个销售汽车的代理也是一个类了。 【代理模式解释】 类型:结构模式 对一些对象提供代理,以限制那些对象去访问其它对象。 【代理模式UML图】 【代理模式-JAVA代码实
23、现】 新建一个买车的接口: package buy_car_package; public interface buy_car_package { public void buy_car(); } 新建一个people人类,具有买车的行为,所以实现接口buy_car_package: package buy_car_imple; import buy_car_package.buy_car_package; public class people implements buy_car_package { private int cas
24、h; private String username; public int getCash() { return cash; } public void setCash(int cash) { this.cash = cash; } public String getUsername() { return username; } public void setUsername(String username) { this.u
25、sername = username; } public void buy_car() { System.out.println(username + "买了一台新车"); } } people类不能拥有车,必须经过proxy代理类的认证,符合条件之后才可以拥有车辆,新建一个代理,这个代理类来考察当前的people是否有资格进行买车: package buy_car_imple; import buy_car_package.buy_car_package; public class proxy_buy_car_imple im
26、plements buy_car_package { private people people; public people getPeople() { return people; } public void setPeople(people people) { this.people = people; } public void buy_car() { if (people.getCash() > 3000) { System.ou
27、t.println(people.getUsername() + "花" + people.getCash() + "块 买了新车 交易结束"); } else { System.out.println(people.getUsername() + "金钱不够,请继续比赛!"); } } } 最后创建一个客户端,用来模拟买车的行为: package run_main; import buy_car_imple.people; import buy_car_
28、imple.proxy_buy_car_imple; public class run_main { public static void main(String[] args) { people people_ref1 = new people(); people_ref1.setCash(4000); people_ref1.setUsername("高洪岩"); people people_ref2 = new people(); people_ref2.setCash(2000
29、); people_ref2.setUsername("岩洪高"); proxy_buy_car_imple proxy_buy_car_imple = new proxy_buy_car_imple(); proxy_buy_car_imple.setPeople(people_ref1); proxy_buy_car_imple.buy_car(); proxy_buy_car_imple.setPeople(people_ref2); proxy_buy_car_imple
30、buy_car(); } } 程序运行结果如下: 高洪岩花4000块 买了新车 交易结束 岩洪高金钱不够,请继续比赛! 这样people就不可能自由的拥有车辆,必须经过proxy的认证之后才可以。 而代理模式在GOF四人帮的介绍中大体有4种使用情景: (1)远程代理。典型的就是客户端与webservice使用的情况,客户端由于是针对OOP编程,而不是针对webservice中的方法进行编程,所以得在客户端模拟一下webservice的环境,用proxy来对webservice进行包装,这样就可以使用proxy代理类来远程操作webs
31、ervice了。 (2)虚拟代理。比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。 (3)安全代理。其实也就是本例中所举的买车的例子,金钱不够不可以买车! (4)智能指引。比如在访问一个对象时检测其是否被锁定等情况。 代理模式proxy示例代码 【外观模式应用场景举例】 比如在玩“极品飞车”这款游戏,你只需要等待的就是倒计时到0时以最快的车速冲到第一名,但游戏者根本没有想过在车冲出去之前要做哪些
32、工作,比如挂档,离合器,油箱检测,调整方向等等的微操作,将这些微操作封装起来变成一个接口就是外观模式了。在WEB开发中的MVC分层架构就是典型的一个外观模式,每一层将操作的具体内容隐藏起来,保留一个接口供上层调用。 【外观模式解释】 类型:结构模式 为子系统中的一组接口提供一个一致的interface接口界面。 【外观模式UML图】 【外观模式-JAVA代码实现】 新建赛车类: package car_package; public class car { public void start() {
33、 System.out.println("车子已启动"); } public void check_stop() { System.out.println("刹车检查"); } public void check_box() { System.out.println("检查油箱"); } public void check_console() { System.out.println("检查仪表盘是否异常"); } } 新建赛车操作的外观类:
34、 package car_facade; import car_package.car; public class car_facade_imple { public void car_go_go(car car_ref) { car_ref.check_box(); car_ref.check_console(); car_ref.check_stop(); car_ref.start(); } } 新建客户端运行类: package run_main; impor
35、t car_facade.car_facade_imple; import car_package.car; public class run_main { public static void main(String[] args) { car_facade_imple car_facade_imple_ref = new car_facade_imple(); car_facade_imple_ref.car_go_go(new car()); } } 程序运行结果如下: 检查油箱 检查仪表盘是否异常 刹
36、车检查 车子已启动 很简单吧,将子操作用一个外观接口封装起来,然后调用这个接口就是调用那些非常复杂的微操作了。 外观模式facade示例代码 【原型模式应用场景举例】 比如在玩“极品飞车”这款游戏,每个游戏者都有自己的账号用来标识玩家,而每个玩家都可以拥有相同的车辆,这样每个玩家都拥有相同的车,比如你的领居有奥迪A6,你的老婆对你说,我也要开和邻居一样的奥迪A6,好了,现在你们家也有奥迪A6了,你们每天开着自己的车去上班,那么在这种情况下就可以应用原型模式了,原型模式很简单哟:)就是类的copy复制。 【原型模式解释】 类型:创建模式
37、 用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象。 【原型模式UML图】 【原型模式-JAVA代码实现】 新建赛车的接口: package car_interface; public interface car_interface { public void start(); public void stop(); } 新建奥迪汽车的实现类: package car_imple; import car_fittings.car_tyre; import car_interface.car_inter
38、face; public class audi_imple implements car_interface, Cloneable { private car_tyre car_tyre_ref; public void start() { System.out.println("奥迪A6启动了"); } public void stop() { System.out.println("奥迪A6停止了"); } public car_tyre getCar_tyre_ref() {
39、 return car_tyre_ref; } public void setCar_tyre_ref(car_tyre car_tyre_ref) { this.car_tyre_ref = car_tyre_ref; } @Override public Object clone() throws CloneNotSupportedException { super.clone(); audi_imple audi_imple = new audi_imple(
40、); audi_imple.setCar_tyre_ref(new car_tyre()); return audi_imple; } } 在奥迪汽车实现类中需要注意的是将原来protected类型的clone方法要变成public,这样才可以对外公开,可以被调用,将秘密公开化。 新建奥迪汽车的配件轮胎类 package car_fittings; public class car_tyre { private String name = "德国制造原版轮胎"; public String ge
41、tName() { return name; } } 新建客户端运行类: package run_main; import car_fittings.car_tyre; import car_imple.audi_imple; import car_interface.car_interface; public class run_main { public static void main(String[] args) { try { audi_imple car_ref_
42、my = new audi_imple(); car_ref_my.setCar_tyre_ref(new car_tyre()); System.out.println("我的奥迪车的参数是:" + car_ref_my); System.out.println("我的奥迪车的轮胎参数是:" + car_ref_my.getCar_tyre_ref()); audi_imple car_ref_other = (audi_imple) car_ref_my.clone();
43、 System.out.println("其它人的奥迪车的参数是:" + car_ref_other); System.out.println("其它人的奥迪车的轮胎参数是:" + car_ref_other.getCar_tyre_ref()); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace();
44、 } } } 程序运行结果如下: 我的奥迪车的参数是:car_imple.audi_imple@9cab16 我的奥迪车的轮胎参数是:car_fittings.car_tyre@1a46e30 其它人的奥迪车的参数是:car_imple.audi_imple@3e25a5 其它人的奥迪车的轮胎参数是:car_fittings.car_tyre@19821f 从打印的结果来看,我的汽车我的轮胎和其它人的汽车和轮胎都是不一样的对象,但“类型”都是一样的:奥迪的汽车,原版的轮胎。本例中也实现了“原型模式”中的“深拷贝/深复制”,即汽车类中有一个
45、对象“轮胎”对象,关于深拷贝/深复制的概念请参考其它的资料。原型模式通常都是在复制对象的时候使用,而在常规的情况下都是使用new重新创建一个,并且重新对属性进行复制,代码重复度很高,原型模式的出现避免了new的硬操作。 原型模式prototype示例代码 【模板方法模式应用场景举例】 比如在玩“极品飞车”这款游戏,每辆车都有显示速度的仪表盘,但有可能A车显示的是蓝色的仪表盘,B车显示的是红色的仪表盘,分析中可以发现,他们都有车速度的值,但显示的方式不太一样。其实模板方法就是最典型的“继承”的使用,大家平时百分百都可以用到,只是不知道叫模板方法模式:)! 【模板方
46、法模式解释】 类型:行为模式 模板方法模式定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 【模板方法模式UML图】 【模板方法模式-JAVA代码实现】 新建赛车的父类: package car_package; public class car_parent { private int speed; public int getSpeed() { return speed; } public void setS
47、peed(int speed) { this.speed = speed; } public void print_speed() { System.out.println("将速度" + this.getSpeed() + "取出来!"); } } 新建红色仪表盘的赛车实现类: package car_imple; import car_package.car_parent; public class car_imple_red extends car_parent { @Overrid
48、e public void print_speed() { super.print_speed(); System.out.println("将速度" + this.getSpeed() + "用红色的仪表盘显示车的速度"); } } 新建蓝色仪表盘的赛车实现类: package car_imple; import car_package.car_parent; public class car_imple_blue extends car_parent { @Override publi
49、c void print_speed() { super.print_speed(); System.out.println("将速度" + this.getSpeed() + "用蓝色的仪表盘显示车的速度"); } } 新建客户端运行类: package run_main; import car_imple.car_imple_blue; import car_imple.car_imple_red; import car_package.car_parent; public class run_main {
50、 public static void main(String[] args) { car_parent car_ref_red = new car_imple_red(); car_ref_red.setSpeed(300); car_ref_red.print_speed(); car_parent car_ref_blue = new car_imple_blue(); car_ref_blue.setSpeed(400); car_ref_blue.print_sp






