890 likes | 1.07k Views
第十二讲 设计模式. 本讲内容. 设计模式的概念 简单工厂模式 工厂方法模式 抽象工厂模式 J2EE 的具体设计模式. 模式( Pattern). 一个围棋下得好的人知道,好的“ 形 ”对于围棋非常重要,形是棋子在棋盘上的 几何形状 的 抽象 化 。 形 就是 模式 ( Pattern) ,也是人脑 把握 和 认识 外界的关键。. 模式 ( 续 ). 在大量不同的问题中 重复出现的一种性质 ,它使得可以使用一种方法来描述 问题实质 并用 本质上相同 , 但细节永不会重复 的方法去解决,这种性质就叫 模式 。. 什么是设计模式.
E N D
本讲内容 • 设计模式的概念 • 简单工厂模式 • 工厂方法模式 • 抽象工厂模式 • J2EE的具体设计模式
模式(Pattern) • 一个围棋下得好的人知道,好的“形”对于围棋非常重要,形是棋子在棋盘上的几何形状的抽象化。 • 形就是模式(Pattern),也是人脑把握和认识外界的关键。
模式(续) • 在大量不同的问题中重复出现的一种性质,它使得可以使用一种方法来描述问题实质并用本质上相同,但细节永不会重复的方法去解决,这种性质就叫模式。
什么是设计模式 • 设计模式描述了不断重复发生的问题以及该问题的核心解决方案。 • 模式的核心在于提供了相关问题的可重用解决方案,通过使用该方案解决同样的问题,避免了大量重复劳动。
模式基本要素 • 模式名称 用来描述模式的词汇,即所要描述问题的较高层次上抽象,恰当的模式名称便于交流设计思想及设计结果。 • 问题 描述了应该在满足何种先决条件时使用模式以及设计问题和问题存在的前因后果。
模式基本要素(续) • 解决方案 描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式; 模式就像一个模板,解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合来解决这个问题。 • 效果 描述了模式应用的效果及使用模式应权衡的问题,对于评价设计选择和理解使用模式的代价具有重要意义,必须包括对系统的灵活性、扩充性或可移植性的复用影响。
成为模式的条件 • 1、它可以解决问题。模式不能仅仅反映问题,而必 须对问题提出解决方案。 2、它所提出解决方案是正确的,而且不是很明显的。 3、它必须是涉及软件系统深层结构的东西,不能仅是 对已有的模块的描述。 4、它必须满足人的审美,简洁美观。 一个美妙的东西不一定就是模式, 但是一个模式必须是一个美妙的东西。
开创性著作 • 《Design Patterns – Elements of Reusable Object-Oriented Software》 • 作者 E.Gamma R. Helm R. Johnson J. Vlissides • 四位作者通常被称为四人帮(Gang of Four, 或GoF),带有戏虐成分。
什么是创立性模式 • 创立性模式(Creational Patterns)是类在实例化时使用的模式。 • 当一些系统在创立对象时,需要动态地决定怎样创立对象,创立哪些对象。 • 创立性模式告诉我们怎样构造和包装这些动态的决定。
什么是结构性模式 • 结构性模式描述类和对象怎样结合在一起成为较大的结构。 • 结构性模式描述两种不同的东西:类与类的实例。
什么是行为性模式 • 行为模式涉及到算法和对象职责间的分配。 • 行为模式不仅是关于类和对象的,而且还描述它们之间的作用。 • 这些模式刻画了运行时刻难以跟踪的复杂的控制流,它帮助软件设计者在设计软件的时候把注意力从控制流转移到对象间的联系上。
Java创建对象的方法 • 所有面向对象的语言都有固定的创立对象的办法。java的办法就是使用new操作符。比如StringBuffer s = new StringBuffer(1000)。 • 使用new操作符的短处是事先必须明确知道要实例化的类是什么。 • 实例化的责任往往与使用实例的责任不加区分。
创立性模式 • 面向对象设计的目的之一,就是把责任进行划分,以分派给不同的对象。 • 创立性模式把对象的创立过程封装起来,使得创立实例的责任与使用实例的责任分割开来。 • 由专门的模块分管实例的创立,而不必事先知道每次是要实例化哪一个类,系统在宏观上不再依赖于对象创立过程的细节。
工厂模式 • 工厂模式就是专门负责将大量有共同接口的类实例化,而且不必事先知道每次是要实例化哪一个类的模式。 • 工厂模式有以下几种形态: 简单工厂(Simple Factory)模式; 工厂方法(Factory Method)模式;抽象工厂(Abstract Factory)模式。
简单工厂(Simple Factory)模式 • 比如说,你有一个描述你的后花园的系统,在你的后花园里有各种的花,但还没有水果。你现在要往你的系统里引进一些新的类,用来描述下列的水果: 葡萄 Grapes, 草莓 Strawberry, 萍果 Apple。
简单工厂(Simple Factory)模式(续) 源代码 源代码 源代码 源代码
简单工厂(Simple Factory)模式(续) • 作为小花果园的主人兼园丁,也是系统的一部分,自然要由一个合适的类来代表,这个类就是 FruitGardener类。 源代码
园丁的工作(简单工厂) • FruitGardener类会根据要求,创立出不同的水果类,比如萍果Apple,葡萄Grape或草莓Strawberry的实例 • 这里的园丁就如同一个可以创建水果产品的工厂一样 • 如果接到不合法的要求,FruitGardener类会给出例外BadFruitException • FruitGardener类会根据要求,创立出不同的水果类,比如萍果Apple,葡萄Grape或草莓Strawberry的实例。 • 这里的园丁就如同一个可以创建水果产品的工厂一样。 • 如果接到不合法的要求,FruitGardener类会给出例外BadFruitException 。 • FruitGardener类会根据要求,创立出不同的水果类,比如萍果Apple,葡萄Grape或草莓Strawberry的实例 • 这里的园丁就如同一个可以创建水果产品的工厂一样 • 如果接到不合法的要求,FruitGardener类会给出例外BadFruitException 源代码
丰收的果园(客户端) • 在使用时,只须呼叫FruitGardener的factory()方法即可。 • try { FruitGardener gardener = new FruitGardener();FruitIF grape = gardener.factory("grape");FruitIF apple = gardener.factory("apple");FruitIF strawberry = gardener.factory("strawberry"); ... } catch(BadFruitException e) { ...}
简单工厂模式的定义 • 总而言之,简单工厂模式就是由一个工厂类根据参数来决定创立出哪一种产品类的实例。
简单工厂模式框架的源代码 public class Creator {public Product factory() { return new ConcreteProduct();} } public interface Product { } public class ConcreteProduct implements Product {public ConcreteProduct(){} }
源代码:FruitIF接口 • 这个接口确定了水果类必备的方法: 种植plant(),生长grow(),以及收获harvest() 。 • package com.javapatterns.simplefactory;public interface FruitIF { void grow(); void harvest(); void plant(); String color = null; String name = null;}
源代码:Apple类 • 萍果是多年生木本植物,因此具备树龄treeAge性质。 • package com.javapatterns.simplefactory;public class Apple implements FruitIF { public void grow() { log("Apple is growing..."); } public void harvest() { log("Apple has been harvested."); } public void plant() { log("Apple has been planted."); } public static void log(String msg) { System.out.println(msg); } public int getTreeAge(){ return treeAge; } public void setTreeAge(int treeAge){ this.treeAge = treeAge; } private int treeAge;}
源代码:Grape类 • 葡萄分为有籽与无籽两种,因此具有seedful性质。 • package com.javapatterns.simplefactory;public class Grape implements FruitIF { public void grow() { log("Grape is growing..."); } public void harvest() { log("Grape has been harvested."); } public void plant() { log("Grape has been planted."); } public static void log(String msg) { System.out.println(msg); } public boolean getSeedful() { return seedful; } public void setSeedful(boolean seedful) { this.seedful = seedful; } private boolean seedful;}
源代码:Strawberry类 • package com.javapatterns.simplefactory; public class Strawberry implements FruitIF { public void grow() { log("Strawberry is growing..."); } public void harvest() { log("Strawberry has been harvested."); } public void plant() { log("Strawberry has been planted."); } public static void log(String msg) { System.out.println(msg); }}
源代码:FruitGardener类 • package com.javapatterns.simplefactory;public class FruitGardener { public FruitIFfactory(String which) throws BadFruitException{ if (which.equalsIgnoreCase("apple")) { return new Apple(); } else if (which.equalsIgnoreCase("strawberry")) { return new Strawberry(); } else if (which.equalsIgnoreCase("grape")) { return new Grape(); } else { throw new BadFruitException("Bad fruit request"); } }}
源代码:BadFruitException类 • package com.javapatterns.simplefactory;public class BadFruitException extends Exception { public BadFruitException(String msg { super(msg); }}
简单工厂模式小结 • 在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定哪一个产品类应当被实例化, 如同一个交通警察站在来往的车辆流中,决定放行哪一个方向的车辆向哪一个方向流动一样。
工厂方法模式 • 工厂方法模式是简单工厂模式的进一步抽象化和推广。 • 它比简单工厂模式聪明的地方在于,它不再作为一个具体的交通警察的面貌出现,而是以交通警察局(工厂)的面貌出现,具体的警察成为工厂方法的执行者。 • 工厂方法模式里不再只由一个工厂类决定哪一个产品类应当被实例化,这个决定被交给子类去作。
开始种植蔬菜 • 我们准备再次引进蔬菜类植物,比如: • 西红柿 (Tomato) • 土豆 (Potato) • 西芥兰花 (Broccoli) • 蔬菜需要喷洒(dust)杀虫剂(pesticide)除虫。
为什么需要工厂方法模式 • 简单工厂模式。FruitGardener掌握所有水果类的生杀大权。
为什么需要工厂方法模式(续) • 再设计一个专管蔬菜类植物的工厂类 ? • 这样做一个明显不足点就是不够一般化和抽象化。 • 在FruitGardener和VeggieGardener类之间明显存在很多共同点,这些共同点应当抽出来一般化和框架化。 • 这样一来,如果后花园的主人决定再在园子里引进些树木类植物时,我们有框架化的处理方法。
工厂方法模式的定义(续) • ConcreteCreator的factory() 方法返还的数据类型是一个接口PlantIF,而不是哪一个具体的产品类。 • 这种设计使得工厂类创立哪一个产品类的实例细节完全封装在工厂类内部。 • 工厂方法模式又叫多形性工厂模式,显然是因为实工厂类都有共同的接口,或者都有共同的抽象父类。
抽象工厂接口(Creator) • 担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创立对象的工厂类必须实现这个接口。
实工厂类 (Conrete Creator) • 担任这个角色的是与应用程序紧密相关的,直接在应用程序调用下,创立产品实例的那样一些类。
实产品 (Concrete Product) • 担任这个角色的类是工厂方法模式所创立的任何对象所属的类。
产品 (Product) • 担任这个角色的类是工厂方法模式所创立的对象的父类,或它们共同拥有的接口。
工厂方法模式在小花果园系统中的实现 源代码 源代码 源代码 源代码 源代码
工厂方法模式在小花果园系统中的实现(续) • 取代了过去的全能角色的是一个抽象的园丁,这个角色规定出具体园丁角色需要实现的具体职能,而真正负责作物管理的则是各种作物的具体园丁角色。
工厂方法模式应该在什么情况下使用 • 如果在发现系统只用一个产品类等级(hierarchy)就可以描述所有已有的产品类,简单工厂模式是很好的解决方案。 • 当发现系统只用一个产品类等级不足以描述所有的产品类,包括以后可能要添加的新的产品类时,就应当考虑采用工厂方法模式。 • 由于工厂方法模式可以容许多个实的工厂类,以每一个工厂类负责每一个产品类等级,因此这种模式可以容纳所有的产品等级。
EJB技术架构中的工厂方法模式 • // 取到 JNDI naming contextContext ctx = new InitialContext ();// 利用ctx 索取 EJB Home 接口EmployeeHome home = (EmployeeHome)ctx.lookup("Employee");// 利用Home 接口创立一个 Session Bean 对象// 这里使用的是标准的工厂方法模式Employee emp = home.create (1001, "John", "Smith");// 调用方法emp.setTel ("212-657-7879");
父类 Gardener • package com.javapatterns.factorymethod;abstract public class Gardener { public abstract PlantIF factory(String which) throws BadFruitException;}
子类 VeggieGardener • package com.javapatterns.factorymethod;public class FruitGardener extends Gardener { public PlantIF factory(String which) { if (which.equalsIgnoreCase("apple")) { return new Apple(); } else if (which.equalsIgnoreCase("strawberry")) { return new Strawberry(); } else if (which.equalsIgnoreCase("grape")) { return new Grape(); } else { throw new BadPlantException("Bad fruit request"); }}}
子类 FruitGardener • package com.javapatterns.factorymethod;public class FruitGardener extends Gardener { public PlantIF factory(String which) { if (which.equalsIgnoreCase("apple")) { return new Apple(); } else if (which.equalsIgnoreCase("strawberry")) { return new Strawberry(); } else if (which.equalsIgnoreCase("grape")) { return new Grape(); } else { throw new BadPlantException("Bad fruit request"); }}}
蔬菜类 Broccoli • 其它的蔬菜类与 Broccoli 相似,因此不再赘述 。 • package com.javapatterns.factorymethod;public class Broccoli implements VeggieIF, PlantIF { public void grow() { log("Broccoli is growing..."); } public void harvest() { log("Broccoli has been harvested."); } public void plant() { log("Broccoli has been planted."); } private static void log(String msg) { System.out.println(msg); } public void pesticideDust(){ }}