浅谈建造者模式(Builder Pattern)

149 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

我们首先来谈谈平常我们使用Javabean的方式:要么直接用getter/setter,要么用构造器

class test{
    public static void main(String[] args) {
        Hero hero1 = new Hero();
        hero1.setName("关羽");
        hero1.setMount("赤兔");
        hero1.setWeapon("青龙偃月刀");
        hero1.setRoute("上路");
        System.out.println(hero1.toString());

        Hero hero2 = new Hero("莫邪", "中路", "雌雄剑", "干将");
        System.out.println(hero2.toString());
    }
}

image.png

// 英雄
class hero{
    // 名称
    private String name;

    // 路线
    private String route;

    // 武器
    private String weapon;

    // 坐骑
    private String mount;
    
    //setter/getter跟构造器方法就省略了,影响阅读
    
    @Override
    public String toString() {
        return "骑着 " + mount + " 拿着 " + weapon + " 走 " + route + " 的是: " + name;
    }
}
  1. 使用Setter方式缺陷很明显:每新增一个属性都要新增一行代码(hero.setter()),属性少的时候无所谓,但是实际开发中属性一箩筐的时候就很烦人。\
  2. 而使用构造器明面上看不出来有什么缺陷,就一行代码嘛,但是要注意,一般我们用构造器都是要所有属性都选上,比如上面英雄class,名称、武器、坐骑、路线在构造的时候都要选上;如果我们想定制,那么好,再重载一下构造方法;如果我们新增一个属性,那么好,又新增一个构造方法;把开发人员的时间都浪费在这种枯燥的代码上。

image.png

有这个时间还不如整个方便的方法(比如用Lombok插件,当然了,这里不详谈),把时间空出来跟小姐姐聊天

image.png

那么在这种情况下可以用到建造者模式:建造者模式属于创建型模式(跟前面的工厂模式、单例模式一样),以前经常了解的都是传统的建造者模式,不过现在流行的是链式;下面都会讲到;

一、传统的建造者模式

传统的建造者模式有四种角色:

  • 产品(Product):具体生产器要构造的复杂对象
  • 抽象建造者(Bulider):抽象建造者是一个接口,创建一个产品各个部件的接口方法,以及返回产品的方法
  • 具体建造者(ConcreteBuilder):按照自己的产品特性,实现抽象建造者对应的接口
  • 指挥者(Director):创建一个复杂的对象,控制具体的流程

image.png

还是直接用实例来讲吧:

(一)产品(Product):就是最终被操作的对象,比如这里每个英雄都可以命名、设置在峡谷走什么线、拿什么武器、有无坐骑等

// 英雄
class hero{
    // 名称
    private String name;

    // 路线
    private String route;

    // 武器
    private String weapon;

    // 坐骑
    private String mount;
    
    //setter/getter跟构造器方法就省略了,影响阅读
   
}

(二)抽象建造者(Bulider):顾名思义--“抽象”,一般是接口,在实际中没什么意思,要搭配具体建造者(ConcreteBuilder)来看,在这里具体建造者可以理解为 游戏策划 就是给 英雄建立形象的,那么问题来了,不同的游戏策划有不一样的想法咯,比如有的策划师整出个百里守约阴人,有的策划师整出个非ban必选的东皇太一,但是最根本的还是要根据基底产品(英雄的必要属性)来,所以抽象出一个建造者

// 游戏策划师
interface GamePlanner {
    // 设计名称
    GamePlanner designName();
    // 设计路线
    GamePlanner designRoute();
    // 设计武器
    GamePlanner designWeapon();
    // 设计坐骑
    GamePlanner designMount();
    // 创建英雄
    hero createHero();
}

(三)具体建造者(ConcreteBuilder):不一样的游戏策划需要根据不同的想法来设计一个具体的英雄,当然了,这里只写了一个具体建造者,实际中可以有多个,比如这里还可以根据不同产品有不一样的设计(设计技能啥的)

// 策略风格的游戏策划
class StrategyGamePlanner implements GamePlanner {
    private Hero hero;

    public StrategyGamePlanner(Hero hero) {
        this.hero = hero;
    }

    @Override
    public GamePlanner designName(String name) {
        hero.setName(name);
        return this;
    }

    @Override
    public GamePlanner designRoute(String route) {
        hero.setRoute(route);
        return this;
    }

    @Override
    public GamePlanner designWeapon(String weapon) {
        hero.setWeapon(weapon);
        return this;
    }

    @Override
    public GamePlanner designMount(String mount) {
        hero.setMount(mount);
        return this;
    }

    @Override
    public Hero createHero() {
        return hero;
    }
}

(四)指挥者(Director):有什么需求根据项目经理来就对了

// 项目经理
class ProjectManager {
    //根据给定的建造者建造不同的房子
    public Hero buildA(StrategyGamePlanner strategyGamePlanner) {
        strategyGamePlanner.designName("关羽").designRoute("上单").designWeapon("青龙偃月刀").designMount("赤兔");
        return strategyGamePlanner.createHero();
    }

    public Hero buildB(StrategyGamePlanner strategyGamePlanner) {
        strategyGamePlanner.designName("百里守约").designRoute("发育路").designWeapon("巴雷特");
        return strategyGamePlanner.createHero();
    }
}

最终:玩家想玩关羽

// 玩家
class Player {
    public static void main(String[] args) {
        // 具体策划师
        StrategyGamePlanner strategyGamePlanner = new StrategyGamePlanner(new Hero());
        // 项目经理
        ProjectManager projectManager = new ProjectManager();
        // 项目经理让策划师设计英雄
        Hero hero = projectManager.buildA(strategyGamePlanner);
        System.out.println(hero.toString());
    }
}

image.png

类图如下:

image.png

二、流行的链式编程,其实上面就已经有用到了,不过在实际开发中我们也经常使用,比如:

(1)StringBuilder类

StringBuilder builder = new StringBuilder();
 builder.append("我是")
     .append("大")
     .append("帅哥");

(2)JDK8之后的新特性流式编程

List<Long> aList = BList.stream().map(UserVO::getId).collect(Collectors.toList());

(3)Lombok的@Builder注解,当然,这个注解跟下面代码同理,简单来说就是创建一个内部类帮我们完成设置属性

// 英雄
class Hero{
    private String name;
    private String route;
    private String weapon;
    private String mount;

    public Hero(String name, String route, String weapon, String mount) {
        this.name = name;
        this.route = route;
        this.weapon = weapon;
        this.mount = mount;
    }

    public static Hero.HeroBuilder builder() {
        return new Hero.HeroBuilder();
    }

    public static class HeroBuilder {
        private String name;
        private String route;
        private String weapon;
        private String mount;
        HeroBuilder() {}

        public Hero.HeroBuilder name(String name) {
            this.name = name;
            return this;
        }
        public Hero.HeroBuilder route(String route) {
            this.route = route;
            return this;
        }
        public Hero.HeroBuilder weapon(String weapon) {
            this.weapon = weapon;
            return this;
        }
        public Hero.HeroBuilder mount(String mount) {
            this.mount = mount;
            return this;
        }
        public Hero build() {
            return new Hero(this.name, this.route, this.weapon, this.mount);
        }
        //toString
    }
}

看似代码量多,但是这样做的好处是,创建hero的时候很方便,当多个地方使用的时候,上面的代码量不值一提

    public static void main(String[] args) {
        Hero build = Hero.builder().name("孙策").route("对抗路").weapon("船锚").mount("泰坦尼克号").build();
    }