• 1. 第3章 创建型模式学时数:6
  • 2. 总体介绍(1 of 3)New不是万能的 有时候不知道具体类型 有时候要创建一系列相关的对象 有时候要组装一个复杂对象 有时候要保证某种类型的对象个数是固定的 有时候要在已有的对象基础上产生新的对象
  • 3. 总体介绍(2 of 3)优先使用对象组合,而不是继承 从固定行为的硬编码 定义较小的行为集,再任意组合成复杂的行为 不只是创建一个类的对象,而是要创建许多相关的对象 在创建这些相关对象时应用创建型模式
  • 4. 总体介绍(3 of 3 )抽象(创建相关的一系列对象过程)意味着对客户隐藏下列内容 创建了哪些具体类的对象(因为客户只知道接口) 对象是怎样被创建的 创建的相关对象是如何组合成更复杂的对象的 Gives flexibility in(可以变化的方面) what gets created(创建什么) who creates it(由谁创建) how it gets created(如何创建) when it gets created(何时创建) 允许你用结构和功能差别很大的对象来配置一个系统,配置可以是静态的,也可以是动态的。
  • 5. 引例在迷宫(maze)游戏中,首先必须创建一个迷宫,一个迷宫对象由多个房间对象构成,房间又由墙壁和门构成,如何创建它呢?A Maze is a set of Rooms. A Room knows its neighbours. another room a wall a door Room、wall、door:迷宫的组成类型。
  • 6. Maze 举例
  • 7. Maze Classespublic abstract class MapSite { public abstract void enter(); } public class Wall extends MapSite { public void enter() { } }MapSite enter()Wall enter()
  • 8. Maze Classespublic class Door extends MapSite { Door(Room s1, Room s2) { side1 = s1; side2 = s2; } public void enter() { } public Room otherSideFrom(Room r) { if( r == side1 ) return side2; else if( r == side2 ) return side1; else return null; } public void setOpen(boolean b) { open = b; } public boolean getOpen() { return open; } private Room side1; private Room side2; boolean open; }MapSite enter()RoomDoor open: boolean enter() otherSideFrom(Room): Room2side1, side2[0..4]
  • 9. Maze Classes public class Direction { public final static int First = 0; public final static int North = First; public final static int South = North+1; public final static int East = South+1; public final static int West = East+1; public final static int Last = West; public final static int Num = Last-First+1; }
  • 10. Maze Classespublic class Room extends MapSite { public Room(int r) { room_no = r; } public void enter() { } public void setSide(int direction, MapSite ms) { side[direction] = ms; } public MapSite getSide(int direction) { return side[direction]; } public void setRoom_no(int r) { room_no = r; } public int getRoom_no() { return room_no; } private int room_no; private MapSite[] side = new MapSite[Direction.Num]; }MapSite enter()Room enter()4
  • 11. Maze Classesimport java.util.Vector; public class Maze { public void addRoom(Room r) { rooms.addElement(r); } public Room getRoom(int r) { return (Room)rooms.elementAt(r); } public int numRooms() { return rooms.size(); } private Vector rooms = new Vector(); }MazeRoom*
  • 12. Creating Mazespublic class MazeGame { public static void main(String args[]) { Maze m = new MazeGame().createMaze(); } public Maze createMaze() { Room r1 = new Room(1); Room r2 = new Room(2); Door d = new Door(r1,r2); r1.setSide(Direction.North, new Wall()); r1.setSide(Direction.East, d); r1.setSide(Direction.West, new Wall()); r1.setSide(Direction.South, new Wall()); r2.setSide(Direction.North, new Wall()); r2.setSide(Direction.West, d); r2.setSide(Direction.East, new Wall()); r2.setSide(Direction.South, new Wall()); Maze m = new Maze(); m.addRoom(r1); m.addRoom(r2); return m; } } r1r2d
  • 13. Maze Creation这也太复杂了吧 @@! 只是要建立一个有两个房间的迷宫而已。 Obvious simplification: Room() could initialize sides with 4 new Wall() That just moves the code elsewhere. 问题出在哪儿呢? 把迷宫的布局写死了!!!不灵活! 是房间的数量还是构成房间的种类数目?? 不能包容变化 Changing the layout can only be done by re-writing, or overriding.
  • 14. 创建型模式带来的好处使得迷宫的建立更加灵活. easy to change the components of a maze e.g., DoorNeedingSpell, EnchantedRoom How can you change createMaze() so that it creates mazes with these different kind of classes? 最大的问题在于: is hard-coding of class names.
  • 15. Creational Patterns 创建型模式Patterns used to abstract the process of instantiating objects. 关于抽象(创建对象过程)的模式 类创建型模式 使用子类产生对象 Factory Method (3讲) 对象创建型模式 将对象的产生委托给另一个对象 Abstract Factory(2讲) Builder (3讲) Prototype (4讲) Singleton (4讲)
  • 16. Abstract Factory(抽象工厂)—对象创建型创建一系列相关对象,而不需要指定真正的 concrete classes. 基本上 Factory 就是用來製造具體成品… 别名:Kit
  • 17. 动机考虑一个支持多种视感标准的GUI工具包,不同的视感风格为窗口组件定义不同的外观和行为。 客户应用不应该为一个特定的视感外观使用具体类硬编码它的窗口组件。 如果要改变?
  • 18. 适用情况Use when: 一个系统要独立于它的产品的创建、组合和表示时 一个系统要由多个产品系列中的一个来配置时 一系列相关对象需要联合使用时 当你提供一个产品类库,而只想显示它们的接口而不是实现时
  • 19. 结构AbstractFactory 声明一个创建对象的接口 ConcreteFactory 实现了抽象工厂所定义的接口
  • 20. StructureAbstractProduct 一类产品的接口. Product 定义一个将被相应的具体工厂创建的产品类。 实现了 AbstractProduct 接口.
  • 21. StructureClient 仅使用 AbstractFactory 和AbstractProduct 接口。 只会去接触AbstractFactory,並不知道具体零件的工厂类型,也不知道具体的产品系列。
  • 22. Sample Codepublic class MazeFactory { Maze makeMaze() { return new Maze(); } Wall makeWall() { return new Wall(); } Room makeRoom(int r) { return new Room(r); } Door makeDoor(Room r1, Room r2) { return new Door(r1,r2);} }
  • 23. Maze Creation (old way) public Maze createMaze() { Room r1 = new Room(1); Room r2 = new Room(2); Door d = new Door(r1,r2); r1.setSide(Direction.North, new Wall()); r1.setSide(Direction.East, d); r1.setSide(Direction.West, new Wall()); r1.setSide(Direction.South, new Wall()); r2.setSide(Direction.North, new Wall()); r2.setSide(Direction.East, d); r2.setSide(Direction.West, new Wall()); r2.setSide(Direction.South, new Wall()); Maze m = new Maze(); m.addRoom(r1); m.addRoom(r2); return m; }
  • 24. Sample Code public Maze createMaze(MazeFactory factory) { Room r1 = factory.makeRoom(1); Room r2 = factory.makeRoom(2); Door d = factory.makeDoor(r1,r2); r1.setSide(Direction.North, factory.makeWall()); r1.setSide(Direction.East, d); r1.setSide(Direction.West, factory.makeWall()); r1.setSide(Direction.South, factory.makeWall()); r2.setSide(Direction.North, factory.makeWall()); r2.setSide(Direction.East, d); r2.setSide(Direction.West, factory.makeWall()); r2.setSide(Direction.South, factory.makeWall()); Maze m = factory.makeMaze() m.addRoom(r1); m.addRoom(r2); return m; }
  • 25. Sample Codepublic class EnchantedMazeFactory extends MazeFactory { public Room makeRoom(int r) { return new EnchantedRoom(r, castSpell()); } public Door makeDoor(Room r1, Room r2) { return new DoorNeedingSpell(r1,r2); } protected castSpell() { // randomly choose a spell to cast; … } }
  • 26. Sample Code public class MazeGame { public static void main(String args[]) { Maze m = new MazeGame().createMaze(new EnchantedMazeFactory()); } }
  • 27. 效果隔离了具体类 帮助控制客户应用创建的具体对象. 将客户应用与具体类隔离 客户通过接口操作对象
  • 28. 效果使得易于交换产品系列 The class of a concrete factory appears only once in the app. where it’s instantiated Easy to change the concrete factory an app uses. The whole product family changes at once 刚才动机举例中的Windows如果要从 PM -> Motif 只要更换factory即可
  • 29. 效果它有利于产品的一致性 When products are designed to work together, it’s important that an application use objects only from one family at a time. AbstractFactory makes this easy to enforce.
  • 30. 效果(缺點)难以支持新种类的产品. Extending AbstractFactory to produce new product types isn’t easy extend factory interface extend all concrete factories add a new abstract product implement new class in each family
  • 31. 实现将工厂作为单件 An app typically needs only one instance of a ConcreteFactory per product family. Best implemented as a Singleton 正常情形下,app只會用到一個ConcreteFactory,比如說 PM 、 Motif EnhantedMazeFactory
  • 32. 实现定义可扩展的工厂 如果需要增加新的产品,很难扩展 给创建对象的操作增加一个参数,该参数标示生产的产品类别。 need only make() more flexible easier in languages that have common subclass e.g. java Object all products have same abstract interface can downcast – not safe
  • 33. 实现创建产品 AbstractFactory 只声明创建产品的API ConcreteFactory 负责实际生产 How? 对每种产品定义 Factory Method virtual overrides for creation methods simple requires new concrete factories for each family, even if they only differ slightly Prototype concrete factory is initialized with a prototypical instance of each product in the family creates new products by cloning doesn’t require a new concrete factory class for each product family
  • 34. Prototype-based Implementationabstract class AbstractProduct implements Cloneable { public abstract int geti(); public abstract Object clone(); } class ConcreteProduct extends AbstractProduct { public ConcreteProduct(int j) { i = j; } public Object clone() { return new ConcreteProduct(i); } public int geti() { return i; } private int i; }
  • 35. Prototype-based Implementationimport java.util.Hashtable; public class ConcreteFactory { void addProduct(AbstractProduct p, String name) { map.put(name, p); } AbstractProduct make(String name) { return (AbstractProduct) ((AbstractProduct)map.get(name)).clone(); } private Hashtable map = new Hashtable(); }
  • 36. Prototype-based Implementationpublic class Main { public static void main(String args[]) { ConcreteFactory f = new ConcreteFactory(); f.addProduct(new ConcreteProduct(42), “ap"); AbstractProduct p = f.make(“ap"); System.out.println(p.geti()); } }
  • 37. Class Registration Implementationabstract class AbstractProduct { public abstract int geti(); } class ConcreteProduct extends AbstractProduct { public int geti() { return i; } private int i = 47; } public class ConcreteFactory { void addProduct(Class c, String name) { map.put(name, c); } Product make(String name) throws Exception { Class c = (Class)map.get(name); return (Product) c.newInstance(); } private Hashtable map = new Hashtable(); }
  • 38. Class Registration Implementationpublic class Main { public static void main(String args[]) throws Exception { ConcreteFactory f = new ConcreteFactory(); f.addProduct(Class.forName("ConcreteProduct"), “ap"); AbstractProduct p = f.make(“ap"); System.out.println(p.geti()); } }
  • 39. Java 举例选自 Java设计模式 一书
  • 40. A GardenMaker Factory编写一个园林规划的程序。 园林的种类可以很多 如:单年生植物园、蔬菜园、热带植物园、多年生植物园 不管是哪种园林,都需要解决以下问题 最佳的中心植物是什么? 最佳的周边植物是什么? 哪些植物喜荫?
  • 41. A GardenMaker Factory抽象工厂 public interface Garden { public Plant getShade(); public Plant getCenter(); public Plant getBorder(); }
  • 42. A GardenMaker Factory具体工厂AnnualGarden public class AnnualGarden implements Garden { public Plant getShade() { return new Plant("Coleus"); } public Plant getCenter() { return new Plant("Marigold"); } public Plant getBorder() { return new Plant("Alyssum"); } }
  • 43. A GardenMaker Factory具体工厂PerennialGarden public class PerennialGarden implements Garden { public Plant getShade() { return new Plant("Astilbe"); } public Plant getCenter() { return new Plant("Dicentrum"); } public Plant getBorder() { return new Plant("Sedum"); } }
  • 44. A GardenMaker Factory具体工厂VeggieGarden public class VeggieGarden implements Garden { public Plant getShade() { return new Plant("Broccoli"); } public Plant getCenter() { return new Plant("Corn"); } public Plant getBorder() { return new Plant("Peas"); } }
  • 45. A GardenMaker Factory产品Plant public class Plant { private String name; public Plant(String pname) { name = pname; //save name } public String getName() { return name; } }
  • 46. 程序运行界面
  • 47. A GardenMaker Factory主程序 public class Gardener extends Frame implements ActionListener { private Checkbox Veggie, Annual, Peren; private Button Center, Border, Shade, Quit; private Garden garden = null; private GardenPanel gardenPlot; private String borderPlant = "", centerPlant = "", shadePlant = ""; public Gardener() { super("Garden planner"); setGUI(); } //---------------------------------- private void setGUI() { setBackground(Color.lightGray); setLayout(new GridLayout(1,2)); Panel left = new Panel(); add(left); Panel right= new Panel(); add(right); //create label and 3 radio buttons on left side left.setLayout(new GridLayout(4, 1)); left.add(new Label("Garden type")); CheckboxGroup grp= new CheckboxGroup(); Veggie = new Checkbox("Vegetable", grp, false); Annual = new Checkbox("Annual", grp, false); Peren = new Checkbox("Perennial", grp, false); left.add(Veggie); left.add(Annual); left.add(Peren); Veggie.addItemListener(new VeggieListener()); Peren.addItemListener(new PerenListener()); Annual.addItemListener(new AnnualListener()); //now create right side right.setLayout(new GridLayout(2,1)); gardenPlot = new GardenPanel(); gardenPlot.setBackground(Color.white); Panel botRight = new Panel(); right.add(gardenPlot); right.add(botRight); Center = new Button("Central"); Border = new Button("Border"); Shade = new Button("Shade"); Quit = new Button("Quit"); botRight.add(Center); Center.addActionListener(this); botRight.add(Border); Border.addActionListener(this); botRight.add(Shade); Shade.addActionListener(this); botRight.add(Quit); Quit.addActionListener(this); setBounds(200,200, 400,300); setVisible(true); } //---------------------------------- public void actionPerformed(ActionEvent e) { Object obj = e.getSource(); if (obj == Center) setCenter(); if (obj == Border) setBorder(); if (obj == Shade) setShade(); if (obj == Quit) System.exit(0); } //---------------------------------- private void setCenter() { if (garden != null) centerPlant = garden.getCenter().getName(); gardenPlot.repaint(); } private void setBorder() { if (garden != null) borderPlant = garden.getBorder().getName(); gardenPlot.repaint(); } private void setShade() { if (garden != null) shadePlant = garden.getShade().getName(); gardenPlot.repaint(); } private void clearPlants() { shadePlant=""; centerPlant=""; borderPlant = ""; gardenPlot.repaint(); } //---------------------------------- static public void main(String argv[]) { new Gardener(); } //-------------------------------- class GardenPanel extends Panel { public void paint (Graphics g) { Dimension sz=getSize(); g.setColor(Color.lightGray); g.fillArc( 0, 0, 80, 80,0, 360); g.setColor(Color.black); g.drawRect(0,0, sz.width-1, sz.height-1); g.drawString(centerPlant, 100, 50); g.drawString( borderPlant, 75, 120); g.drawString(shadePlant, 10, 40); } } //----------------------------- class VeggieListener implements ItemListener { public void itemStateChanged(ItemEvent e) { garden = new VeggieGarden(); clearPlants(); } } //----------------------------- class PerenListener implements ItemListener { public void itemStateChanged(ItemEvent e) { garden = new PerennialGarden(); clearPlants(); } } //----------------------------- class AnnualListener implements ItemListener { public void itemStateChanged(ItemEvent e) { garden = new AnnualGarden(); clearPlants(); } } } //end of Gardener class
  • 48. 第2个例子public class RunAbstractFactoryPattern {     public static void main(String [] arguments){         System.out.println("Example for the AbstractFactory pattern");         System.out.println();         System.out.println(" (take a look in the RunPattern code. Notice that you can");         System.out.println("  use the Address and PhoneNumber classes when writing");         System.out.println("  almost all of the code. This allows you to write a very");         System.out.println("  generic framework, and plug in Concrete Factories");         System.out.println("  and Products to specialize the behavior of your code)");         System.out.println();                  System.out.println("Creating U.S. Address and Phone Number:");         AddressFactory usAddressFactory = new USAddressFactory();         Address usAddress = usAddressFactory.createAddress();         PhoneNumber usPhone = usAddressFactory.createPhoneNumber();                  usAddress.setStreet("142 Lois Lane");         usAddress.setCity("Metropolis");         usAddress.setRegion("WY");         usAddress.setPostalCode("54321");         usPhone.setPhoneNumber("7039214722");                  System.out.println("U.S. address:");         System.out.println(usAddress.getFullAddress());         System.out.println("U.S. phone number:");         System.out.println(usPhone.getPhoneNumber());         System.out.println();         System.out.println();                  System.out.println("Creating French Address and Phone Number:");         AddressFactory frenchAddressFactory = new FrenchAddressFactory();         Address frenchAddress = frenchAddressFactory.createAddress();         PhoneNumber frenchPhone = frenchAddressFactory.createPhoneNumber();                  frenchAddress.setStreet("21 Rue Victor Hugo");         frenchAddress.setCity("Courbevoie");         frenchAddress.setPostalCode("40792");         frenchPhone.setPhoneNumber("011324290");                  System.out.println("French address:");         System.out.println(frenchAddress.getFullAddress());         System.out.println("French phone number:");         System.out.println(frenchPhone.getPhoneNumber());     } } class FrenchAddressFactory implements AddressFactory{     public Address createAddress(){         return new FrenchAddress();     }          public PhoneNumber createPhoneNumber(){         return new FrenchPhoneNumber();     } } class USAddressFactory implements AddressFactory{     public Address createAddress(){         return new USAddress();     }          public PhoneNumber createPhoneNumber(){         return new USPhoneNumber();     } } class USPhoneNumber extends PhoneNumber{     private static final String COUNTRY_CODE = "01";     private static final int NUMBER_LENGTH = 10;          public String getCountryCode(){ return COUNTRY_CODE; }          public void setPhoneNumber(String newNumber){         if (newNumber.length() == NUMBER_LENGTH){             super.setPhoneNumber(newNumber);         }     } } interface AddressFactory{     public Address createAddress();     public PhoneNumber createPhoneNumber(); } abstract class Address{     private String street;     private String city;     private String region;     private String postalCode;          public static final String EOL_STRING =         System.getProperty("line.separator");     public static final String SPACE = " ";          public String getStreet(){ return street; }     public String getCity(){ return city; }     public String getPostalCode(){ return postalCode; }     public String getRegion(){ return region; }     public abstract String getCountry();          public String getFullAddress(){         return street + EOL_STRING +             city + SPACE + postalCode + EOL_STRING;     }          public void setStreet(String newStreet){ street = newStreet; }     public void setCity(String newCity){ city = newCity; }     public void setRegion(String newRegion){ region = newRegion; }     public void setPostalCode(String newPostalCode){ postalCode = newPostalCode; } } abstract class PhoneNumber{     private String phoneNumber;     public abstract String getCountryCode();          public String getPhoneNumber(){ return phoneNumber; }          public void setPhoneNumber(String newNumber){         try{             Long.parseLong(newNumber);             phoneNumber = newNumber;         }         catch (NumberFormatException exc){         }     } } class FrenchPhoneNumber extends PhoneNumber{     private static final String COUNTRY_CODE = "33";     private static final int NUMBER_LENGTH = 9;          public String getCountryCode(){ return COUNTRY_CODE; }          public void setPhoneNumber(String newNumber){         if (newNumber.length() == NUMBER_LENGTH){             super.setPhoneNumber(newNumber);         }     } } class FrenchAddress extends Address{     private static final String COUNTRY = "FRANCE";          public String getCountry(){ return COUNTRY; }          public String getFullAddress(){         return getStreet() + EOL_STRING +             getPostalCode() + SPACE + getCity() +             EOL_STRING + COUNTRY + EOL_STRING;     } } class USAddress extends Address{     private static final String COUNTRY = "UNITED STATES";     private static final String COMMA = ",";          public String getCountry(){ return COUNTRY; }          public String getFullAddress(){         return getStreet() + EOL_STRING +             getCity() + COMMA + SPACE + getRegion() +             SPACE + getPostalCode() + EOL_STRING +             COUNTRY + EOL_STRING;     } }
  • 49. Builder(生成器)意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  • 50. Builder(生成器)动机 一个RTF文档阅读器应能将一个RTF文档转换成多种格式的文档。 转换后的格式可能很多,而解析RTF文档的算法只有一个 封装变化点
  • 51. 效果 每种具体的转换器封装了一种新的格式的文档的创建和装配。 可以重用RTF的语法分析器。 也可以重用各种转换器。SGML reader
  • 52. 适用性Use When: 当创建复杂对象的算法应该独立于该对象的组成部分及装配方式时 RTF Parser算法 Text (TexText、TextWidget)对象 当构造过程允许被构造的对象有不同的表示时 一个复杂对象 多个表示对象 RTF文档 ASCII文档 Tex文档 TextWidget文档
  • 53. 结构Builder 为创建一个Product对象的各个部件指定抽象接口。 Concrete Builder 实现Builder的接口以构造和装配该产品的各个部件。 定义并明确它所创建的表示。 提供一个检索产品的接口
  • 54. StructureDirector 构造一个使用Builder接口的对象。 Product 表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
  • 55. CollaborationsThe client creates the Director object and configures it with the Builder object. Director notifies the builder whenever a part of the product should be built. Builder handles requests from the director and adds parts to the product. The client retrieves the product from the builder.
  • 56. Sample Codepublic abstract class MazeBuilder { public void buildRoom(int r){} public void buildDoor(int r1, int direction, int r2){} public Maze getMaze(){return null;} } public class MazeGame { … public Maze createMaze(MazeBuilder b) { b.buildRoom(1); b.buildRoom(2); b.buildDoor(1, Direction.North, 2); return b.getMaze(); } … }
  • 57. Sample Codepublic class StandardMazeBuilder extends MazeBuilder { private Maze currentMaze; public Maze getMaze() { if( currentMaze==null ) currentMaze = new Maze(); return currentMaze; } … }
  • 58. Sample Codepublic class StandardMazeBuilder extends MazeBuilder { … public void buildRoom(int r) { if( getMaze().getRoom(r) == null ) { Room room = new Room(r); getMaze().addRoom(room); for(int d = Direction.First; d <= Direction.Last; d++) room.setSide(d, new Wall()); } } … }
  • 59. Sample Codepublic class StandardMazeBuilder extends MazeBuilder { … public void buildDoor(int r1, int d, int r2) { Room room1 = getMaze().getRoom(r1); Room room2 = getMaze().getRoom(r2); if( room1 == null ) { buildRoom(r1); room1 = getMaze().getRoom(r1); } if( room2 == null ) { buildRoom(r2); room2 = getMaze().getRoom(r2); } Door door = new Door(room1, room2); room1.setSide(d, door); room2.setSide(Direction.opposite(d), door); } … }
  • 60. Sample Codepublic class CountingMazeBuilder extends MazeBuilder { private int rooms = 0; private int doors = 0; public void buildDoor(int r1, int direction, int r2) { doors++; } public void buildRoom(int r) { rooms++; } public int getDoors() { return doors; } public int getRooms() { return rooms; } }
  • 61. Sample Codepublic class MazeGame { public static void main(String args[]) { MazeGame mg = new MazeGame(); Maze m = mg.createMaze(new StandardMazeBuilder()); System.out.println(m); CountingMazeBuilder cmb = new CountingMazeBuilder(); mg.createMaze(cmb); System.out.println("rooms = "+cmb.getRooms()); System.out.println("doors = "+cmb.getDoors()); } … }
  • 62. 效果它使你可以改变一个产品的内部表示 只需要定义一个新的生成器 它将构造代码和表示代码分开 生成器封装了一个复杂对象的创建和表示方式 它使你可对构造过程进行更精细的控制 生成器在导向器的控制下一步一步地构造产品
  • 63. 实现Assembly interface(装配和构造接口) 有时,新生成的部件仅仅只是添加到产品中,如动机中的例子 有时,可能需要访问前面已生成的部件 need an interface for doing this that hides Products cookie of some sort beware order of construction Product hierarchy? often no great similarity no great need don’t use up a precious inheritance dimension abstract v.s. empty methods? empty methods more generally useful
  • 64. 举例
  • 65. 例1 Pissa饼/** "Product" */ class Pizza { private String dough = ""; private String sauce = ""; private String topping = ""; public void setDough (String dough)     { this.dough = dough; } public void setSauce (String sauce)     { this.sauce = sauce; } public void setTopping (String topping) { this.topping = topping; } } /** "Abstract Builder" */ abstract class PizzaBuilder { protected Pizza pizza; public Pizza getPizza() { return pizza; } public void createNewPizzaProduct() { pizza = new Pizza(); } public abstract void buildDough(); public abstract void buildSauce(); public abstract void buildTopping(); } /** "ConcreteBuilder" */class HawaiianPizzaBuilder extends PizzaBuilder { public void buildDough()   { pizza.setDough("cross"); } public void buildSauce()   { pizza.setSauce("mild"); } public void buildTopping() { pizza.setTopping("ham+pineapple"); } } /** "ConcreteBuilder" */class SpicyPizzaBuilder extends PizzaBuilder { public void buildDough()   { pizza.setDough("pan baked"); } public void buildSauce()   { pizza.setSauce("hot"); } public void buildTopping() { pizza.setTopping("pepperoni+salami"); } } /** "Director" */class Waiter { private PizzaBuilder pizzaBuilder; public void setPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; } public Pizza getPizza() { returnpizzaBuilder.getPizza(); } public void constructPizza() { pizzaBuilder.createNewPizzaProduct(); pizzaBuilder.buildDough(); pizzaBuilder.buildSauce(); pizzaBuilder.buildTopping(); } } /** A customer ordering a pizza. */class BuilderExample { public static void main(String[] args) { Waiter waiter = new Waiter(); PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder(); waiter.setPizzaBuilder ( hawaiian_pizzabuilder ); waiter.constructPizza(); Pizza pizza = waiter.getPizza(); } }
  • 66. 例1 Pissa饼/** "Product" */ class Pizza { private String dough = ""; private String sauce = ""; private String topping = ""; public void setDough (String dough)     { this.dough = dough; } public void setSauce (String sauce)     { this.sauce = sauce; } public void setTopping (String topping) { this.topping = topping; } } /** "Abstract Builder" */ abstract class PizzaBuilder { protected Pizza pizza; public Pizza getPizza() { return pizza; } public void createNewPizzaProduct() { pizza = new Pizza(); } public abstract void buildDough(); public abstract void buildSauce(); public abstract void buildTopping(); }
  • 67. 例1 Pissa饼/** "ConcreteBuilder" */ class HawaiianPizzaBuilder extends PizzaBuilder { public void buildDough()   { pizza.setDough("cross"); } public void buildSauce()   { pizza.setSauce("mild"); } public void buildTopping() { pizza.setTopping("ham+pineapple"); } } /** "ConcreteBuilder" */ class SpicyPizzaBuilder extends PizzaBuilder { public void buildDough()   { pizza.setDough("pan baked"); } public void buildSauce()   { pizza.setSauce("hot"); } public void buildTopping() { pizza.setTopping("pepperoni+salami"); } }
  • 68. 例1 Pissa饼/** "Director" */ class Waiter { private PizzaBuilder pizzaBuilder; public void setPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; } public Pizza getPizza() { return pizzaBuilder.getPizza(); } public void constructPizza() { pizzaBuilder.createNewPizzaProduct(); pizzaBuilder.buildDough(); pizzaBuilder.buildSauce(); pizzaBuilder.buildTopping(); } } /** A customer ordering a pizza. */ class BuilderExample { public static void main(String[] args) { Waiter waiter = new Waiter(); PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder(); waiter.setPizzaBuilder ( hawaiian_pizzabuilder ); waiter.constructPizza(); Pizza pizza = waiter.getPizza(); } }
  • 69. 例2
  • 70. package com.apwebco.patterns.gof.builder; // Builders public abstract class PromoKitBuilder { protected PromoKit promoKit = new PromoKit(); public abstract void buildVideoPart(); public abstract void buildGarmentPart(); public abstract void buildBookPart(); public abstract PromoKit getPromoKit(); } public class MenPromoKitBuilder extends PromoKitBuilder { public void buildVideoPart() { // add videos to PromoKit based on men-specific preferences } public void buildGarmentPart() { // add men garments to PromoKit } public void buildBookPart() { // add books to PromoKit based on men-specific preferences } public PromoKit getPromoKit() { return promoKit; } }
  • 71. public class WomenPromoKitBuilder extends PromoKitBuilder { public void buildVideoPart() { // add videos to PromoKit based on women-specific preferences } public void buildGarmentPart() { // add women garments to PromoKit } public void buildBookPart() { // add books to PromoKit based on women-specific preferences } public PromoKit getPromoKit() { return promoKit; } }
  • 72. // Director public class PromoKitDirector { public PromoKit createPromoKit(PromoKitBuilder builder) { builder.buildVideoPart(); builder.buildGarmentPart(); builder.buildBookPart(); return builder.getPromoKit(); } } // Integration with overal application public class Application { public static void main(String[] args) { String gendre = "M"; PromoKitDirector director = new PromoKitDirector(); PromoKitBuilder promoKitBuilder = null; if (gendre.equals("M")) { promoKitBuilder = new MenPromoKitBuilder(); } else if (gendre.equals("F")) { promoKitBuilder = new WomenPromoKitBuilder(); } else { // .... } PromoKit result = director.createPromoKit(promoKitBuilder); } }
  • 73. Factory Method(工厂方法)动机 定义一个用于创建对象的接口,让子类决定实例化哪一个类。 别名 虚构造器( Virtual Constructor)
  • 74. Factory Method(工厂方法)动机 P70 factory method
  • 75. 适用性Use when: 当一个类不知道它所必须创建的对象的类的时候 当一个类希望由它的子类来指定它所创建的对象的类时 当类将创建对象的职责委托给多个帮助子类中的某一个,将哪一个帮助子类是代理者这一信息局部化时(Hide the secret of which helper subclass is the current delegate.)
  • 76. 结构Product defines the interface of objects the factory method creates (比如說Document ) ConcreteProduct implements the Product interface (比如說 MyDocument)
  • 77. 结构Creator declares the factory method which return a Product type. [define a default implementation] [call the factory method itself] ConcreteCreator overrides the factory method to return an instance of a ConcreteProduct
  • 78. 示例代码public class MazeGame { public static void main(String args[]) { Maze m = new MazeGame().createMaze(); } // factory methods: private Maze makeMaze() { return new Maze(); } private Wall makeWall() { return new Wall(); } private Room makeRoom(int r) { return new Room(r); } private Door makeDoor(Room r1, Room r2) { return new Door(r1,r2); } public Maze createMaze() { … } }
  • 79. 示例代码 public Maze createMaze() { Room r1 = makeRoom(1); Room r2 = makeRoom(2); Door d = makeDoor(r1,r2); r1.setSide(Direction.North, makeWall()); r1.setSide(Direction.East, d); r1.setSide(Direction.West, makeWall()); r1.setSide(Direction.South, makeWall()); r2.setSide(Direction.North, makeWall()); r2.setSide(Direction.East, d); r2.setSide(Direction.West, makeWall()); r2.setSide(Direction.South, makeWall()); Maze m = makeMaze(); m.addRoom(r1); m.addRoom(r2); return m; }
  • 80. 示例代码public class BombedMazeGame extends MazeGame { private Wall makeWall() { return new BombedWall(); } private Room makeRoom(int r) { return new RoomWithABomb(r); } } public class EnchantedMazeGame extends MazeGame { private Room makeRoom(int r) { return new EnchantedRoom(r, castSpell()); } private Door makeDoor(Room r1, Room r2) { return new DoorNeedingSpell(r1,r2); } private Spell castSpell() { return new Spell(); } }
  • 81. 示例代码 public static void main(String args[]) { Maze m = new EnchantedMazeGame().createMaze(); } public static void main(String args[]) { Maze m = new BombedMazeGame().createMaze(); }
  • 82. 效果Advantage: 不再将与特定应用有关的类绑定到你的代码中 Can work with any user-defined ConcreteProduct classes. Disadvantage: 客户可能仅仅为了创建一个特定的ConcreteProduct 对象,就不得不创建Creator的子类。
  • 83. 效果为子类提供hook 父类可以提供缺省的实现,子类可以重定义 always more flexible than direct object creation 连接平行的类层次 将哪些类应一同工作的信息局部化了
  • 84. 实现2种主要类型 creator class is abstract requires subclass to implement creator class is concrete, and provides a default implementation optionally allows subclass to re-implement 参数化工厂方法 takes a class id as a parameter to a generic make() method. 代码见P73 命名习惯 use ‘makeXXX()’ type conventions (e.g., MacApp – DoMakeClass()) 使用模板以避免创建子类 P74,75
  • 85. 实现Lazy initialization 在C++中, 一定要注意不要在Creator的构造函数中调用工厂方法(因为这时还不可用) can use lazy instatiation: Product getProduct() { if( product == null ) { product = makeProduct(); } return product; }
  • 86. 举例
  • 87. (本页无文本内容)
  • 88. Prototype(原型)意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
  • 89. Prototype(原型)动机 图形工具类与图形类 e.g., reduce # of classes (# of tools) by initializing a generic tool with a prototype
  • 90. 适用性Use When: 当要实例化的类是在运行时刻指定时 the classes to be instantiated are specified at run-time 为了避免创建一个与产品类层次平行的工厂类层次时 to avoid building a class hierarchy of factories to parallel the hierarchy of products 当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些 when instances can have only one of a few states,may be better to initialize once, and then clone prototypes
  • 91. 结构Prototype declares an interface for 自我复制 ConcretePrototype implements an operation for cloning itself Client creates a new object by asking a prototype to clone itself
  • 92. Sample Codepublic class MazePrototypeFactory extends MazeFactory { private Maze prototypeMaze; private Wall prototypeWall; private Room prototypeRoom; private Door prototypeDoor; public MazePrototypeFactory(Maze pm, Wall pw, Room pr, Door pd) { prototypeMaze = pm; prototypeWall = pw; prototypeRoom = pr; prototypeDoor = pd; } … }
  • 93. Sample Codepublic class MazePrototypeFactory extends MazeFactory { Wall makeWall() { Wall wall = null; try { wall = (Wall)prototypeWall.clone(); } catch(CloneNotSupportedException e) { throw new Error(); } return wall; } Room makeRoom(int r) { Room room = null; try { room = (Room)prototypeRoom.clone(); } catch(CloneNotSupportedException e) { throw new Error(); } room.initialize(r); return room; } … }
  • 94. Sample Codepublic abstract class MapSite implements Cloneable { public abstract void enter(); public String toString() { return getClass().getName(); } public Object clone() throws CloneNotSupportedException { return super.clone(); } }
  • 95. Sample Codepublic class Door extends MapSite { public Door(Room s1, Room s2) { initialize(s1,s2); } public void initialize(Room s1, Room s2) { side1 = s1; side2 = s2; open = true; } private Room side1; private Room side2; boolean open; … }
  • 96. Sample Codepublic class Room extends MapSite { public Room(int r) { initialize(r); } public void initialize(int r) { room_no = r; } public Object clone() throws CloneNotSupportedException { Room r = (Room)super.clone(); r.side = new MapSite[Direction.Num]; return r; } … private int room_no; private MapSite[] side = new MapSite[Direction.Num]; }
  • 97. Sample Codepublic class EnchantedRoom extends Room { public EnchantedRoom(int r, Spell s) { super(r); spell = s; } public Object clone() throws CloneNotSupportedException { EnchantedRoom r = (EnchantedRoom)super.clone(); r.spell = new Spell(); return r; } private Spell spell; }
  • 98. Sample Codepublic static void main(String args[]) { MazeFactory mf = new MazePrototypeFactory( new Maze(), new Wall(), new Room(0), new Door(null,null)); Maze m = new MazeGame().createMaze(mf); } public static void main(String args[]) { MazeFactory mf = new MazePrototypeFactory( new Maze(), new Wall(), (Room)Class.forName("EnchantedRoom").newInstance(), (Door)Class.forName("DoorNeedingSpell").newInstance()); Maze m = new MazeGame().createMaze(mf); }
  • 99. 效果对客户隐藏了具体的产品类 Many of the same as AbstractFactory 运行时刻增加和删除产品 Can add and remove products at run-time 改变值以指定新对象 new objects via new values,setting state on a prototype 类似于 defining a new class 改变结构以指定新对象 new structures,a multi-connected prototype + deep copy 减少子类的构造 reducing subclass 數量,no need to have a factory or creator hierarchy 用类动态配置应用 dynamic load cannot reference a new class’s constructor statically must register a prototype 缺陷 每个Protype的子类都必须实现Clone操作 implement clone() all over the place (can be tough).
  • 100. 实现使用一个原型管理器 store and retrieve in a registry (like Abstract Factory e.g.). 浅拷贝还是深拷贝? consider a correct implementation of clone() for Maze. (在Java之中要implement出cloneable!!) 初始化克隆对象 一般来说,不能在Clone操作中传递这些值 引入Initialize操作,该操作使用初始化参数并据此设定克隆对象的内部状态。
  • 101. 举例
  • 102. public class PrototypeFactory { AbstractSpoon prototypeSpoon; AbstractFork prototypeFork; public PrototypeFactory(AbstractSpoon spoon, AbstractFork fork) { prototypeSpoon = spoon; prototypeFork = fork; } public AbstractSpoon makeSpoon() { return (AbstractSpoon)prototypeSpoon.clone(); } public AbstractFork makeFork() { return (AbstractFork)prototypeFork.clone(); } }
  • 103. public abstract class AbstractSpoon implements Cloneable { String spoonName; public void setSpoonName(String spoonName) { this.spoonName = spoonName; } public String getSpoonName() { return this.spoonName; } public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("AbstractSpoon is not Cloneable"); } return object;}}
  • 104. public abstract class AbstractFork implements Cloneable { String forkName; public void setForkName(String forkName) { this.forkName = forkName; } public String getForkName() { return this.forkName; } public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("AbstractFork is not Cloneable"); } return object; }}
  • 105. public class SoupSpoon extends AbstractSpoon { public SoupSpoon() { setSpoonName("Soup Spoon"); } }
  • 106. public class SaladSpoon extends AbstractSpoon { public SaladSpoon() { setSpoonName("Salad Spoon"); } }
  • 107. public class SaladFork extends AbstractFork { public SaladFork() { setForkName("Salad Fork"); } }
  • 108. class TestPrototype { public static void main(String[] args) { System.out.println( "Creating a Prototype Factory with " + " a SoupSpoon and a SaladFork"); PrototypeFactory prototypeFactory = new PrototypeFactory(new SoupSpoon(), new SaladFork()); AbstractSpoon spoon = prototypeFactory.makeSpoon(); AbstractFork fork = prototypeFactory.makeFork(); System.out.println("Getting the Spoon and Fork name:"); System.out.println("Spoon: " + spoon.getSpoonName() + ", Fork: " + fork.getForkName()); System.out.println(" "); System.out.println("Creating a Prototype Factory " + "with a SaladSpoon and a SaladFork"); prototypeFactory = new PrototypeFactory(new SaladSpoon(), new SaladFork()); spoon = prototypeFactory.makeSpoon(); fork = prototypeFactory.makeFork(); System.out.println("Getting the Spoon and Fork name:"); System.out.println("Spoon: " + spoon.getSpoonName() + ", Fork: " + fork.getForkName()); } }
  • 109. 抽象工厂模式中基于原型模式的实现 幻灯34-38页。
  • 110. Singleton(单件)意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  • 111. Singleton(单件)动机 Many times need only one instance of an object one file system one print spooler … How do we ensure there is exactly one instance, and that the instance is easily accessible? Global variable is accessible, but can still instantiate multiple instances. make the class itself responsible 这个类可以保证没有其它实例可以被创建(通过截取创建新对象的请求) 可以提供一个访问该实例的方法
  • 112. 适用性Use when: 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时 there must be exactly one instance accessible from a well-known access point 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时 The sole instance should be extensible via subclassing clients should be able to use the extended instance without modifying their code
  • 113. 结构Singleton defines a class-scoped instance() operation that lets clients access its unique instance may be responsible for creating its own unique instance
  • 114. 示例代码package penny.maze.factory; public class MazeFactory { MazeFactory() { } private static MazeFactory theInstance = null; public static MazeFactory instance() { if( theInstance == null ) { String mazeKind = AppConfig.getProperties().getProperty("maze.kind"); if( mazeKind.equals("bombed") ) { theInstance = new BombedMazeFactory(); } else if( mazeKind.equals("enchanted") ) { theInstance = new EnchantedMazeFactory(); } else { theInstance = new MazeFactory(); } } return theInstance; } … }
  • 115. Sample Code# Maze application parameters maze.kind=enchanted file .mazerc:
  • 116. Sample Codeimport java.io.*; import java.util.Properties; public class AppConfig { public static Properties getProperties() { if( props == null ) { props = new Properties(defaults()); try { props.load(new FileInputStream(".mazerc")); } catch(IOException e) { System.err.println("Cannot read .mazerc, using defaults"); } } return props; } private static Properties defaults() { Properties p = new Properties(); p.put("maze.kind", "bombed"); return p; } private static Properties props = null; }
  • 117. Sample Code - More Dynamic# Maze application parameters maze.factory=EnchantedMazeFactory file .mazerc:
  • 118. Sample Code - More Dynamicpackage penny.maze.factory; public class MazeFactory { MazeFactory() { } private static MazeFactory theInstance = null; public static MazeFactory instance() { if( theInstance == null ) { String mazeFactory = AppConfig.getProperties().getProperty("maze.factory"); try{ theInstance = (MazeFactory) Class.forName(mazeFactory).newInstance(); } catch(Exception e) { theInstance = new MazeFactory(); } } return theInstance; } … }
  • 119. 效果对唯一实例的受控访问 Because singleton encapsulates the sole instance, it has strict control. 减少名空间 one access method only 允许可变数目的实例 can change your mind to have e.g., 5 instances
  • 120. 实现Ensuring a unique instance P85 代码. Subclassing the singleton class 单件注册表 P86 代码
  • 121. 另类举例class Printer { //this is a prototype for a printer-spooler class //such that only one instance can ever exist static boolean instance_flag = false; //true if 1 instance public Printer() throws SingletonException { if (instance_flag) throw new SingletonException("Only one printer allowed"); else instance_flag = true; //set flag for 1 instance System.out.println("printer opened"); } public void finalize() { instance_flag = false; } } class SingletonException extends RuntimeException { //new exception type for singleton classes public SingletonException() { super(); } public SingletonException(String s) { super(s); } } public class SinglePrinter { static public void main(String argv[]) { Printer pr1, pr2; //open one printer--this should always work System.out.println("Opening one printer"); try { pr1 = new Printer(); } catch (SingletonException e) { System.out.println(e.getMessage()); } //try to open another printer --should fail System.out.println("Opening two printers"); try { pr2 = new Printer(); } catch (SingletonException e) { System.out.println(e.getMessage()); } } }
  • 122. 创建型模式的讨论创建对象的两种方法 生成创建对象的类的子类 工厂方法 定义一个对象负责明确产品对象的类,并将它作为系统参数 抽象工厂、生成器和原型 工厂对象产生一系列相关的对象 生成器对象装配一个复杂的对象 原型通过拷贝对象来创建对象 考虑在Prototype模式中描述的绘图编辑器框架 P89
  • 123. 创建型模式的讨论考虑迷宫中的工厂方法 If createMaze() calls virtuals to construct components Factory Method (class scoped) If createMaze() is passed a parameter object to create rooms, walls, … Abstract Factory If createMaze() is passed a parameter object to create and connect-up mazes Builder If createMaze is parameterized with various prototypical rooms, doors, walls, … which it copies and then adds to the maze Prototype Need to ensure there is only one maze per game, and everybody can access it, and can extend or replace the maze without touching other code. Singleton
  • 124. 创建型模式的总结Factory Method 本质:用一个virtual method完成创建过程 Abstract Factory 一个product族的factory method构成了一个factory接口 Prototype 通过product原型来构造product,Clone+prototype manager Builder 通过一个构造算法和builder接口把构造过程与客户隔离开 Singleton 单实例类型,如何构造这单个实例?如何访问这单个实例?
  • 125. 创建型模式的总结了解每一种模式的实质 具体实现的时候可能会有变化情况,或者扩展,或者退化 factory method是基础,abstract factory是它的扩展 factory method、abstract factory、prototype都涉及到类层次结构中对象的创建过程,有所取舍 prototype需要prototype manager factory method需要依附一个creator类 abstract factory需要一个平行的类层次 根据应用的其他需求,以及语言提供的便利来决定使用哪种模式
  • 126. creational patterns小结(续)builder往往适合于特定的结构需要,它所针对的product比较复杂 singleton有比较强烈的物理意义,可以用在许多细微的地方,不一定与类层次关联 这些patterns都很常见,有时需要结合两种或者多种模式完成系统中对象的构造过程