创建型模式
设计模式 

创建型模式包括:Abstract Factory、Builder、Factory Method、Prototype、Singleton

创建型模式概述

将通过一个为电脑创建迷宫的游戏来说明各个模式的实现,以及它们的异同。 枚举类Direction指定了房间的东西南北:

public enum Direction {
    North(0), South(1), East(2), West(3);
    int pos;
    Direction(int pos) {
        this.pos = pos;
    }
    public int getPos () {
        return pos;
    }
}

MapSite 是所有迷宫组件的公共抽象类,定义了迷宫中构件之间的主要关系。因为是说明类,所以仅定义了一个操作Enter,决定了你在进入什么:

public interface MapSite {
    void enter();
}

RoomDoorWallMapSite 的子类:

public class Room implements MapSite {
    private MapSite side[] = new MapSite[4];
    private int roomNo;
    public Room(int roomNo) {
        this.roomNo = roomNo;
    }
    MapSite getSide(Direction d) { return side[d.getPos()]; }
    void setSide (Direction d, MapSite m) {
        side[d.getPos()] = m;
    }
    @Override
    public void enter() {
    }
}
public class Door implements MapSite{
    private Room room1;
    private Room room2;
    private boolean isOpen;
    public Door(Room room1, Room room2) {
        this.room1 = room1;
        this.room2 = room2;
    }
    public Room otherSideFrom (Room oRoom) {
        return room1;
    }
    @Override
    public void enter() {

    }
}
public class Wall implements MapSite {
    @Override
    public void enter() {
    }
}

Maze 定义了一个房间集合:

public class Maze {
    private List<Room> rooms = new ArrayList<Room>();
    public void addRoom (Room room) {
        rooms.add(room);
    }
    Room getRoom (int i) {
        return rooms.get(i);
    }
}

MazeGame 用来创建一个迷宫游戏,一个简单的方式是使用一系列操作将构件增加到迷宫中,然后连接它们:

public class MazeGame {
    Maze createMaze () {
        Maze maze = new Maze();
        Room r1 = new Room(1);
        Room r2 = new Room(2);
        Door theDoor = new Door(r1, r2);
        maze.addRoom(r1);
        maze.addRoom(r2);
        r1.setSide(Direction.North, new Wall());
        r1.setSide(Direction.South, theDoor);
        ...
        return maze;
    }
}

这个函数仅用来创建一个有两个房间的迷宫,就相当复杂。虽然有办法让它变得简单——比如在 Room 的构造器中,可以提前用墙壁来初始化房间的每面–但这仅仅将代码移动到了其他地方。这个函数的真正问题在于它不灵活。它对迷宫的布局进行了硬编码,改变布局意味着改变这个函数,或者重定义它–这意味着重新实现这个过程,或是对它的部分进行改变–这容易产生错误并且不利于重用 创建型模式显示如何使设计更灵活,但未必会更小。 假设在一个包含魔法的迷宫中重用一个已有的迷宫布局,这样有魔法的迷宫游戏有了新的构件,像 DoorNeedSpell 用咒语才能打开的门; EnchantedRoom 有不寻常东西的房间等,如何才能更容易地改变 createMaze 才能创建魔法迷宫? 这种情况下,改变最大的障碍就是对被实例化的类进行硬编码。创建型模式提供了多种不同方法从实例化它们的代码中去除这些具体类的显示引用:

  • 如果 createMaze 调用的是接口而不是构造器来创建房间等,那么可以创建一个 MazeGame 的子类并重新定义 createMaze ,从而改变被实例化的类,这是 Factory Method
  • 如果传递一个对象给 createMaze 做参数来创建房间等,那么通过传递不同的参数来改变房间等,这是 Abstract Factory
  • 如果传递一个对象给 createMaze,这个对象可以在它所创建的迷宫中使用增加房间、墙壁和门的操作,来创建一个新迷宫,那么可以使用继承来改变迷宫的一些部分或该迷宫被创建的方式,这是 Builder
  • 如果 createMaze 由多种原型房间、墙壁和门对象参数化,它拷贝并将这些对象增加到迷宫中,那么用不同的对象替换这些原型对象以改变迷宫的构成,这是 Prototype

创建产品族的抽象工厂 – Abstract Factory

目的

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

适用性

以下情况适用于 抽象工厂:

  1. 一个系统要对立于它的产品的创建、组合和表示
  2. 一个系统要由多个产品系列中的一个来配置时
  3. 当你要强调一系列相关的产品对象的设计以便进行联合使用时
  4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时

结构

此模式的结构图如下: AbstractFactorycreateProductA()createProductB()ConcreteFactory1createProductA()createProductB()ConcreteFactory2createProductA()createProductB()AbstractProductAProductA1ProductA2AbstractProductBProductB1ProductB2client

参与者

  • AbstractFactory: 声明一个创建抽象产品对象的操作接口
  • ConcreteFactory: 实现创建具体产品对象的操作
  • AbstractProduct: 为一类产品对象声明一个接口
  • ConcreteProduct: 定义一个将被相应的具体工厂创建的产品对象,实现AbstractProduct 接口
  • Client: 仅使用 AbstractFactoryAbstractProduct 接口

通常在运行时刻创建一个 ConcreteFactory 类的实例。这一具体的工厂创建具有特定实现的产品对象,为创建不同的产品对象,客户应使用不同的具体工厂。 AbstractFactory 将产品对象的创建延迟到它的 ConcreteFactory 子类

优缺点

抽象工厂有如下的优缺点:

  1. 它分离了具体的类。抽象工厂模式帮助你控制一个应用创建的对象的类,封装了产品对象的责任和过程,将客户与具体类相分离,客户通过它们的抽象接口操纵实例。
  2. 它使得易于交换产品系列。一个具体工厂类在一个应用中只出现一次——其初始化的时候。这使得改变一个应用的具体工厂变得很容易,只需改变具体的工厂即可使用不同的产品配置。
  3. 它有利于产品的一致性。当一个系列中的产品对象被设计成一起工作时,一个应用只能使用同一系列的对象。
  4. 难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品,这是因为 AbstractFactory 接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及到 AbstractFactory 类及其所有子类的改变。

实现(模式组合)

下面是 AbstractFactory 模式中一些有用的意见和建议:

  1. 工厂使用单例模式。一个应用中一般每个产品系列只需要一个 ConcreteFactory。
  2. 创建产品。AbstractFactory 仅声明一个创建产品的接口,真正创建产品是由ConcreteFactory 实现的,最通常的一个方法是为每一个产品定义一个工厂方法。具体的工厂将为每个产品重定义该工厂方法以指定产品。
  3. 定义可扩展的工厂。抽象工厂难以支持新增种类的产品,一个比较灵活但不太安全的设计是给创建对象的操作增加一个参数,该参数指定将被创建对象的种类,但是需要客户向下类型转换。当然也可以使用泛型方法。

你可以实现一个魔法迷宫的工厂类:

public class EnchantedMazeFactory implements MazeFactory {
    @Override
    public Maze makeMaze() {
        return new EnchantedMaze();
    }

    @Override
    public Wall makeWall() {
        return new Wall();
    }

    @Override
    public Room makeRoom(int roomNo) {
        return new EnchantedRoom(roomNo);
    }

    @Override
    public Door makeDoor(Room r1, Room r2) {
        return new DoorNeedSpell(r1, r2);
    }
}

使用如下方式创建一个游戏

MazeGame game;
EnchantedMazeFactory factory;
game.createMaze(factory);

相关模式

AbstractFactory 类通常用工厂方法 (FactoryMethod) 实现,也可以用 Prototype 实现。 一个具体的工厂通常是一个单件。

local_offer #设计模式 
navigate_before navigate_next