zoukankan      html  css  js  c++  java
  • 【一起学系列】之剩下的设计模式们

    前言

    开发】:老大,你教了我挺多设计模式的,已经全部教完了吗?

    BOSS】:没呢,还有好几个设计模式没说过呢,今天再传授你三个吧,分别是建造者模式,责任链模式,备忘录模式,如何?

    开发】:好啊,我最喜欢学习了!

    建造者模式

    意图

    将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    核心代码

    定义建造接口

    public interface Builder {
    
        void buildPartOne();
    
        void buildPartTwo();
    
        void buildPartThr();
    
        /***
         * 一般情况肯定是一个复杂的对象
         */
        String getResult();
    }
    

    定义实际建造工人

    public class ConcreteBuilder implements Builder {
    
        private StringBuffer buffer = new StringBuffer();
    
        @Override
        public void buildPartOne() {
            buffer.append("i am part one
    ");
        }
    
        @Override
        public void buildPartTwo() {
            buffer.append("i am part two
    ");
        }
    
        @Override
        public void buildPartThr() {
            buffer.append("i am part Thr
    ");
        }
    
        @Override
        public String getResult() {
            return buffer.toString();
        }
    }
    

    如何创建不同的表示?

    定义督公

    public class Director {
    
        private Builder builder;
    
        public Director(Builder builder) {
            this.builder = builder;
        }
    
        public void setBuilder(Builder builder) {
            this.builder = builder;
        }
    
        public void construct() {
            builder.buildPartOne();
            builder.buildPartTwo();
            builder.buildPartThr();
        }
    }
    

    模拟调用

    public class App {
    
        /***
         * 建造者模式
         *     建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
         *
         * 主要解决
         *     主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
         *
         * 何时使用
         *      一些基本部件不会变,而其组合经常变化的时候。
         *
         * 如何解决
         *     将变与不变分离开。
         *
         * 关键代码
         *     建造者:创建和提供实例
         *     建造者接口:依赖接口编程
         *     指导者:管理建造出来的实例的依赖关系
         *     产品:建造者所生产的产品
         * 建造者作为参数进入指导者构造方法,通过特定普遍的构造顺序或算法执行,得到产品
         *
         * 应用实例:
         *     1.去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"
         *     2.StringBuilder
         */
        public static void main(String[] args){
            // 创建建造者接口并指向具体建造者 - 包含最终产品
            Builder concreteBuilder = new ConcreteBuilder();
    
            // 创建指导者, 把具体建造者即工人作为参数传入, 通过统一方法执行相应的构建命令
            Director director = new Director(concreteBuilder);
            director.construct();
    
            // 从工人即具体建造者获取产品
            String result = concreteBuilder.getResult();
            System.out.println(result);
        }
    }
    

    建造者的延展思考:链式调用

    链式调用让代码更优雅~

    public class MyBuilder {
    
        // 省略不必要的代码
    
        MyBuilder withName(String name) {
            this.setName(name);
            return this;
        }
    
    
        MyBuilder withYear(String year) {
            this.setYear(year);
            return this;
        }
    
    
        MyBuilder withSex(String sex) {
            this.setSex(sex);
            return this;
        }
    }
    

    UML图

    代码见下方~

    责任链模式

    意图

    使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有一个对象处理它为止

    典型应用

    Log4J 日志系统即是使用了责任链的思想,通过不同日志级别的传递,按级别处理日志

    简单实现一个Log等级系统

    抽象类

    定义日志等级,设置下一个处理器,抽象出写入方法

    public abstract class AbstractLogger {
    
        // 责任级别
        public static int INFO = 1;
        public static int DEBUG = 2;
        public static int ERROR = 3;
    
        // 当前级别
        int level;
    
        //责任链中的下一个元素
        AbstractLogger nextLogger;
        public void setNextLogger(AbstractLogger nextLogger){
            this.nextLogger = nextLogger;
        }
    
        // 记录日志
        public void logMessage(int level, String message){
            if(this.level <= level){
                write(message);
            }
    
            if(nextLogger != null){
                nextLogger.logMessage(level, message);
            }
        }
    
        // 抽象方法 -> 重写具体日志输出类型
        abstract protected void write(String message);
    }
    

    具体日志类

    public class InfoLoger extends AbstractLogger {
    
        public InfoLoger(int level){
            this.level = level;
        }
    
        @Override
        protected void write(String message) {
            System.out.println("InfoLoger Console::Logger: " + message);
        }
    }
    

    为了避免重复,只展示一个类

    实际调用

    public class App {
        
        public static void main(String[] args){
            AbstractLogger log = getChainOfLoggers();
            log.logMessage(AbstractLogger.INFO, "i am info");
            log.logMessage(AbstractLogger.DEBUG, "i am debug");
            log.logMessage(AbstractLogger.ERROR, "i am error");
        }
    
        private static AbstractLogger getChainOfLoggers(){
            AbstractLogger error = new ErrorLoger(AbstractLogger.ERROR);
            AbstractLogger debug = new DebugLoger(AbstractLogger.DEBUG);
            AbstractLogger info = new InfoLoger(AbstractLogger.INFO);
    
            error.setNextLogger(debug);
            debug.setNextLogger(info);
            return error;
        }
    }
    
    // 输出结果:
    // InfoLoger Console::Logger: i am info
    //
    // ------------------------
    //
    // DebugLoger Console::Logger: i am debug
    // InfoLoger Console::Logger: i am debug
    //
    // ------------------------
    //
    // ErrorLoger Console::Logger: i am error
    // DebugLoger Console::Logger: i am error
    // InfoLoger Console::Logger: i am error
    

    总结

    多种形式

    • 当前pattern下类似日志级别形式, 只要等级比A大,那B,C都会处理
    • 如A->B->C 由低到高级别执行,只要执行就返回等
    • 最高级形式: 低级发起请求后, 高级任一处理后,请求反馈即可(涉及到异步相关,线程通信)

    优点

    • 降低耦合度。它将请求的发送者和接收者解耦
    • 简化了对象。使得对象不需要知道链的结构
    • 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任
    • 增加新的请求处理类很方便

    缺点

    • 不能保证请求一定被接收
    • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
    • 可能不容易观察运行时的特征,有碍于除错

    使用场景

    • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
    • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
    • 可动态指定一组对象处理请求

    UML图

    代码见下方~

    备忘录模式

    意图

    在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后可将对象恢复到原先保存的状态

    核心代码

    备忘录

    /**
     * ******************************
     * description:  备忘录,确定数据结构即可
     * ******************************
     */
    public class Memento {
        Map<String, String> data;
    }
    

    模拟短信场景

    /**
     * ******************************
     * description:  模拟短信场景
     * ******************************
     */
    public class MessageData {
    
        private String time;
    
        private String message;
    
        /**
         * 存储数据
         */
        public Memento saveMemento () {
            Map<String, String> map = Maps.newHashMap();
            map.put("TIME",    time);
            map.put("MESSAGE", message);
            return new Memento(map);
        }
    
        /**
         * 取出数据
         */
        public void getFromMemento(Memento memento){
            time    = memento.getData().get("TIME");
            message = memento.getData().get("MESSAGE");
        }
    
        // 省略部分代码
    }
    

    备忘录存储容器

    public class MementoTaker {
    
        private List<Memento> mementoList = new ArrayList<>();
    
        public void add(Memento state){
            mementoList.add(state);
        }
    
        public Memento get(int index){
            return mementoList.get(index);
        }
    }
    

    核心调用代码

    public class App {
        public static void main(String[] args) throws InterruptedException {
    
            // 创建备忘录管理者
            MementoTaker mementoTaker = new MementoTaker();
    
            MessageData messageData = new MessageData();
            messageData.setTime(System.currentTimeMillis() + "");
            messageData.setMessage("This is messgae first.");
            mementoTaker.add(messageData.saveMemento());
    
            System.out.println("First: -> " + messageData);
    
            Thread.sleep(2000);
    
            messageData.setTime(System.currentTimeMillis() + "");
            messageData.setMessage("This is messgae second.");
            mementoTaker.add(messageData.saveMemento());
    
            System.out.println("Second: -> " + messageData);
    
            Thread.sleep(2000);
    
            // 回复初次状态
            messageData.getFromMemento(mementoTaker.get(0));
    
            System.out.println("********************检测数据是否回到初始状态******************");
            System.out.println(messageData);
        }
    }
    

    模式总结:其实该模式非常简单,即确定好数据结构在容器中存储一份,以便后续恢复,或者重新使用等等

    总结

    主要解决

    所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态

    备忘录思想的实践

    • 打游戏时的存档
    • Windows 里的 ctri + z
    • 数据库的事务管理

    UML图

    相关代码链接

    GitHub地址

    • 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
    • 提供了友好的阅读指导
  • 相关阅读:
    MySQL常用命令记录
    VM新安装centos7无法连接网络的问题
    nginx + tomcat实现负载均衡
    Redis集群分布(Windows版)
    7.2 基础知识ArrayMap
    7.1 基础知识Android消息处理机制
    6.5 Android硬件访问服务使用反射
    6.4 Android硬件访问服务编写HAL代码
    6.3 Android硬件访问服务APP代码
    6.2、Android硬件访问服务编写系统代码
  • 原文地址:https://www.cnblogs.com/kkzhilu/p/13264157.html
Copyright © 2011-2022 走看看