Builder模式

Builder模式

定义

将一个复杂对象的构建与它的表象分离,使得一样的构建能够建立不一样的表示。bash

使用场景

相同的方法,不一样的执行顺序,产生不一样的事件结果时app

多个部件或零件,均可以装配到一个对象中,可是产生的运行结果又不相同时ide

产品类很是复杂,或者产品类中的调用顺序产生不一样做用时ui

初始化一个对象特别复杂时this

实现

经典方案

经典的Builder模式通常拥有4个角色spa

  • Product 产品类或产品抽象类
  • Builder 抽象类
  • ConcreteBuilder 具体Builder实现类
  • Director 组装类
public class Meal {
    private  String food;
    private  String drink;

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }
}

复制代码

菜单类Meal在这里充当Product的角色,提供了事物food和饮料drink供客人选择。code

public abstract class MealBuilder {

    protected Meal meal = new Meal();

    public abstract void buildFood();

    public abstract void buildDrink();

    public Meal getMeal() {
        return meal;
    }
}

复制代码

MealBuilder是一个抽象Builder,提供了2个抽象方法buildFood()和buildDrink()供给客人点餐,而食物的品种咱们并不知道,具体的食物和套餐是交给子类去实现的。对象

public class SubMealBuilderA extends MealBuilder {
    @Override
    public void buildFood() {
        meal.setFood("一个鸡腿堡");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("一杯可乐");
    }
}

复制代码

SubMealBuilderA 是做为ConcreteBuilder的,他给出了一个具体实现方案---套餐A( 包含一个鸡腿堡和一杯可乐)事件

public class SubMealBuilderB extends MealBuilder {
    @Override
    public void buildFood() {
        meal.setFood("一个鸡肉卷");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("一杯果汁");
    }
}

复制代码

SubMealBuilderB 也是做为ConcreteBuilder的,他给出了一个具体实现方案---套餐B( 包含一个鸡肉卷和一杯果汁)rem

public class Waiter {
    private MealBuilder mealBuilder;

    public void setMealBuilder(MealBuilder mealBuilder) {
        this.mealBuilder = mealBuilder;
    }

    public Meal construct() {
        mealBuilder.buildDrink();
        mealBuilder.buildFood();
        return mealBuilder.getMeal();
    }
}

复制代码

Waiter类充当Director的角色,客人将选好的套餐告诉服务员后,服务员将客人选择的食物组装起来而且通知后厨开始生产。

public class Test {

  public static void main(String[] args) {

    Waiter waiter = new Waiter();
    waiter.setMealBuilder(new SubMealBuilderA());
    Meal meal = waiter.construct();

    log(meal);

    waiter.setMealBuilder(new SubMealBuilderB());
    meal = waiter.construct();

    log(meal);
  }

  private static void log(Meal meal) {
    System.out.println(meal.getClass() + ":" + meal.getFood() + ":" + meal.getDrink());
  }
}

复制代码

运行结果

class com.example.jc.myapplication.builder.custom.Meal:一个鸡腿堡:一杯可乐
class com.example.jc.myapplication.builder.custom.Meal:一个鸡肉卷:一杯果汁
复制代码

改进方案

在实际中咱们更常见的Builder模式是有对经典模式又必定修改的。如AlertDialog的Builer模式,它的AlertDialog.Builer同时扮演了Builder、 ConcreteBuilder、 Director的角色,简化了Builer的使用。同时这种形式的Builer也更常见。

public class WaiterX {
  private MenuController mWaiter;

  WaiterX() {
    mWaiter = new MenuController();
  }

  public static class Builder {
    private MenuController.MenuParams p;

    public Builder() {
      p = new MenuController.MenuParams();
    }

    public Builder setFood(String food) {
      p.food = food;
      return this;
    }

    public Builder setDrink(String drink) {
      p.drink = drink;
      return this;
    }

    public Builder setCount(int count) {
      p.count = count;
      return this;
    }

    public Builder setType(int type) {
      p.type = type;
      return this;
    }

    public Builder setRemark(String remark) {
      p.remark = remark;
      return this;
    }

    public WaiterX create() {
      WaiterX waiter = new WaiterX();
      p.apply(waiter.mWaiter);
      return waiter;
    }

  }

  @Override public String toString() {
    return "WaiterX{" + "mWaiter=" + mWaiter + '}';
  }
}

复制代码

在WaiterX中经过一个静态内部类Builder来负责调度产品的组件,再经过create()方法将组装后的总体返回。而具体的装细节被隐藏了,经过交由MenuController.MenuParams去专门处理细节信息。

public class MenuController {

  private String food;
  private String drink;
  /**
   * 点单分量
   */
  private int count;
  /**
   * 类型  外带,食堂
   */
  private int type;
  /**
   * 备注
   */
  private String remark;

  public void setFood(String food) {
    this.food = food;
  }

  public void setDrink(String drink) {
    this.drink = drink;
  }

  public void setCount(int count) {
    this.count = count;
  }

  public void setType(int type) {
    this.type = type;
  }

  public void setRemark(String remark) {
    this.remark = remark;
  }

  public static class MenuParams {

    public String food;
    public String drink;
    public int count;
    public int type;
    public String remark;

    public void apply(MenuController waiter) {
      if (drink != null) {
        waiter.setDrink(drink);
      }
      if (food != null) {
        waiter.setFood(food);
      }
      if (count > 1) {
        waiter.setCount(count);
      } else {
        waiter.setCount(1);
      }
      if (type > 0) {
        waiter.setType(type);
      } else {
        waiter.setType(0);
      }

      if (remark != null) {
        waiter.setRemark(remark);
      }
    }
  }

  @Override public String toString() {
    return "MenuController{" + (food != null ? "food='" + food + '\'' : "") + (drink != null ? ", drink='" + drink + '\'' : "") + (count > 0 ? ", count='" + count + '\'' : "")

        + (type > 0 ? ", type='" + type + '\'' : "") + (remark != null ? ", remark='" + remark + '\'' : "") + '}'; } } 复制代码

MenuController负责餐点的具体细节处理,用于记录客户具体点了什么食物、分量、需求等信息

public class Test {
  public static void main(String[] args) {
    WaiterX waiterX = new WaiterX.Builder().setFood("香辣鸡腿堡")
        .setDrink("可乐")
        .setType(2)
        .setRemark("少盐")
        .setCount(3)
        .create();
    System.out.println(waiterX.toString());

  }
}

复制代码

结果

WaiterX{mWaiter=MenuController{food='香辣鸡腿堡', drink='可乐', count='3', type='2', remark='少盐'}}
复制代码

经过这种形式,咱们在调用的时候就能采用链式调用的方式来处理了,使用起来更加方便。

缺点是 当有新的元素或者新的属性须要添加时候,须要对多个类进行修改。如 要添加一个字段 取餐时间time,则WaiterX和MenuController中都必须对这个字段time进行支持,这就务必要对2个类进行修改了。

相关文章
相关标签/搜索