软件设计模式
1.桥接模式
定义:将抽象与实现分离,使他们可以独立的变化。它是用组合关系代替继承关系来实现,用于多个维度变化的实例,如图形既可按形状分,又可按颜色分。
优点:
1)由于抽象与实现分离,所以扩展能力强;
2)其实现细节对客户透明。
缺点:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
package chapter04.text5; /** * 桥接模式包含以下主要角色: ⑴ 抽象化(Abstraction)角色: 定义抽象类, 并包含一个对实现化对象的引用。 * */ public abstract class Bag { //引用实现化角色 protected IColor color; protected ISex sex; //设置颜色的方法 public void setColor(IColor color) { this.color=color; } public void setSex(ISex sex) { this.sex = sex; } //添加获取包的信息 public abstract String getName();//抽象方法 } |
package chapter04.text5; /** * ⑵ 扩展抽象化(Refined Abstraction)角色: * 是抽象化角色的子类,实现父类中的业务方法, * 并通过组合关系调用实现化角色中的业务方法。 */ public class KuaBag extends Bag { @Override public String getName() { return color.getColor()+sex.getSex()+"挎包"; } } |
package chapter04.text5; /** * ⑵ 扩展抽象化(Refined Abstraction)角色: * 是抽象化角色的子类,实现父类中的业务方法, * 并通过组合关系调用实现化角色中的业务方法。 */ public class MoneyBag extends Bag { @Override public String getName() { return color.getColor()+sex.getSex()+"钱包...."; } } |
package chapter04.text5; /** * * ⑶ 实现化(Implementor)角色: * 定义实现化角色的接口, * 供扩展抽象化角色调用。 * */ public interface IColor {//颜色 //获取颜色 public String getColor(); } |
package chapter04.text5; /**⑷ 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。*/ public class RedColor implements IColor { @Override public String getColor() { return "红色的..."; } } |
package chapter04.text5; /**⑷ 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。*/ public class BlueColor implements IColor { @Override public String getColor() { return "蓝色的..."; } } |
package chapter04.text5; public interface ISex { public String getSex(); } package chapter04.text5; public class ManImpl implements ISex { @Override public String getSex() { return "男士"; } } package chapter04.text5; public class LadyImpl implements ISex { @Override public String getSex() { return "女士"; } } |
package chapter04.text5; public class TestMain { public static void main(String[] args) {
IColor r=new RedColor();//红色 IColor b=new BlueColor();//蓝色 ISex m=new ManImpl();//男士 ISex f=new LadyImpl();//女士 Bag k=new KuaBag();//挎包 Bag q=new MoneyBag();//钱包
k.setColor(r); k.setSex(m); System.out.println(k.getName());
q.setColor(b); q.setSex(f); System.out.println(q.getName());
q.setColor(r);//红色的钱包 q.setSex(m); System.out.println(q.getName());
k.setColor(b);//蓝色的钱包 k.setSex(f); System.out.println(k.getName()); } } |
2.享元(Flyweight)模式
在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题内存占用过多。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
如:围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等等。
这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生的背景。
模式的定义与特点
定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来提高系统资源的利用率。
优点:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:1)为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性;
- 读取享元模式的外部状态会使得运行时间稍微变长。
模式的结构与实现
享元模式中存在以下2种状态:
1)内部状态:即不会随着环境的改变而改变的可共享部分;
2)外部状态:指随环境改变而改变的不可以共享的部分。
享元模式的实现要领就是区分应用中的这2种状态,并将外部状态外部化。下面我们来分析其基本结构和实现方法。
模式的结构与实现(续)
1.模式的结构
享元模式的主要角色有:
⑴ 抽象享元(Flyweight)角色:是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
⑵ 具体享元(ConcreteFlyweight)角色:实现抽象享元角色中所规定的接口。
⑶ 非享元(UnsharableFlyweight)角色:是不可以共享的外部状态,它以参数的形式注入到具体享元的相关方法中。
- 享元工厂(FlyweightFactoiy)角色:负责创建和管理享元角色。
享元模式的主要角色有:
⑴ 抽象享元(Flyweight)角色:是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
亨元模式 * 意图:运用共享技术有效地支持大量细粒度的对象。 * 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。 * 何时使用:
5、系统不依赖于这些对象身份,这些对象是不可分辨的。 * 如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。 * 关键代码:用 HashMap 存储这些对象。 * 应用实例:
* 优点:大大减少对象的创建,降低系统的内存,使效率提高。 * 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。 * 使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。 * 注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。 | package chapter05.text3.fw; /** * 享元模式的主要角色有: ⑴ 抽象享元(Flyweight)角色: 是所有的具体享元类的基类, 为具体享元规范需要实现的公共接口, 非享元的外部状态以参数的形式通过方法传入。 */ public interface FlyWeight_Pet { //接口 //内部状态:即不会随着环境的改变而改变的可共享部分; //创建获取毛色方法 public String getColor(); //外部状态:指随环境改变而改变的不可以共享的部分。 //获取猫的信息 public void display(UnsharableFlyWeight_Animal animal); } |
⑵ 具体享元(ConcreteFlyweight)角色:实现抽象享元角色中所规定的接口。
package chapter05.text3.cfw; import chapter05.text3.fw.FlyWeight_Pet; import chapter05.text3.ufw.UnsharableFlyWeight_Animal; /** * ⑵ 具体享元(ConcreteFlyweight)角色:实现抽象享元角色中所规定的接口。 */ public class ConcreteFlyWeight_CatPet implements FlyWeight_Pet { //子类成员变量 毛色 public String color; //构造函数 public ConcreteFlyWeight_CatPet(String color) { this.color = color; } @Override public String getColor() {//获取猫的毛色信息 return this.color; } @Override public void display(UnsharableFlyWeight_Animal animal) { System.out.println( String.format("%s的%s爱吃%s", this.getColor(), animal.getName(), animal.getFood())); } } |
⑶ 非享元(UnsharableFlyweight)角色:是不可以共享的外部状态,它以参数的形式注入到具体享元的相关方法中。
package chapter05.text3.ufw; /** * ⑶ 非享元(UnsharableFlyweight)角色: * 是不可以共享的外部状态, * 它以参数的形式注入到具体享元的相关方法中。 */ public class UnsharableFlyWeight_Animal { //私有字段 private String name; private String food; //有参数的构造函数 public UnsharableFlyWeight_Animal(String name, String food) { super(); this.name = name; this.food = food; } //自动生成getter setter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFood() { return food; } public void setFood(String food) { this.food = food; } } |
package chapter05.text3.fwf; /** * * (4)享元工厂(FlyweightFactoiy)角色: 负责创建和管理享元角色。 * */ import java.util.HashMap; import java.util.Map; import chapter05.text3.cfw.ConcreteFlyWeight_CatPet; import chapter05.text3.fw.FlyWeight_Pet; public class FlyWeightFactory_CatPet {
Private static Map<String, FlyWeight_Pet> map= new HashMap<String, FlyWeight_Pet>();
Public static FlyWeight_Pet getPet(String color) { if (map.get(color)==null) { FlyWeight_Pet pet=new ConcreteFlyWeight_CatPet(color); map.put(color, pet);
} return map.get(color); } } |
package chapter05.text3.client.main; import chapter05.text3.fw.FlyWeight_Pet; import chapter05.text3.fwf.FlyWeightFactory_CatPet; import chapter05.text3.ufw.UnsharableFlyWeight_Animal; public class ClientMain { public static void main(String[] args) { //客户端 FlyWeight_Pet pet1=FlyWeightFactory_CatPet.getPet("白色"); FlyWeight_Pet pet2=FlyWeightFactory_CatPet.getPet("黑色"); FlyWeight_Pet pet3=FlyWeightFactory_CatPet.getPet("黄色"); FlyWeight_Pet pet4=FlyWeightFactory_CatPet.getPet("花色"); pet1.display(new UnsharableFlyWeight_Animal("英短虎斑", "鸡胸肉")); pet2.display(new UnsharableFlyWeight_Animal("蓝猫", "猫粮")); pet3.display(new UnsharableFlyWeight_Animal("家猫", "猫罐头")); pet4.display(new UnsharableFlyWeight_Animal("无毛猫", "虾仁")); } } |
3.外观模式
模式的定义与特点
定义:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
优点:1)降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响到调用它的客户类;2)对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易;3)降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响到外观对象。
缺点:1)不能很好地限制客户使用子系统类;2)增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
模式的结构与实现
1.模式的结构
外观模式的结构比较简单,主要包含以下主要角色:
⑴ 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
⑵ 子系统(SubSystem)角色:实现系统的部分功能,客户可以通过外观角色访问它。
⑶ 客户(client)角色:通过一个外观角色访问各个子系统的功能。
package chapter05.text2.waiguan; import chapter05.text2.biz.BizKanBing; import chapter05.text2.biz.BizTiJian; import chapter05.text2.biz.BizZhongYi; /** * 外观(Facade)角色:为多个子系统对外提供一个共同的接口。 */ public class FacadeWaiGuan { private BizKanBing kb=new BizKanBing(); private BizTiJian tj=new BizTiJian(); private BizZhongYi zy=new BizZhongYi();
public void service() { kb.zhiLiao(); tj.checkBody(); zy.buy(); } } |
package chapter05.text2.biz; /** * 子系统(SubSystem)角色:实现系统的部分功能,客户可以通过外观角色访问它。 */ public class BizKanBing { public void zhiLiao() { System.out.println("生病了需要治疗!"); } } package chapter05.text2.biz; /** * 子系统(SubSystem)角色:实现系统的部分功能,客户可以通过外观角色访问它。 */ public class BizTiJian { public void checkBody() { System.out.println("检查身体!"); } } |
package chapter05.text2.biz; /** * 子系统(SubSystem)角色:实现系统的部分功能,客户可以通过外观角色访问它。 */ public class BizZhongYi { public void buy() { System.out.println("看中医买药!"); } } |
package chapter05.text2.user; import chapter05.text2.waiguan.FacadeWaiGuan; /** * 客户(client)角色:通过一个外观角色访问各个子系统的功能。 */ public class ClientMain { public static void main(String[] args) { FacadeWaiGuan serviceFuWU=new FacadeWaiGuan(); serviceFuWU.service(); } } |
模式的应用场景 通常在以下情况下可以考虑使用外观模式: 1)对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。 2)对于一个复杂系统,其子系统很多,外观模式可以为系统设计一个简单的接口供外界访问。 3)当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。 |
模式的扩展 在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题,右边其结构图: |
4.组合模式
- 模式的定义与特点
定义:有时又叫做部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
优点:
1)组合模式使得客户端代码可以一致地处理单个对象和组合对象,无需关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
2)更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”。
缺点:1)设计较复杂,客户端需要花更多时间理清类之间的层次关系;2)不容易限制容器中的构件;3)不容易用继承的方法来增加构件的新功能。
- 模式的结构与实现
1.模式的结构
组合模式包含以下主要角色:
⑴ 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的缺省行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
⑵ 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中声明的公共接口。
⑶ 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含Add()、Remove()、GetChild()等方法。
- 5.4.2 模式的结构与实现(续)
组合模式分为透明式的组合模式和安全式的组合模式:
1.透明式的组合模式的结构
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无需区别树叶对象和树枝对象,对于客户端来说是透明的,右边是其结构图:
- 模式的结构与实现(续)
组合模式分为透明式的组合模式和安全式的组合模式:
2.安全式的组合模式的结构
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性,右边是其结构图:
//该模式的实现方式见后面的实例
模式的应用实例 【例5.4】用组合模式实现当用户在商店购物后,显示其所选商品信息,并计算所选商品总价的功能。 分析:假如李先生用1个红色小袋子装了2包婺源特产(单价7.9元)、1张婺源地图(单价9.9元);用1个白色小袋子装了2包韶关香菇(单价68元)和3包韶关红茶(单价180元);用1个中袋子装了前面的红色小袋子和1个景德镇瓷器(单价380元);用1个大袋子装了前面的中袋子、白色小袋子和1双李宁牌运动鞋(单价198元),现在要求编程显示李先生放在大袋子中的所以商品信息并计算要支付的总价。本实例可按安全组合模式设计,其结构图见下页: 用组合模式实现当用户在商店购物后,显示其所选商品信息,并计算所选商品总价的功能。 |
package chapter06.text1.cgj; /** 1.模式的结构 组合模式包含以下主要角色: ⑴ 抽象构件(Component)角色: 它的主要作用是为树叶构件和树枝构件声明公共接口, 并实现它们的缺省行为。 在透明式的组合模式中抽象构件还声明访问和管理子类的接口; 在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。 */ //创建物品类---抽象构件 public interface Articles { public float calculation();//合计 public void show();//展示 } |
package chapter06.text1.sy; import chapter06.text1.cgj.Articles; /** ⑵ 树叶构件(Leaf)角色:是组合中的叶节点对象, 它没有子节点,用于实现抽象构件角色中声明的公共接口。 */ //商品---树叶 public class Goods implements Articles {
private String name;//商品名称 private int quantity;//购买商品的数量 private float unitPrice;//商品单价 @Override public float calculation() {//计算商品价格 //商品个数乘以单价 return quantity*unitPrice; } @Override public void show() {//显示商品信息 System.out.println( "数量:"+this.quantity +"\t单价:"+this.unitPrice +"¥\t商品名称:"+this.name); } //为了快速传参 设计的有参数的构造函数 public Goods(String name, int quantity, float unitPrice) { super(); this.name = name; this.quantity = quantity; this.unitPrice = unitPrice; } } |
package chapter06.text1.sz; import java.util.ArrayList; import chapter06.text1.cgj.Articles; /** ⑶ 树枝构件(Composite)角色: 是组合中的分支节点对象, 它有子节点。 它实现了抽象构件角色中声明的接口, 它的主要作用是存储和管理子部件, 通常包含Add()、Remove()、GetChild()等方法。 */ //购物袋---树枝 public class Bags implements Articles { private String bname;//创建袋子的名称 //创建集合 存放商品 private ArrayList<Articles> bags = new ArrayList<Articles>(); @Override public float calculation() {//合计 float s = 0; for (Object obj : bags) { s += ((Articles)obj).calculation(); } return s; } @Override public void show() {//显示 for (Object obj : bags) { ((Articles)obj).show(); } } //创建构造函数 快速赋值 public Bags(String bname) { super(); this.bname = bname; } //add 向集合中添加数据 public void add(Articles articles) { bags.add(articles); } //remove 从集合中移除数据 public void remove(Articles articles) { bags.remove(articles); } //get 获取数据信息 public Articles getChild(int i) { return bags.get(i); } } |
package chapter06.text1.main; import chapter06.text1.sy.Goods; import chapter06.text1.sz.Bags; public class ShoopingTest { public static void main(String[] args) { float s=0; Bags bigbag, mediumBag,smallRedBag,whiBags; bigbag=new Bags("大袋子"); mediumBag=new Bags("中袋子"); smallRedBag=new Bags("红色小袋子"); whiBags=new Bags("白色袋子");
Goods goods; goods=new Goods("螃蟹", 2, 40f);//80¥ smallRedBag.add(goods);//小红
goods=new Goods("香螺",10, 30f);//300¥ smallRedBag.add(goods);//小红
goods=new Goods("虾", 2, 20f);//40¥ smallRedBag.add(goods);//小红
goods=new Goods("红茶",1, 150f); whiBags.add(goods);//中 goods=new Goods("茶器", 1, 350f); whiBags.add(goods);//中 goods=new Goods("茅台", 1, 1550f); whiBags.add(goods);//中 mediumBag.add(smallRedBag); mediumBag.add(whiBags); bigbag.add(mediumBag);//大含中 System.out.println("您选购的商品有:"); bigbag.show(); s=bigbag.calculation(); System.out.println("小红袋子里的商品价格是:"+smallRedBag.calculation()); System.out.println("中_袋子里的商品价格是:"+mediumBag.calculation()); System.out.println("白_袋子里的商品价格是:"+whiBags.calculation());
System.out.println("袋子里面的商品一共多少钱?"); System.out.println(s+"元"); } } |
模式的应用场景 前面我们分析了组合模式的结构与特点,现在来分析它的以下应用场景: 1)在需要表示一个对象整体与部分的层次结构的场合。 2)要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。 |
模式的扩展 如果对前面介绍的组合模式中的树叶结点和树枝结点进行抽象,也就是说树叶结点和树枝结点还有子结点,这时组合模式就扩展成复杂的组合模式了,右边是其结构图: |
5.原型模式
模式的定义与特点
定义:用一个已经创建的实例作为原型,通过拷贝该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。如:Windows中的COPY操作。
特点:创建对象非常高效,根本无需知道对象创建的细节。
2.3.2 模式的结构与实现
1.模式的结构
原型模式包含以下主要角色:
⑴ 抽象原型类:规定了具体原型对象必须实现的接口。
⑵ 具体原型类:实现抽象原型类的clone方法,它可被复制。
⑶ 访问者类:使用具体原型类中的clone方法来复制新的对象。
模式的实现
原型模式的克隆分为浅克隆和深克隆,Java中的Object类提供了浅克隆的clone()方法,具体原型类只要实现Cloneable接口就可实现对象的浅克隆,这里的Cloneable接口就是抽象原型类。
原型管理器实例:
用原型模式模拟“孙悟空”复制自己。
分析:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空,这实际上是用到了原型模式,右边是其结构图:
主要角色:
- 抽象原型类;
package yuanxingmodle; //原型模式 /** * 原件 * * 复印件 */ //创建了一个 抽象的原型类 public interface MeiHouWang extends Cloneable {
public Object clone() ;//方法重写
public void doSomeThing(); } |
- 具体原型类;
package yuanxingmodle; public class FenShenWuKong implements MeiHouWang {
@Override public void doSomeThing() { System.out.println("美猴王分身------>>孙悟空!"); System.out.println("悟空在陪师傅西天取经!"); }
@Override public Object clone() { FenShenWuKong wukong=null; try { System.out.println("+++++++++++++++++++>>>>>"); wukong = (FenShenWuKong)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return wukong; } } |
package yuanxingmodle; public class FenShenLaosun implements MeiHouWang { @Override public void doSomeThing() { System.out.println("美猴王分身---------老孙!"); System.out.println("老孙去找观世音菩萨!去也!"); }
@Override public Object clone(){ FenShenLaosun laosun=null; try { System.out.println("------------->>>>>"); laosun = (FenShenLaosun)super.clone(); } catch (CloneNotSupportedException e) {
e.printStackTrace(); } return laosun; }
} |
原型管理器:
package yuanxingmodle; import java.util.HashMap; //创建分身管理器 public class FenShenManager {
private HashMap<String, MeiHouWang> map= new HashMap<String, MeiHouWang>();
public FenShenManager() { map.put("w", new FenShenWuKong()); map.put("s", new FenShenLaosun()); }
public void addFenshen(String key,MeiHouWang value) { map.put(key, value); }
public MeiHouWang getFenShen(String key) { MeiHouWang m=map.get(key); return (MeiHouWang)m.clone();//复制原件得来的复制分身 } } |
- 访问类
package yuanxingmodle; public class MainText { public static void main(String[] args) { FenShenManager fm=new FenShenManager();
MeiHouWang wk=(FenShenWuKong)fm.getFenShen("w"); wk.doSomeThing(); MeiHouWang sun=(FenShenLaosun)fm.getFenShen("s"); sun.doSomeThing(); } } |
效果:
6.装饰器模式
模式的定义与特点
定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
优点:
1)采用装饰模式扩展对象的功能比采用继承方式更加灵活;
2)可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
缺点:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
模式的结构与实现
1.模式的结构
装饰模式主要包含以下角色:
⑴ 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
⑵ 具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
⑶ 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
⑷ 具体装饰(Concrete Decorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
模式的应用场景
装饰模式通常在以下几种情况使用:
1)当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
2)当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
3)当对象的功能要求可以动态地添加,也可以再动态地撤销时。
package chapter05.text1; //抽象构建:定义一个抽象接口以规范准备接收附加责任的对象 public abstract class Drink {//创建饮品
public String dsc;//描述 private float price=0.0F;//价格 //-计算费用的抽象方法,由子类实现 public abstract float cost();
public String getDsc() { return dsc; } public void setDsc(String dsc) { this.dsc = dsc; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } } |
package chapter05.text1.gj; import chapter05.text1.Drink; /** * * 具体构件1: * 实现抽象构件,通过装饰角色为其添加一些职责 * */ public class Coffee extends Drink { @Override public float cost() { return super.getPrice(); } } |
package chapter05.text1.gj.ext; import chapter05.text1.gj.Coffee; /** * * 具体构件2: * 实现抽象构件,通过装饰角色为其添加一些职责 * */ public class Espresso extends Coffee { public Espresso() { setDsc("意式浓缩"); setPrice(30.0F); } } |
package chapter05.text1.gj.ext; import chapter05.text1.gj.Coffee; public class LongBlack extends Coffee { public LongBlack() { setDsc("美式咖啡"); setPrice(10.0F); } } |
package chapter05.text1.gj.ext; import chapter05.text1.gj.Coffee; public class ShortBlack extends Coffee { public ShortBlack() { setDsc("美式咖啡"); setPrice(9.0F); } } |
package chapter05.text1.decr; import chapter05.text1.Drink; /** * *抽象装饰角色:继承抽象构件Drink,并包含具体构件的实例, *可以通过其子类扩展具体构件的功能 */ public class Decorator extends Drink { //装饰者组合了抽象构件(被装饰者) private Drink obj;
public Decorator(Drink obj) { this.obj = obj; } @Override public float cost() { //计算费用,父类的费用+自己的费用 return super.getPrice()+obj.cost(); } @Override public String getDsc() { //输出描述:父类的描述+自己的描述 return dsc+" "+getPrice()+" "+obj.getDsc(); } } package chapter05.text1.decr.ext; import chapter05.text1.Drink; import chapter05.text1.decr.Decorator; //具体装饰1 public class Milk extends Decorator { public Milk(Drink obj) { super(obj); setDsc("牛奶"); setPrice(2.5F); } } |
package chapter05.text1.decr.ext; import chapter05.text1.Drink; import chapter05.text1.decr.Decorator; //具体装饰2 public class Suger extends Decorator { public Suger(Drink obj) { super(obj); setDsc("焦糖"); setPrice(3.0F); } } |
package chapter05.text1.decr.ext; import chapter05.text1.Drink; import chapter05.text1.decr.Decorator; //具体装饰3 public class Chocolate extends Decorator { public Chocolate(Drink obj) { super(obj); setDsc("巧克力"); setPrice(3.0F); } } |
package chapter05.text1.main; import chapter05.text1.Drink; import chapter05.text1.decr.ext.Chocolate; import chapter05.text1.decr.ext.Milk; import chapter05.text1.gj.ext.LongBlack; public class BarMain { public static void main(String[] args) { Drink d1=new LongBlack(); System.out.println("订单:"+d1.getDsc()+"\n价格="+d1.cost()+"¥\n\n"); //一份牛奶 d1=new Milk(d1); System.out.println("订单:"+d1.getDsc()+"\n价格="+d1.cost()+"¥\n\n"); //两份牛奶 d1=new Milk(d1); System.out.println("订单:"+d1.getDsc()+"\n价格="+d1.cost()+"¥\n\n"); //两份牛奶再加一份巧克力 d1=new Chocolate(d1); System.out.println("订单:"+d1.getDsc()+"\n价格="+d1.cost()+"¥\n\n"); } } |
订单:美式咖啡 价格=10.0¥ 订单:牛奶 2.5 美式咖啡 价格=12.5¥ 订单:牛奶 2.5 牛奶 2.5 美式咖啡 价格=15.0¥ 订单:巧克力 3.0 牛奶 2.5 牛奶 2.5 美式咖啡 价格=18.0¥ |
7.抽象工厂模式(Abstract Factory)模式
- 3.2.1 模式的定义与特点
定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
使用抽象工厂模式一般要满足以下条件:
1)系统中有多个产品族,每个具体工厂创建同一族的但属于不同等级结构的产品。
2)系统一次只可能消费其中某一族产品,即同族的产品一起使用。
优点:1)可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理;2)当增加一个新的产品族时不需要修改原代码,满足开闭原则。
缺点:当产品族中需要增加一个新等级的产品时,则所有的工厂类都需要进行修改。
- 3.2.2 模式的结构与实现
1.模式的结构
抽象工厂模式的主要角色有:
1)抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法newProduct(),可以创建多个不同等级的产品。
2)具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
3)抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
4)具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
- 3.2.3 模式的应用实例
【例3.2】 用抽象工厂模式设计农场类。
分析:农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等,所以本实例比前面介绍的畜牧场类复杂,必须用抽象工厂模式来实现,右边是其结构图:
- 3.2.4 模式的应用场景
抽象工厂模式通常适用以下场景:
1)当需要创建的对象是一系列相互关联或相互依赖的产品族时,如:电器工厂中的电视机、洗衣机、空调等。
2)系统中有多个产品族,但每次只使用其中的某一族产品。如:有的粉丝只喜欢穿李宁牌的衣、裤和鞋。
3)系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
- 3.2.5 模式的扩展
抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
1)当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
2)当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
另一方面,当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。
8. 建造者(Builder)模式
在软件开发过程中有时需要创建一个复杂的对象,这个复杂对象通常由多个子部件按一定的步骤组合而成。如:电脑是由CPU、主板、内存条、硬盘、显卡、机箱、显示器、键盘、鼠标等部件组装而成的,采购员不可能自己去组装电脑,而是将电脑的配置要求告诉老板,老板指挥技术人员去组装电脑,然后再交给要买电脑的采购员。生活中这样的例子很多,它们都是由多个部件构成的,各个部件可以灵活选择,但其创建步骤都大同小异。这类产品的创建无法用前面介绍的工厂模式描述,只有建造者模式可以很好地描述该类产品的创建。
- 3.3.1 模式的定义与特点
定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
优点:1)各个具体的建造者相互独立的,有利于系统的扩展;
2)客户端不必知道产品内部组成的细节,便于控制细节风险。
缺点:1)产品的组成部分必须相同,这限制了其使用范围;
2)如果产品的内部变化复杂,该模式会增加很多的建造者类。
- 3.3.2 模式的结构与实现
1.模式的结构
建造者模式的主要角色有:
1)产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个组成部件。
2)抽象建造者(Builder):是一个包含创建产品各个子部件的抽象方法的接口,它通常还包含一个返回复杂产品的方法getResult()。
3)具体建造者(ConcreteBuilder):实现了Builder接口,完成复杂产品的各个部件的具体创建方法。
4)指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
- 3.3.2 模式的结构与实现
1.模式的结构
右边是其结构图:
2.模式的实现
//该模式的实现代码见下页:
- 模式的应用场景
建造者模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用:
1)创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
2)创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。
- 模式的扩展
建造者模式在应用过程中可以根据需要改变,如果创建的产品种类只有一种,只需要一个具体的建造者,这时可以省略掉抽象建造者,甚至可以省略掉指导者角色。
9.单例(Singleton)模式
- 模式的定义与特点
定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows中的任务管理器、回收站等。
特点:1)单例类只有一个实例对象;2)该单例对象必须由单例类自行创建;3)单例类对外提供一个访问该单例的全局访问点。
- 2.2.2 模式的结构与实现
1.模式的结构
单例模式的主要角色有:
⑴ 单例类:包含一个实例且能自行创建这个实例的类。
⑵ 访问者类:使用单例的类。
其结构图如下:
普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用它生成多个实例了。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例,有2种常见的实现形式。
- 第1种:懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用getInstance方法时才去创建这个单例。代码如下:
public class LazySingleton{
private static volatile LazySingleton instance=null;//保证instance在所有线程中同步
private LazySingleton(){ } //private避免类在外部被实例化
public static synchronized LazySingleton getInstance(){//getInstance方法前加同步
if(instance==null){ instance=new LazySingleton(); }
return instance;
}
}
- 第2种:饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在了。
public class HungrySingleton{
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){ }
public static HungrySingleton getInstance(){
return instance;
}
}
- 模式的应用实例
【例2.1】 用懒汉式单例模式模拟产生美国当今总统对象。
10.工厂方法(Factory Method)模式
- 模式的定义与特点
定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于GoF的23种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”,本书不介绍。
优点:1)用户知道具体工厂的名称就可得到所要的产品,无须知道产品的创建过程;2)增加新的产品时只需要添加具体产品类和对应的具体工厂类,无需修改源代码,满足开闭原则。
缺点:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
- 模式的结构与实现
1.模式的结构
工厂方法模式的主要角色有:
1)抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法newProduct()来创建产品。
2)具体工厂(Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
3)抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
4)具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
- 3.1.2 模式的结构与实现
1.模式的结构
右边是其结构图:
2.模式的实现
【例3.1】 用工厂方法模式设计畜牧场。
分析:有很多种类的畜牧场,如养马场用于养马,养牛场用于养牛。所以该实例用工厂方法模式比较适合,右边是其结构图:
11.代理(Proxy)模式
- 模式的定义与特点
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
优点:1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;2)代理对象可以扩展目标对象的功能;3)代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度。
缺点:1)在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢;2)增加了系统的复杂度。
- 模式的结构与实现
1.模式的结构
代理模式的主要角色有:
⑴ 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
⑵ 真实主题(RealSubject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是我们最终要引用的对象。
⑶ 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问或控制或扩展真实主题的功能。
其结构图如下:
- 模式的应用场景
1)远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问它实际访问的是网盘空间。
2)虚拟代理,这种方式通常用于要创建的目标对象开销很大时。比如下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
3)安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
4)智能指引,主要用于当调用目标对象时,代理附加一些额外的处理功能。比如增加计算真实对象的引用次数的功能,这样当该对象没有引用时,就可以自动释放它。
5)延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate中就存在属性的延迟加载和关联表的延时加载。
12.适配器(Adapter)模式
- 模式的定义与特点
定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类结构型模式和对象结构型模式2种。
优点:
1)客户端通过适配器可以透明地调用目标接口;
2)程序员不需要修改原有代码而重用现有的适配者类;
3)将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
缺点:对于类适配器来说,更换适配器的实现过程比较复杂。
- 模式的结构与实现
1.模式的结构
适配器模式包含以下主要角色:
⑴ 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
⑵ 适配者(Adaptee)类:是被访问和适配的现存组件库中的组件接口。
⑶ 适配器(Adapter)类:它一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
适配器模式分为:类适配器模式和对象适配器模式2种。
- 模式的应用场景
适配器模式通常适用以下场景:
1)以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
2)使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
- 模式的扩展
适配器模式可扩展为双向适配器模式,双向适配器类既可以把适配者接口转换成目标接口,也可以把目标接口转换成适配者接口,右边是其结构图:
- 模式的应用实例
【例4.2】 用适配器模式模拟新能源汽车的发动机。
分析:新能源汽车的发动机有电能发动机和光能发动机等,各种发动机的驱动方法不同,客户端希望用统一的发动机驱动方法drive()访问这两种发动机,所以要为这些发动机定义适配器,右边是其结构图: