行为型模式:备忘录模式

作者: LieBrother | 来源:发表于2019-03-27 08:52 被阅读5次

    十一大行为型模式之九:备忘录模式。

    简介

    姓名 :备忘录模式

    英文名 :Memento Pattern

    价值观 :凡事要有备份

    个人介绍
    Without violating encapsulation,capture and externalize an object's internal state so that the object can be restored to this state later.
    在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
    (来自《设计模式之禅》)

    你要的故事

    点开看这篇文章的各位,都是。。。程序界的大佬。作为程序猿,免不了『上线』这件小事。每逢上线必祭天。。。上线这件事我们很多人都操作过,每家公司有不同的上线流程以及上线的技术能力。按照发布的平台的完善程度大概分为 3 种。

    1. 发布平台牛逼的公司:只需要按下『一键部署』按钮,就搞定上线,按下『回滚』按钮,就搞定回滚上一个版本。
    2. 发布平台稍差点的公司:可能就得多个步骤操作了,上线:备份并关闭应用、部署并启动新应用;回滚:关闭新应用、恢复旧应用并启动。
    3. 没有发布平台的公司:那就全程手工操作,上线:关闭旧应用、复制旧应用到备份空间、复制新应用到部署环境、启动新应用;回滚:关闭新应用、删除新应用、从备份空间复制旧应用到部署环境、启动旧应用。

    其实通过发布平台完善程度可以侧面反映企业的技术成熟程度。怎么说呢?发布系统的操作难易程度在我们作为程序猿心中,都有一个可接受的范围。假设刚开始是单体应用,一个 tomcat 和一个 war 就搞定,你需要发布平台么?并不需要,咱 ctrl+Cctrl+Vshutdownstartup 就行,还弄什么发布平台。当系统有多套服务时,每次上线都需要部署 10 个机器的应用,这时你能忍么?要是还是手工操作,那发布一次系统得花费好长时间,要是再搞不好,回滚一次,一晚上都没了;这时就会逼迫开发出一个简易的发布平台,把对多台机器的操作步骤放到发布平台上。当系统是以微服务的架构发展时,每个服务都有上百个实例,那这时就不能简单的把操作步骤搬到发布平台了,还得简化步骤,最终变成上面说的 一键部署一键回滚

    上面的 3 种我都亲身经历过。。。在刚出来实习时候,就经历了第 3 种情况,因为是单体应用,一个应用搞定所有东西,手动部署已满足要求。到了银行工作,接触到了云平台,那时就只需要一个按钮就唰唰唰的部署了,也是上面说的第 1 种。而现在,正在经历第 2 种发布平台,只是简单的把操作步骤搬到了系统上,目前的情况是机器越来越多,操作步骤没删减的话,每次发布会花费很多时间,这也会去促进开发出更方便使用的发布平台。

    回到今天的主题,今天讲的是备忘录模式,从字面上理解,就是讲备份东西,有了备份就可以恢复。上面讲了一大堆发布的东西,也是咱们工作中接触蛮多的事情,发布的最核心就是要支持部署新应用以及回滚老应用回滚特别重要,它能够保证在新应用出现异常的情况下,马上恢复到旧应用可用的状态,减少异常的影响面。发布这东东也很符合备忘录模式,下面通过模拟发布步骤代码来讲备忘录模式。这里讲的不是上面说的第 1 种,这里围绕着第 3 种发布步骤写,不管哪种发布平台,它们的底层都是一样的。

    先定义应用实例这个类,应用一般会有应用名、版本号信息。

    /**
     * 应用实例
     */
    class App {
        private String content;
        private String version;
    
        public App(String content, String version) {
            this.content = content;
            this.version = version;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public String getVersion() {
            return version;
        }
    
        public void setVersion(String version) {
            this.version = version;
        }
    
        @Override
        public String toString() {
            return "App{" +
                    "content='" + content + '\'' +
                    ", version='" + version + '\'' +
                    '}';
        }
    }
    

    定义 AppBackup 来充当备忘录角色,它有一个属性就是 App,也就是备份的应用。

    /**
     * 应用备份(充当备忘录角色)
     */
    class AppBackup {
    
        private App app;
    
        public AppBackup(App app) {
            this.app = app;
        }
    
        public App getApp() {
            return app;
        }
    
        public void setApp(App app) {
            this.app = app;
        }
    }
    

    有了备忘录,也需要一个空间来存放备忘录,并对外提供备忘录。

    /**
     * 备份空间
     */
    class Space {
        private AppBackup appBackup;
    
        public AppBackup getAppBackup() {
            return appBackup;
        }
    
        public void setAppBackup(AppBackup appBackup) {
            this.appBackup = appBackup;
        }
    }
    

    有了这些备份机制,还需要有一个程序猿来部署,这位同学需要掌握发布步骤的所有过程,部署新应用以及回滚旧应用。

    /**
     * 部署应用的同学
     */
    class Deployer {
    
        // 要部署的应用
        private App app;
    
        public App getApp() {
            return app;
        }
    
        // 设置部署应用
        public void setApp(App app) {
            this.app = app;
        }
    
        // 创建应用的备份
        public AppBackup createAppBackup() {
            return new AppBackup(app);
        }
    
        // 从备忘录恢复应用
        public void setAppBackup(AppBackup appBackup) {
            this.app = appBackup.getApp();
        }
    
        // 显示应用的信息
        public void showApp() {
            System.out.println(this.app.toString());
        }
    
        // 暂停应用
        public void stopApp() {
            System.out.println("暂停应用:" + this.app.toString());
        }
    
        // 启动应用
        public void startApp() {
            System.out.println("启动应用:" + this.app.toString());
        }
    }
    

    再献上测试代码。

    public class MementoTest {
    
        public static void main(String[] args) {
            Deployer deployer = new Deployer();
            deployer.setApp(new App("apply-system", "1.0.0"));
    
            System.out.println("1. 暂停旧应用");
            deployer.stopApp();
    
            System.out.println("2. 备份旧应用");
            Space space = new Space();
            space.setAppBackup(deployer.createAppBackup());
    
            System.out.println("3. 拷贝新应用到服务器");
            deployer.setApp(new App("apply-system", "2.0.0"));
            deployer.showApp();
    
            System.out.println("4. 启动新应用");
            deployer.startApp();
    
            System.out.println("5. 有异常,暂停新应用");
            deployer.stopApp();
    
            System.out.println("6. 回滚旧应用,拷贝备份的旧应用到服务器");
            deployer.setAppBackup(space.getAppBackup());
            deployer.showApp();
    
            System.out.println("7. 启动备份的旧应用");
            deployer.startApp();
        }
    
    }
    
    打印结果:
    1. 暂停旧应用
    暂停应用:App{content='apply-system', version='1.0.0'}
    2. 备份旧应用
    3. 拷贝新应用到服务器
    App{content='apply-system', version='2.0.0'}
    4. 启动新应用
    启动应用:App{content='apply-system', version='2.0.0'}
    5. 有异常,暂停新应用
    暂停应用:App{content='apply-system', version='2.0.0'}
    6. 回滚旧应用,拷贝备份的旧应用到服务器
    App{content='apply-system', version='1.0.0'}
    7. 启动备份的旧应用
    启动应用:App{content='apply-system', version='1.0.0'}
    

    备忘录模式代码实现搞定。有同学会不会觉得挺麻烦的,为什么要有AppBackup?我们看看个人介绍,在对象之外保存状态,AppBackup 就是对象之外的对象,用来保存旧应用。

    总结

    备忘录模式定义了一个备份机制。在很多场景都有类似备忘录模式的实现,比如数据库的事务的回滚机制。在平常业务开发中并没有经常使用这个设计模式,但是我们有使用它的思想,比如我们用数据库或者其他中间件做备份数据,其中备份思想是一致的。

    推荐阅读

    行为型模式:状态模式

    行为型模式:观察者模式

    行为型模式:迭代器模式

    设计模式系列文章持续更新中,欢迎关注公众号,一起交流学习。

    LieBrother

    相关文章

      网友评论

        本文标题:行为型模式:备忘录模式

        本文链接:https://www.haomeiwen.com/subject/wjravqtx.html