一、场景问题
大家很多人应该都玩过LOL
(英雄联盟)这款游戏,博主从S4
赛季一直玩到现在,可以说这块游戏陪伴了我整个大学生涯。正好现在S10
入围赛已经快接近尾声,即将到精彩的小组赛了。当前这篇文章不是讨论游戏哈。大家在刚接触这款游戏时,系统推荐的游戏角色最多的应该都是流浪法师、寒冰射手、德玛西亚之力这三个新手英雄。所以业务场景就是设计程序构造这三种角色(法师、射手和战士)。
二、解决方案
1、传统解决思路
为了方便讲解,我们使用简单工厂模式来实现。
1.1、代码展示
-
游戏角色实体类
public class GameRole { /** * 角色名称 */ private String name; /** * 角色定位 */ private String rolePosition; /** * 头发 */ private String hair; /** * 性别 */ private String sex; /** * 衣服 */ private String clothes; /** * 武器 */ private String arms; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRolePosition() { return rolePosition; } public void setRolePosition(String rolePosition) { this.rolePosition = rolePosition; } public String getHair() { return hair; } public void setHair(String hair) { this.hair = hair; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getClothes() { return clothes; } public void setClothes(String clothes) { this.clothes = clothes; } public String getArms() { return arms; } public void setArms(String arms) { this.arms = arms; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(name).append('\n'); sb.append("角色定位:").append(rolePosition).append('\n'); sb.append("头发:").append(hair).append('\n'); sb.append("性别:").append(sex).append('\n'); sb.append("服装:").append(clothes).append('\n'); sb.append("武器:").append(arms).append('\n'); return sb.toString(); } }
-
角色工厂类
public class GameRoleFactory { /** * 构造角色 * @author xianzilei **/ public static GameRole construct(String type) { //1-法师 2-射手 3-战士 if ("1".equals(type)) { GameRole gameRole = new GameRole(); gameRole.setName("瑞兹"); gameRole.setRolePosition("法师"); gameRole.setHair("光头"); gameRole.setSex("男"); gameRole.setClothes("法师服"); gameRole.setArms("卷轴"); return gameRole; } else if ("2".equals(type)) { GameRole gameRole = new GameRole(); gameRole.setName("寒冰"); gameRole.setRolePosition("射手"); gameRole.setHair("白色头发"); gameRole.setSex("女"); gameRole.setClothes("女王嫁衣"); gameRole.setArms("水晶箭"); return gameRole; } else if ("3".equals(type)) { GameRole gameRole = new GameRole(); gameRole.setName("盖伦"); gameRole.setRolePosition("战士"); gameRole.setHair("黑发"); gameRole.setSex("男"); gameRole.setClothes("战士盔甲"); gameRole.setArms("大宝剑"); return gameRole; } else { throw new RuntimeException("不支持的类型"); } } }
根据不同的类型创建不同的角色。
1.2、客户端测试
public static void main(String[] args) {
//1-法师 2-射手 3-战士
//构建法师角色
GameRole gameRole = GameRoleFactory.construct("1");
System.out.println(gameRole);
//构建射手角色
GameRole gameRole2 = GameRoleFactory.construct("2");
System.out.println(gameRole2);
//构建战士角色
GameRole gameRole3 = GameRoleFactory.construct("3");
System.out.println(gameRole3);
}
输出
瑞兹
角色定位:法师
头发:光头
性别:男
服装:法师服
武器:卷轴
寒冰
角色定位:射手
头发:白色头发
性别:女
服装:女王嫁衣
武器:水晶箭
盖伦
角色定位:战士
头发:黑发
性别:男
服装:战士盔甲
武器:大宝剑
1.3、弊端分析
- 创建代码冗余
- 仅适用于创建简单的对象,对于复杂对象会导致代码臃肿。
- 对象与对象的构造没有完全分离
- 扩展时违反开闭原则
2、建造者模式
基于上面的弊端,我们可以通过建造者模式解决
2.1、代码修改
-
游戏角色实体类不变
-
抽象游戏角色构造器
public abstract class AbstractGameRoleBuilder { //游戏角色 protected GameRole gameRole = new GameRole(); //构造角色地位 public abstract void buildRolePosition(); //构造头发 public abstract void buildHair(); //构造性别 public abstract void buildSex(); //组装衣服 public abstract void buildClothes(); //组装武器 public abstract void buildArms(); //返回构造的实例 public GameRole getGameRole() { return gameRole; } }
方法中定义构建角色的各个部分的构造方法,不仅将对象的表示和构造分离,也将各部分的构造分离
-
具体游戏角色构造器
-
法师角色构造器
public class MageGameRoleBuilder extends AbstractGameRoleBuilder { public MageGameRoleBuilder() { super.gameRole.setName("瑞兹"); } @Override public void buildRolePosition() { gameRole.setRolePosition("法师"); } @Override public void buildHair() { gameRole.setHair("光头"); } @Override public void buildSex() { gameRole.setSex("男"); } @Override public void buildClothes() { gameRole.setClothes("法师服"); } @Override public void buildArms() { gameRole.setArms("卷轴"); } }
-
射手角色构造器
public class ShooterGameRoleBuilder extends AbstractGameRoleBuilder { public ShooterGameRoleBuilder() { super.gameRole.setName("寒冰"); } @Override public void buildRolePosition() { gameRole.setRolePosition("射手"); } @Override public void buildHair() { gameRole.setHair("白色头发"); } @Override public void buildSex() { gameRole.setSex("女"); } @Override public void buildClothes() { gameRole.setClothes("女王嫁衣"); } @Override public void buildArms() { gameRole.setArms("水晶箭"); } }
-
战士射手角色构造器
public class WarriorGameRoleBuilder extends AbstractGameRoleBuilder { public WarriorGameRoleBuilder() { super.gameRole.setName("盖伦"); } @Override public void buildRolePosition() { gameRole.setRolePosition("战士"); } @Override public void buildHair() { gameRole.setHair("黑发"); } @Override public void buildSex() { gameRole.setSex("男"); } @Override public void buildClothes() { gameRole.setClothes("战士盔甲"); } @Override public void buildArms() { gameRole.setArms("大宝剑"); } }
将三种角色的构造分离,便于扩展
-
-
游戏角色构造指挥器
public class GameRoleDirector { //构造器 private AbstractGameRoleBuilder builder; public GameRoleDirector(AbstractGameRoleBuilder builder) { this.builder = builder; } public void setBuilder(AbstractGameRoleBuilder builder) { this.builder = builder; } /** * 构建角色 * @author xianzilei **/ public GameRole construct() { builder.buildRolePosition(); builder.buildHair(); builder.buildSex(); builder.buildArms(); builder.buildClothes(); return builder.getGameRole(); } }
与客户端直接交互,负责根据不同的构造器创建不同的对象实例。
2.2、客户端测试
public static void main(String[] args) {
//构建法师角色
AbstractGameRoleBuilder gameRoleBuilder = new MageGameRoleBuilder();
GameRoleDirector director = new GameRoleDirector(gameRoleBuilder);
GameRole gameRole = director.construct();
System.out.println(gameRole);
//构建射手角色
AbstractGameRoleBuilder gameRoleBuilder2 = new ShooterGameRoleBuilder();
director.setBuilder(gameRoleBuilder2);
GameRole gameRole2 = director.construct();
System.out.println(gameRole2);
//构建战士角色
AbstractGameRoleBuilder gameRoleBuilder3 = new WarriorGameRoleBuilder();
director.setBuilder(gameRoleBuilder3);
GameRole gameRole3 = director.construct();
System.out.println(gameRole3);
}
输出
瑞兹
角色定位:法师
头发:光头
性别:男
服装:法师服
武器:卷轴
寒冰
角色定位:射手
头发:白色头发
性别:女
服装:女王嫁衣
武器:水晶箭
盖伦
角色定位:战士
头发:黑发
性别:男
服装:战士盔甲
武器:大宝剑
2.3、代码分析
- 各类游戏角色的构造过程分离,便于扩展
- 角色的各部分构造分离,便于拆分组合
三、模式概述
1、模式定义
建造者模式(Builder Pattern):指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。
建造者模式的核心:构造与表示分离
2、模式结构
-
产品角色(Product)
这里的产品角色指的是包含多个组件的复杂对象,对象的创建由各个构造器来创建
-
抽象建造者(Builder)
生成器接口,包含创建产品各个子部件的抽象方法,一般也包含返回产品的方法(可抽象可实现)。
-
具体建造者(Concrete Builder)
生成器接口实现,根据不同的类型构造不同的产品组件
-
指挥者(Director)
保存构造器引用,根据不同类型的构造器组装组件,从而完成复杂对象的创建,构造过程不涉及具体的产品信息。是客户端的交互对象。
3、模式结构图
4、优缺点
- 优点
- 产品的构建与表示分离,从而使构建算法可以复用,同时也可以灵活切换具体的产品。
- 指挥器与具体的构造器没有关联,即具体的构造细节对指挥器是透明的,如果想要改变产品的内部细节,是无需修改指挥器,只需修改具体的构造器内部即可。
- 方便新的构造器扩展,遵循开闭原则。
- 缺点
- 产品的组成部分需要相同才能使用
- 增加产品的类型就需要新增与之对应的构造器
- 产品发生了变化,与之对应的构造器就需要同步修改,维护成本大。
5、使用场景
- 对象内部结构复杂且具有共性。
- 需要隔离复杂对象的表示和创建过程。