1. 程式人生 > 資料庫 >52 條 SQL 語句效能優化策略

52 條 SQL 語句效能優化策略

2020年11月29日15:25:07

備忘錄模式

引子

曾經有一份真摯的愛情擺在我的面前,但是我沒有珍惜,等我失去後才後悔莫及,塵世間最痛苦的事情莫過於此。

如果上天能夠給我一個再來一次的機會,我會對那個女孩說三個字:我愛你。

如果非要在這份愛上加一個期限,我希望是一萬年!

——至尊寶

定義

Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. ——《Design Patterns: Elements of Reusable Object-Oriented Software》

備忘錄模式(memento pattern),在不破壞封裝型的前提下,獲取並儲存一個物件的內部狀態,以便以後物件可以恢復到這個狀態。——《設計模式:可複用面向物件軟體的基礎》

圖示

備忘錄模式結構圖:

結構圖顯示了備忘錄模式的物件角色有三,一是發起人(Originator), 二是備忘錄(Memento),三是管理者(Caretaker)。

備忘錄模式流程圖:

流程圖展示了備忘錄模式的工作流程:

1、管理者(Caretaker)呼叫發起人(Originator)createMemento建立備忘錄儲存狀態

2、管理者呼叫發起人的setMemento從備忘錄獲取備份的狀態恢復

角色

發起人(Originator):從結構圖中的兩個方法createMemento和setMemento可以看出,發起人負責的是建立一個備忘錄Memento,以及在需要的時候使用Memento恢復自己的內部狀態。發起人可以根據需要決定儲存和恢復哪些內部狀態。

備忘錄(Memento):負責存在發起人(Originator)物件的內部狀態,並可防止Originator以外的其他物件訪問備忘錄(Memento)。備忘錄有兩個介面,Caretaker只能看到備忘錄的窄介面,它只能將備忘錄傳遞給其他物件;Originator能夠看到一個寬介面,允許他訪問返回到先前狀態所需的所有資料。怎麼做?Memento作為Originator的內部類就是可以實現了。

管理者(Caretaker):負責儲存好備忘錄,不能對備忘錄的內容進行操作或檢查。

程式碼示例

故事背景:

小時候,總想著長大,但是誰知道長大後一點有不好玩,朝九晚九。

我想玩電視劇回到年輕的劇情,讓我好好感受在春天放牛,夏天放鴨,秋天抓魚,冬天等小豬出生的日子。

有天我撿到一個相框,只要我把小時候的照片放到相框裡,我就可以回到小時候。

發起人(Originator)和備忘錄(Memento):

// 我
public class I {
    private int age;
    private String doingWhat;

    public Photo createPhoto() {
        return new Photo(this.age, this.doingWhat);
    }

    public void backToThePast(Photo photo) {
        this.age =  photo.getAge();
        this.doingWhat = photo.getDoingSomething();
        System.out.println("時光倒流,我現在是" + age + "歲,可以:" + doingWhat);
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setDoingWhat(String doingWhat) {
        this.doingWhat = doingWhat;
    }

    public void print() {
        System.out.println("我現在是" + age + "歲,可以:" + doingWhat);
    }

    // 照片(備忘錄)
    public class Photo {
        private int age;
        private String doingWhat;

        public Photo(int age, String doingWhat) {
            this.age = age;
            this.doingWhat = doingWhat;
        }

        private int getAge() {
            return age;
        }

        private String getDoingSomething() {
            return doingWhat;
        }

    }
}

作為備忘錄角色的照片(photo)是我(I)的內部類,對我(I)來說是擁有寬介面包含getAge、getDoingSomething等方法,當然你也可以加其他方法,而這些方法對於其他物件是不可見的。

管理者(Caretaker):

// 時光倒流相框
public class Frame {
    private List<I.Photo> photoList = new ArrayList<>();

    public I.Photo getPhoto(int i) {
        return photoList.get(i);
    }

    public void add(I.Photo photo) {
        photoList.add(photo);
    }

}

測試類:

public class MementoPatternTest {
    public static void main(String[] args) {
        I i = new I();

        Frame frame = new Frame();

        i.setAge(12);
        i.setDoingWhat("春天放牛,夏天放鴨,秋天抓魚,冬天等小豬出生");
        i.print();
        frame.add(i.createPhoto());

        i.setAge(18);
        i.setDoingWhat("唸書");
        frame.add(i.createPhoto());
        i.print();

        i.setAge(25);
        i.setDoingWhat("工作");
        i.print();

        i.backToThePast(frame.getPhoto(0));
    }
}

測試結果圖:

使用場景

如果你需要建立物件狀態的快照來恢復物件之前的狀態時,你可以使用備忘錄模式。

如果直接訪問物件的狀態會破壞封裝,可以使用備忘錄模式。

優點

  • 你可以在不破壞物件封裝情況的前提下建立物件狀態快照。

缺點

  • 如果客戶端過於頻繁地建立備忘錄, 程式將消耗大量記憶體。
  • 管理者必須完整跟蹤原發器的生命週期, 這樣才能銷燬棄用的備忘錄。一般來說是這樣的,但是Java有垃圾回收器,管理者不存在,備忘錄也會被自動回收。

總結

備忘錄模式,在不破壞封裝型的前提下,獲取並儲存一個物件的內部狀態,以便以後物件可以恢復到這個狀態。該模式有三個角色:發起人(Originator)、備忘錄(Memento)、管理者(caretaker)。發起人建立備忘錄和利用備忘錄恢復狀態,備忘錄儲存發起人狀態,管理者儲存備忘錄。

2020年11月29日21:48:47