菜鳥成長系列-Builder 建造者模式

glmapper發表於2017-12-30

菜鳥成長系列-概述
菜鳥成長系列-物件導向的四大基礎特性
菜鳥成長系列-多型、介面和抽象類
菜鳥成長系列-物件導向的6種設計原則
菜鳥成長系列-單例模式 菜鳥成長系列-工廠模式

建造者模式也是建立型中的一種,用於負責建立物件。建造者模式可以將一個產品的內部表象與產品的生成過程分割開來,從而可以使一個建造過程生成具有不同的內部表象的產品物件。

什麼是產品的內部表象?

一個產品常有不用的組成成分作為產品的零件,這些零件有可能是物件,也有可能不是物件,他們通常又叫做產品的內部表象。不同的產品可以有不同的內部表象,也就是可以有不同的零件。使用建造者模式可以使客戶端不需要知道所生成的產品物件有哪些零件,每個產品的對應零件彼此有何不同,是怎麼建造出來的,以及怎樣組成產品。

工廠模式與建造者模式

上一篇我們討論了工廠模式,我們知道工廠模式一般都是建立一個產品,注重的是把這個產品建立出來就行,只要建立出來,不關心這個產品的組成部分。從程式碼上看,工廠模式就是一個方法,用這個方法就能生產出產品。那麼對於建造者模式呢? 建造者模式也是建立一個產品,但是不僅要把這個產品建立出來,還要關係這個產品的組成細節, 組成過程。從程式碼上看(下面給出),建造者模式在建造產品時,這個產品有很多方法,建造者模式會根據這些相同方法但是不同執行順序建造出不同組成細節的產品。

建造者模式的結構

菜鳥成長系列-Builder 建造者模式
結構元件角色說明:

  • 抽象建造者(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("00001111@glmapper.com");
	}

	public void buildToAddress() {
		emailProduct.setToAddress("00001112@glmapper.com");
	}

	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 :
[
    fromAddress=00001111@glmapper.com,
    toAddress=00001112@glmapper.com,
    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 = "00001111@glmapper.com";
		String toAddress = "00001112@glmapper.com";
		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,建造者模式就到這裡了,大家元旦快樂!

相關文章