菜鸟成长系列-Builder 建造者模式
菜鸟成长系列-概述
菜鸟成长系列-面向对象的四大基础特性
菜鸟成长系列-多态、接口和抽象类
菜鸟成长系列-面向对象的6种设计原则
菜鸟成长系列-单例模式菜鸟成长系列-工厂模式
建造者模式也是创建型中的一种,用于负责创建对象。建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
什么是产品的内部表象?
一个产品常有不用的组成成分作为产品的零件,这些零件有可能是对象,也有可能不是对象,他们通常又叫做产品的内部表象。不同的产品可以有不同的内部表象,也就是可以有不同的零件。使用建造者模式可以使客户端不需要知道所生成的产品对象有哪些零件,每个产品的对应零件彼此有何不同,是怎么建造出来的,以及怎样组成产品。
工厂模式与建造者模式
上一篇我们讨论了工厂模式,我们知道工厂模式一般都是创建一个产品,注重的是把这个产品创建出来就行,只要创建出来,不关心这个产品的组成部分。从代码上看,工厂模式就是一个方法,用这个方法就能生产出产品。那么对于建造者模式呢?建造者模式也是创建一个产品,但是不仅要把这个产品创建出来,还要关系这个产品的组成细节, 组成过程。从代码上看(下面给出),建造者模式在建造产品时,这个产品有很多方法,建造者模式会根据这些相同方法但是不同执行顺序建造出不同组成细节的产品。
建造者模式的结构
结构组件角色说明:- 抽象建造者(Builder):抽象类, 规范产品的组建,一般是由子类实现具体的组件过程
- 具体建造者(ConcreteBuilder ):具体的构建器
- 导演者(Director) : 统一组装过程(可省略)
- 产品(Product):产品的抽象类
Director角色是与客户端打交道的角色。Director将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体的ConcreteBuilder角色。ConcreteBuilder是做具体建造工作的,但是对客户端是透明的。
一般来说,每有一个产品类,就有一个相应的具体建造者类。这些产品应当有一样数目的零件,而每有一个零件就相应的在所有的建造者角色里有一个建造方法。
建造一封邮件
通过代码来看下各个角色的职责:
- Director
package com.glmapper.model.builder; /** * * 导演者类 * @author glmapper * @time 2017年12月30日上午11:33:26 * @version glmapper_v1.0 * */ public class Director { private Builder builder; /** * 产品构造方法,负责调用各个零件建造方法 */ public EmailProduct construct(){ builder = new ConcreteBuilder(); builder.buildFromAddress(); builder.buildToAddress(); builder.buildSubject(); builder.buildContent(); builder.buildSupplement(); return builder.returnEmailProduct(); } }
- Builder
package com.glmapper.model.builder; /** * 抽象建造者 提供不同的构建组件方法 * @author glmapper * @time 2017年12月30日上午11:31:34 * @version glmapper_v1.0 * */ public interface Builder { /** * 构建发件人信息 */ public void buildFromAddress(); /** * 构建收件人信息 */ public void buildToAddress(); /** * 构建邮件内容 */ public void buildContent(); /** * 构建邮件附件 */ public void buildSupplement(); /** * 构建邮件主题 */ public void buildSubject(); /** * 返回构建的产品 */ public EmailProduct returnEmailProduct(); }
- ConcreteBuilder
package com.glmapper.model.builder; /** * * 具体产品的建造器 * @author glmapper * @time 2017年12月30日上午11:31:11 * @version glmapper_v1.0 * */ public class ConcreteBuilder implements Builder{ private EmailProduct emailProduct = new EmailProduct(); public void buildFromAddress() { emailProduct.setFromAddress("[email protected]"); } public void buildToAddress() { emailProduct.setToAddress("[email protected]"); } public void buildContent() { emailProduct.setContent("我写了一个建造者模式的例子,希望大佬给点意见"); } public void buildSupplement() { emailProduct.setSupplement("附件:BuilderDemo.rar"); } public void buildSubject() { emailProduct.setSubject("给大佬的一封建造者模式的Demo"); } public EmailProduct returnEmailProduct() { System.out.println(emailProduct.toString()); return emailProduct; } }
- Product
package com.glmapper.model.builder; /** * 产品类(产品类中包括不同组件:此处使用字段方式模式组件) * @author glmapper * @time 2017年12月30日上午11:30:13 * @version glmapper_v1.0 * */ public class EmailProduct { /** * 发件地址 */ private String fromAddress; /** * 收件地址 */ private String toAddress; /** * 邮件主题 */ private String subject; /** * 邮件内容 */ private String content; /** * 邮件附件 */ private String supplement; public String getFromAddress() { return fromAddress; } public void setFromAddress(String fromAddress) { this.fromAddress = fromAddress; } public String getToAddress() { return toAddress; } public void setToAddress(String toAddress) { this.toAddress = toAddress; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getSupplement() { return supplement; } public void setSupplement(String supplement) { this.supplement = supplement; } @Override public String toString() { return "EmailProduct [fromAddress=" + fromAddress + ", toAddress=" + toAddress + ", subject=" + subject + ", content=" + content + ", supplement=" + supplement + "]"; } }
- 客户端
package com.glmapper.model.builder; /** * 客户端 * @author glmapper * @time 2017年12月30日上午11:45:53 * @version glmapper_v1.0 * */ public class MainTest { public static void main(String[] args) { new Director().construct(); } }
- 结果
建造发件人信息组件... 建造收件人信息组件... 建造邮件主题信息组件... 建造邮件内容信息组件... 建造邮件附件信息组件... (为了方便看,这里把结果的显示做了调整) EmailProduct : [ [email protected], [email protected], subject=给大佬的一封建造者模式的Demo, content=我写了一个建造者模式的例子,希望大佬给点意见, supplement=附件:BuilderDemo.rar ]
建造者模式的关注点
有些情况下,一个对象会有一些重要的性质,在他们没有恰当的值之前,对象不能作为一个完整的产品来使用。就如上面发送一个电子邮件所示,电子邮件有发件人地址、收件人地址、主题、内容、附录等部分,而在收件人地址没有赋值之前,这个电子邮件是不能发送的。在某些情况下,一个对象的一些性质必须按照某个顺序赋值才有意义,在某个性质没有赋值之前,另一个性质则无法赋值。这些情况使得性质本身的建造涉及到复杂的业务逻辑。
而此时,对象相当于一个有待建造的产品,而对象的这些性质相当于产品的零件,建造产品的过程是建造零件的过程。由于建造零件的过程很复杂,因此,这些零件的健在过程往往会被“外部化”到另一个称作为建造者的对象里,建造者对象返回给客户端的是一个全部零件都建造完毕的产品对象。
在实际的应用过程中,建造者模式也有不同的变种,比如说省略抽象建造者角色或者省略导演者角色等等,在某些情况下,建造者模式可以通过省略某些角色来达到过度到模板方法模式。
OK,关于建造者模式的其他变种这里就不讨论了,留一个想象空间!(这段字代表一个微笑的表情(* ̄︶ ̄))
java中建造者模式:JavaMail
JavaMail是一组J2SE的扩展API的一个类库,我们可以使用这个API来开发一个功能完备的电子邮件客户端软件。在JavaMail中就主要使用了建造者模式,当然还有我们上一篇中说道的抽象工厂模式。
建造者模式在JavaMail中的使用
package com.glmapper.model.builder; import java.util.Properties; import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; /** * 邮件发送-建造者模式 * @author glmapper * @time 2017年12月30日下午2:04:46 * @version glmapper_v1.0 * */ public class MailSender { private static MimeMessage message; public static void main(String[] args) { //基本属性 String smptHost = "smpt.xxx.com"; String fromAddress = "[email protected]"; String toAddress = "[email protected]"; Properties p= new Properties(); p.put("mail.smtp.host", smptHost); Session session = Session.getDefaultInstance(p); try { InternetAddress to = new InternetAddress(toAddress); InternetAddress from = new InternetAddress(fromAddress); //创建message对象 message = new MimeMessage(session); //下面就是组装零件的过程 message.setFrom(from); message.setRecipient(Message.RecipientType.TO, to); message.setSubject("hello builder"); message.setText("我写了一个建造者模式的例子,希望大佬给点意见"); Transport.send(message); } catch (Exception e) { e.printStackTrace(); } } }
OK,建造者模式就到这里了,大家元旦快乐!