Java進階篇設計模式之三 ----- 建造者模式和原型模式


前言

上一篇中我們學習了工廠模式,介紹了簡單工廠模式、工廠方法和抽象工廠模式。本篇則介紹設計模式中屬於創建型模式的建造者模式和原型模式。

建造者模式

簡介

建造者模式是屬於創建型模式。建造者模式使用多個簡單的對象一步一步構建成一個復雜的對象。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
簡單的來說就是將一個復雜的東西抽離出來,對外提供一個簡單的調用,可以在同樣的構建過程創建不同的表示。和工廠模式很相似,不過相比而言更加注重組件的裝配。

這里用一個示例來進行說明。
我們一天吃的食物有這些,煎餅、盒飯、拉面、豆漿、牛奶和果汁。分為三餐、早餐、午餐和晚餐,餐點主要包含吃的(俗稱飯)和喝的(豆漿,果汁之類的),那么我們可以把煎餅和豆漿作為早餐,盒飯和果汁作為午餐,這樣我們可以清楚的知道要吃早餐和午餐包含什么食物。

首先我們定義一個食物類,有兩個屬性,吃的和喝的。

class Meal{
	private String food;
	private String drinks;
	
	public String getFood() {
		return food;
	}
	public void setFood(String food) {
		this.food = food;
	}
	
	public String getDrinks() {
		return drinks;
	}
	public void setDrinks(String drinks) {
		this.drinks = drinks;
	}
}

定義了食物時候,我們在定義一個食物的標准接口,一份食物包含什么,其實也就是吃的和喝的。

interface IBuilderFood{
	void buildFood();
	void buildDrinks();
	Meal createMeal();
}

食物接口定義一個吃的和一個喝的組件,然后通過createMeal()方法返回我們需要的食物。
那么現在我們便可以定義一份早餐和午餐。
代碼示例:

class Breakfast implements IBuilderFood{
	Meal meal;

	public Breakfast(){
		meal=new Meal();
	}
	
	@Override
	public void buildFood() {
		meal.setFood("煎餅");
	}

	@Override
	public void buildDrinks() {
		meal.setDrinks("豆漿");	
	}
	
	@Override
	public Meal createMeal() {
		return meal;
	}
}

class Lunch implements IBuilderFood{
	Meal meal;

	public Lunch(){
		meal=new Meal();
	}
	
	@Override
	public void buildFood() {
		meal.setFood("盒飯");
	}

	@Override
	public void buildDrinks() {
		meal.setDrinks("果汁");	
	}
	
	@Override
	public Meal createMeal() {
		return meal;
	}
}

定義完之后,建造早餐和午餐的的過程已經完畢了。但是這並不是建造者模式,它有個核心的Director(導演者),它用來創建復雜對象的部分,對該部分進行完整的創建或者按照一定的規則進行創建。那么這里我們可以創建一個Director,用來創建一份餐點。至於創建的是什么餐點,它不用知道,這一點由調用者來進行決定。

這里我們就可以定義一個飯店,可以創建一份餐點,創建什么餐點有顧客決定。
代碼示例:

class FoodStore{
	public Meal createBreakfast(IBuilderFood bf){
		bf.buildDrinks();
		bf.buildFood();
		return bf.createMeal();
	}
}

創建完成這個Director之后,我們再來進行調用測試。

代碼示例:


public class BuilderTest {

	public static void main(String[] args) {
		FoodStore foodStore=new FoodStore();
		Meal meal=foodStore.createBreakfast(new Breakfast());
		Meal meal2=foodStore.createBreakfast(new Lunch());
		System.out.println("小明早上吃的是:"+meal.getFood()+",喝的飲料是:"+meal.getDrinks());
		System.out.println("小明中午吃的是:"+meal2.getFood()+",喝的飲料是:"+meal2.getDrinks());	
	}

}

輸出結果:

小明早上吃的是:煎餅,喝的飲料是:豆漿
小明中午吃的是:盒飯,喝的飲料是:果汁

簡單的介紹了下建造者模式的運作原理,可以概況為這4點:

  1. Builder:指定一個抽象的接口,規定該產品所需實現部件的創建,並不涉及具體的對象部件的創建。

  2. ConcreteBuilder:需實現Builder接口,並且針對不同的邏輯,進行不同方法的創建,最終提供該產品的實例。

  3. Director:用來創建復雜對象的部分,對該部分進行完整的創建或者按照一定的規則進行創建。

  4. Product:示被構造的復雜對象。

使用場景:
適用一些基本組件不便,但是組合經常變化的時候。比如超市促銷的大禮包。

優點:

  1. 建造者獨立,易擴展。
  2. 便於控制細節風險。

缺點

  1. 內部結構復雜,不易於理解。
  2. 產品直接需要有共同點,范圍有控制。

原型模式

原型模式(Prototype Pattern)是用於創建重復的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

一般來說我們在創建對象的時候是直接創建的,但是創建該對象的代價很大的時候,重復的二次創建就有些不划算,這時我們就可以使用原型模式。
打個比方,我們都發送過郵件,在節日的時候一般發送的是祝福語句,在這些祝福語句中,一般除了名字不一樣之外,大部分都是一樣的。這時我們就可以利用該模式來進行相應出創建。

這里還是用一個的簡單的示例來說明。
小明和小紅在同一天生日,然后我們需要給他們發送郵件進行祝福,但是由於比較懶,祝福語除了名字之外都是一樣的。這時我們就可以先完成祝福語的編寫,然后克隆該祝福語,最后根據不同的名稱進行發送。不過這里就從簡了,只是簡單的打印下而已。

代碼示例:

public class PrototypeTest {

	public static void main(String[] args) {
		Mail mail=new Mail();
		mail.setMsg("生日快樂!");
		Mail mail2=(Mail) mail.clone();
		mail.setName("小明");
		mail2.setName("小紅");
		System.out.println(mail.toString());
		System.out.println(mail2.toString());
	}
}

 class Mail implements Cloneable {
	private String name;
	private String msg;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	public Object clone() {
		Object clone = null;
		try {
			clone = super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}

	@Override
	public String toString() {
		return name + ":" + msg ;
	}
	
}

輸出結果:

小明:生日快樂!
小紅:生日快樂!

看完原型模式的創建,是不是感覺就是和Java中克隆即為類似呢?
實際上它的核心也就是克隆。
克隆有兩種,淺克隆和深克隆,本文主要介紹的是淺克隆。
淺克隆:

在淺克隆中,如果原型對象的成員變量是值類型,將復制一份給克隆對象;如果原型對象的成員變量是引用類型,則將引用對象的地址復制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。
簡單來說,在淺克隆中,當對象被復制時只復制它本身和其中包含的值類型的成員變量,而引用類型的成員對象並沒有復制。
實現Cloneable接口並重寫Object類中的clone()方法;

深克隆:

在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復制一份給克隆對象,深克隆將原型對象的所有引用對象也復制一份給克隆對象。

簡單來說,在深克隆中,除了對象本身被復制外,對象所包含的所有成員變量也將復制。
實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆。

使用場景:

  1. 類初始化的時候需要消耗大量資源的時候;
  2. 獲取數據庫連接繁瑣的時候;
  3. 一個對象,有很多個修改者的時候;

優點:
1.可以提升性能;

缺點:
1.因為必須實現Cloneable 接口,所以用起來可能不太方便。

其它

音樂推薦

原創不易,如果感覺不錯,希望給個推薦!您的支持是我寫作的最大動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm    
個人博客出處:http://www.panchengming.com
原創不易,轉載請標明出處,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM