Java设计模式04:常用设计模式之建造者模式(创建型模式)
1. Java之建造者模式(Builder Pattern)
(1)概念:
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 [ 构建与表示分离, 同构建不同表示 ]
与抽象工厂的区别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造模式可以强制实行一种分步骤进行的建造过程。
建造模式是将复杂的内部创建封装在内部,对于外部调用的人来说,只需要传入建造者和建造工具,对于内部是如何建造成成品的,调用者无需关心。
举个简单的例子,如汽车,有很多部件,车轮,方向盘,发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一部汽车,这个装配过程也很复杂(需要很好的组装技术), builder模式就是为了将部件和组装分开。
(2)演示案例代码示例:
我们通过一个例子来引出Builder模式。假设有一个Person类,我们通过该Person类来构建一大批人,这个Person类里有很多属性,最常见的比如name,age,weight,height等等,并且我们允许这些值不被设置,也就是允许为null,该类的定义如下:
public class Person {
private String name;
private int age;
private double height;
private double weight; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public double getHeight() {
return height;
} public void setHeight(double height) {
this.height = height;
} public double getWeight() {
return weight;
} public void setWeight(double weight) {
this.weight = weight;
}
}
然后我们为了方便可能会定义一个构造方法:
public Person(String name, int age, double height, double weight) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
或许为了方便new对象,你还会定义一个空的构造方法:
public Person() {
}
甚至有时候你很懒,只想传部分参数,你还会定义如下类似的构造方法:
public Person(String name) {
this.name = name;
} public Person(String name, int age) {
this.name = name;
this.age = age;
} public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
于是你就可以这样创建各个需要的对象:
Person p1=new Person();
Person p2=new Person("张三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("赵六",17,170,65.4);
可以想象一下这样创建的坏处,最直观的就是四个参数的构造函数的最后面的两个参数到底是什么意思,可读性不怎么好,如果不点击看源码,鬼知道哪个是weight哪个是height。还有一个问题就是当有很多参数时,编写这个构造函数就会显得异常麻烦,这时候如果换一个角度,试试Builder模式,你会发现代码的可读性一下子就上去了。
我们给Person增加一个静态内部类Builder类,并修改Person类的构造函数,代码如下:
public class Person {
private String name;
private int age;
private double height;
private double weight; private Person(Builder builder) {
this.name=builder.name;
this.age=builder.age;
this.height=builder.height;
this.weight=builder.weight;
}
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public double getHeight() {
return height;
} public void setHeight(double height) {
this.height = height;
} public double getWeight() {
return weight;
} public void setWeight(double weight) {
this.weight = weight;
} static class Builder{
private String name;
private int age;
private double height;
private double weight;
public Builder name(String name){
this.name=name;
return this;
}
public Builder age(int age){
this.age=age;
return this;
}
public Builder height(double height){
this.height=height;
return this;
} public Builder weight(double weight){
this.weight=weight;
return this;
} public Person build(){
return new Person(this);
}
}
}
从上面的代码中我们可以看到,我们在Builder类里定义了一份与Person类一模一样的变量,通过一系列的成员函数进行设置属性值,但是返回值都是this,也就是都是Builder对象,最后提供了一个build函数用于创建Person对象,返回的是Person对象,对应的构造函数在Person类中进行定义,也就是构造函数的入参是Builder对象,然后依次对自己的成员变量进行赋值,对应的值都是Builder对象中的值。此外Builder类中的成员函数返回Builder对象自身的另一个作用就是让它支持链式调用,使代码可读性大大增强。
于是我们就可以这样创建Person类:
Person.Builder builder=new Person.Builder();
Person person=builder
.name("张三")
.age(18)
.height(178.5)
.weight(67.4)
.build();
有没有觉得创建过程一下子就变得那么清晰了。对应的值是什么属性一目了然,可读性大大增强。
(3)Builder模式在Android中使用:
其实在Android中, Builder模式也是被大量的运用。
• 比如常见的对话框的创建:
AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("标题")
.setIcon(android.R.drawable.ic_dialog_alert)
.setView(R.layout.myview)
.setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { }
})
.setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { }
})
.create();
dialog.show();
• 其实在java中有两个常见的类也是Builder模式,那就是StringBuilder和StringBuffer,只不过其实现过程简化了一点罢了。
我们再找找Builder模式在各个框架中的应用:
• 如Gson中的GsonBuilder,代码太长了,就不贴了,有兴趣自己去看源码,这里只贴出其Builder的使用方法:
GsonBuilder builder=new GsonBuilder();
Gson gson=builder.setPrettyPrinting()
.disableHtmlEscaping()
.generateNonExecutableJson()
.serializeNulls()
.create();
• 再看看著名的网络请求框架OkHttp:
Request.Builder builder=new Request.Builder();
Request request=builder.addHeader("","")
.url("")
.post(body)
.build();
除了Request外,Response也是通过Builder模式创建的。贴一下Response的构造函数:
private Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
}
可见各大框架中大量的运用了Builder模式。最后总结一下:
- 定义一个静态内部类Builder,内部的成员变量和外部类一样。
- Builder类通过一系列的方法用于成员变量的赋值,并返回当前对象本身(this)。
- Builder类提供一个build方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder。
- 外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值。
2. Java之建造者模式应用场景(JavaMail)
使用场景:
假设有一个电子杂志系统,定期地向用户的电子邮件信箱发送电子杂志。用户可以通过网页订阅电子杂志,也可以通过网页结束订阅。当客户开始订阅时,系统发送一个电子邮件表示欢迎,当客户结束订阅时,系统发送一个电子邮件表示欢送。本例子就是这个系统负责发送“欢迎”和“欢送”邮件的模块。
这个系统含有客户端(Client)、导演者(Director)、抽象建造者(Builder)、具体建造者(WelcomeBuilder和GoodbyeBuilder)、产品(WelcomeMessage和GoodbyeMessage)等角色。
源代码
抽象类AutoMessage源代码,send()操作仅仅是示意性的,并没有给出任何发送电子邮件的代码。
(1)抽象类AutoMessage:
public abstract class AutoMessage {
//收件人地址
private String to;
//发件人地址
private String from;
//标题
private String subject;
//内容
private String body;
//发送日期
private Date sendDate;
public void send(){
System.out.println("收件人地址:" + to);
System.out.println("发件人地址:" + from);
System.out.println("标题:" + subject);
System.out.println("内容:" + body);
System.out.println("发送日期:" + sendDate);
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public Date getSendDate() {
return sendDate;
}
public void setSendDate(Date sendDate) {
this.sendDate = sendDate;
} }
(2)具体产品类WelcomeMessage:
public class WelcomeMessage extends AutoMessage {
/**
* 构造子
*/
public WelcomeMessage(){
System.out.println("发送欢迎信息");
}
}
(3)具体产品类GoodbyeMessage:
public class GoodbyeMessage extends AutoMessage{
/**
* 构造子
*/
public GoodbyeMessage(){
System.out.println("发送欢送信息");
}
}
(4)抽象建造者类
public abstract class Builder {
protected AutoMessage msg; //标题零件的建造方法
public abstract void buildSubject(); //内容零件的建造方法
public abstract void buildBody(); //收件人零件的建造方法
public void buildTo(String to){
msg.setTo(to);
} //发件人零件的建造方法
public void buildFrom(String from){
msg.setFrom(from);
} //发送时间零件的建造方法
public void buildSendDate(){
msg.setSendDate(new Date());
} /**
* 邮件产品完成后,用此方法发送邮件
* 此方法相当于产品返还方法
*/
public void sendMessage(){
msg.send();
}
}
(5)具体建造者WelcomeBuilder
public class WelcomeBuilder extends Builder {
public WelcomeBuilder(){
msg = new WelcomeMessage();
} @Override
public void buildBody() {
// TODO Auto-generated method stub
msg.setBody("欢迎内容");
} @Override
public void buildSubject() {
// TODO Auto-generated method stub
msg.setSubject("欢迎标题");
}
}
(6)具体建造者GoodbyeBuilder
public class GoodbyeBuilder extends Builder {
public GoodbyeBuilder(){
msg = new GoodbyeMessage();
}
@Override
public void buildBody() {
// TODO Auto-generated method stub
msg.setBody("欢送内容");
}
@Override
public void buildSubject() {
// TODO Auto-generated method stub
msg.setSubject("欢送标题");
}
}
(7)导演者Director,这个类提供一个construct()方法,此方法调用建造者的建造方法,包括buildTo()、buildFrom()、buildSubject()、buildBody()、buildSendDate()等,从而一部分一部分地建造出产品对象,既AutoMessage对象。
public class Director {
Builder builder;
/**
* 构造子
*/
public Director(Builder builder){
this.builder = builder;
}
/**
* 产品构造方法,负责调用各零件的建造方法
*/
public void construct(String toAddress , String fromAddress){
this.builder.buildTo(toAddress);
this.builder.buildFrom(fromAddress);
this.builder.buildSubject();
this.builder.buildBody();
this.builder.buildSendDate();
this.builder.sendMessage();
}
}
(8)客户端Client
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Builder builder = new WelcomeBuilder();
Director director = new Director(builder);
director.construct("toAddress@126.com", "fromAddress@126.com"); }
}
3. 附加:建造模式分成两个很重要的部分:
(1). 一个部分是Builder接口,这里是定义了如何构建各个部件,也就是知道每个部件功能如何实现,以及如何装配这些部件到产品中去;
(2). 另外一个部分是Director,Director是知道如何组合来构建产品,也就是说Director负责整体的构建算法,而且通常是分步骤地来执行。
不管如何变化,建造模式都存在这么两个部分,一个部分是部件构造和产品装配,另一个部分是整体构建的算法。认识这点是很重要的,因为在建造模式中,强调的是固定整体构建的算法,而灵活扩展和切换部件的具体构造和产品装配的方式。
再直白点说,建造模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。具体的构造实现可以很方便地扩展和切换,从而可以灵活地组合来构造出不同的产品对象。
Java设计模式04:常用设计模式之建造者模式(创建型模式)的更多相关文章
- FactoryMethod工厂方法模式(创建型模式)
1.工厂方法模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只 ...
- Prototype原型模式(创建型模式)
1.原型模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只有一 ...
- Java 23种设计模式详尽分析与实例解析之一--创建型模式
面向对象的设计原则 常用的面向对象设计原则包括7个,这些原则并不是独立存在的,它们相互依赖.互为补充. Java设计模式 创建型模式 简单工厂模式 模式动机: 考虑一个简单的软件应用场景,一个软件系统 ...
- 设计模式(二): BUILDER生成器模式 -- 创建型模式
1.定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 2.适用场景 1. 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式 ...
- 设计模式之美:Creational Patterns(创建型模式)
创建型模式(Creational Patterns)抽象了对象实例化过程. 它们帮助一个系统独立于如何创建.组合和表示它的那些对象. 一个类创建型模式使用继承改变被实例化的类. 一个对象创建型模式将实 ...
- 设计模式(五):PROTOTYPE原型模式 -- 创建型模式
1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2.适用场景 原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对 ...
- 工厂方法模式——创建型模式02
1. 简单工厂模式 在介绍工厂方法模式之前,先介绍一下简单工厂模式.虽然简单工厂模式不属于GoF 23种设计模式,但通常将它作为学习其他工厂模式的入门,并且在实际开发中使用的也较为频繁. (1 ...
- C#设计模式--工厂模式(创建型模式)
一.简单工厂模式(UML类图): 核心类代码: public class Calc { public double NumberA { get; set; } public double Number ...
- 建造者模式与原型模式/builder模式与prototype模式/创建型模式
建造者模式 定义 用于简化复杂对象的创建 JDK中的建造者模式 java.lang.StringBuilder中的append()方法,每次调用后返回修改后的对象本身. public StringBu ...
- 工厂模式/factory模式/创建型模式
工厂模式 普通工厂模式 原本需要new出来的对象,通过一个类的方法去搞定,Factory.build(parameter),类似这种. public interface Sender { public ...
随机推荐
- php之利用递归写无限极分类
<?php //无限极分类 //parent 的值,是该栏目的父栏目的id 反之是 /*0 安徽 合肥 北京 海淀 中关村 上地 河北 石家庄 */ $area = array( array(' ...
- JavaScript中document.cookie
“某些 Web 站点在您的硬盘上用很小的文本文件存储了一些信息,这些文件就称为 Cookie.”—— MSIE 帮助.一般来说,Cookies 是 CGI 或类似,比 HTML 高级的文件.程序等创建 ...
- jQuery 自动完成文本框
jQuery自动完成插件开源软件 http://www.oschina.net/project/tag/329/jquery-autocomplete jQuery TextExt http://te ...
- css+js自动化开发之第十五天
一.css上一篇的补充 1.position(页面分层) (1)fiexd将标签固定在页面的某个位置 position属性:top,left,right,bottom (2)relative+abso ...
- 一个CS出身的基本素养
从前天10号提交Paper之后,连三个晚上之后突然正常起来竟然变成倒时差状态. 这周打算给自己一个空窗期,好好想想下两到三个月要做的事. 好吧,除了"一日一算法",当下两个月还有一 ...
- Python字符编码讲解
声明:本文参考 Python字符编码详解 在计算机中我们不管用什么语言和程序,最终数据在计算机中的都是字节码(也就是01形式)的形式存在的,如果 计算机直接把字节码显示在屏幕上,很明显一般人看不懂字节 ...
- BZOJ 1048 分割矩阵
Description 将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了 ...
- bcb6 中安装 delphi7 的控件包
其实只要新建一个包,然后把 d7 中的包包含的 pas 文件也包含进去就行了. 同时它会把对应的 bpi 文件也自动包含. 对于 bcb6 中没有的 PosEx 函数可以这样处理,把 d7 中的实现复 ...
- oracle中触发器中:new和:old 的使用方法--4
转载▼ create or replace trigger TRI_PrintTest before delete or insert or update on TEST_EXAM --触发事 ...
- jquery 学习第一课之start
1.$选取符 ( $ == jQuery ) (1) $("div").addClass("special");选取本页面中的所有<div>元素,然 ...