宋航-设计模式.ppt_第1页
宋航-设计模式.ppt_第2页
宋航-设计模式.ppt_第3页
宋航-设计模式.ppt_第4页
宋航-设计模式.ppt_第5页
已阅读5页,还剩149页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、设 计 模 式,主 讲:宋 航,设计模式,程序设计是思维具体化的一种方式,是思考如何解决问题的过程,设计模式是在解决问题的过程中,一些良好思路的经验集成,最早讲设计模式,人们总会提到 Gof 的著作,它最早将经典的 23 种模式集合在一起说明,对后期学习程序设计,尤其是对从事对象导向程序设计的人们起了莫大的影响。 后来设计模式一词被广泛的应用到各种经验集成,甚至还有反模式(AntiPattern),反模式教导您如何避开一些常犯且似是而非的程序设计思维。,开闭原则,“开闭”原则讲的是一个软件实体应当对扩展开放,对修改关闭。这个原则说的是, 在设计一个模块的时候,应当使这个模块可以在不被修改的前提

2、不被扩展。换言之,应当可以在不必修改源代码的情况下改受这个模块的行为。 通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化 中的软件系统有一定的适应性和灵活件。 已有的软件模块,特别是最重要的抽象层模块不能再修改,这就使变化小的软件 系统有一定的稳定性和延续性。 解决问题的关键在于抽象化。在像Java语言这样的而向对象的编程语言里面,可以 给系统定义出一个一劳永逸、不再更改的抽象设计,此设计允许有无穷无尽的行为在实现.,里氏代换原则,里氏代换原则的严格表达是:一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它 根本不能察觉出基类对象和子类对象的区别。 里氏代

3、换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才能真正被复用,而衍生类也才能够在基类的基础上增加新的行为。,依赖倒转原则,在面向对象的系统里面,两个类之间可以发生三种不同的耦合关系: 零耦合关系:如果两个类没有耦合关系,就称之为零耦合。 具体耦合关系:具体耦合发生在两个具体的(可实例化 的)类之间,经出一个类对另外一个具体类的直接引用造成。 抽象耦合关系:抽象耦合关系发生在一个具体类和一个抽象类之间,使两个必须发生关系的类之间存有最大的灵活性。 简单地说,依赖倒转原则要求客户端依赖于抽象耦合,接口隔离原则,一个接口对应一个角色,而不是多个角色。 定制服务

4、也一个重要的设计原则。它的意思是说,如果客户端仅仅需要某一些方法的话,那么就应当向客户端提供这些方法,而不要提供不需 要的方法。,迪米特原则,在软件系统中,一个模块设计得好不好的标志,就是该模块在多大的程度上将自己的内部数据和其他与实现有关的细节隐藏起来。这一概念就是“信息的隐藏”,或者叫做“封装”,也就是大家熟悉的软件设计的基本教义之一。 信息的隐藏非常重要的原因在于,它可以使各个子系统之间脱耦。这种脱耦化可以有效地加快系统的开 发过程,因为可以独立地同时开发各个模块。它可以使维护过程变得容易,因为所有的模块都容易读懂,特别是不必担心对其他模块的影响。 一旦确认某一个模块是性能的障碍时,设计

5、人员可以到对这个模块本身进行优化,而不必担心影响到其他的模块。 信息的隐藏可以促进软件的复用。一个系统的规模越大,信息的隐藏就越是重 要,而信息隐藏的威力也就越明显。,Simple Factory模式(又称Static Factory模式),假设有一个八音盒工厂,购买八音盒的客人不用知道八音盒是如何制作的,他只要知道如何播放八音盒就可以了,以 UML 类别图来表示以上的概念:,例子,public interface IMusicBox public void play(); public class PianoBox implements IMusicBox public void play(

6、) System.out.println(拨放钢琴音乐:); public class ViolinBox implements IMusicBox public void play() System.out.println(拨放小提琴音乐_); ,例子,public class MusicBoxFactory public static IMusicBox createMusicBox(String name) throws InstantiationException, IllegalAccessException, ClassNotFoundException / 这边使用的是Java的

7、Reflection机制来产生实例 / 不过客户端不用管啦 / 以后就算改变了程序,客户端程序是不用更改的 return (IMusicBox) Class.forName(name).newInstance(); ,例子,public class MusicBoxDemo public static void main(String args) throws Exception playMusicBox(MusicBoxFactory.createMusicBox(PianoBox); playMusicBox(MusicBoxFactory.createMusicBox(ViolinBox

8、); public static void playMusicBox(IMusicBox musicBox) musicBox.play(); 由于客户端只依赖于IMusicBox接口,所以即使您日后改变了createMusicBox()中的实作方式,对客户端是一点影响也没有的。,Simple Factory的类别结构,抽象工厂,产品族,采用抽象工厂模式设计出的系统类图,在什么情形下应当使用抽象工厂模式,(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节。这对于所有形态的工厂模式都是重要的; (2)这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品;(上面这一条叫做抽

9、象工厂模式的原始用意。) (3)同属于同一个产品族的产品是在一起使用的,这一约束必须要在系统的设计中体现出来; (4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。,类图,系统的设计图,抽象工厂模式的另一个例子,这个例子描述微型计算机配件的生产。这个系统所需要的产品族有两个,一个系列是PC,或称IBM 及IBM 克隆机系列;另一个系列是MAC,或称MacIntosh 系列。产品等级结构也有两个,一个是RAM,另一个是CPU。,系统的设计图,“开-闭”原则,“开-闭”原则要求一个软件系统可以在不修改原有代码的情况下,通过扩展达到增强其功能的目的。对于一个涉及到多个

10、产品等级结构和多个产品族的系统,其功能的增强不外乎两个方面: 增加新的产品族; 增加新的产品等级结构。 那么抽象工厂模式是怎样支持这两方面功能增强的呢?,增加新的产品族,在产品等级结构的数目不变的情况下,增加新的产品族,就意味着在每一个产品等级结构中增加一个(或者多个)新的具体(或者抽象和具体)产品角色。 由于工厂等级结构是与产品等级结构平行的登记机构,因此,当产品等级结构有所调整时,需要将工厂等级结构做相应的调整。现在产品等级结构中出现了新的元素,因此,需要向工厂等级结构中加入相应的新元素就可以了。 换言之,设计师只需要向系统中加入新的具体工厂类就可以了,没有必要修改已有的工厂角色或者产品角

11、色。,构造模式,您想要建立一个迷宫产生程序,迷宫使用二维数组来定义,0表示道路,1表示墙,2表示宝物,根据所定义的二维迷宫数组,您想要程序自动产生各种不同材质的迷宫,例如砖墙迷宫,钻石迷宫等等。 您可以在程序中定义两个角色,一个是指导迷宫建立的Director角色,一个是按照指导者指示建立迷宫的Builder角色,Director根据定义的迷宫数组来指导Builder,只要更换Builder,就可以完成不同材质的迷宫。,构造模式,public class MazeDirector private int maze; private IMazeBuilder mazeBuilder; publi

12、c void setMaze(int maze) this.maze = maze; public void setMazeBuilder(IMazeBuilder mazeBuilder) this.mazeBuilder = mazeBuilder; public void buildMaze() for(int i = 0; i maze.length; i+) for(int j = 0; j mazei.length; j+) / 由于mazeBuilder是IMazeBuilder型态 / 所以无论Builder实例为何,这边的程序都无需变动 switch (mazeij) cas

13、e 0: mazeBuilder.createRoadBlock(); break; case 1: mazeBuilder.createWallBlock(); break; case 2: mazeBuilder.createTreasureBlock(); break; default: System.out.println(undefined); mazeBuilder.nextRow(); ,public interface IMazeBuilder public void createRoadBlock(); public void createWallBlock(); publi

14、c void createTreasureBlock(); public void nextRow(); public class SolidMazeBuilder implements IMazeBuilder public void createWallBlock() System.out.print(); public void createRoadBlock() System.out.print(); public void createTreasureBlock() System.out.print($ ); public void nextRow() System.out.prin

15、tln(); ,public class DiamondMazeBuilder implements IMazeBuilder public void createWallBlock() System.out.print(); public void createRoadBlock() System.out.print(); public void createTreasureBlock() System.out.print(* ); public void nextRow() System.out.println(); ,public class Main public static voi

16、d main(String args) int maze = 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 2, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1; MazeDirector mazeDirector = new MazeDirector(); mazeDirector.setMaze(maze); System.out.println(Build SolidMaze.); mazeDire

17、ctor.setMazeBuilder(new SolidMazeBuilder(); mazeDirector.buildMaze(); System.out.println(Build DiamondMaze.); mazeDirector.setMazeBuilder( new DiamondMazeBuilder(); mazeDirector.buildMaze(); ,构造模式,简单来说,建筑者模式适用的场合,在于使得您可以依赖抽象的建筑蓝图,而实际建造时可以使用不同的实例,这是其之所以命为Builder的原因。,Factory Method模式,Factory Method模式在

18、一个抽象类中留下某个创建组件的抽象方法没有实作,其它与组件操作相关联的方法都先依赖于组件所定义的接口,而不是依赖于组件的实现,当您的成品中有一个或多个组件无法确定时,您先确定与这些组件的操作接口,然后用组件的抽象操作接口先完成其它的工作,组件的实现则推迟至实现组件接口的子类完成,一旦组件加入,即可完成您的成品。 假设您要完成一个文件编辑器,您希望这个编辑器可以适用于所有类型的档案编辑,例如RTF、DOC、TXT等等,尽管这些文件有着不同的格式,您先确定的是这些文件必然具备的一些操作接口,例如储存、开启、关闭等等,您用一个IDocument类型来进行操作,这么一来这个框架就无需考虑实际的储存、开

19、启等细节是如何进行的。,Factory Method模式,Factory Method模式,public abstract class AbstractEditor private IDocument document; public abstract IDocument createDocument(); public void newDocument() document = createDocument(); document.open(); public void saveDocument() if(document != null) document.save(); public v

20、oid closeDocument() if(document != null) document.close(); ,Factory Method模式,public interface IDocument public void open(); public void save(); public void close(); public class RTFEditor extends AbstractEditor public IDocument createDocument() return new RTFDocument(); ,Factory Method模式,public clas

21、s RTFDocument implements IDocument public RTFDocument() System.out.println(建立RTF文件); public void open() System.out.println(开启文件); public void save() System.out.println(储存文件); public void close() System.out.println(关闭文件); ,Factory Method,Factory Method中的AbstractOperator中拥有一个抽象的factoryMethod()方法,它负责生成

22、一个IProduct类型的对象,由于目前还不知道将如何实现这个类型,所以将之推迟至子类别中实现,在AbstractOperator中先实现IProduct操作接口沟通的部份,只要接口统一了,利用多型操作即可完成各种不同的IProduct类型之对象操作。,Prototype模式,您从图书馆的期刊从发现了几篇您感兴趣的文章,由于这是图书馆的书,您不可以直接在书中作记号或写字,所以您将当中您所感兴趣的几个主题影印出来,这下子您就可在影印的文章上画记重点。 Prototype模式的作用有些类似上面的描述,您在父类别中定义一个clone()方法,而在子类别中重新定义它,当客户端对于所产生的对象有兴趣并想

23、加以利用,而您又不想破坏原来的对象,您可以产生一个对象的复本给它。,Prototype模式,Prototype具有展示的意味,就像是展览会上的原型车款,当您对某个车款感兴趣时,您可以购买相同款示的车,而不是车展上的车。 在软件设计上的例子会更清楚的说明为何要进行对象复制,假设您要设计一个室内设计软件,软件中有一个展示家具的工具列,您只要点选工具列就可以产生一个家具复本,例如一张椅子或桌子,您可以拖曳这个复制的对象至设计图中,随时改变它的位置、颜色等等,当您改变设计图中的对象时,工具列上的原型工具列是不会跟着一起改变的,这个道理是无需解释的。,Prototype模式,Prototype模式,Pr

24、ototype模式的重点在于clone(),它负责复制对象本身并传回,但这个clone()本身在实作上存在一些困难,尤其是当对象本身又继承另一个对象时,如何确保复制的对象完整无误,在不同的程序语言中有不同的作法。在Java中的作法是透过实作一个Cloneable接口,它只是一个声明的界面,并无规定任何实作的方法,您的目的是改写Object的clone ()方法,使其具备有复制对象的功能。,Prototype模式,public abstract class AbstractFurniture implements Cloneable public abstract void draw(); /

25、在Design Pattern上,以下的clone是抽象未实作的 / 实际上在Java中class都继承自Object / 所以在这边我们直接重新定义clone() / 这是为了符合Java现行的clone机制 protected Object clone() throws CloneNotSupportedException return super.clone(); ,import java.awt.*;public class CircleTable extends AbstractFurniture protected Point center; public void setCent

26、er(Point center) this.center = center; protected Object clone () throws CloneNotSupportedException Object o = super.clone(); if(this.center != null) (CircleTable) o).center = (Point) center.clone(); return o; public void draw() System.out.println(t圆桌t中心:( + center.getX() + , + center.getY()+ ); ,imp

27、ort java.awt.*;public class SquareTable extends AbstractFurniture protected Rectangle rectangle; public void setRectangle(Rectangle rectangle) this.rectangle = rectangle; protected Object clone () throws CloneNotSupportedException Object o = super.clone(); if(this.rectangle != null) (SquareTable) o)

28、.rectangle = (Rectangle) rectangle.clone(); return o; public void draw() System.out.print(t方桌t位置:( + rectangle.getX() + , + rectangle.getY()+ ); System.out.println( / 宽高:( + rectangle.getWidth() + , + rectangle.getHeight()+ ); ,import java.util.*;public class House private Vector vector; public Hous

29、e() vector = new Vector(); public void addFurniture(AbstractFurniture furniture) vector.addElement(furniture); System.out.println(现有家具.); Enumeration enumeration = vector.elements(); while(enumeration.hasMoreElements() AbstractFurniture f = (AbstractFurniture) enumeration.nextElement(); f.draw(); Sy

30、stem.out.println(); ,import java.awt.*;public class Application private AbstractFurniture circleTablePrototype; public void setCircleTablePrototype(AbstractFurniture circleTablePrototype) this.circleTablePrototype = circleTablePrototype; public void runAppExample() throws Exception House house = new

31、 House(); CircleTable circleTable = null; / 从工具列选择一个家具加入房子中 circleTable = (CircleTable) circleTablePrototype.clone(); circleTable.setCenter(new Point(10, 10); house.addFurniture(circleTable); / 从工具列选择一个家具加入房子中 circleTable = (CircleTable) circleTablePrototype.clone(); circleTable.setCenter(new Point(

32、20, 30); house.addFurniture(circleTable); public static void main(String args) throws Exception Application application = new Application(); application.setCircleTablePrototype(new CircleTable(); application.runAppExample(); ,Prototype,Java中的clone()方法是继承自Object,AbstractFurniture的子类别则override这个clone(

33、)方法,以复制其本身并传回。,单例(Singleton)模式,单例单例显然单例模式的要点有三个; 一是某各类只能有一个实例; 二是它必须自行创建这个事例; 三是它必须自行向整个系统提供这个实例。在下面的对象图中,有一个“单例对象”,而“客户甲”、“客户乙”和“客户丙”是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。,资源管理,在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。 每台计算机可以有若干传

34、真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。 这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。,单例模式的结构,单例模式有以下的特点: 单例类只可有一个实例。 单例类必须自己创建自己这惟一的实例。 单例类必须给所有其他对象提供这一实例。 虽然单例模式中的单例类被限定只能有一个实例,但是单例模式和单例类可以很容易被推广到任意且有限多个实例的情况,

35、这时候称它为多例模式(Multiton Pattern)和多例类.,饿汉式单例类,public class EagerSingleton private static final EagerSingleton m_instance = new EagerSingleton(); private EagerSingleton() public static EagerSingleton getInstance() return m_instance; 在这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造子会被调用。这时候,单例类的惟一实例就被创建出来了。 Java 语言中

36、单例类的一个最重要的特点是类的构造子是私有的,从而避免外界利用构造子直接创建出任意多的实例。值得指出的是,由于构造子是私有的,因此,此类不能被继承。,懒汉式单例类,public class LazySingleton private static LazySingleton m_instance = null; private LazySingleton() synchronized public static LazySingleton getInstance() if (m_instance = null) m_instance = new LazySingleton(); return

37、m_instance; 读者可能会注意到,在上面给出懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。,使用单例模式的条件,使用单例模式有一个很重要的必要条件: 在一个系统要求一个类只有一个实例时才应当使用单例模式。 反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。,错误例子一,问:我的一个系统需要一些“全程”变量。学习了单例模式后,我发现可以使用一个单例类盛放所有的“全程”变量。请问这样做对吗? 答:这样做是违背单例模式的用意的。单例模式只应当在有真正的“单一实例”的需求时才可使用。一个设计得当的系统不应当有所谓的“全程”变量,这些变量应当放到它们所描述的实体

38、所对应的类中去。将这些变量从它们所描述的实体类中抽出来,放到一个不相干的单例类中去,会使得这些变量产生错误的依赖关系和耦合关系。,有状态的单例类,一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable)单例对象。 有状态的可变的单例对象常常当做状态库(repositary)使用。比如一个单例对象可以持有一个int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码。 当然,一个单例类可以持有一个聚集,从而允许存储多个状态。,没有状态的单例类,另一方面,单例类也可以是没有状态的(stateless),仅用做提供工具性函数的对象

39、。 既然是为了提供工具性函数,也就没有必要创建多个实例,因此使用单例模式很合适。一个没有状态的单例类也就是不变(Immutable)单例类.,Java 的Runtime 对象,在Java 语言内部,java.lang.Runtime 对象就是一个使用单例模式的例子。在每一个Java应用程序里面,都有惟一的一个Runtime 对象。通过这个Runtime 对象,应用程序可以与其运行环境发生相互作用。 Runtime 类提供一个静态工厂方法getRuntime():, 通过调用此方法,可以获得Runtime 类惟一的一个实例. Runtime 对象通常的用途包括:执行外部命令;返回现有内存即全部内

40、存;运行垃圾收集器;加载动态库等。 import java.io.*; public class CmdTest public static void main(String args) throws IOException Process proc = Runtime.getRuntime().exec(notepad.exe); ,什么是不完全的单例类,package com.javapatterns.singleton.demos; public class LazySingleton public LazySingleton() 造成这种情况出现的原因有以下几种可能: (1)初学者的错

41、误。许多初学者没有认识到单例类的构造子不能是公开的,因此犯下这个错误。有些初学Java 语言的学员甚至不知道一个Java 类的构造子可以不是公开的。 (2)当初出于考虑不周,将一个类设计成为单例类,后来发现此类应当有多于一个的实例。为了弥补错误,干脆将构造子改为公开的 (3)设计师的Java 知识很好,而且也知道单例模式的正确使用方法,但是还是有意使用这种不完全的单例模式,因为他意在使用一种“改良”的单例模式。,Object Adapter,您的计算机是个旧计算机,新的鼠标都在使用USB接口了,而您的计算机上并没有USB,而只有一个PS2接口,这时您可以使用一个USB转PS2的接头作为转换,这

42、样您的计算机就可以使用新鼠标了(当然您也可以使用USB扩充卡,意思是相同的)。 类似的概念,有时候您想在原来的程序中加入一个外部组件,例如一个类别,但是这个类别与您目前所设计的程序在接口上并不一致,为了让这个外部类与原程序的接口一致,您必须使用一个类别作为中介来配接它们,这时您可以采用Adapter模式。 举个例子来说,在Java 1.0中有个Enumeration,您在这个版本发行之后,使用它来设计了一个MessageApplication,Object Adapter,import java.util.*;public class MessageApplication public voi

43、d showAllMessage(Enumeration enum) Object msg; while(enum.hasMoreElements() msg = enum.nextElement(); System.out.println(msg); ,Object Adapter,import java.util.*;public class MessageClient private MessageApplication msgApp; public void run() Vector vector = new Vector(); for(int i = 0; i 10; i+) vec

44、tor.addElement(物件 + i); msgApp = new MessageApplication(); msgApp.showAllMessage(vector.elements(); public static void main(String args) MessageClient msgClient = new MessageClient(); msgClient.run(); ,Object Adapter,import java.util.*;public class IteratorAdapter implements Enumeration private Iter

45、ator iterator; IteratorAdapter(Iterator iterator) this.iterator = iterator; / 转接界面 public boolean hasMoreElements() return iterator.hasNext(); public Object nextElement() throws NoSuchElementException return iterator.next(); ,import java.util.*;public class MessageClient / We could still use Message

46、Application private Enumeration iteratorAdapter; public void run() List arrayList = new ArrayList(); for(int i = 0; i 10; i+) arrayList.add(物件 + i); iteratorAdapter = new IteratorAdapter(arrayList.iterator(); / We could still use MessageApplication MessageApplication msgApp = new MessageApplication(

47、); msgApp.showAllMessage(iteratorAdapter); public static void main(String args) MessageClient msgClient = new MessageClient(); msgClient.run(); ,Object Adapter,Class Adapter,Adapter模式的另一种作法是Class Adapter模式,在这个模式下,Adapter直接继承Adaptee(要引进的新类别),以拥有当中的成员及方法,在C+中的话可以这么作:,Class Adapter,如果有SomeClass类别与Other

48、Class类别,您想要SomeAndOther类别可以同时拥有SomeClass类别与 OtherClass类别中已定义好的操作,并可以进行多型操作,在C+中可以用多重继承来达到,但在Java中显然的无法使用多重继承,怎么办?您可以在设计上先绕个弯,先使用两个接口分别定义好SomeClass与OtherClass两个类别的公开方法,例如: public interface ISome public void doSome();public interface IOther public void doOther();,Class Adapter,接着让SomeClass与OtherClass分

49、别实作两个接口: public class SomeClass implements ISome public void doSome() . public class OtherClass implements IOther public void doOther() . ,Class Adapter,SomeAndOther如何同时拥有两个SomeClass与OtherClass类别已定义好的操作?并可以多型操作?SomeAndOther可以继承其中之一,并拥有其中之一,例如: public class SomeAndOther extends SomeClass implements I

50、Other private IOther other = new OtherClass(); public void doOther() other.doOther(); 虽不满意,但至少解决了目前的问题,当然这边只是其中一例,毕竟C+是C+,Java是Java,两者语法并不是一对一的关系,视实际需求还可以变化一下。,设计模式之Bridge,Bridge定义 :将抽象和行为划分开来,各自独立,但能动态的结合. 为什么使用?通常,当一个抽象类或接口有多个具体实现(concrete subclass),这些concrete之间关系可能有以下两种:1. 这多个具体实现之间恰好是并列的, 2.实际应用

51、上,常常有可能在这多个concrete class之间有概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象和行为.,Bridge的意义,在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或说将它们之间的强关联改换成弱关联。 所谓强关联,就是在编译时期已经确定的,无法在运行时期动态改变的关联; 所谓弱关联,就是可以动态地确定并且可以在运行时期动态地改变的关联。 显然继承关系是强关联,而聚合关系是弱关联。将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联换成为弱关 联。因此,桥梁模式中的所谓脱耦,就是指在一个软件

52、系统的抽象化和实现化之间使用组合聚合关系而不是继承关系,从而使两者可以相对独立地变化。,问题,空中巴士、波音和麦道都是飞机制造商, 它们都生产载客飞机和载货飞机:现在需要设计一个系 统,描述这些飞机制造商以及它们所制造的飞机种类。,设计1,设计2,Composite定义,将对象以树形结构组织起来,以达成“部分整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. Composite比较容易理解,想到Composite就应该想到树形结构图。组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法

53、的对象并实现调用执行。可以用牵一动百来形容。 所以Composite模式使用到Iterator模式,和Chain of Responsibility模式类似。,Composite好处,1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。 2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。,如何使用Composite?,首先定义一个接口或抽象类,这是设计模式通用方式了,其他设计模式对接口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Com

54、posite组合体的对象们(或称部件Component)。,抽象类,public abstract class Equipmentprivate String name; /网络价格public abstract double netPrice();/折扣价格public abstract double discountPrice();/增加部件方法public boolean add(Equipment equipment) return false; /删除部件方法public boolean remove(Equipment equipment) return false; /注意这里,

55、这里就提供一种用于访问组合体类的部件方法。public Iterator iter() return null; public Equipment(final String name) =name; ,Disk-零件,public class Disk extends Equipment public Disk(String name) super(name); /定义Disk网络价格为1 public double netPrice() return 1.; /定义了disk折扣价格是0.5 对折。 public double discountPrice() return

56、.5; ,组合体,abstract class CompositeEquipment extends Equipmentprivate int i=0; /定义一个Vector 用来存放儿子private List equipment=new ArrayList();public CompositeEquipment(String name) super(name); public boolean add(Equipment equipment) this.equipment.add(equipment); return true; public double netPrice() doubl

57、e netPrice=0.;Iterator iter=equipment.iterator();for(iter.hasNext()netPrice+=(Equipment)iter.next().netPrice();return netPrice;,public double discountPrice() double discountPrice=0.;Iterator iter=equipment.iterator();for(iter.hasNext()discountPrice+=(Equipment)iter.next().discountPrice();return disc

58、ountPrice; /注意这里,这里就提供用于访问自己组合体内的部件方法。/上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元素.public Iterator iter()return equipment.iterator() ;/重载Iterator方法 public boolean hasNext() return iequipment.size(); /重载Iterator方法 public Object next() if(hasNext() return equipment.elementAt(i+);else throw new NoSuchElementException(); ,源代码,我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子Cab

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论