抽象工厂模式 -- AbstractFactory

2017-03-13 08:46:30   最后更新: 2017-03-14 16:20:25   访问数量:457




抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

比如我们需要创建一个标准用户界面,需要定义不同的视感风格,比如滚动条、窗口、按钮等,我们不关心某个特定视感风格的创建行为,如果硬编码每一个视觉组件,代码将很庞大且很难维护,因此,我们可以定义一个抽象的 WidgetFactory 类,这个类声明了创建每一类基本窗口组件的接口,每一类窗口组件都有一个抽象类,具体的子类实现窗口组件特定的视感风格,WidgetFactory 只需要返回没一个抽象窗口组件类对应的具体对象操作即可,这样使用者就不依赖于一般的视感风格

 

如上图所示,我们使用 AbstractFactory 来创建客户端 client,我们不关心创建的是具体的 ProductA 还是 ProductB,由抽象的 AbstractFactory 的具体实现类 ConcreteFactory1 和 ConcreteFactory2 来实现,这样,当我们需要从风格 1 切换到风格 2 或者增加新的风格 3 时,几乎不需要修改我们的业务代码,而仅仅需要引入不同的实现类即可

 

AbstractFactory 具有下列特点:

  • 分离了具体的类,将使用者与实现分离,用户只需要操作抽象接口,具体的实现将不会出现在用户代码中
  • 交换产品系列变得容易,在一个应用中,一个具体的工厂类仅会出现一次,此后用户只会操作抽象的 AbstractFactory 完成产品的配置,因此只需要改变不同的配置或引入新的工厂对象即可完成产品系列的交换
  • 有利于产品的一致性,在实际的项目中,如果一个应用一次只能使用同一个系列中的对象,那么抽象工厂模式将非常容易去实现这样的限制
  • 难以支持新种类的产品,由于 AbstractFactory 接口确定了可以被创建的产品集合,如果需要增加 AbstractProductC,那么就需要改变所有他的实现类,因此工作量是非常巨大的,这是 AbstractFactory 模式存在的最大问题

 

一个应用中每个产品系列通常只需要一个 ConcreteFactory 实例,因此工厂通常最好是单例模式的

每个产品通常都有一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以便创建指定的产品

针对上面提到的 AbstractFactory 存在的一个问题,即增加新的产品集合工作量巨大的问题,有一个并不安全的做法,即 AbstractFactory 中仅定义一个 make() 方法,他通过传入一个标识符参数来指定具体创建的产品

 

我们仍然通过此前使用过的例子 MazeGame 来讨论 AbstractFactory 的具体应用

对于我们的迷宫游戏来说,总共有三种 Product:Room、Wall 和 Door

因此我们需要创建一个具有创建他们的方法的 MazeFactory:

 

MazeFactory

package com.techlog.designpattern.mazegame.factory; import com.techlog.designpattern.mazegame.model.Door; import com.techlog.designpattern.mazegame.model.Maze; import com.techlog.designpattern.mazegame.model.Room; import com.techlog.designpattern.mazegame.model.Wall; import org.springframework.stereotype.Service; /** * 2017/3/13. * Created by techlog */ @Service public interface MazeFactory { Maze makeMaze(); Wall makeWall(); Room makeRoom(int roomNo); Door makeDoor(Room room1, Room room2, boolean isOpen); }

 

 

MazeGameFactory

接下来,我们就可以用它来创建整个迷宫了

package com.techlog.designpattern.mazegame.service; import com.techlog.designpattern.mazegame.constant.DirectionEnum; import com.techlog.designpattern.mazegame.factory.MazeFactory; import com.techlog.designpattern.mazegame.model.*; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; import static com.techlog.designpattern.mazegame.constant.DirectionEnum.*; /** * 2017/3/12. * Created by techlog */ @Service public class MazeGameService { @Resource MazeFactory factory; public Maze createMaze() { Maze aMaze = factory.makeMaze(); Room room1 = factory.makeRoom(1); Room room2 = factory.makeRoom(2); Door door = factory.makeDoor(room1, room2, true); aMaze.addModel(room1); aMaze.addModel(room2); aMaze.addModel(door); Map<DirectionEnum, AbstractModel> room1sides = new HashMap<>(); room1sides.put(NORTH, new Wall()); room1sides.put(EAST, door); room1sides.put(SOUTH, new Wall()); room1sides.put(WEST, new Wall()); room1.setSides(room1sides); Map<DirectionEnum, AbstractModel> room2sides = new HashMap<>(); room1sides.put(NORTH, new Wall()); room1sides.put(EAST, new Wall()); room1sides.put(SOUTH, new Wall()); room1sides.put(WEST, door); room2.setSides(room2sides); return aMaze; } }

 

 

讲解

看上去 createMaze 并没有发生太大的变化,然而,如果此时我们要创建一个被施了魔法的迷宫,我们将会有新的三个组件:EnchantedRoom、EnchantedWall、EnchantedDoor,如果使用此前我们所使用的代码,那么 createMaze 方法中的创建过程将必须全部重新生成,并且如果这些组件具有魔法值、施法次数等属性的话,createMaze 中将必须加入更多的初始化代码,而如果此时我们又需要创建一个会爆炸的迷宫,又有三个新的组件:BoomRoom、BoomWall、BoomDoor,那么 createMaze 又需要完全重新编写,增加每个组件独特的创建方式,例如是否可爆炸、爆炸范围、爆炸次数、杀伤性等属性的初始化

使用我们这里介绍的抽象工厂模式,我们只需要分别创建实现了 MazeFactory 接口的具体实现类:EnchantedFactory 或 BoomFactory 并且注入到 MazeGameService 中,而 MazeGameService 将不需要做任何修改

同时,我们的抽象工厂保证了在创建一个魔法迷宫时,将不会有一个会爆炸的组件被奇怪的生成出来

 






技术帖      龙潭书斋      设计模式      创建型模式      factory      抽象工厂      design pattern      abstract factory     


1#100000: (回复)2017-06-16 20:30:22

vision画的图?

2#博主: (回复)2017-07-29 18:48:05

回复:1#使用 processon 画的

京ICP备15018585号