建立型設計模式之原型模式

模式介紹

原型模式(Prototype Pattern)簡單來講就是程序界的複製粘貼,它能夠實現對象的深拷貝。html

咱們建立一個對象,一般有3種方式:java

  • new 關鍵字
  • 反射
  • 實現Cloneable接口的clone()方法

若是須要對已有的對象進行復制,且建立對象成本較高(例如須要較多的I/O),能夠考慮使用原型模式。spring

在原型模式中,所發動建立的對象經過請求原型對象來拷貝原型對象本身來實現建立過程,固然所發動建立的對象須要知道原型對象的類型。這裏也就是說所發動建立的對象只須要知道原型對象的類型就能夠得到更多的原型實例對象,至於這些原型對象時如何建立的根本不須要關心。設計模式

模式優缺點

優勢

一、若是建立新的對象比較複雜時,能夠利用原型模式簡化對象的建立過程,同時也可以提升效率。數組

二、可使用深克隆保持對象的狀態。bash

三、原型模式提供了簡化的建立結構。ide

四、原型模式向客戶隱藏了建立對象的複雜性。客戶只須要知道要建立對象的類型,而後經過請求就能夠得到和該對象如出一轍的新對象,無須知道具體的建立過程。性能

缺點

一、在實現深克隆的時候可能須要比較複雜的代碼。 咱們雖然能夠利用原型模式來得到一個新對象,但有時對象的複製可能會至關的複雜,好比深克隆。this

二、須要爲每個類配備一個克隆方法,並且這個克隆方法須要對類的功能進行通盤考慮,這對全新的類來講不是很難,但對已有的類進行改造時,不必定是件容易的事,必須修改其源代碼,違背了「開閉原則」。spa

實現

角色

Prototype:抽象原型類。聲明克隆自身的接口。 ConcretePrototype:具體原型類。實現克隆的具體操做。 Client:客戶類。讓一個原型克隆自身,從而得到一個新的對象。

接口

咱們都知道Object是祖宗,全部的Java類都繼承至Object,而Object類提供了一個clone()方法,該方法能夠將一個java對象複製一份,所以在java中能夠直接使用clone()方法來複制一個對象。可是須要實現clone的Java類必需要實現一個接口:Cloneable.該接口表示該類可以複製且具體複製的能力,若是不實現該接口而直接調用clone()方法會拋出CloneNotSupportedException異常。
複製代碼

步驟

第一步:定義抽象原型。

package net.ijiangtao.tech.designpattern.prototype;

import lombok.*;

import java.util.HashMap;
import java.util.Map;

/** * 定義一個抽象原型 * * @author ijiangtao * @create 2019-07-17 21:20 **/
@Getter
@Setter
@NoArgsConstructor  //lombok : 無參構造
@AllArgsConstructor //lombok : 全參構造
@ToString
public class Prototype implements Cloneable{

    private Integer id;

    private String name;

    private Map<String, Double> scores;

    @Override
    protected Prototype clone() {
        Prototype filePrototype = null;
        try {
            //有了下面這句話,基本類型就能克隆了
            filePrototype = (FileConcretePrototype) super.clone();
            
            //下面要對每個複雜對象進行分別克隆
            filePrototype.scores = (Map<String, Double>) ((HashMap)this.scores).clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return filePrototype;
    }

}
複製代碼

第二步:定義具體原型。

package net.ijiangtao.tech.designpattern.prototype;

import java.util.Map;

/** * 定義具體原型 * * @author ijiangtao * @create 2019-07-17 21:21 **/
public class FileConcretePrototype extends Prototype {

    public FileConcretePrototype(Integer id, String name, Map<String, Double> scores) {
        super(id, name, scores);
    }

    public void show() {
        System.out.println(" File Data : ");
        System.out.println("file name : " + this.getName());
        System.out.println("file id : " + this.getId());
        System.out.println("file scores : " + this.getScores());
    }

}
複製代碼

第三步:定義用戶去模擬對象克隆。

package net.ijiangtao.tech.designpattern.prototype;

import java.util.HashMap;
import java.util.Map;

/**
 * client
 *
 * @author ijiangtao
 * @create 2019-07-17 21:27
 **/
public class Client {


    public static void main(String[] args) {

        String fileName = "scores文件";
        int fileId = 1;
        Map<String, Double> fileScores = new HashMap<>();
        fileScores.put("張三", 99.99);
        fileScores.put("李斯", 79.99);

        //第一步建立出一個實例對象
        FileConcretePrototype fileA = new FileConcretePrototype(fileId, fileName, fileScores);

        //第二步克隆出來幾個
        FileConcretePrototype fileB = (FileConcretePrototype) fileA.clone();
        FileConcretePrototype fileC = (FileConcretePrototype) fileA.clone();

        //第三步:輸出一下克隆出來的對象是否有變化
        fileB.show();
        fileC.show();

    }
}

複製代碼

最後輸出是:

File Data : 
file name : scores文件
file id : 1
file scores : {李斯=79.99, 張三=99.99}
 File Data : 
file name : scores文件
file id : 1
file scores : {李斯=79.99, 張三=99.99}
複製代碼

咱們能夠看到,克隆出來的兩個文件和以前的文件是同樣的,並且咱們實現了深拷貝,對於數組、引用等對象一樣的適用。

特色

Java中任何實現了Cloneable接口的類均可以經過調用clone()方法來複制一份自身而後傳給調用者。通常而言,clone()方法知足:

(1) 對任何的對象x,都有x.clone() !=x,即克隆對象與原對象不是同一個對象。

//第一步建立出一個實例對象
FileConcretePrototype fileA = new FileConcretePrototype(fileId, fileName, fileScores);

//第二步克隆出來幾個
FileConcretePrototype fileB = (FileConcretePrototype) fileA.clone();
FileConcretePrototype fileC = (FileConcretePrototype) fileA.clone();

//第三步:引用賦值
FileConcretePrototype fileA2 =fileA;

System.out.println(fileA==fileB);//輸出false
System.out.println(fileA==fileA2);//輸出true
複製代碼

(2) 對任何的對象x,都有x.clone().getClass()==x.getClass(),即克隆對象與原對象的類型同樣。

//第一步建立出一個實例對象
FileConcretePrototype fileA = new FileConcretePrototype(fileId, fileName, fileScores);

//第二步克隆出來幾個
FileConcretePrototype fileB = (FileConcretePrototype) fileA.clone();
FileConcretePrototype fileC = (FileConcretePrototype) fileA.clone();

//第三步:引用賦值
FileConcretePrototype fileA2 =fileA;

System.out.println(fileA.getClass()==fileC.getClass()); //輸出true
複製代碼

(3) 若是對象x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。

首先重寫 equals 和 hashCode 方法:

@EqualsAndHashCode // lombok : over write equals and hashCode methods based on relevant fields.
public class FileConcretePrototype extends Prototype 複製代碼

而後與克隆之後的對象比較:

//第一步建立出一個實例對象
FileConcretePrototype fileA = new FileConcretePrototype(fileId, fileName, fileScores);

//第二步克隆出來幾個
FileConcretePrototype fileB = (FileConcretePrototype) fileA.clone();
FileConcretePrototype fileC = (FileConcretePrototype) fileA.clone();

//第三步:引用賦值
FileConcretePrototype fileA2 =fileA;

System.out.println(fileA.equals(fileB));//輸出true
System.out.println(fileC.equals(fileB));//輸出true
複製代碼

相關連接

深刻理解原型模式 ——經過複製生成實例

設計模式之原型模式

設計模式讀書筆記-原型模式

java的淺拷貝和深拷貝

spring中的scope詳解

spring的scope爲prototype的bean的正確使用方法

BeanUtils.copyProperties()方法 && 關於BeanUtil.copyProperties性能


Wechat-westcall
相關文章
相關標籤/搜索