Java設計模式:備忘錄模式

作者:網絡 | 發布時間:2020年10月30日 | 閱讀:831

定義:在不破壞封裝性的前提下(xià),捕獲一(yī)個對象的内部狀态,并在該對象之外(wài)保存這個狀态。這樣就可以将該對象恢複到原先保存的狀态

類型:行爲類

類圖:

memento-pattern

我(wǒ)們在編程的時候,經常需要保存對象的中(zhōng)間狀态,當需要的時候,可以恢複到這個狀态。比如,我(wǒ)們使用Eclipse進行編程時,假如編寫失誤(例如不小(xiǎo)心誤删除了幾行代碼),我(wǒ)們希望返回删除前的狀态,便可以使用Ctrl+Z來進行返回。這時我(wǒ)們便可以使用備忘錄模式來實現。

備忘錄模式的結構

  • 發起人:記錄當前時刻的内部狀态,負責定義哪些屬于備份範圍的狀态,負責創建和恢複備忘錄數據。

  • 備忘錄:負責存儲發起人對象的内部狀态,在需要的時候提供發起人需要的内部狀态。

  • 管理角色:對備忘錄進行管理,保存和提供備忘錄。

通用代碼實現

    class Originator {
        private String state = "";

        public String getState() {
            return state;
        }
        public void setState(String state) {
            this.state = state;
        }
        public Memento createMemento(){
            return new Memento(this.state);
        }
        public void restoreMemento(Memento memento){
            this.setState(memento.getState());
        }
    }

    class Memento {
        private String state = "";
        public Memento(String state){
            this.state = state;
        }
        public String getState() {
            return state;
        }
        public void setState(String state) {
            this.state = state;
        }
    }
    class Caretaker {
        private Memento memento;
        public Memento getMemento(){
            return memento;
        }
        public void setMemento(Memento memento){
            this.memento = memento;
        }
    }
    public class Client {
        public static void main(String[] args){
            Originator originator = new Originator();
            originator.setState("狀态1");
            System.out.println("初始狀态:"+originator.getState());
            Caretaker caretaker = new Caretaker();
            caretaker.setMemento(originator.createMemento());
            originator.setState("狀态2");
            System.out.println("改變後狀态:"+originator.getState());
            originator.restoreMemento(caretaker.getMemento());
            System.out.println("恢複後狀态:"+originator.getState());
        }
    }

代碼演示了一(yī)個單狀态單備份的例子,邏輯非常簡單:Originator類中(zhōng)的state變量需要備份,以便在需要的時候恢複;Memento類中(zhōng),也有一(yī)個state變量,用來存儲Originator類中(zhōng)state變量的臨時狀态;而Caretaker類就是用來管理備忘錄類的,用來向備忘錄對象中(zhōng)寫入狀态或者取回狀态。


多狀态多備份備忘錄

通用代碼演示的例子中(zhōng),Originator類隻有一(yī)個state變量需要備份,而通常情況下(xià),發起人角色通常是一(yī)個javaBean,對象中(zhōng)需要備份的變量不止一(yī)個,需要備份的狀态也不止一(yī)個,這就是多狀态多備份備忘錄。實現備忘錄的方法很多,備忘錄模式有很多變形和處理方式,像通用代碼那樣的方式一(yī)般不會用到,多數情況下(xià)的備忘錄模式,是多狀态多備份的。其實實現多狀态多備份也很簡單,最常用的方法是,我(wǒ)們在Memento中(zhōng)增加一(yī)個Map容器來存儲所有的狀态,在Caretaker類中(zhōng)同樣使用一(yī)個Map容器才存儲所有的備份。下(xià)面我(wǒ)們給出一(yī)個多狀态多備份的例子:

    class Originator {
        private String state1 = "";
        private String state2 = "";
        private String state3 = "";

        public String getState1() {
            return state1;
        }
        public void setState1(String state1) {
            this.state1 = state1;
        }
        public String getState2() {
            return state2;
        }
        public void setState2(String state2) {
            this.state2 = state2;
        }
        public String getState3() {
            return state3;
        }
        public void setState3(String state3) {
            this.state3 = state3;
        }
        public Memento createMemento(){
            return new Memento(BeanUtils.backupProp(this));
        }

        public void restoreMemento(Memento memento){
            BeanUtils.restoreProp(this, memento.getStateMap());
        }
        public String toString(){
            return "state1="+state1+"state2="+state2+"state3="+state3;
        }
    }
    class Memento {
        private Map stateMap;

        public Memento(Map map){
            this.stateMap = map;
        }

        public Map getStateMap() {
            return stateMap;
        }

        public void setStateMap(Map stateMap) {
            this.stateMap = stateMap;
        }
    }
    class BeanUtils {
        public static Map backupProp(Object bean){
            Map result = new HashMap();
            try{
                BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                for(PropertyDescriptor des: descriptors){
                    String fieldName = des.getName();
                    Method getter = des.getReadMethod();
                    Object fieldValue = getter.invoke(bean, new Object[]{});
                    if(!fieldName.equalsIgnoreCase("class")){
                        result.put(fieldName, fieldValue);
                    }
                }

            }catch(Exception e){
                e.printStackTrace();
            }
            return result;
        }

        public static void restoreProp(Object bean, Map propMap){
            try {
                BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                for(PropertyDescriptor des: descriptors){
                    String fieldName = des.getName();
                    if(propMap.containsKey(fieldName)){
                        Method setter = des.getWriteMethod();
                        setter.invoke(bean, new Object[]{propMap.get(fieldName)});
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    class Caretaker {
        private Map memMap = new HashMap();
        public Memento getMemento(String index){
            return memMap.get(index);
        }

        public void setMemento(String index, Memento memento){
            this.memMap.put(index, memento);
        }
    }
    class Client {
        public static void main(String[] args){
            Originator ori = new Originator();
            Caretaker caretaker = new Caretaker();
            ori.setState1("中(zhōng)國");
            ori.setState2("強盛");
            ori.setState3("繁榮");
            System.out.println("===初始化狀态===n"+ori);

            caretaker.setMemento("001",ori.createMemento());
            ori.setState1("軟件");
            ori.setState2("架構");
            ori.setState3("優秀");
            System.out.println("===修改後狀态===n"+ori);

            ori.restoreMemento(caretaker.getMemento("001"));
            System.out.println("===恢複後狀态===n"+ori);
        }
    }

備忘錄模式的優缺點和适用場景

備忘錄模式的優點有:

  • 當發起人角色中(zhōng)的狀态改變時,有可能這是個錯誤的改變,我(wǒ)們使用備忘錄模式就可以把這個錯誤的改變還原。

  • 備份的狀态是保存在發起人角色之外(wài)的,這樣,發起人角色就不需要對各個備份的狀态進行管理。

備忘錄模式的缺點:

  • 在實際應用中(zhōng),備忘錄模式都是多狀态和多備份的,發起人角色的狀态需要存儲到備忘錄對象中(zhōng),對資(zī)源的消耗是比較嚴重的。

如果有需要提供回滾操作的需求,使用備忘錄模式非常适合,比如jdbc的事務操作,文本編輯器的Ctrl+Z恢複等。

相關内容