一、基本介绍
建造者模式(Build Pattern)又称生成器模式,该模式封装一个产品的构造过程,一步一步地创建出一个复杂的对象,它允许用户通过指定复杂对象的类型和具体内容来构建对象,不需要知道内部的构建细节。
下面是建造者的原理UML类图:
建造者模式的四个角色说明:
- Product(产品对象):一个具体的产品对象
- Builder(抽象建造者):用于创建一个产品对象的各个部件指定的接口/抽象类
- ConcreteBuilder(具体建造者):实现接口,构建和装配产品的各个部件。
- Director(指挥者):用于创建一个复杂的对象,它主要有两个作用:隔离客户与对象的生产过程和负责控制产品对象的生产过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。
二、使用
下面应用建造者模式来解决一个实际案例
1.场景
假设你在广州做一个导游,现在你需要为游客制定多个“羊城一日游”的行程安排的计划,其中每个计划去的景点可能不太一样,另外如果是外地的游客则需要提供旅馆住宿。
所以需要一个弹性的数据结构,代表客人的规划以及所有的变化。要创建这样的计划,就需要使用建造者模式来实现创建复杂的结构的同时,又不会和创建它的步骤混在一起。
2.UML类图设计
其中PlanA不包含住宿,PlanB包含住宿
3.代码实现
Plan类,担当产品对象的角色
public class Plan {
public String morningPlan;
public String afternoonPlan;
public String nightPlan;
public String sleepPlan;
//省略getter和setter方法
//省略toString方法
Director类,担当指挥者的角色
public class Director {
AbstractBuilder planBuilder;
public Director(AbstractBuilder planBuilder) {
// TODO Auto-generated constructor stub
this.planBuilder = planBuilder;
}
public void setPlanBuilder(AbstractBuilder planBuilder) {
this.planBuilder = planBuilder;
}
public Plan constructPlan() {
System.out.println("计划制定:");
planBuilder.arrangeMorning();
planBuilder.arrangeAfternoon();
planBuilder.arrangeNight();
planBuilder.arrangeSleep();
return planBuilder.getPlan();
}
}
AbstractPlanBuilder类,担当抽象建造者的角色
public abstract class AbstractBuilder {
Plan plan = new Plan();
public abstract void arrangeMorning();
public abstract void arrangeAfternoon();
public abstract void arrangeNight();
public abstract void arrangeSleep();
public Plan getPlan() {
return plan;
}
}
PlanABuilder,PlanBBuilder都担当具体建造者的角色
public class PlanABuilder extends AbstractBuilder {
@Override
public void arrangeMorning() {
// TODO Auto-generated method stub
plan.setMorningPlan("出发时间:8:00,目的地:白云山");
System.out.println("已制定好早上的计划");
}
@Override
public void arrangeAfternoon() {
// TODO Auto-generated method stub
plan.setAfternoonPlan("出发时间:12:00,目的地:长隆欢乐世界");
System.out.println("已制定好下午的计划");
}
@Override
public void arrangeNight() {
// TODO Auto-generated method stub
plan.setNightPlan("出发时间: 20:00 ,目的地:广州塔");
System.out.println("已制定好晚上的计划");
}
@Override
public void arrangeSleep() {
System.out.println("该计划不安排住宿");
}
}
public class PlanBBuilder extends AbstractBuilder {
@Override
public void arrangeMorning() {
// TODO Auto-generated method stub
plan.setMorningPlan("出发时间:8:15,目的地:大夫山");
System.out.println("已制定好早上的计划");
}
@Override
public void arrangeAfternoon() {
// TODO Auto-generated method stub
plan.setAfternoonPlan("出发时间:11:30,目的地:长隆欢乐世界");
System.out.println("已制定好下午的计划");
}
@Override
public void arrangeNight() {
// TODO Auto-generated method stub
plan.setNightPlan("出发时间: 20:00 ,目的地:广州塔");
System.out.println("已制定好晚上的计划");
}
@Override
public void arrangeSleep() {
System.out.println("晚上住宿在白天鹅宾馆");
}
}
Client类作为客户类,只需调用Director的consructPlan即可完成计划对象的创建
public class Client {
public static void main(String[] args) {
Director director = new Director(new PlanABuilder());
Plan p1 = director.constructPlan();
System.out.println("计划详情:" + p1);
director.setPlanBuilder(new PlanBBuilder());
Plan p2 = director.constructPlan();
System.out.println("计划详情:" + p2);
}
}
程序运行结果:
三、典型应用
java.lang.StringBuilder中的建造者模式
StringBuilder类的继承和实现关系,如图:
Appendable接口定义了三个append方法,其担当抽象建造者的角色,定义了抽象方法。
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
AbstractStringBuilder实现了Appendable接口的三个方法,AbstractStringBuilder类担当了具体建造者的角色,当由于其是抽象类,所以不能实例化。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
//省略
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
//省略
}
StringBuilder即担当了指挥者角色,同时担当了具体的建造者的角色,还是产品对象。其建造方法是由其父类AbstractStringBuilder来实现,下面三个重载的append方法都是建造方法,只不过比较简单,只有一个方法调用。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
//省略
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
@Override
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
@Override
public StringBuilder append(char c) {
super.append(c);
return this;
}
}
//省略
四、总结
- 建造者模式的优点:
- 在建造者模式中,向客户端隐藏产品内部的表现,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
- 建造者模式的缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
- 建造者模式与抽象工厂模式的比较:
与抽象工厂模式相比, 建造者模式返回一个组装好的完整产品 ,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
如果将抽象工厂模式看成 汽车配件生产工厂 ,生产一个产品族的产品,那么建造者模式就是一个 汽车组装工厂 ,通过对部件的组装可以返回一辆完整的汽车。
参考:
1.4.建造者模式
2.设计模式 | 建造者模式及典型应用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44325.html