生成器模式 -- Builder

2017-03-14 09:06:18   最后更新: 2017-03-14 16:20:37   访问数量:205




上一篇日志中,我们介绍了抽象工厂模式

抽象工厂模式 -- AbstractFactory

本篇日志中,我们来介绍另一个创建型模式 -- 生成器模式

 

生成器模式是用来将一个复杂对象的构建与他的表示相分离,使相同的构建过程可以创建不同的表示

 

 

 

如上图所示,Builder 模式由下列组件构成

  • Builder -- 为创建一个 Product 对象的各个部件指定的抽象接口
  • ConcreteBuilder -- 实现 Builder 的接口以构建和装配该产品的某个部件,并且需要提供一个检索产品部件的接口
  • Director -- 构造一个使用 Builder 的对象
  • Product -- 被最终构造出来的复杂对象,ConcreteBuilder 创建该产品的内部表示并且定义他的装配过程

 

Builder 对象提供给导向器创造各个模块的抽象借口,每个 ConcreteBuilder 都实现他感兴趣的部分接口,Director 按照需要顺次调用 Builder 中的接口实现相应部分的构建,最终完成整个 Product 的构建

Director 并不关心每个组件的实现细节,他只负责最终产品的构成

 

 

 

  1. 可以很容易的改变一个产品的内部表示,Builder 对象对外提供一个抽象的接口,从而隐藏了这个复杂产品的表示和内部结构以及装配过程,因此如果需要改变产品内部表示,那么只需要实现新的生成器即可,调用者既无法感知也无需做任何修改
  2. 将构造代码与表示代码分开,提高了对象的模块性,客户不需要知道定义产品内部结构的类的所有信息
  3. 整个构造过程可以被更加精细的控制,由于整个构造过程是分模块按步骤进行构造的,因此,整个过程能够更好地反映产品的构造过程,从而实现对整个构造过程的精细控制

 

接下来我们通过 builder 模式构建我们此前介绍过的 MazeGame 游戏

 

Builder

首先我们需要一个抽象的构建各个部分的 builder 接口:

package com.techlog.designpattern.mazegame.builder; import com.techlog.designpattern.mazegame.constant.DirectionEnum; import com.techlog.designpattern.mazegame.model.Door; import com.techlog.designpattern.mazegame.model.Maze; import com.techlog.designpattern.mazegame.model.AbstractModel; import com.techlog.designpattern.mazegame.model.Room; import java.util.Map; /** * Created by techlog on 2017/3/14. */ public interface MazeBuilder { Maze buildMaze(); Room buildRoom(int roomNO); Room buildRoom(int roomNO, Map<DirectionEnum, AbstractModel> models); Door buildDoor(Room room1, Room room2, boolean isOpen); Maze getMaze(); }

 

 

ConcreteBuilder

接下来就需要实现 Builder 中的构建方法了:

package com.techlog.designpattern.mazegame.builder; import com.techlog.designpattern.mazegame.constant.DirectionEnum; import com.techlog.designpattern.mazegame.model.Door; import com.techlog.designpattern.mazegame.model.Maze; import com.techlog.designpattern.mazegame.model.AbstractModel; import com.techlog.designpattern.mazegame.model.Room; import org.springframework.stereotype.Service; import java.util.Map; /** * Created by techlog on 2017/3/14. */ @Service public class StantardMazeBuilder implements MazeBuilder { private static Maze maze = null; @Override public Maze buildMaze() { if (maze == null) { maze = new Maze(); } return maze; } @Override public Room buildRoom(int roomNO) { if (maze == null) { buildMaze(); } Room room = null; if (maze.getRoomByNO(roomNO) != null) { room = new Room(); maze.addModel(room); } return room; } @Override public Room buildRoom(int roomNO, Map<DirectionEnum, AbstractModel> sides) { Room room = buildRoom(roomNO); if (room != null) { room.setSides(sides); return room; } return null; } @Override public Door buildDoor(Room room1, Room room2, boolean isOpen) { if (maze == null) { buildMaze(); } Door door = new Door(); door.setRoom1(room1); door.setRoom2(room2); door.setOpen(isOpen); maze.addModel(door); return door; } @Override public Maze getMaze() { return maze; } }

 

 

Director

director 的工作就是来操作 Builder 实现最终产品的构建

package com.techlog.designpattern.mazegame.service; import com.techlog.designpattern.mazegame.builder.MazeBuilder; import com.techlog.designpattern.mazegame.constant.DirectionEnum; 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.*; /** * Created by techlog on 2017/3/14. */ @Service public class MazeGameDirector { @Resource private MazeBuilder builder; public Maze createMazeGame() { Maze maze = builder.buildMaze(); Room room1 = builder.buildRoom(1); Room room2 = builder.buildRoom(2); Door door = builder.buildDoor(room1, room2, true); 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()); builder.buildRoom(1, room1sides); Map<DirectionEnum, AbstractModel> room2sides = new HashMap<>(); room2sides.put(NORTH, new Wall()); room2sides.put(EAST, new Wall()); room2sides.put(SOUTH, new Wall()); room2sides.put(WEST, door); builder.buildRoom(2, room2sides); return maze; } }

 

 

扩展

如果需要将标准的迷宫组件变成一个魔法迷宫组件,只需要向 MazeGameDirector 中注入 Builder 的另一个实现 EnchantedMazeBuilder 即可,Director 并不关心各个组件的创建细节

而如果需要创建另一个 MazeGame,或改变现有游戏中组件的结构和顺序,那么就需要修改 MazeGameDirector 中的实现或是创建新的 Director

 

作为另一个创建型模式,初学的朋友可能觉得他和 Abstract Factory 没什么区别,其实这两个模式有着本质的区别

抽象工厂模式是由 client 直接调用指定的抽象工厂生成对应的产品,整个生成过程 client 都不关心

而 Builder 模式则是由 Director 调用 Builder 中的 build 方法生成产品,Director 与 Builder 拥有明确的分工,Builder 的实现类只关心每个组件的实现细节,而不关心组件间的依赖关系和产品的生成,而 Director 则与之相反,Director 只关心产品有哪些组件构成,按什么顺序构建,而不关心组件的底层实现细节

 

因此,如果一个产品没有复杂到需要拆分成多个组件,并且需要隐藏组件的实现细节,那么使用抽象工厂模式即可,如果构建一个大型产品,它是由多个组件组合而成,组件高度内聚、独立构建,那么,Builder 模式将会是非常合适的选择

 






技术帖      龙潭书斋      面向对象      oop      设计模式      创建型模式      builder      design pattern      生成器     


京ICP备15018585号