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

2023-10-29 21:00:45

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

原型模式(Prototype Pattern)用於建立重複的物件,同時又能保證效能。

本體給外部提供一個克隆體進行使用。

比如我們做一個SjdwzMybatis,用來運算元據庫,從資料庫裡面查出很多記錄,其中很多記錄改變很少。每次查資料庫,把所有資料都封裝一個物件,然後返回。假設有很多執行緒,來查如下記錄:

Student student = new Student("張三","男");

如果每次都建立物件封裝並返回,這樣系統就會有很多student;這樣就會浪費記憶體。

Student類如下:

public class Student {
    private String name;
    private Integer age;

    public Student() {
        System.out.println("建立了Student物件");
    }
    //省略getter() 、 setter() toString()
}

SjdwzMybatis如下:

public class SjdwzMybatis {

    /**
     * 通過name獲取Student
     */
    public Student queryStudent(String name){
        return queryStudentFromDB(name);
    }

    /**
     * 演示從資料庫查Student
     */
    private Student queryStudentFromDB(String name) {
        //簡單演示,查詢到了
        System.out.println("從資料庫查詢到了:"+name);
        Student student = new Student();
        student.setName(name);
        student.setAge(16);
        return student;
    }
}

測試類:

public class ProtoTypeTest {
    public static void main(String[] args) {
        SjdwzMybatis sjdwzMybatis = new SjdwzMybatis();
        Student stu1 = sjdwzMybatis.queryStudent("zhangsan");
        Student stu2 = sjdwzMybatis.queryStudent("zhangsan");
        Student stu3 = sjdwzMybatis.queryStudent("zhangsan");
        Student stu4 = sjdwzMybatis.queryStudent("zhangsan");
    }
}

這樣會有大量具有相同屬性的student被外部建立,同時查庫次數過多。

我們是否能設計一個快取,來儲存查過的內容,再查相同的記錄時,可以很快拿到原來的原型物件呢?

那我們的SjdwzMybatis便變成了如下程式碼:

public class SjdwzMybatis {
	//快取
    private Map<String,Student> stuCache = new HashMap<>();

    /**
     * 通過name獲取Student
     */
    public Student queryStudent(String name){
        if(stuCache.containsKey(name)){
            return stuCache.get(name);
        }else{
            return queryStudentFromDB(name);
        }
    }

    /**
     * 演示從資料庫查Student
     */
    private Student queryStudentFromDB(String name) {
        //簡單演示,查詢到了
        System.out.println("從資料庫查詢到了:"+name);
        Student student = new Student();
        student.setName(name);
        student.setAge(16);
        //存入記憶體
        stuCache.put(name,student);
        return student;
    }
}

但是這是否會有問題呢?

如果我們把stu1的屬性改了,那麼stu2、stu3、stu4的屬性也會被改變,這會影響到我們快取裡的資料,造成髒快取資料;同時我們查出來的內容,並沒有提交修改,不能就把原資料給修改掉。

原型模式

我們把Student修改成如下程式碼,這便是原型模式:

//實現Cloneable介面,這只是一個標記,還需要重寫clone()方法
public class Student implements Cloneable{
    private String name;
    private Integer age;

    //重寫clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = new Student();
        student.setName(this.name);
        student.setAge(this.age);
        return student;
    }
}

然後SjdwzMybatis修改為如下程式碼:

public class SjdwzMybatis {
    //快取
    private Map<String,Student> stuCache = new HashMap<>();

    /**
     * 通過name獲取Student
     */
    public Student queryStudent(String name) throws CloneNotSupportedException {
        if(stuCache.containsKey(name)){
            return (Student) stuCache.get(name).clone();
        }else{
            return queryStudentFromDB(name);
        }
    }

    /**
     * 演示從資料庫查Student
     */
    private Student queryStudentFromDB(String name) throws CloneNotSupportedException {
        //簡單演示,查詢到了
        System.out.println("從資料庫查詢到了:"+name);
        Student student = new Student();
        student.setName(name);
        student.setAge(16);
        //存入記憶體
        stuCache.put(name,(Student) student.clone());
        return student;
    }
}

從資料庫查出來放入快取的物件與從快取取出來的都是clone出來的。

可以看到,我們對stu1修改,並不會影響其他的資料了。