Appearance
建造者模式
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
优点
- 封装性好,构建和表示分离。
- 扩展性好,各个具体建造者相互独立,有利于系统的解耦。
- 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
缺点
- 产生多余的Builder对象。
- 产品内部发生变化,建造者都要修改,成本较大。
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时。
- 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。
- 在对象创建过程中会涉及很多中间步骤,并且这些中间步骤依赖具体逻辑,这个时候使用建造者模式可以很方便地理清逻辑。
- 当使用对象内部状态中如果含有多个参数,这些参数之间相互依赖,必须同时存在时。
代码示例
这里写一个游戏角色,角色分为玩家和怪物 角色有:名字、血量、攻击力、防御力、技能等
要创建的对象
java
package com.design.pattern.builder.one;
import java.util.List;
import java.util.stream.Collectors;
public class Role {
// 角色名
private String name;
// 性别
private String sex;
// 年龄
private int age;
// 门派
private String race;
// 种族
private String sect;
// 技能
private List<String> skills;
// 生命值
private int health;
// 攻击
private int attack;
// 防御
private int defense;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getRace() {
return race;
}
public void setRace(String race) {
this.race = race;
}
public String getSect() {
return sect;
}
public void setSect(String sect) {
this.sect = sect;
}
public List<String> getSkills() {
return skills;
}
public void setSkills(List<String> skills) {
this.skills = skills;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getAttack() {
return attack;
}
public void setAttack(int attack) {
this.attack = attack;
}
public int getDefense() {
return defense;
}
public void setDefense(int defense) {
this.defense = defense;
}
@Override
public String toString() {
String format = "╭───────────────────────────────\n"+
"├角色名称:%s\n"+
"├角色年龄:%d\n"+
"├角色性别:%s\n"+
"├角色门派:%s\n"+
"├角色种族:%s\n"+
"├角色技能:%s\n"+
"├角色生命值:%d\n"+
"├角色攻击力:%d\n"+
"├角色防御力:%d\n"+
"╰───────────────────────────────";
return String.format(format,
this.name,
this.age,
this.sex,
this.sect,
this.race,
String.join(",", this.skills),
this.health,
this.attack,
this.defense);
}
}
抽象建造者
规定建造的步骤
java
package com.design.pattern.builder.one;
public abstract class RoleBuilder {
protected Role role = new Role();
public abstract void buildName();
public abstract void buildAge();
public abstract void buildSex();
public abstract void buildRace();
public abstract void buildSect();
public abstract void buildSkills();
public abstract void buildHealth();
public abstract void buildAttack();
public abstract void buildDefense();
public Role build() {
return role;
}
}
具体建造者
玩家建造者
java
package com.design.pattern.builder.one;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class PlayerBuilder extends RoleBuilder{
private final Random random = new Random();
public char getRandomChineseCharacter() {
// '4E00' 到 '9FA5' 的Unicode码点范围
int base = 0x4E00;
int range = 0x9FA5 - base + 1;
// 生成一个随机索引
int index = (int) (Math.random() * range) + base;
// 将索引转换为字符
return (char) index;
}
private String randomName() {
int len = random.nextInt(3) + 1;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < len; i++) {
stringBuilder.append(getRandomChineseCharacter());
}
return stringBuilder.toString();
}
@Override
public void buildName() {
role.setName(randomName());
}
@Override
public void buildAge() {
role.setAge(random.nextInt(100) + 1);
}
@Override
public void buildSex() {
role.setSex(random.nextInt(2) == 0 ? "男" : "女");
}
@Override
public void buildRace() {
String[] races = { "人族", "妖族", "魔族", "仙族" };
role.setRace(races[random.nextInt(races.length)]);
}
@Override
public void buildSect() {
String[] sects = { "天界", "地界", "海界", "火界" };
role.setSect(sects[random.nextInt(sects.length)]);
}
@Override
public void buildSkills() {
String[] skills = {
"重击", "绝对防御", "红莲业火", "真实之眼", "伪装",
"疾走", "传送", "破甲", "旋风斩", "暴走", "噬魂之手",
"暗影突袭", "太极拳", "八卦掌", "七十二变", "八极拳", "铁山靠",
"立地通天炮", "两仪顶", "九阴白骨爪", "降龙十八掌", "葵花宝典",
"九阳神功", "降龙十八掌", "一阳指", "六脉神剑", "九阴真经",
"凌波微步"
};
int size = random.nextInt(skills.length);
Set<String> set = new HashSet<>();
for(int i = 0; i < size; i++) {
set.add(skills[random.nextInt(skills.length)]);
}
role.setSkills(new ArrayList<>(set));
}
@Override
public void buildHealth() {
role.setHealth(random.nextInt(1000) + 200);
}
@Override
public void buildAttack() {
role.setAttack(random.nextInt(30) + 20);
}
@Override
public void buildDefense() {
role.setDefense(random.nextInt(10) + 10);
}
}
怪物建造者
java
package com.design.pattern.builder.one;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
public class MonsterBuilder extends RoleBuilder{
private final static Map<String, List<String>> raceMap = new HashMap<>();
static {
raceMap.put("哥布林", new ArrayList<>(Collections.singletonList("哥布林")));
raceMap.put("水族", new ArrayList<>(Arrays.asList("鱼妖", "沼泽巨鳄", "利维坦")));
raceMap.put("兽族", new ArrayList<>(Arrays.asList("鸡精", "猪妖", "猫妖", "野狼", "野猪",
"巨兔", "古巨鸡", "九头蛇", "九头鸟", "九尾狐", "熔岩火蜥")));
raceMap.put("虫族", new ArrayList<>(Arrays.asList("蟑螂", "大黄蜂", "巨型蜈蚣", "黄金蟑螂", "蟑螂", "蜂群", "吸血恐蚊")));
}
private final Random random = new Random();
@Override
public void buildName() {
List<String> races = new ArrayList<>(raceMap.keySet());
String race = races.get(random.nextInt(races.size()));
role.setRace(race);
List<String> names = raceMap.get(race);
role.setName(names.get(random.nextInt(names.size())));
}
@Override
public void buildAge() {
role.setAge(random.nextInt(10000));
}
@Override
public void buildSex() {
role.setSex(random.nextInt(2) == 0 ? "雄" : "雌");
}
@Override
public void buildRace() {
// 设置名字的时候设置了
}
@Override
public void buildSect() {
role.setSect("无");
}
@Override
public void buildSkills() {
String[] skills = {
"吸血", "弹跳", "普攻", "海啸", "尖啸", "火焰冲击", "撕咬", "剧毒", "蛰刺",
"魅惑", "闪避", "急速", "毒素免疫", "突进", "飞行", "隐身", "传送", "死亡翻滚",
"突击", "甩尾", "利爪", "咆哮", "爪击", "践踏", "投掷", "棍棒精通"
};
int size = random.nextInt(skills.length);
Set<String> set = new HashSet<>();
for(int i = 0; i < size; i++) {
set.add(skills[random.nextInt(skills.length)]);
}
role.setSkills(new ArrayList<>(set));
}
@Override
public void buildHealth() {
role.setHealth(random.nextInt(100000) + 2000);
}
@Override
public void buildAttack() {
role.setAttack(random.nextInt(300) + 200);
}
@Override
public void buildDefense() {
role.setDefense(random.nextInt(100) + 100);
}
}
导演类
建造者模式中一般会有一个导演类,用于指导建造者如何建造对象。
java
package com.design.pattern.builder.one;
public class Director {
private final RoleBuilder roleBuilder;
public Director(RoleBuilder roleBuilder) {
this.roleBuilder = roleBuilder;
}
public Role createRole() {
roleBuilder.buildName();
roleBuilder.buildAge();
roleBuilder.buildSex();
roleBuilder.buildRace();
roleBuilder.buildSect();
roleBuilder.buildSkills();
roleBuilder.buildHealth();
roleBuilder.buildAttack();
roleBuilder.buildDefense();
return roleBuilder.build();
}
}
使用
java
package com.design.pattern.builder.one;
public class Client {
public static void main(String[] args) {
Director playDirector = new Director(new PlayerBuilder());
Role player = playDirector.createRole();
System.out.println(player.toString());
Director monsterDirector = new Director(new MonsterBuilder());
Role monster = monsterDirector.createRole();
System.out.println(monster.toString());
}
}
运行结果:
txt
╭───────────────────────────────
├角色名称:擣
├角色年龄:40
├角色性别:男
├角色门派:火界
├角色种族:魔族
├角色技能:立地通天炮,七十二变,九阳神功,铁山靠,葵花宝典,绝对防御
├角色生命值:240
├角色攻击力:32
├角色防御力:14
╰───────────────────────────────
╭───────────────────────────────
├角色名称:哥布林
├角色年龄:5296
├角色性别:雌
├角色门派:无
├角色种族:哥布林
├角色技能:剧毒,普攻,蛰刺,甩尾,飞行
├角色生命值:66943
├角色攻击力:253
├角色防御力:178
╰───────────────────────────────