设计模式 - 建造者模式
定义:将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式将一个复杂对象的构建步骤和步骤中需要执行的细节分开,将构建的步骤交给一个指挥者类,而需要执行的细节由不同的类去实现,这样客户端只需要指定执行细节的类,不需要知道构建步骤和执行细节,就能创建出一个我们需要的复杂对象,以下是建造者模式的结构类图
其中将构建产品的步骤在Director中,而每个步骤的细节则在具体的建造者类ConcreteBuilderA和ConcreteBuilderB中,所以客户端只需要知道使用哪个具体的建造者类,就能构建出需要的产品,接下来我们来看下具体的代码实现
产品类
public class Product { private List<String> parts = new ArrayList<>(); public void add(String part) { parts.add(part); } public void show() { System.out.println("开始创建:"); for (String part : parts) { System.out.println(part); } } }
建造者接口
public interface Builder { /** * 建造部件1 */ void buildPart1(); /** * 建造部件2 */ void buildPart2(); /** * 获取构建好的产品对象 * * @return 产品对象 */ Product getResult(); }
两个具体的建造者类
public class ConcreteBuilderA implements Builder { private Product product = new Product(); @Override public void buildPart1() { product.add("部件A1"); } @Override public void buildPart2() { product.add("部件A2"); } @Override public Product getResult() { return product; } }
public class ConcreteBuilderB implements Builder { private Product product = new Product(); @Override public void buildPart1() { product.add("部件B1"); } @Override public void buildPart2() { product.add("部件B2"); } @Override public Product getResult() { return product; } }
指挥者类
public class Director { /** * 产品构建步骤 * * @param builder 构建者 */ public void construct(Builder builder) { builder.buildPart1(); builder.buildPart2(); } }
测试代码的调用和输出结果
Director director = new Director(); Builder builderA = new ConcreteBuilderA(); director.construct(builderA); Product productA = builderA.getResult(); productA.show(); System.out.println("-------"); Builder builderB = new ConcreteBuilderB(); director.construct(builderB); Product productB = builderB.getResult(); productB.show();
以上便是结构类图的代码实现,接下来从不使用和使用建造者模式两种方式来实现一个功能,来体现该模式的优点,这个功能是一个人创建某个游戏的两个账号,先不使用建造者模式来实现,代码如下
账号类
public class GameAccount { private String type; private String areaCode; private String nickname; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getAreaCode() { return areaCode; } public void setAreaCode(String areaCode) { this.areaCode = areaCode; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } @Override public String toString() { return "账号信息:\n" + type + "\n" + areaCode + "\n" + nickname; } }
账号创建抽象类
public interface AccountBuilder { /** * 选择账号类型 */ void chooseType(); /** * 选择区号 */ void chooseAreaCode(); /** * 填写昵称 */ void fillNickname(); /** * 获取账号对象 * * @return 账号对象 */ GameAccount getGameAccount(); }
用来创建两种不同账号的类
public class AccountBuilderA implements AccountBuilder { private GameAccount gameAccount = new GameAccount(); @Override public void chooseType() { gameAccount.setType("类型:微信"); } @Override public void chooseAreaCode() { gameAccount.setAreaCode("区号:47区"); } @Override public void fillNickname() { gameAccount.setNickname("昵称:打野送人头"); } @Override public GameAccount getGameAccount() { return gameAccount; } }
public class AccountBuilderB implements AccountBuilder { private GameAccount gameAccount = new GameAccount(); @Override public void chooseType() { gameAccount.setType("类型:QQ"); } @Override public void chooseAreaCode() { gameAccount.setAreaCode("区号:49区"); } @Override public void fillNickname() { gameAccount.setNickname("昵称:辅助抢人头"); } @Override public GameAccount getGameAccount() { return gameAccount; } }
创建账号的代码
AccountBuilder builderA = new AccountBuilderA(); builderA.chooseType(); builderA.chooseAreaCode(); builderA.fillNickname(); System.out.println(builderA.getGameAccount()); System.out.println("--------------"); AccountBuilder builderB = new AccountBuilderB(); builderB.chooseType(); builderB.chooseAreaCode(); builderB.fillNickname(); System.out.println(builderB.getGameAccount());
输出结果
从创建账号的代码可以看出,若另一个人也需要创建这两种账号,那创建账号的代码就得重复编写,当然也可以将创建账号步骤的代码移到构建账号的类中,也就是将选择账号类型、选择区号和填写昵称三个步骤封装到某个方法中,代码很简单就不贴出来了。这样虽然能解决上面提到的重复的问题,但是构建的步骤仍需要在两个创建账号的类中编写,这里只有两个类而且步骤只有三个不容易出现错误,若步骤变多或者类变多,在编写的时候可能就会因为大意将其中的步骤顺序写错甚至缺失或重复某个步骤,导致最终的结果有误。可以看出不管创建什么账号步骤都是一致的,可以使用建造者模式来实现这一功能,将会避免发生以上错误,只需要在原来的基础上增加一个创建账号的指挥者类
public class AccountDirector { /** * 创建账号 * * @param accountBuilder 账号创建者 */ public void createAccount(AccountBuilder accountBuilder) { accountBuilder.chooseType(); accountBuilder.chooseAreaCode(); accountBuilder.fillNickname(); } }
创建账号的代码变成如下所示
AccountDirector director = new AccountDirector(); AccountBuilder builderA = new AccountBuilderA(); director.createAccount(builderA); System.out.println(builderA.getGameAccount()); System.out.println("-------------"); AccountBuilder builderB = new AccountBuilderB(); director.createAccount(builderB); System.out.println(builderB.getGameAccount());
输出与上面一致,以上就是整个功能的实现代码,可以通过点击以下链接获取代码和类图