原创

设计模式详解(五)——建造者模式


一、场景问题

大家很多人应该都玩过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、使用场景

  • 对象内部结构复杂且具有共性。
  • 需要隔离复杂对象的表示和创建过程。
Java
设计模式
  • 作者:贤子磊
  • 发表时间:2021-06-24 21:42
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 评论

    您需要登录后才可以评论