Java建立型模式的讨论

        建立型模式抽象了实例化过程。它们帮助一个系统独立于如何建立、组合和表示它的那些对象。一个类建立型模式使用继承改变被实例化的类,而一个对象建立型模式将实例化委托给另外一个对象。java

建立型模式有两个特色:数组

  • 将具体的产品类的信息封装起来。ide

  • 隐藏了产品类的实例是如何建立和组合的。布局

        所以,建立型模式在什么被建立,谁建立它,它是怎样被建立的,以及什么时候建立这些方面提供了极大的灵活性。ui


        建立型模式之间的关系this

  • 有时建立型模式是相互竞争的。例如,在有些状况下,Abstract Factory和Prototype用起来都很好。编码

  • 有时它们是互补的。例如,Builder可使用其它模式去实现某个构件的构建。Prototype能够在它的实现中使用Singleton。spa

1 引入一个迷宫的示例

        由于建立型模式紧密相关,咱们经过一个通用的例子来研究它们的类似点和相异点。prototype

        为一个电脑游戏建立一个迷宫。这个迷宫将随着各类模式的不一样而略有区别。设计

        咱们仅关注迷宫是如何被建立的。咱们将迷宫定义为一系列房间,一个房间有四面;这四面要么是一堵墙,要么是到另外一个房间的一扇门。


        定义一个接口MapSite表示通用的迷宫组件,它只有一个操做enter(),表示你进入了什么——另外一个房间或碰壁。

        使用enum来定义房间的四面:东南西北。

        实现MapSite接口的具体组件包括Room,Door和Wall三个类:

  • Room:保存一个房间号,并经过一个Map关联其四面对应的MapSite。

  • Door:关联两个房间。

  • Wall:墙壁对象。

        注意:MapSite层次结构实际上能够看作一个Composite模式的实现,其中MapSite做为Component接口,Room做为Composite,Door和Wall做为Leaf。用户可使用MapSite接口来一致地使用Room、Door和Wall对象。

package net.tequila.maze;
/**
 * 通用的迷宫组件接口,做为Composite模式的Component。
 */
public interface MapSite {
       /**
        * 进入操做。
        *
        * 若是你进入一个房间,那么你的位置将发生改变。<br/>
        * 当你试图进入一扇门,若是门是开着的,你进入另外一个房间;若是门是关着的,那么你就会碰壁。
        */
       void enter();
}
package net.tequila.maze;
/**
 * 使用enmu来定义房间的四面:东南西北。
 */
public enum Direction {
       NORTH, EAST, SOUTH, WEST
}
package net.tequila.maze;
import java.util.HashMap;
import java.util.Map;
/**
 * 房间,做为Composite模式的Composite。
 *
 * 保存一个房间号,并经过一个<code>Map<Direction, MapSite></code>关联其四面对应的MapSite。
 */
public class Room implements MapSite, Cloneable {
       private int roomNo;
       private Map<Direction, MapSite> sides = new HashMap<Direction, MapSite>();
 
       public Room() {
       }
 
       public Room(int roomNo) {
              this.roomNo = roomNo;
       }
 
       public int getRoomNo() {
              return roomNo;
       }
 
       public void setRoomNo(int roomNo) {
              this.roomNo = roomNo;
       }
 
       public void setSides(Map<Direction, MapSite> sides) {
              this.sides = sides;
       }
 
       public MapSite getSide(Direction d) {
              return sides.get(d);
       }
 
       public void setSide(Direction d, MapSite mapSite) {
              sides.put(d, mapSite);
       }
 
       @Override
       public void enter() {
              System.out.println("enter room " + roomNo);
       }
 
       // 重定义clone(),Prototype模式能够经过该方法克隆自身。
       @Override
       public Object clone() {
              Object object = null;
              try {
                     object = super.clone();
              } catch (CloneNotSupportedException e) {
                     e.printStackTrace();
              }
              return object;
       }
}
package net.tequila.maze;
/**
 * 门,做为Composite模式的一个Leaf。
 *
 * 关联两个Room。
 */
public class Door implements MapSite, Cloneable {
       private Room room1;
       private Room room2;
       private boolean isOpen;
 
       public Door() {
       }
 
       public Door(Room room1, Room room2) {
              this.room1 = room1;
              this.room2 = room2;
       }
 
       public Room getRoom1() {
              return room1;
       }
 
       public void setRoom1(Room room1) {
              this.room1 = room1;
       }
 
       public Room getRoom2() {
              return room2;
       }
 
       public void setRoom2(Room room2) {
              this.room2 = room2;
       }
 
       @Override
       public void enter() {
              if (isOpen)
                     System.out.println("enter a door between room " + room1.getRoomNo()
                                   + " and room " + room2.getRoomNo());
              else
                     System.out.println("the door is closed between room "
                                   + room1.getRoomNo() + " and room " + room2.getRoomNo());
       }
 
       public Room otherSideFrom(Room room) {
              if (room == room1)
                     return room2;
              else if (room == room2)
                     return room1;
              else
                     return null;
       }
 
       // 重定义clone(),Prototype模式能够经过该方法克隆自身。
       @Override
       public Object clone() {
              Object object = null;
              try {
                     object = super.clone();
              } catch (CloneNotSupportedException e) {
                     e.printStackTrace();
              }
              return object;
       }
}
package net.tequila.maze;
/**
 * 墙,做为Composite模式的一个Leaf。
 */
public class Wall implements MapSite, Cloneable {
       @Override
       public void enter() {
              System.out.println("collide a wall...");
       }
 
       // 重定义clone(),Prototype模式能够经过该方法克隆自身。
       @Override
       public Object clone() {
              Object object = null;
              try {
                     object = super.clone();
              } catch (CloneNotSupportedException e) {
                     e.printStackTrace();
              }
              return object;
       }
}


        而后,定义一个Maze类表示房间集合,经过房间号能够找到一个特定的房间。

        房间号可使用线性搜索、hash表、甚至一个简单数组进行一次查找。但这里不考虑这些细节,而是将注意力集中于如何指定一个迷宫对象的构件上。

package net.tequila.maze;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
 * 迷宫:表示房间集合。
 */
public class Maze implements Cloneable {
       private Map<Integer, Room> rooms = new HashMap<Integer, Room>();
 
       public Collection<Room> getRooms() {
              return rooms.values();
       }
 
       public void setRooms(Map<Integer, Room> rooms) {
              this.rooms = rooms;
       }
 
       public Room getRoom(int roomNo) {
              return rooms.get(roomNo);
       }
 
       public void addRoom(Room room) {
              rooms.put(room.getRoomNo(), room);
       }
 
       // 重定义clone(),Prototype模式能够经过该方法克隆自身。
       @Override
       public Object clone() {
              Object object = null;
              try {
                     object = super.clone();
              } catch (CloneNotSupportedException e) {
                     e.printStackTrace();
              }
              return object;
       }
}


        最后,定义一个类MazeGame,由它来建立迷宫。一个简单直接的建立迷宫的方法是使用一系列操做将构件增长到迷宫中,而后链接它们。例如,下面的createMaze()建立一个简单迷宫,这个迷宫由两个房间和它们之间的一扇门组成。

package net.tequila.maze;
/**
 * 负责建立迷宫。
 */
public class MazeGame {
       /**
        * 建立迷宫。这个迷宫由两个房间和它们之间的一扇门组成。
        *
        * 使用一系列操做将构件增长到迷宫中,而后链接它们。
        *
        * @return
        */
       public Maze createMaze() {
              Maze maze = new Maze();
 
              Room room1 = new Room(1);
              Room room2 = new Room(2);
              maze.addRoom(room1);
              maze.addRoom(room2);
 
              Door door = new Door(room1, room2);
 
              room1.setSide(Direction.NORTH, new Wall());
              room1.setSide(Direction.EAST, door);
              room1.setSide(Direction.SOUTH, new Wall());
              room1.setSide(Direction.WEST, new Wall());
              room2.setSide(Direction.NORTH, new Wall());
              room2.setSide(Direction.EAST, new Wall());
              room2.setSide(Direction.SOUTH, new Wall());
              room2.setSide(Direction.WEST, door);
 
              return maze;
       }
}

        

        建立迷宫的方法的问题在于它不够灵活。它对迷宫的布局进行硬编码;改变布局意味着改变这个方法,或是重定义它。这容易产生错误而且不利用重用。

2 引入模式来扩展迷宫

        假如你想在迷宫游戏中重用一个已有的迷宫布局来建立新的迷宫,同时替换新的构件:例如,EnchantedRoom,一个能够有不寻常东西的房间,好比魔法钥匙或是咒语;DoorNeedingSpell,一扇仅随着一个咒语才能锁上和打开的门。你怎样才能较容易地改变createMaze()以让它用新类型的对象建立迷宫呢?

        这种状况下,改变的最大障碍是对被实例化的类进行硬编码。建立型模式提供了多种不一样方法从实例化它们的代码中除去对这些具体类的显式引用。

  • 若是createMaze()调用抽象方法而不是构造器来建立它所须要的房间、门和墙壁,那么能够建立一个MazeGame的子类并重定义这些抽象方法,从而改变被实例化的类。这是Factory Method模式的一个例子。

  • 若是传递一个对象给createMaze()作参数来建立房间、门和墙壁,那么能够传递不一样的参数来改变房间、门和墙壁的类。这是Abstract Factory模式的一个例子,传递的参数是一个ConcreteFactory实例。

  • 若是传递一个对象给createMaze(),这个对象能够在它所建造的迷宫中使用增长房间、门和墙壁的操做,来全面建立一个新的迷宫,那么可使用继承来改变迷宫的一些部分或该迷宫被建造的方式。这是Builder模式的一个例子,传递的参数是一个ConcreteBuilder实例。

  • 若是createMaze()由多种原型的房间、门和墙壁对象参数化,它拷贝并将这些对象增长到迷宫中,那么能够用不一样的对象替换这些原型对象以改变迷宫的构成。这是Prototype模式的一个例子。

  • 剩下的建立型模式,Singleton,能够保证每一个游戏中仅有一个迷宫并且全部的游戏对象均可以迅速访问它。Singleton也使得迷宫易于扩展或替换,且不需变更已有的代码。


        在应用不一样的模式来改进这个例子以前,先扩展一下迷宫的组件,以便咱们能够建立不一样的迷宫组合。增长下列四个组件:

  • EnchantedRoom:施了魔法的房间。

  • DoorNeedingSpell:须要咒语的门。

  • RoomWithABomb:有一个炸弹的房间。

  • BombedWall:墙,可能因为炸弹爆炸而毁坏。

        能够看出,EnchantedRoom和DoorNeedingSpell应该是关联的,二者用于构成一个魔法迷宫;而RoomWithABomb和BombedWall也是关联的,二者用于构成一个炸弹迷宫。

package net.tequila.maze;
/**
 * 施了魔法的房间。
 */
public class EnchantedRoom extends Room {
       /** 咒语 **/
       private String spell;
 
       public EnchantedRoom(int roomNo, String spell) {
              super(roomNo);
              this.spell = spell;
       }
 
       @Override
       public void enter() {
              System.out.println("enter enchanted room " + getRoomNo());
       }
}
package net.tequila.maze;
/**
 * 须要咒语的门。
 */
public class DoorNeedingSpell extends Door {
       /** 咒语 **/
       private String spell;
 
       public DoorNeedingSpell(Room room1, Room room2, String spell) {
              super(room1, room2);
              this.spell = spell;
       }
 
       @Override
       public void enter() {
              System.out.println("enter a \"" + spell + "\" spell door between room "
                            + getRoom1().getRoomNo() + " and room "
                            + getRoom2().getRoomNo());
       }
}
package net.tequila.maze;
/**
 * 有一个炸弹的房间。
 */
public class RoomWithABomb extends Room {
       public RoomWithABomb() {
              super();
       }
 
       public RoomWithABomb(int roomNo) {
              super(roomNo);
       }
 
       @Override
       public void enter() {
              System.out.println("enter room " + getRoomNo() + " with bomb");
       }
}
package net.tequila.maze;
/**
 * 墙,可能因为炸弹爆炸而毁坏。
 */
public class BombedWall extends Wall {
       private boolean bomb;
 
       public BombedWall() {
       }
 
       public BombedWall(boolean bomb) {
              this.bomb = bomb;
       }
 
       public boolean isBomb() {
              return bomb;
       }
 
       @Override
       public void enter() {
              if (bomb)
                     System.out.println("enter a bombed wall");
              else
                     super.enter();
       }
}

2.1 引入Factory Method模式

        使用Factory Method模式来建立上面讨论的迷宫。

        Factory Method模式的结构以下图:

        

        下面简单介绍一下相关的类。

  • MazeGame:每个工厂方法返回一个给定类型的迷宫构件,并提供一个缺省的实现。它做为Factory Method模式的Creator。

  • EnchantedMazeGame:重定义工厂方法,以实现一个魔法迷宫。它做为Factory Method模式的ConcreteCreator。

  • BombedMazeGame:重定义工厂方法,以实现一个炸弹迷宫。它做为Factory Method模式的ConcreteCreator。

package net.tequila.maze.factorymethod;
 
import net.tequila.maze.Direction;
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.Wall;
 
/**
 * 每个工厂方法返回一个给定类型的迷宫构件,并提供一个缺省的实现。它做为Factory Method模式的Creator。
 */
public class MazeGame {
       /**
        * 建立迷宫。这个迷宫由两个房间和它们之间的一扇门组成。
        *
        * 使用工厂方法进行重写。
        *
        * @return
        */
       public Maze createMaze() {
              Maze maze = makeMaze();
 
              Room room1 = makeRoom(1);
              Room room2 = makeRoom(2);
              maze.addRoom(room1);
              maze.addRoom(room2);
 
              Door door = makeDoor(room1, room2);
 
              room1.setSide(Direction.NORTH, makeWall());
              room1.setSide(Direction.EAST, door);
              room1.setSide(Direction.SOUTH, makeWall());
              room1.setSide(Direction.WEST, makeWall());
              room2.setSide(Direction.NORTH, makeWall());
              room2.setSide(Direction.EAST, makeWall());
              room2.setSide(Direction.SOUTH, makeWall());
              room2.setSide(Direction.WEST, door);
              return maze;
       }
 
       // ================================================================
       // 每个工厂方法返回一个给定类型的迷宫构件,并提供缺省的实现。
       // ================================================================
 
       protected Maze makeMaze() {
              return new Maze();
       }
 
       protected Room makeRoom(int roomNo) {
              return new Room(roomNo);
       }
 
       protected Door makeDoor(Room room1, Room room2) {
              return new Door(room1, room2);
       }
 
       protected Wall makeWall() {
              return new Wall();
       }
}
package net.tequila.maze.factorymethod;
 
import net.tequila.maze.Door;
import net.tequila.maze.DoorNeedingSpell;
import net.tequila.maze.EnchantedRoom;
import net.tequila.maze.Room;
 
/**
 * 重定义工厂方法,以实现一个魔法迷宫。它做为Factory Method模式的ConcreteCreator。
 */
public class EnchantedMazeGame extends MazeGame {
       @Override
       protected Room makeRoom(int roomNo) {
              return new EnchantedRoom(roomNo, castSpell());
       }
 
       @Override
       protected Door makeDoor(Room room1, Room room2) {
              return new DoorNeedingSpell(room1, room2, castSpell());
       }
 
       private String castSpell() {
              return "magic";
       }
}
package net.tequila.maze.factorymethod;
 
import net.tequila.maze.BombedWall;
import net.tequila.maze.Room;
import net.tequila.maze.RoomWithABomb;
import net.tequila.maze.Wall;
 
/**
 * 重定义工厂方法,以实现一个炸弹迷宫。它做为Factory Method模式的ConcreteCreator。
 */
public class BombedMazeGame extends MazeGame {
       @Override
       protected Room makeRoom(int roomNo) {
              return new RoomWithABomb(roomNo);
       }
 
       @Override
       protected Wall makeWall() {
              return new BombedWall(true);
       }
}

2.2 引入Abstract Factory模式

        使用Abstract Factory模式来建立上面讨论的迷宫。

        Abstract Factory模式的结构以下图:


        下面简单介绍一下相关的类。

  • MazeFactory:建立迷宫的组件,建立迷宫组件的方法使用Factory Method实现。MazeFactory仅是工厂方法的一个集合。 注意MazeFactory不是一个抽象类,所以它既做为AbstractFactory也做为ConcreteFactory。这是Abstract Factory模式的简单应用的另外一个一般的实现。

  • EnchantedMazeFactory:建立魔法迷宫的组件,它做为Abstract Factory模式的ConcreteFactory。

  • BombedMazeFactory:建立炸弹迷宫的组件,它做为Abstract Factory模式的ConcreteFactory。

  • MazeGame:做为Abstract Factory模式的Client , crateMaze()根据不一样的工厂来建立不一样的迷宫。

package net.tequila.maze.abstractfactory;
 
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.Wall;
 
/**
 * 建立迷宫的组件,建立迷宫组件的方法使用Factory Method实现。
 *
 * MazeFactory仅是工厂方法的一个集合。 注意MazeFactory不是一个抽象类,所以它既做为Abstract
 * Factory也做为ConcreteFactory。这是AbstractFactory模式的简单应用的另外一个一般的实现。
 */
public class MazeFactory {
       public Maze makeMaze() {
              return new Maze();
       }
 
       public Room makeRoom(int roomNo) {
              return new Room(roomNo);
       }
 
       public Door makeDoor(Room room1, Room room2) {
              return new Door(room1, room2);
       }
 
       public Wall makeWall() {
              return new Wall();
       }
}
package net.tequila.maze.abstractfactory;
 
import net.tequila.maze.Door;
import net.tequila.maze.DoorNeedingSpell;
import net.tequila.maze.EnchantedRoom;
import net.tequila.maze.Room;
 
/**
 * 建立魔法迷宫的组件,它做为Abstract Factory模式的ConcreteFactory。
 */
public class EnchantedMazeFactory extends MazeFactory {
       @Override
       public Room makeRoom(int roomNo) {
              return new EnchantedRoom(roomNo, castSpell());
       }
 
       @Override
       public Door makeDoor(Room room1, Room room2) {
              return new DoorNeedingSpell(room1, room2, castSpell());
       }
      
       private String castSpell(){
              return "magic";
       }
}
package net.tequila.maze.abstractfactory;
 
import net.tequila.maze.BombedWall;
import net.tequila.maze.Room;
import net.tequila.maze.RoomWithABomb;
import net.tequila.maze.Wall;
 
/**
 * 建立炸弹迷宫的组件,它做为Abstract Factory模式的ConcreteFactory。
 */
public class BombedMazeFactory extends MazeFactory {
       @Override
       public Room makeRoom(int roomNo) {
              return new RoomWithABomb(roomNo);
       }
 
       @Override
       public Wall makeWall() {
              return new BombedWall(true);
       }
}
package net.tequila.maze.abstractfactory;
 
import net.tequila.maze.Direction;
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
 
/**
 * 根据不一样的工厂来建立不一样的迷宫,做为Abstract Factory模式的Client。
 */
public class MazeGame {
       /**
        * 建立迷宫。
        *
        * 以MazeFactory做为参数来,方法中再也不须要对类名进行硬编码。
        *
        * @param factory
        * @return
        */
       public Maze createMaze(MazeFactory factory) {
              Maze maze = factory.makeMaze();
 
              Room room1 = factory.makeRoom(1);
              Room room2 = factory.makeRoom(2);
              maze.addRoom(room1);
              maze.addRoom(room2);
 
              Door door = factory.makeDoor(room1, room2);
 
              room1.setSide(Direction.NORTH, factory.makeWall());
              room1.setSide(Direction.EAST, door);
              room1.setSide(Direction.SOUTH, factory.makeWall());
              room1.setSide(Direction.WEST, factory.makeWall());
              room2.setSide(Direction.NORTH, factory.makeWall());
              room2.setSide(Direction.EAST, factory.makeWall());
              room2.setSide(Direction.SOUTH, factory.makeWall());
              room2.setSide(Direction.WEST, door);
 
              return maze;
       }
}

2.3 引入Builder模式

        使用Builder模式来建立上面讨论的迷宫。

        Builder模式的结构以下图:

         下面简单介绍一下相关的类。

  • MazeBuilder:定义建立迷宫组件的接口,它做为Builder模式的Builder。

  • StandardMazeBuilder:重定义建立迷宫组件的方法,它做为Builder模式的ConcreteBuilder。

  • MazeGame:根据不一样的生成器,以一个统一的过程来构造不一样的迷宫,它做为Builder模式的Director。

package net.tequila.maze.builder;
 
import net.tequila.maze.Maze;
 
/**
 * 定义建立迷宫组件的接口,它做为Builder模式的Builder。
 *
 * 将构造产品部件的方法定义为空方法,便于子类只重定义它们感兴趣的方法。
 */
public abstract class MazeBuilder {
       public abstract Maze getMaze();
 
       public void buildMaze() {
       }
 
       /**
        * 建立一个房间。
        *
        * @param roomNo
        */
       public void buildRoom(int roomNo) {
       }
 
       /**
        * 建造一扇两个房间之间的门。
        *
        * @param roomNo1
        * @param roomNo2
        */
       public void buildDoor(int roomNo1, int roomNo2) {
       }
}
package net.tequila.maze.builder;
 
import net.tequila.maze.Direction;
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.Wall;
 
/**
 * 重定义建立迷宫组件的方法,它做为Builder模式的ConcreteBuilder。
 */
public class StandardMazeBuilder extends MazeBuilder {
       private Maze maze;
 
       public StandardMazeBuilder() {
              maze = new Maze();
       }
 
       @Override
       public Maze getMaze() {
              return maze;
       }
 
       @Override
       public void buildMaze() {
              maze = new Maze();
       }
 
       /**
        * 建立一个房间并建造它四周的墙壁。
        */
       @Override
       public void buildRoom(int roomNo) {
              if (maze.getRoom(roomNo) == null) {
                     Room room = new Room(roomNo);
                     maze.addRoom(room);
 
                     room.setSide(Direction.NORTH, new Wall());
                     room.setSide(Direction.EAST, new Wall());
                     room.setSide(Direction.SOUTH, new Wall());
                     room.setSide(Direction.WEST, new Wall());
              }
       }
 
       /**
        * 建造一扇两个房间之间的门,同时查找这两个房间并找到它们之间的墙。
        */
       @Override
       public void buildDoor(int roomNo1, int roomNo2) {
              Room room1 = maze.getRoom(roomNo1);
              Room room2 = maze.getRoom(roomNo2);
              Door door = new Door(room1, room2);
 
              room1.setSide(commonWall(room1, room2), door);
              room2.setSide(commonWall(room2, room1), door);
       }
 
       /**
        * 功能示意方法,决定两个房间之间公共墙壁的方位。
        *
        * @param room1
        * @param room2
        * @return
        */
       private Direction commonWall(Room room1, Room room2) {
              for (Direction d : Direction.values()) {
                     if (room1.getSide(d) instanceof Door) {
                            return d;
                     }
              }
              for (Direction d : Direction.values()) {
                     if (room2.getSide(d) instanceof Door) {
                            if (d == Direction.NORTH)
                                   return Direction.SOUTH;
                            else if (d == Direction.EAST)
                                   return Direction.WEST;
                            else if (d == Direction.SOUTH)
                                   return Direction.NORTH;
                            else
                                   return Direction.EAST;
                     }
              }
              return Direction.EAST;
       }
}
package net.tequila.maze.builder;
 
import net.tequila.maze.Maze;
 
/**
 * 根据不一样的生成器,以一个统一的过程来构造不一样的迷宫,它做为Builder模式的Director。
 */
public class MazeGame {
       /**
        * 以MazeBuilder做为参数,方法中再也不须要对类名进行硬编码。
        *
        * @param builder
        * @return
        */
       public Maze createMaze(MazeBuilder builder) {
              builder.buildMaze();
              builder.buildRoom(1);
              builder.buildRoom(2);
              builder.buildDoor(1, 2);
              return builder.getMaze();
       }
 
       /**
        * 重用MazeBuilder来建立不一样种类的迷宫。
        *
        * @param builder
        * @return
        */
       public Maze createComplexMaze(MazeBuilder builder) {
              builder.buildRoom(1);
              // ...
              builder.buildRoom(1001);
              return builder.getMaze();
       }
}

2.4 引入Prototype模式

        Prototype模式的结构以下图:


        定义MazeFactory的子类MazePrototypeFactory。该子类使用它要建立的对象的原型来初始化,这样咱们就不须要仅仅为了改变它所建立的墙壁或房间的类而生成子类了。

  • MazePrototypeFactory:使用Prototype模式重定义工厂方法,它做为Abstract Factory的ConcreteFactory。

  • MazeGame:根据不一样的工厂来建立不一样的迷宫,做为Abstract Factory模式的Client。在Prototype实现中,做为参数的工厂,其实是由不一样原型集合初始化的MazePrototypeFactory实例。

package net.tequila.maze.prototype;
 
import java.util.HashMap;
 
import net.tequila.maze.Direction;
import net.tequila.maze.Door;
import net.tequila.maze.MapSite;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.Wall;
 
/**
 * 使用Prototype模式重定义工厂方法,它做为Abstract Factory的ConcreteFactory。
 *
 * 注意:这里的克隆使用浅度克隆。
 */
public class MazePrototypeFactory extends MazeFactory {
       private Maze prototypeMaze;
       private Room prototypeRoom;
       private Door prototypeDoor;
       private Wall prototypeWall;
 
       public MazePrototypeFactory(Maze maze, Room room, Door door, Wall wall) {
              this.prototypeMaze = maze;
              this.prototypeRoom = room;
              this.prototypeDoor = door;
              this.prototypeWall = wall;
       }
 
       @Override
       public Maze makeMaze() {
              Maze maze = (Maze) prototypeMaze.clone();
              maze.setRooms(new HashMap<Integer, Room>());
              return maze;
       }
 
       @Override
       public Room makeRoom(int roomNo) {
              Room room = (Room) prototypeRoom.clone();
              room.setRoomNo(roomNo);
              room.setSides(new HashMap<Direction, MapSite>());
              return room;
       }
 
       @Override
       public Door makeDoor(Room room1, Room room2) {
              Door door = (Door) prototypeDoor.clone();
              door.setRoom1(room1);
              door.setRoom2(room2);
              return door;
       }
 
       @Override
       public Wall makeWall() {
              Wall wall = (Wall) prototypeWall.clone();
              return wall;
       }
}
package net.tequila.maze.prototype;
 
import net.tequila.maze.BombedWall;
import net.tequila.maze.Direction;
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.RoomWithABomb;
import net.tequila.maze.Wall;
 
/**
 * 根据不一样的工厂来建立不一样的迷宫,做为Abstract Factory模式的Client。
 */
public class MazeGame {
       /**
        * 以MazeFactory做为参数,方法中再也不须要对类名进行硬编码。
        *
        * @param factory
        * @return
        */
       public Maze createMaze(MazeFactory factory) {
              Maze maze = factory.makeMaze();
 
              Room room1 = factory.makeRoom(1);
              Room room2 = factory.makeRoom(2);
              maze.addRoom(room1);
              maze.addRoom(room2);
 
              Door door = factory.makeDoor(room1, room2);
 
              room1.setSide(Direction.NORTH, factory.makeWall());
              room1.setSide(Direction.EAST, door);
              room1.setSide(Direction.SOUTH, factory.makeWall());
              room1.setSide(Direction.WEST, factory.makeWall());
              room2.setSide(Direction.NORTH, factory.makeWall());
              room2.setSide(Direction.EAST, factory.makeWall());
              room2.setSide(Direction.SOUTH, factory.makeWall());
              room2.setSide(Direction.WEST, door);
 
              return maze;
       }
 
// 使用基本迷宫构件的原型初始化MazePrototypeFactory,建立一个原型的或缺省的迷宫。
       public MazePrototypeFactory simpleMazeFactory() {
              return new MazePrototypeFactory(new Maze(), new Room(), new Door(),
                            new Wall());
       }
 
       // 使用爆炸主题的原型集合初始化MazePrototypeFactory,建立一个爆炸迷宫。
       public MazePrototypeFactory bombedMazeFactory() {
              return new MazePrototypeFactory(new Maze(), new RoomWithABomb(),
                            new Door(), new BombedWall(true));
       }
}

2.4 引入Singleton模式

        Singleton模式的结构以下图:


        假定定义一个MazeFactory用于建造上面讨论的迷宫,Maze应用应该仅须要一个迷宫工厂的实例,且这个实例对建造迷宫任何部件的代码都是可用的。因此将MazeFactory做为单件。

        因为MazeFactory存在多个子类,因此客户端经过配置(如环境变量、配置文件等)来决定使用哪个子类。

package net.tequila.maze.singleton;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
 
import net.tequila.maze.Door;
import net.tequila.maze.Maze;
import net.tequila.maze.Room;
import net.tequila.maze.Wall;
 
/**
 * 建立迷宫的组件。这里引入Singleton模式,将MazeFactory做为单件。
 *
 * MazeFactory仅是工厂方法的一个集合。 注意MazeFactory不是一个抽象类,所以它既做为Abstract
 * Factory也做为ConcreteFactory。这是AbstractFactory模式的简单应用的另外一个一般的实现。
 */
public class MazeFactory {
       private static MazeFactory instance;
 
       /**
        * 经过配置(如环境变量、配置文件等)选择迷宫的种类,并增长代码用于实例化适当的MazeFactory子类。
        * 缺点在于只要新增了一个MazeFactory的子类,都必须修改getInstance()。
        */
       public static MazeFactory getInstance() {
              if (instance == null) {
                     Properties props = new Properties();
                     InputStream in = null;
                     try {
                            in = MazeFactory.class.getResourceAsStream("maze.properties");
                            props.load(in);
                     } catch (IOException e) {
                            e.printStackTrace();
                     } finally {
                            try {
                                   in.close();
                            } catch (IOException e) {
                                   // ignore
                            }
                     }
 
                     String mazeStyle = props.getProperty("mazeStyle");
                     if ("enchanted".equals(mazeStyle))
                            instance = new EnchantedMazeFactory();
                     else if ("bombed".equals(mazeStyle))
                            instance = new BombedMazeFactory();
                     else
                            instance = new MazeFactory();
              }
              return instance;
       }
 
       protected MazeFactory() {
       }
 
       public Maze makeMaze() {
              return new Maze();
       }
 
       public Room makeRoom(int roomNo) {
              return new Room(roomNo);
       }
 
       public Door makeDoor(Room room1, Room room2) {
              return new Door(room1, room2);
       }
 
       public Wall makeWall() {
              return new Wall();
       }
}

3 建立型模式的讨论

        用一个系统建立建立的那些对象对系统进行参数化有两种经常使用方法。

(1)生成建立对象的类的子类:对应于使用Factory Method模式。

缺点在于,仅为了改变产品类,就可能须要建立一个新的子类。这样的改变多是级联的。例如,若是产品的建立者自己是由一个工厂方法建立的,那么你也必须重定义它的建立者。

(2)依赖于对象复合:定义一个对象负责明确产品对象的类,并将它做为该系统的参数。这是Abstract Factory、Builder和Prototype模式的关键特征。

  • Abstract Factory由这个工厂对象产生多个类的对象。

  • Builder由这个工厂对象使用一个相对复杂的协议,逐步建立一个复杂产品。

  • Prototype由该工厂对象经过拷贝原型对象来建立产品对象。在这种状况下,由于原型负责返回产品对象,因此工厂对象和原型是同一个对象。


        再回过头来看前面的迷宫示例,能够有多种方法经过产品类来参数化MazeGame。

  • 使用Factory Method模式,将为每个种类的迷宫建立一个MazeGame的子类(普通迷宫对应MazeGame,魔法迷宫对应EnchantedMazeGame,炸弹迷宫对应BombedMazeGame), MazeGame定义了建立不一样迷宫构件的方法(包括房间、门和墙壁等),并提供了一个缺省的实现,每一个MazeGame的子类都会重定义它感兴趣的方法。

  • 使用Abstract Factory模式,将有一个MazeFactory类层次对应于每个种类的迷宫, 在这种状况下每个工厂建立一个特定的迷宫,MazeFactory将建立普通迷宫,EnchantedMazeFactory将建立魔法迷宫,BombedMazeFactory将建立炸弹迷宫对应,等等。MazeGame将以建立合适种类的迷宫的工厂做为参数。

  • 使用Prototype模式,每一个MapSite的子类将实现clone操做,而且MazePrototypeFactory将以它所建立的MapSite的原型来初始化。


        究竟哪种模式最好取决于诸多因素。

        Factory Method使一个设计能够定制且只略微有一些复杂。使用Abstract Factory、Builder和Prototype的设计设置比使用Factory Method的设计更灵活(好比在初始化操做中),但它们也更加复杂。

        一般,设计以使用Factory Method开始,而且当设计者发现须要更大的灵活性时,设计便向其它建立型模式演化。


下面再对建立型模式的实现作一个比较。

  • 简单工厂是工厂方法的一种特例。

    工厂方法延迟到子类来选择实现。若是直接在工厂类里选择实现,就退化成简单工厂了。

  • 抽象工厂一般使用工厂方法来实现,固然也可使用原型。

  • 抽象工厂能够退化为工厂方法。

    工厂方法模式关注的是单个产品的建立;虽然工厂类中能够有多个工厂方法用于建立多个对象,可是这些对象之间通常是没有联系的。抽象工厂模式关注的是一系列产品对象的建立,并且这一系列对象是构建新的对象所须要的组成部分,也就是这一系列被建立的对象相互之间是有约束的。若是抽象工厂中只定义一个方法,直接建立产品,那么就退化成为工厂方法了。

  • 生成器须要建立产品部件的实例,可使用工厂方法或原型来获得部件的实例。

  • 生成器和抽象工厂的比较。

    生成器负责建立产品部件,并将这个部件对象装配到产品对象中。能够理解为,生成器和工厂方法配合使用。再进一步,若是在实现生成器的时候,只有建立产品部件的功能,而没有组装的功能,那么生成器实现和抽象工厂的实现是相似的。在这种状况下,Builder接口相似于抽象工厂的接口,Builder的具体实现类相似于具体工厂,并且Builder接口里面定义的建立各个部件的方法也是有关联的,它们负责构建一个复杂对象所须要的部件对象。

相关文章
相关标签/搜索