软件建模训练(6-2) 设计模式 张静 南京理工大学计算机科学与工程学院
设计模式概论 设计模式的诞生与发展 设计模式的定义与分类 GoF设计模式简介 设计模式的优点
软件设计模式的诞生与发展 软件模式 1990年,软件工程界开始关注Christopher Alexander等在这一住宅、公共建筑与城市规划领域的重大突破,最早将该模式的思想引入软件工程方法学的是1991-1992年以“四人组(Gang of Four,GoF,分别是Erich Gamma, Richard Helm, Ralph Johnson和John Vlissides)”自称的四位著名软件工程学者,他们在1994年归纳发表了23种在软件开发中使用频率较高的设计模式,旨在用模式来统一沟通面向对象方法在分析、设计和实现间的鸿沟。
设计模式的诞生与发展 Gang of Four
设计模式的诞生与发展 Erich Gamma 苏黎世大学计算机科学博士,是Eclipse、 JUnit 等项目主要技术负责人之一。 墨尔本大学计算机科学博士,原IBM 研究员,现在波士顿顾问集团供职。 Richard Helm 康奈尔大学计算机科学博士, 伊利诺伊大学教授。 Ralph Johnson 斯坦福大学计算机科学博士, 原IBM研究员,于2005年11月24日因脑瘤去世,享年44岁。 John Vlissides
设计模式的诞生与发展 软件模式 软件模式是将模式的一般概念应用于软件开发领域,即软件开发的 总体指导思路或参照样板。软件模式并非仅限于设计模式,还包括 架构模式、分析模式和过程模式等,实际上,在软件生存期的每一 个阶段都存在着一些被认同的模式。 软件模式可以认为是对软件开发这一特定“问题”的“解法”的某种统一 表示,它和Alexander所描述的模式定义完全相同,即软件模式等于 一定条件下的出现的问题以及解法。软件模式的基础结构由4个部分 构成:问题描述、前提条件(环境或约束条件)、解法和效果。
设计模式的诞生与发展 设计模式的发展 1987年,Kent Beck和Ward Cunningham借鉴Alexander的模式思想在程序开发 中开始应用一些模式 ,在OOPSLA会议上发表了他们的成果。 1990年,OOPSLA与ECOOP联合举办,Erich Gamma和Richard Helm等人开始 讨论有关模式的话题(Bruce Anderson主持),“四人组” 正式成立,并开始着手 进行设计模式的分类整理工作。 1991 年,OOPSLA,Bruce Anderson主持了首次针对设计模式的研讨会 。 1992 年,OOPSLA ,Anderson再度主持研讨会,模式已经逐渐成为人们讨论 的话题。 注: OOPSLA (Object-Oriented Programming, Systems, Languages & Applications, 面向对象编程、系统、语言和应用大会),编程语言及软件工程国际顶级会 议,2010年改为SPLASH --- Systems, Programming, Languages and Applications: Software for Humanity
设计模式的诞生与发展 设计模式的发展 1993年,Kent Beck 和 Grady Booch 赞助了第一次关于设计模式的会议,这 个设计模式研究组织发展成为著名的Hillside Group研究组。 1994 年,由Hillside Group发起,在美国伊利诺伊州(Illinois)的Allerton Park召 开了第1届关于面向对象模式的世界性会议,名为PLoP(Pattern Languages of Programs, 编程语言模式会议),简称PLoP‘94。 1995年,PLoP‘95 仍在伊利诺伊州的Allerton Park举行 ,“四人组”出版了《设 计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)一书,本书成为1995年最抢手的面向对象书籍,也 成为设计模式的经典书籍。
设计模式的诞生与发展 设计模式的发展 从1995年至今,设计模式在软件开发中得以广泛应 用,在Sun的Java SE/Java EE平台和Microsoft的.net平 台设计中就应用了大量的设计模式。 诞生了越来越多的与设计模式相关的书籍和网站, 设计模式也作为一门独立的课程或作为软件体系结 构等课程的重要组成部分出现在国内外研究生和大 学教育的课堂上。
设计模式的定义与分类 设计模式的定义 设计模式(Design Pattern)是一套被反复使用、多数人 知晓的、经过分类编目的、代码设计经验的总结, 使用设计模式是为了可重用代码、让代码更容易被 他人理解、保证代码可靠性。
设计模式的定义与分类 设计模式的基本要素 设计模式一般有如下几个基本要素:模式名称、问 题、目的、解决方案、效果、实例代码和相关设计 模式,其中的关键元素包括以下四个方面: 模式名称 (Pattern name) 问题 (Problem) 解决方案 (Solution) 效果 (Consequences)
设计模式的定义与分类 设计模式的分类 根据其目的(模式是用来做什么的)可分为创建型 (Creational),结构型(Structural)和行为型(Behavioral)三 种: 创建型模式主要用于创建对象。 结构型模式主要用于处理类或对象的组合。 行为型模式主要用于描述对类或对象怎样交互和怎样 分配职责。
设计模式的定义与分类 设计模式的分类 根据范围,即模式主要是用于处理类之间关系还是 处理对象之间的关系,可分为类模式和对象模式两 种: 类模式处理类和子类之间的关系,这些关系通过继承建立, 在编译时刻就被确定下来,是属于静态的。 对象模式处理对象间的关系,这些关系在运行时刻变化,更 具动态性。
GoF设计模式简介 范围\目的 创建型模式 结构型模式 行为型模式 类模式 工厂方法模式 (类)适配器模式 解释器模式 模板方法模式 对象模式 抽象工厂模式 建造者模式 原型模式 单例模式 (对象)适配器模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 职责链模式 命令模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 访问者模式
设计模式的优点 设计模式是从许多优秀的软件系统中总结出的成功的、能 够实现可维护性复用的设计方案,使用这些方案将避免我 们做一些重复性的工作,而且可以设计出高质量的软件系 统。 设计模式的主要优点如下: 设计模式融合了众多专家的经验,并以一种标准的形式供广大开发人员所 用,它提供了一套通用的设计词汇和一种通用的语言以方便开发人员之间 沟通和交流,使得设计方案更加通俗易懂。对于使用不同编程语言的开发 和设计人员可以通过设计模式来交流系统设计方案,每一个模式都对应一 个标准的解决方案,设计模式可以降低开发人员理解系统的复杂度。
设计模式的优点 设计模式使人们可以更加简单方便地复用成功的设计和 体系结构,将已证实的技术表述成设计模式也会使新系 统开发者更加容易理解其设计思路。设计模式使得重用 成功的设计更加容易,并避免那些导致不可重用的设计 方案。 设计模式使得设计方案更加灵活,且易于修改。 设计模式的使用将提高软件系统的开发效率和软件质量, 且在一定程度上节约设计成本。 设计模式有助于初学者更深入地理解面向对象思想,一 方面可以帮助初学者更加方便地阅读和学习现有类库与 其他系统中的源代码,另一方面还可以提高软件的设计 水平和代码质量。
补充:面向对象设计的原则 设计原则名称 设计原则简介 重要性 单一职责原则 (Single Responsibility Principle, SRP) 类的职责要单一,不能将太多的职责放在一个类中 ★★★★☆ 开闭原则 (Open-Closed Principle, OCP) 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一 个软件实体的基础上去扩展其功能 ★★★★★ 里氏代换原则 (Liskov Substitution Principle, LSP) 在软件系统中,一个可以接受基类对象的地方必然可以接受一 个子类对象 依赖倒转原则 (Dependency Inversion Principle, DIP) 要针对抽象层编程,而不要针对具体类编程 接口隔离原则 (Interface Segregation Principle, ISP) 使用多个专门的接口来取代一个统一的接口 ★★☆☆☆ 合成复用原则 (Composite Reuse Principle, CRP) 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚 至不使用继承关系 迪米特法则 (Law of Demeter, LoD) 一个软件实体对其他实体的引用越少越好,或者说如果两个类 不必彼此直接通信,那么这两个类就不应当发生直接的相互作 用,而是通过引入一个第三者发生间接交互 ★★★☆☆
创建模式 创建模式概述 简单工厂模式 工厂方法模式 原型模式
创建型模式 创建型模式概述 创建型模式(Creational Pattern)对类的实例化过程进行 了抽象,能够将软件模块中对象的创建和对象的使 用分离。为了使软件的结构更加清晰,外界对于这 些对象只需要知道它们共同的接口,而不清楚其具 体的实现细节,使整个系统的设计更加符合单一职 责原则。
创建型模式 创建型模式概述 创建型模式在创建什么(What),由谁创建(Who),何 时创建(When)等方面都为软件设计者提供了尽可能 大的灵活性。创建型模式隐藏了类的实例的创建细 节,通过隐藏对象如何被创建和组合在一起达到使 整个系统独立的目的。
创建型模式 创建型模式简介 简单工厂模式(Simple Factory) 工厂方法模式(Factory Method) 抽象工厂模式(Abstract Factory) 建造者模式(Builder) 原型模式(Prototype) 单例模式(Singleton)
简单工厂模式 模式动机 只需要知道水果的名字则可得到相应的水果
简单工厂模式 模式定义 简单工厂模式(Simple Factory Pattern):又称为静态 工厂方法(Static Factory Method)模式,它属于类创 建型模式。在简单工厂模式中,可以根据参数的不 同返回不同类的实例。简单工厂模式专门定义一个 类来负责创建其他类的实例,被创建的实例通常都 具有共同的父类。
简单工厂模式 模式结构
简单工厂模式 模式结构 简单工厂模式包含如下角色: Factory:工厂角色 Product:抽象产品角色 ConcreteProduct:具体产品角色
简单工厂模式 模式分析 分析如下代码: 代码复杂,难以维护 public void pay(String type) { if(type.equalsIgnoreCase("cash")) //现金支付处理代码 } else if(type.equalsIgnoreCase("creditcard")) //信用卡支付处理代码 else if(type.equalsIgnoreCase("voucher")) //代金券支付处理代码 else …… 代码复杂,难以维护
简单工厂模式 模式分析 重构后的代码: 抽象支付类 具体支付类 public abstract class AbstractPay { public abstract void pay(); } 具体支付类 public class CashPay extends AbstractPay { public void pay() //现金支付处理代码 }
简单工厂模式 模式分析 重构后的代码: 支付工厂 public class PayMethodFactory { public static AbstractPay getPayMethod(String type) if(type.equalsIgnoreCase("cash")) return new CashPay(); //根据参数创建具体产品 } else if(type.equalsIgnoreCase("creditcard")) return new CreditcardPay(); //根据参数创建具体产品 ……
简单工厂模式 模式分析 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者 修改起来都相对容易。 在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方便, 可通过类名直接调用,而且只需要传入一个简单的参数即可,在实际开发 中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修 改参数时无须修改任何Java源代码。 简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要 修改工厂类的判断逻辑,这一点与开闭原则是相违背的。 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数, 就可以获取你所需要的对象,而无须知道其创建细节。
简单工厂模式 模式实例与解析 实例一:简单电视机工厂 某电视机厂专为各知名电视机品牌代工生产各类电视机, 当需要海尔牌电视机时只需要在调用该工厂的工厂方法 时传入参数“Haier”,需要海信电视机时只需要传入参数 “Hisense”,工厂可以根据传入的不同参数返回不同品牌 的电视机。现使用简单工厂模式来模拟该电视机工厂的 生产过程。
简单工厂模式 模式实例与解析 实例一:简单电视机工厂
简单工厂模式 模式应用 public final static DateFormat getDateInstance(); (1) 在JDK类库中广泛使用了简单工厂模式,如工具类 java.text.DateFormat,它用于格式化一个本地日期或者时间。 (2) Java加密技术 (参考: DESEncrypt.java) public final static DateFormat getDateInstance(); public final static DateFormat getDateInstance(int style); public final static DateFormat getDateInstance(int style,Locale locale); //获取不同加密算法的密钥生成器 KeyGenerator keyGen=KeyGenerator.getInstance("DESede"); //创建密码器 Cipher cp=Cipher.getInstance("DESede");
简单工厂模式 模式优缺点 简单工厂模式的优点 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的 实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品; 简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂 类用于创建对象。 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所 对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使 用者的记忆量。 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加 新的具体产品类,在一定程度上提高了系统的灵活性。
简单工厂模式 模式优缺点 简单工厂模式的缺点 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都 要受到影响。 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统 的复杂度和理解难度。 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较 多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继 承的等级结构。
工厂方法模式 简单工厂模式的不足 在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品 类进行实例化的中心位置,它知道每一个产品对象的创建细节, 并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当 有新产品要加入到系统中时,必须修改工厂类,加入必要的处理 逻辑,这违背了“开闭原则”。在简单工厂模式中,所有的产品都 是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具 体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩 展性,而工厂方法模式则可以很好地解决这一问题。
工厂方法模式 模式动机 考虑这样一个系统,按钮工厂 类可以返回一个具体的按钮实 例,如圆形按钮、矩形按钮、 菱形按钮等。在这个系统中, 如果需要增加一种新类型的按 钮,如椭圆形按钮,那么除了 增加一个新的具体产品类之外, 还需要修改工厂类的代码,这 就使得整个设计在一定程度上 违反了“开闭原则”。
工厂方法模式 模式动机 现在对该系统进行修改,不再设计一个按钮工厂类来统一负责所有 产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完 成,我们先定义一个抽象的按钮工厂类,再定义具体的工厂类来生 成圆形按钮、矩形按钮、菱形按钮等,它们实现在抽象按钮工厂类 中定义的方法。这种抽象化的结果使这种结构可以在不修改具体工 厂类的情况下引进新的产品,如果出现新的按钮类型,只需要为这 种新类型的按钮创建一个具体的工厂类就可以获得该新按钮的实例, 这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性, 更加符合“开闭原则”。
工厂方法模式 模式动机 使用工厂方法模式设计的按钮工厂
工厂方法模式 模式定义 工厂方法模式(Factory Method Pattern)又称为工厂模 式,也叫虚拟构造器(Virtual Constructor)模式或者多 态工厂(Polymorphic Factory)模式,它属于类创建型模 式。在工厂方法模式中,工厂父类负责定义创建产 品对象的公共接口,而工厂子类则负责生成具体的 产品对象,这样做的目的是将产品类的实例化操作 延迟到工厂子类中完成,即通过工厂子类来确定究 竟应该实例化哪一个具体产品类。
工厂方法模式 模式结构
工厂方法模式 模式结构 工厂方法模式包含如下角色: Product:抽象产品 ConcreteProduct:具体产品 Factory:抽象工厂 ConcreteFactory:具体工厂
工厂方法模式 模式分析 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面 向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且 克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有 产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅 负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例 化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色 的情况下引进新产品。
工厂方法模式 模式分析 当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品 对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改, 也不需要修改客户端,很好地符合了“开闭原则”。而简单工厂模式在 添加新产品对象后不得不修改工厂方法,扩展性不好。工厂方法模 式退化后可以演变成简单工厂模式。
抽象工厂模式 模式分析 抽象工厂类代码: 抽象工厂类 public abstract class PayMethodFactory { public abstract AbstractPay getPayMethod(); }
抽象工厂方法 模式分析 具体工厂类代码: 具体工厂类 public class CashPayFactory extends PayMethodFactory { public AbstractPay getPayMethod() return new CashPay(); }
工厂方法模式 模式分析 PayMethodFactory factory; AbstractPay payMethod; 客户类代码片段: 为了提高系统的可扩展性和灵活性,在定义工厂和产品时都必须使用抽象层, 如果需要更换产品类,只需要更换对应的工厂即可,其他代码不需要进行任 何修改。 PayMethodFactory factory; AbstractPay payMethod; factory=new CashPayFactory(); payMethod =factory.getPayMethod(); payMethod.pay();
工厂方法模式 模式分析 配置文件代码: 在实际的应用开发中,一般将具体工厂类的实例化过程进行改进,不直 接使用new关键字来创建对象,而是将具体类的类名写入配置文件中, 再通过Java的反射机制,读取XML格式的配置文件,根据存储在XML文件 中的类名字符串生成对象。 <?xml version="1.0"?> <config> <className>CashPayFactory</className> </config>
工厂方法模式 模式分析 Java反射(Java Reflection): 是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制, 包括类的方法、属性、超类等信息,还包括实例的创建和实例类型的判 断等。可通过Class类的forName()方法返回与带有给定字符串名的类或接 口相关联的Class对象,再通过newInstance()方法创建此对象所表示的类的 一个新实例,即通过一个类名字符串得到类的实例。 //创建一个字符串类型的对象 Class c = Class.forName(“String”); Object obj = c.newInstance(); return obj;
工厂方法模式 模式分析 工具类XMLUtil代码片段: //创建DOM文档对象 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("config.xml")); //获取包含类名的文本节点 NodeList nl = doc.getElementsByTagName("className"); Node classNode=nl.item(0).getFirstChild(); String cName=classNode.getNodeValue(); //通过类名生成实例对象并将其返回 Class c=Class.forName(cName); Object obj=c.newInstance(); return obj;
工厂方法模式 模式分析 修改后的客户类代码片段: PayMethodFactory factory; AbstractPay payMethod; factory=(PayMethodFactory)XMLUtil.getBean(); //getBean()的返回类型为Object,此处需要进行强制类型转换 payMethod =factory.getPayMethod(); payMethod.pay();
工厂方法模式 模式实例与解析 实例一:电视机工厂 将原有的工厂进行分割,为每种品牌的电视机提供一个 子工厂,海尔工厂专门负责生产海尔电视机,海信工厂 专门负责生产海信电视机,如果需要生产TCL电视机或创 维电视机,只需要对应增加一个新的TCL工厂或创维工厂 即可,原有的工厂无须做任何修改,使得整个系统具有 更加的灵活性和可扩展性。
工厂方法模式 模式实例与解析 实例一:电视机工厂
工厂方法模式 模式优缺点 工厂方法模式的优点 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客 户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产 品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够 使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则 完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式, 是因为所有的具体工厂类都具有同一抽象父类。 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽 象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具 体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这 样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
工厂方法模式 模式优缺点 工厂方法模式的缺点 在添加新产品时,需要编写新的具体产品类,而且还要 提供与之对应的具体工厂类,系统中类的个数将成对增 加,在一定程度上增加了系统的复杂度,有更多的类需 要编译和运行,会给系统带来一些额外的开销。 由于考虑到系统的可扩展性,需要引入抽象层,在客户 端代码中均使用抽象层进行定义,增加了系统的抽象性 和理解难度,且在实现时可能需要用到DOM、反射等技 术,增加了系统的实现难度。
工厂方法模式 模式适用环境 在以下情况下可以使用工厂方法模式: 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要 知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对 象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象 工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建 的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类 对象将覆盖父类对象,从而使得系统更容易扩展。 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可 以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将 具体工厂类的类名存储在配置文件或数据库中。
工厂方法模式 模式应用 Java消息服务JMS(Java Messaging Service) : //使用上下文和JNDI得到连接工厂的引用,ctx是上下文Context类型的对象 QueueConnectionFactory qConnFact=(QueueConnectionFactory)ctx.lookup("cfJndi"); //使用连接工厂创建一个连接 QueueConnection qConn=qConnFact.createQueueConnection(); //使用连接创建一个会话 QueueSession qSess=qConn.createQueueSession(false,javax.jms.QueueSession. AUTO_ACKNOWLEDGE); //使用上下文和JNDI得到消息队列的引用 Queue q=(Queue)ctx.lookup("myQueue"); //使用连接创建一个需要发送的消息类型的实例 QueueSender qSend=qSess.createSender(q); System.out.println("开始发送消息......");
工厂方法模式 模式扩展 使用多个工厂方法:在抽象工厂角色中可以定义多个工厂方法,从而使具体 工厂角色实现这些不同的工厂方法,这些方法可以包含不同的业务逻辑,以 满足对不同的产品对象的需求。 产品对象的重复使用:工厂对象将已经创建过的产品保存到一个集合(如数 组、List等)中,然后根据客户对产品的请求,对集合进行查询。如果有满 足要求的产品对象,就直接将该产品返回客户端;如果集合中没有这样的产 品对象,那么就创建一个新的满足要求的产品对象,然后将这个对象在增加 到集合中,再返回给客户端。 多态性的丧失和模式的退化:如果工厂仅仅返回一个具体产品对象,便违背 了工厂方法的用意,发生退化,此时就不再是工厂方法模式了。一般来说, 工厂对象应当有一个抽象的父类型,如果工厂等级结构中只有一个具体工厂 类的话,抽象工厂就可以省略,也将发生了退化。当只有一个具体工厂,在 具体工厂中可以创建所有的产品对象,并且工厂方法设计为静态方法时,工 厂方法模式就退化成简单工厂模式。
原型模式 模式动机
原型模式 模式动机 在面向对象系统中,使用原型模式来复制一个对象 自身,从而克隆出多个与原型对象一模一样的对象。 在软件系统中,有些对象的创建过程较为复杂,而 且有时候需要频繁创建,原型模式通过给出一个原 型对象来指明所要创建的对象的类型,然后用复制 这个原型对象的办法创建出更多同类型的对象,这 就是原型模式的意图所在。
原型模式 模式定义 原型模式(Prototype Pattern):原型模式是一种对象创 建型模式,用原型实例指定创建对象的种类,并且 通过复制这些原型创建新的对象。原型模式允许一 个对象再创建另外一个可定制的对象,无须知道任 何创建的细节。 原型模式的基本工作原理是通过将一个原型对象传 给那个要发动创建的对象,这个要发动创建的对象 通过请求原型对象拷贝原型自己来实现创建过程。
原型模式 模式结构
原型模式 模式结构 原型模式包含如下角色: Prototype:抽象原型类 ConcretePrototype:具体原型类 Client:客户类
原型模式 模式分析 在原型模式结构中定义了一个抽象原型类,所有的Java类都继承自 java.lang.Object,而Object类提供一个clone()方法,可以将一个Java 对象复制一份。因此在Java中可以直接使用Object提供的clone()方法 来实现对象的克隆,Java语言中的原型模式实现很简单。 能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个 Java类支持复制。如果一个类没有实现这个接口但是调用了clone()方 法,Java编译器将抛出一个CloneNotSupportedException异常。
原型模式 模式分析 示例代码: public class PrototypeDemo implements Cloneable { …… public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("Not support cloneable"); } return object; }
原型模式 模式分析 通常情况下,一个类包含一些成员对象,在使用原 型模式克隆对象时,根据其成员对象是否也克隆, 原型模式可以分为两种形式:深克隆和浅克隆。
原型模式 模式分析 浅克隆与深克隆 浅克隆 深克隆
原型模式 模式分析 Java语言提供的clone()方法将对象复制了一份并返回 给调用者。一般而言,clone()方法满足: (1) 对任何的对象x,都有x.clone() !=x,即克隆对象与原对 象不是同一个对象。 (2) 对任何的对象x,都有x.clone().getClass()==x.getClass(), 即克隆对象与原对象的类型一样。 (3) 如果对象x的equals()方法定义恰当,那么 x.clone().equals(x)应该成立。
原型模式 模式优缺点 原型模式的优点 当创建新的对象实例较为复杂时,使用原型模式可以简 化对象的创建过程,通过一个已有实例可以提高新实例 的创建效率。 可以动态增加或减少产品类。 原型模式提供了简化的创建结构。 可以使用深克隆的方式保存对象的状态。
原型模式 模式优缺点 原型模式的缺点 需要为每一个类配备一个克隆方法,而且这个克隆方法 需要对类的功能进行通盘考虑,这对全新的类来说不是 很难,但对已有的类进行改造时,不一定是件容易的事, 必须修改其源代码,违背了“开闭原则”。 在实现深克隆时需要编写较为复杂的代码。
原型模式 模式适用环境 在以下情况下可以使用原型模式: 创建新对象成本较大,新的对象可以通过原型模式对已有对象 进行复制来获得,如果是相似对象,则可以对其属性稍作修改。 如果系统要保存对象的状态,而对象的状态变化很小,或者对 象本身占内存不大的时候,也可以使用原型模式配合备忘录模 式来应用。相反,如果对象的状态变化很大,或者对象占用的 内存很大,那么采用状态模式会比原型模式更好。 需要避免使用分层次的工厂类来创建分层次的对象,并且类的 实例对象只有一个或很少的几个组合状态,通过复制原型对象 得到新实例可能比使用构造函数创建一个新实例更加方便。
原型模式 模式应用 (1) 原型模式应用于很多软件中,如果每次创建一个 对象要花大量时间,原型模式是最好的解决方案。 很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就 是原型模式的应用,复制得到的对象与原型对象是 两个类型相同但内存地址不同的对象,通过原型模 式可以大大提高对象的创建效率。
原型模式 模式应用 (2) 在Struts2中为了保证线程的安全性,Action对象的创建使用了原型 模式,访问一个已经存在的Action对象时将通过克隆的方式创建出一 个新的对象,从而保证其中定义的变量无须进行加锁实现同步,每 一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导 致的并发和同步问题。 (3) 在Spring中,用户也可以采用原型模式来创建新的bean实例,从 而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原 有实例对象不造成任何影响。
原型模式 模式扩展 带原型管理器的原型模式
原型模式 模式扩展 相似对象的复制 很多情况下,复制所得到的对象与原型对象并不是完全 相同的,它们的某些属性值存在异同。通过原型模式获 得相同对象后可以再对其属性进行修改,从而获取所需 对象。如多个学生对象的信息的区别在于性别、姓名和 年龄,而专业、学院、学校等信息都相同,为了简化创 建过程,可以通过原型模式来实现相似对象的复制。