設計模式------原型模式與建造者模式

設計模式------原型模式與建造者模式

目錄

設計模式--------原型模式與建造者模式

1、原型模式

1.1 什麼是原型模式 

1.2 結構圖

1.3 淺克隆與深克隆實例

1.4 典型應用

1.5 小總結

2、構建者模式

2.1 什麼是構建者模式

2.2 結構圖

2.3 實例演示

2.4 典型應用

2.5 小總結


1、原型模式

1.1 什麼是原型模式 

原型模式(Prototype Pattern):使用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式是一種對象創建型模式。

原型模式的工作原理很簡單:將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝自己來實現創建過程。

原型模式是一種“另類”的創建型模式,創建克隆對象的工廠就是原型類自身,工廠方法由克隆方法來實現。

需要注意的是通過克隆方法所創建的對象是全新的對象,它們在內存中擁有新的地址,通常對克隆所產生的對象進行修改對原型對象不會造成任何影響,每一個克隆對象都是相互獨立的。通過不同的方式修改可以得到一系列相似但不完全相同的對象。

1.2 結構圖

  • 原型模式共有以下幾種角色:
    • Prototype(抽象原型類)定義clone方法的接口,類型爲接口、抽象類或者具體的類,是所有具體原型類的父類
    • ConcretePrototype(具體原型類)實現clone方法的實現了,可以爲多個類,提供不同的clone方式
    • Client(客戶類)讓一個原型對象複製新的對象
  • 原型方法的核心是如何實現clone方法,不同的clone方法最終的效果也是不一樣的

1.3 淺克隆與深克隆實例

淺拷貝:當對象被複制時,只複製對象本身與值類型的成員變量,引用類型的成員變量沒有被複制

深拷貝:當對象被複制時,對象本身、值類型成員變量、引用類型成員變量都會被複制,原型對象與複製對象完全獨立

區別:淺克隆和深克隆的主要區別在於是否支持引用類型的成員變量的複製

package com.zps.framerlearn.設計模式.原型對象;

import lombok.Data;

import java.io.*;
import java.util.List;

/**
 * @description: 原型對象
 * @author: zps
 * @create: 2020-05-02 16:57
 **/
@Data
public class Student implements Cloneable , Serializable{
    private String name;
    private int age;
    private List<String> hobbby;

    //淺克隆
    public Student clone(){
        try {
            return (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    //深克隆
    public Student deepClone() throws IOException, ClassNotFoundException {
        //將對象寫入流中
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);

        //將對象從流中取出
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ((Student)ois.readObject());
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hobbby=" + hobbby +
                '}';
    }
}

測試

package com.zps.framerlearn.設計模式.原型對象;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 測試原型模式
 * @author: zps
 * @create: 2020-05-02 17:01
 **/
public class Test {
    public static void main(String args[]) throws IOException, ClassNotFoundException {
        Student stu = new Student();
        stu.setName("張三");
        stu.setAge(21);
        List<String> hobhy = new ArrayList<>();
        hobhy.add("看書");
        hobhy.add("寫字");
        stu.setHobbby(hobhy);
        System.out.println(stu);

        Student student = stu.deepClone();
        student.getHobbby().add("游泳");
        System.out.println(student);
        System.out.println(stu);
    }
}

深克隆測試運行結果

Student{name='張三', age=21, hobbby=[看書, 寫字]}
Student{name='張三', age=21, hobbby=[看書, 寫字, 游泳]}
Student{name='張三', age=21, hobbby=[看書, 寫字]}

實現深克隆主要有兩種方式:

(1)通過序列化的方式:

 public Student deepClone() throws IOException, ClassNotFoundException {
        //將對象寫入流中
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);

        //將對象從流中取出
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ((Student)ois.readObject());
    }

(2)手動對引用對象進行硬編碼克隆:   

@Override
    protected Object clone() throws CloneNotSupportedException {
        Student stu = (Studetn)super.clone();

        //深克隆
        stu.hobby = (Student) stu.hobby.clone();
        return stu;
    }

 

1.4 典型應用

(1)ArrayList 對 clone 的重寫如下:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
       
    //省略

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
    //...省略
}

(2)HashMap中的應用

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    @Override
    public Object clone() {
        HashMap<K,V> result;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
        result.reinitialize();
        result.putMapEntries(this, false);
        return result;
    }
    // ...省略...
}

1.5 小總結

原型模式的主要優點如下:

  • 當創建新的對象實例較爲複雜時,使用原型模式可以簡化對象的創建過程,通過複製一個已有實例可以提高新實例的創建效率
  • 可以使用深克隆的方式保存對象的狀態,使用原型模式將對象複製一份並將其狀態保存起來,以便在需要的時候使用(如恢復到某一歷史狀態),可輔助實現撤銷操作。

原型模式的主要缺點如下:

  • 當對已有的類進行改造時,需要修改源代碼,違背了“開閉原則”。
  • 在實現深克隆時需要編寫較爲複雜的代碼,而且當對象之間存在多重的嵌套引用時,爲了實現深克隆,每一層對象對應的類都必須支持深克隆,實現起來可能會比較麻煩

2、構建者模式

2.1 什麼是構建者模式

建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種對象創建型模式。

建造者模式一步一步創建一個複雜的對象,它允許用戶只通過指定複雜對象的類型和內容就可以構建它們,用戶不需要知道內部的具體構建細節。

2.2 結構圖

  • 建造者模式主要包含如下幾個角色:
    • Builder(抽象建造者):構建產品對象部件方法的抽象接口,可以是接口也可以是抽象類,供指揮者使用;方法包含兩類bulidX()與getResult()方法
    • ConcreteBuilder(具體建造者):實現抽象建造者接口,一個具體建造者對應一中具體的產品對象
    • Product(產品角色):建造者模式最終產物
    • Director(指揮者):又稱爲導演類,它負責安排產品對象的各個部件的建造順序
  • 抽象建造者提供抽象建造部件方法,具體建造者實現這些抽象方法,指揮者調用這些抽象方法,完成構建同樣構建過程但是不同實現的對象的建造
  • 建造者模式與抽象工廠模式的區別是前者是建造一個完整的複雜產品,而後者是一系列產品,前者像汽車組裝廠,後者像汽車配件生產廠

2.3 實例演示

import lombok.Data;

/**
 * @description: 產品:課程
 * @author: zps
 * @create: 2020-05-02 19:18
 **/
@Data
public class Course {
    private String name;
    private String ppt;
    private String video;
    private String note;
    private String homework;

    @Override
    public String toString() {
        return "Course{" +
                "name='" + name + '\'' +
                ", ppt='" + ppt + '\'' +
                ", video='" + video + '\'' +
                ", note='" + note + '\'' +
                ", homework='" + homework + '\'' +
                '}';
    }
}

/**
 * @description: 課程建造者
 * @author: zps
 * @create: 2020-05-02 19:20
 **/
public class CourseBuilder {

    private Course course = new Course();

    public void addName(String name){
        course.setName(name);
    }
    public void addPpt(String ppt){
        course.setName(ppt);
    }
    public void addVideo(String video){
        course.setName(video);
    }
    public void addNote(String note){
        course.setName(note);
    }
    public void addHomework(String homework){
        course.setName(homework);
    }

    public Course builder(){
        return course;
    }
}
/**
 * @description: 測試
 * @author: zps
 * @create: 2020-05-02 19:24
 **/
public class Test {
    public static void main(String args[]){
        CourseBuilder builder = new CourseBuilder();

        builder.addName("喝了酒");
        builder.addNote("yes");

        System.out.println(builder.builder());
    }
}

可以改造成爲鏈接方式。


/**
 * @description: 課程建造者
 * @author: zps
 * @create: 2020-05-02 19:20
 **/
public class CourseBuilder {

    private Course course = new Course();

    public CourseBuilder addName(String name){
        course.setName(name);
        return this;
    }
    public CourseBuilder addPpt(String ppt){
        course.setName(ppt);
        return this;
    }
    public CourseBuilder addVideo(String video){
        course.setName(video);
        return this; 
    }
    public CourseBuilder addNote(String note){
        course.setName(note);
        return this;
    }
    public CourseBuilder addHomework(String homework){
        course.setName(homework);
        return this;
    }

    public Course builder(){
        return course;
    }
}

 

2.4 典型應用

(1)java.lang.StringBuilder 中的建造者模式

          StringBuilder的類圖

Appendable 接口如下

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;
}

StringBuilder 中的 append 方法使用了建造者模式,不過裝配方法只有一個,並不算複雜,append 方法返回的是 StringBuilder 自身,即採用了連接方法

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    // ...省略...
}

StringBuilder 的父類 AbstractStringBuilder 實現了 Appendable 接口

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    // ...省略...
}

我們可以看出,Appendable 爲抽象建造者,定義了建造方法,StringBuilder 既充當指揮者角色,又充當產品角色,又充當具體建造者,建造方法的實現由 AbstractStringBuilder 完成,而 StringBuilder 繼承了 AbstractStringBuilder


       注意:StringBuilder 和 StringBuffer 的主要區別是後者是線程安全的。

(2)mybatis 中 mybatis 中的建造者模式

2.5 小總結

建造者模式的優點:

  • 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象
  • 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。由於指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合 “開閉原則”。

建造者模式的缺點:

  • 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和運行成本。
  • 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。

建造者模式與工廠模式的區別:

  • 建造者模式更加註重方法的調用順序,工廠模式注重與創建對象
  • 關注點:工廠模式只需要把對象創建出來就可以了,而建造者模式不僅需要創建出這個對象,還要知道這個對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章