创建型模式

2017-03-12 18:24:27   最后更新: 2017-03-21 13:49:19   访问数量:457




创建模式是对对象实例化的抽象,他们帮助一个系统独立于如何创建、组合和表示系统中的对象,而是将对象实例化工作委托给另一个对象

这些创建型模式有一个共同的特点,就是他们封装了系统使用哪些具体的类的信息,从而隐藏了这些类的实例是如何被创建和组合的,整个系统仅仅知道这些对象由抽象类所定义的接口

创建型模式在创建什么、谁来创建、何时创建都给于了开发人员很大的灵活性,你可以在编译时或是运行时将多个结构不同功能各异的对象配置成为一个系统

 

共有五个创建型模式:

抽象工厂模式 -- AbstractFactory

生成器模式 -- Builder

工厂方法 -- Factory Method

原型模式 -- Prototype

单例模式 -- Singleton

 

接下来的日志中,我们就会介绍上述的五种模式,每个模式都有着适合自己的场景,为了展现他们适用的场景,我们就需要一个实际的例子来说明一下

 

在接下来的日志中,我们将用 java 语言来展现一个迷宫游戏 MazeGame 的创建过程,之所以使用 java 而不是原著中的 c++ 语言,主要是由于 java 语言的面向对象能力以及 java 的火热程度,并且比之多年未用的 C++,java 对于笔者也更为熟悉一些,同时,也给与读到博文的同学多一种理解的示例

那么,如果需要创建这样一个迷宫游戏,我们需要怎么构建这套系统呢?

我们将要创建的迷宫游戏类似于下面这个样子:

 

如图所示,我们的迷宫由房间、墙、门三个基本元素构成,游戏者只能见到该游戏的局部,从一个房间走到另一个房间,而房间的四面可能由墙、门或另一个房间构成,而门具备开着或关着两种属性

 

那么,接下来,我们用 java 来实现这个系统:

 

DirectionEnum

首先我们需要一个描述方向的 enum

package com.techlog.designpattern.mazegame.constant; /** * * Created by techlog on 2017/3/12. */ public enum DirectionEnum { NORTH, EAST, SOUTH, WEST }

 

 

AbstractModel

我们的房间、门和墙都是游戏的基本元素,因此我们需要一个基本元素类作为基类派生出他们,这样,他们就可以抽象的表示出元素之间的位置关系了

这里我们使用接口进行实现:

package com.techlog.designpattern.mazegame.model; /** * 2017/3/12. * Created by techlog */ public abstract class AbstractModel { public abstract void enter(); }

 

 

Room

package com.techlog.designpattern.mazegame.model; import com.techlog.designpattern.mazegame.constant.DirectionEnum; import java.util.Map; /** * 2017/3/12. * Created by techlog */ public class Room extends AbstractModel { @Override public void enter() { System.out.println("enter room " + roomNO); } public AbstractModel getSide(DirectionEnum directionEnum) { return sides == null ? null : sides.get(directionEnum); } public void setSides(Map<DirectionEnum, AbstractModel> sides) { this.sides = sides; } public void setRoomNO(int roomNO) { this.roomNO = roomNO; } public int getRoomNO() { return roomNO; } private Map<DirectionEnum, AbstractModel> sides; private int roomNO; }

 

对于 room,首先我们定义了两个成员,分别是用来描述他四周相邻模块的 sides 与唯一标记房间的房间号 roomNO

 

Wall

对于 Wall 就很简单了,我们既不需要定义 Wall 的标记号,也不关心他的相邻关系

package com.techlog.designpattern.mazegame.model; /** * 2017/3/12. * Created by techlog */ public class Wall extends AbstractModel { @Override public void enter() { System.out.println("Enter Denied"); } }

 

 

Door

门的实现也很简单,我们需要门具有是否开着的属性,和两侧 Room 的引用:

package com.techlog.designpattern.mazegame.model; /** * 2017/3/12. * Created by techlog */ public class Door extends AbstractModel { @Override public void enter() { if (isOpen) { System.out.println("Enter Room Success"); } else { System.out.println("Enter Denied"); } } 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; } public boolean isOpen() { return isOpen; } public void setOpen(boolean open) { isOpen = open; } private Room room1; private Room room2; private boolean isOpen; }

 

 

Maze

我们已经定义好了游戏中的全部三个元素,那么接下来就是使用一个类来将他们联系起来了

package com.techlog.designpattern.mazegame.model; import java.util.List; /** * 2017/3/12. * Created by techlog */ public class Maze { public void addModel(AbstractModel modelIface) { modelList.add(modelIface); } public Room getRoomByNO(int number) { for (AbstractModel modelIface : modelList) { if (modelIface instanceof Room && ((Room) modelIface).getRoomNO() == number) { return (Room) modelIface; } } return null; } private List<AbstractModel> modelList; }

 

他保存了游戏的全部元素,并且实现了 addModel 方法,用来添加元素来扩展我们的游戏地图

 

MazeGameService

接下来,就让我们来创建一个 MazeGame

package com.techlog.designpattern.mazegame.service; import com.techlog.designpattern.mazegame.constant.DirectionEnum; import com.techlog.designpattern.mazegame.model.*; import java.util.HashMap; import java.util.Map; import static com.techlog.designpattern.mazegame.constant.DirectionEnum.*; /** * 2017/3/12. * Created by techlog */ public class MazeGameService { public Maze createMaze() { Maze maze = new Maze(); Room room1 = new Room(); Room room2 = new Room(); Door door = new Door(); door.setOpen(true); door.setRoom1(room1); door.setRoom2(room2); room1.setRoomNO(1); 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); room2.setRoomNO(2); 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); maze.addModel(room1); maze.addModel(room2); maze.addModel(door); return maze; } }

 

 

上述长长的代码仅仅做了一件事:创建一个具有两个房间的迷宫游戏,显然,他是相当复杂的,我们应该想办法来让这个过程变得简单

同时,我们的 createMaze 方法是非常不灵活的,他对迷宫布局进行了硬编码,我们必须改变这个方法或者重新定义它才能实现布局的调整,我们不能实现重用,同时,产生错误的几率也非常高

创建型模式就是为了使类似的创建设计变得更灵活而诞生的,下面就是几个优化的角度:

  • Factory Method -- 通过调用 Maze 对象的 abstract 方法而不是构造方法来创建房间、门和墙,那么就可以创建一个 Maze 的子类来实现不同的创建方法
  • Abstract Factory -- 通过传递一个对象给 createMaze 方法作为参数,来实现房间、门和墙壁的创建,那么通过传递不同的对象就可以改变创建的对象了
  • Builder -- 如果传递给 createMaze 作为参数的对象具备创建房间、墙壁和门的操作,那么通过集成这个对象,就可以改变迷宫的一些部分或建造方式
  • Prototype -- 如果将房间、墙壁和门的对象参数化,那么只需要拷贝并将这些对象增加到迷宫中就可以改变迷宫的构成
  • Singleton -- 我们需要保证一个游戏中只有一个迷宫,那么我们就要使用 Singleton 模式了

 






技术帖      cpp      龙潭书斋      java      面向对象      oop      prototype      设计模式      创建型模式      sigleton      factory      builder     


京ICP备15018585号