关于软件设计:架构师日记深入理解软件设计模式-京东云技术团队

2次阅读

共计 16761 个字符,预计需要花费 42 分钟才能阅读完成。

作者:京东批发 刘慧卿

一 设计模式与编程语言

1.1 什么是设计模式

设计模式(Design pattern):由软件开发人员在软件开发中面临常见问题的解决方案,是通过长时间的试验积攒总结进去的,它使设计更加灵便和优雅,复用性更好。从实用的角度来看,它代表了某一类问题的最佳实际。

设计模式到底解决了开发过程中的哪些难题呢,它又是如何来解决的呢?

其外围是:复用和解耦。使不稳固依赖于稳固、具体依赖于形象,以此加强软件设计适应变动的能力。

1.2 什么是编程范式

要探讨设计模式和编程语言的关系,还得从编程范式谈起。编程范式一词最早来自 Robert Floyd 在 1979 年图灵奖的颁奖演说,是程序员对待程序的观点,代表了程序设计者认为程序应该如何被构建和执行的认识,与软件建模形式和架构格调有严密关系。

以后支流的编程范式有三种:

1. 结构化编程(structured programming)

2. 面向对象编程(object-oriented programming)

3. 函数式编程(functional programming)

这几种编程范式之间的关系如下:

1. 起初是非结构化编程,指令(goto 指令)能够轻易跳转,数据能够轻易援用。起初有了结构化编程,人们把 goto 语句去掉了,束缚了指令的方向性,过程之间是单向的,但数据却是能够全局拜访的;

2. 起初面向对象编程的时候,人们罗唆将数据与其严密耦合的办法放在一个逻辑边界内,束缚了数据的作用域,靠关系来查找;

3. 到函数式编程的时候,人们束缚了数据的可变性,通过一系列函数的组合来形容数据,从源到指标映射规定的编排,两头它是无状态的;

编程范式是形象的,编程语言是具体的。编程范式是编程语言背地的思维,要通过编程语言来体现。C 语言的支流编程范式是结构化编程,而 Java 语言的支流编程范式是面向对象编程,起初 Java8 开始反对 Lambda 表达式,将函数式编程范式的内容交融进来,同时新诞生的语言一开始就反对多范式,比方 Scala,Go 和 Rust 等。

从结构化编程到面向对象编程,再到函数式编程,形象水平越来越高(离图灵机模型越来越远),与畛域问题的间隔越来越近。直观的来讲,就是解决事实问题的效率晋升了,灵活性和执行效率随之有所降落。

设计模式无论用什么语言实现都是能够的,然而因为语言的各自差异化特点,不是每种语言都完满或对立实现各种设计模式。比方 Java 外面有策略模式,那是因为 Java8 之前不反对办法传递,不能把一个办法当作参数传给他人,所以有了策略模式。而 JavaScript 等语言能够间接传函数,就基本没必要造一个策略模式进去。

1.3 什么是多态个性

面向对象编程语言有三大个性:封装、继承和多态。

1. 封装即信息暗藏或数据保护,“数据结构 ” 通过裸露无限的拜访接口,受权内部仅能通过 ” 数据结构 ” 提供的办法 (函数) 来拜访其外部的数据;

2. 继承的益处是能够实现代码复用,但不应适度应用,如果继承的档次过深就会导致代码可读性和可维护性变差。因而倡议少用继承而多用组合模式;

3. 多态能够分为变量的多态, 办法的多态, 类的多态。通常强调的是类的多态,多态的实现是指子类能够替换父类,在理论代码运行过程中调用子类的办法实现;

多态能够说是面向对象中最重要的一个个性,是解决我的项目中紧偶合的问题,进步代码的可扩展性和可复用性的外围,是很多设计模式、设计准则、编程技巧的代码实现根底。

多态比拟直观的了解就是去实现某个动作,当不同的对象去实现时会产生出不同的状态,其作用范畴能够是办法的参数和办法的返回类型。

多态这种个性也须要编程语言提供非凡的语法机制来实现,Java 中多态能够通过 ” 子类继承父类 + 子类重写父类办法 + 父类援用指向子类对象 ” 的形式实现,还能够通过 ” 接口语法 ” 的形式实现。C++ 中则应用 virtual(虚函数)关键字来实现。像一些动静语言如 Python 也能够通过 duck-typing 的语法实现,另外 Go 语言中的 ” 隐藏式接口 ” 也算是 duck-typing。

Python 语言,实现多态示例如下:

class MyFile:
    def write(self):
        print('I write a message into file.')
        
class MyDB:
    def write(self):
        print('I write data into db.')
        
def doIt(writer):
    writer.write()

def demo():
    myFile= MyFile()
    myDB = MyDB()
    doIt(myFile)
    doIt(myDB)

二 设计模式与架构模式

2.1 理解架构模式

对给定上下文的软件架构中常见问题的一种通用的可复用的解决方案,能够为设计大型软件系统的各个方面提供相应的领导。它不仅显示了软件需要和软件结构之间的对应关系,而且指定了整个软件系统的组织和拓扑构造,提供了一些设计决策的基本原理,常见的架构设计模式如下:

架构模式 模式形容 实用场景
分层模式(Layered pattern) 用于可分解为子工作的结构化程序,每个子工作都位于特定的形象层级,每一层都为上一层提供服务。 桌面应用程序;电子商务 web 应用程序;挪动 App;
客户端 - 服务器模式(Client-server pattern) 服务器将向多个客户端提供服务。客户端从服务器申请服务,服务器向这些客户端提供相干服务。 电子邮件、文档共享和银行等在线应用程序;基于 IPC 的应用程序;
主从模式(Master-slave pattern) 主节点将工作调配给雷同的从节点,并依据从节点返回的后果计算最终后果。 数据库主从复制;过程内多线程调度;
管道 - 过滤器模式(Pipe-filter pattern) 用于结构生成和解决数据流的零碎。每个解决步骤都蕴含一个过滤器组件。要解决的数据通过管道传递。这些管道可用于缓冲或同步目标。 编译器;
代理模式(Broker pattern) 通过解耦组件来结构分布式系统。 消息中间件;;网络传输中的代理软件
点对点模式(Peer-to-peer pattern) 每个组件都称为对等节点。对等节点既能够作为客户机(从其余对等节点申请服务),也能够作为服务器(向其余对等节点提供服务)。 文件共享网络;多媒体协定;
事件 - 总线模式(Event-bus pattern) 订阅公布模式,事件源将音讯公布到事件总线上的特定通道,监听者订阅特定的通道。 告诉服务;注册核心;
模型 - 视图 - 控制器模式(Model-view-controller pattern) MVC 模式,解耦组件并容许无效的代码重用 web 应用程序架构;GUI 应用程序;
黑板模式(Blackboard pattern) 对于没有确定解决方案策略的问题十分有用,所有的组件都能够达到黑板。组件能够生成增加到黑板上的新数据对象。组件在黑板上查找特定类型的数据,并通过与现有的常识源进行模式匹配找到这些数据。 语音辨认;车辆辨认及追踪;
解释器模式(Interpreter pattern) 用于设计一个解释专用语言编写的程序组件。 数据库查询语言,如 SQL 用于形容通信协议的语言;

2.2 理解设计模式

在 1995 年,有四位编程界的前辈合著了一本书,书名叫做《Design Patterns: Elements of Reusable Object-Oriented Software》,翻译过去就是《设计模式:可复用面向对象软件的根底》,书外面总共收录了 23 种设计模式。这本书是软件研发畛域重要的里程碑,合著此书的四位作者,被业内称为 GoF(Gang of Four),因而这本书也被人称为 GoF 设计模式。

设计模式依照目标来分类有:创立、构造、行为三种,依照作用范畴来分类有:类模式和对象模式两种。

1. 创立型模式:用于创建对象,就是将对象的创立与应用拆散。从而升高零碎的耦合度,使用者不须要关注对象的创立细节,对象的创立由相干的工厂来实现。

2. 结构型模式:形容如何将类,对象,接口之间按某种布局组成更大的构造。

3. 行为型模式:用于形容程序在运行时简单的流程管制,即形容多个类或对象之间怎么相互协作共同完成单个对象都无奈独自实现的工作,它波及算法与对象间职责的调配。

23 种设计模式如下:

类型 模式名称 模式形容
创立型 单例模式(Singleton) 某个类只能生成一个实例,该类提供了一个全局拜访点供内部获取该实例,其拓展是无限多例模式。
工厂办法模式(Factory Method) 定义一个用于创立产品的接口,由子类决定生产什么产品。  
形象工厂模式(AbstractFactory) 提供一个创立产品族的接口,其每个子类能够生产一系列相干的产品。  
建造者模式(Builder) 将一个简单对象分解成多个绝对简略的局部,而后依据不同须要别离创立它们,最初构建成该简单对象。  
原型模式(Prototype) 将一个对象作为原型,通过对其进行复制而克隆出多个和原型相似的新实例。  
结构型 适配器模式(Adapter) 将一个类的接口转换成客户心愿的另外一个接口,使得本来因为接口不兼容而不能一起工作的那些类能一起工作。
桥接模式(Bridge) 将形象与实现拆散,使它们能够独立变动。它是用组合关系代替继承关系来实现,从而升高了形象和实现这两个可变维度的耦合度。  
组合模式(Composite) 将对象组合成树状层次结构,使用户对单个对象和组合对象具备统一的拜访性。  
装璜模式(Decorator) 动静的给对象减少一些职责,即减少其额定的性能。  
外观模式(Facade) 为多个简单的子系统提供一个统一的接口,使这些子系统更加容易被拜访。  
亨元模式(Flyweight) 使用共享技术来无效地反对大量细粒度对象的复用。  
代理模式(Proxy) 为某对象提供一种代理以管制对该对象的拜访。即客户端通过代理间接地拜访该对象,从而限度、加强或批改该对象的一些个性。  
行为型 模板办法模式(TemplateMethod) 定义一个操作中的算法骨架,而将算法的一些步骤提早到子类中,使得子类能够不扭转该算法构造的状况下重定义该算法的某些特定步骤。
策略模式(Strategy) 定义了一系列算法,并将每个算法封装起来,使它们能够互相替换,且算法的扭转不会影响应用算法的客户。  
命令模式(Command) 将一个申请封装为一个对象,使发出请求的责任和执行申请的责任宰割开。  
职责链模式(Chain of Responsibility) 把申请从链中的一个对象传到下一个对象,直到申请被响应为止。通过这种形式去除对象之间的耦合。  
状态模式(State) 容许一个对象在其外部状态产生扭转时扭转其行为能力。  
观察者模式(Observer) 多个对象间存在一对多关系,当一个对象产生扭转时,把这种扭转告诉给其余多个对象,从而影响其余对象的行为。  
中介者模式(Mediator) 定义一个中介对象来简化原有对象之间的交互关系,升高零碎中对象间的耦合度,使原有对象之间不用相互了解。  
迭代器模式(Iterator) 提供一种办法来程序拜访聚合对象中的一系列数据,而不裸露聚合对象的外部示意。  
访问者模式(Visitor) 在不扭转汇合元素的前提下,为一个汇合中的每个元素提供多种拜访形式,即每个元素有多个访问者对象拜访。  
备忘录模式(Memento) 在不毁坏封装性的前提下,获取并保留一个对象的外部状态,以便当前复原它。  
解释器模式(Interpreter) 提供如何定义语言的文法,以及对语言句子的解释办法,即解释器。  

2.3 小结

•架构模式更像是宏观策略层面的设计,设计模式则更像是战略目标拆解进去的具体任务的实现计划;

•软件架构是软件的一种搭建模式,往往规定了软件的模块组成,通信接口(含通信数据结构),组件模型,集成框架等,往往规定了具体的细节;

•设计模式是一种软件的实现办法,是一种形象的方法论,是为了更好的实现软件而演绎进去的无效办法;

•实现一种软件架构,不同组成部分可能用到不同的设计模式,某个局部也可能能够采纳不同的设计模式来实现;

三 利用实际指南

3.1 实用场景

不应用设计模式也能实现业务诉求,零碎也可能失常运行,为什么要应用设计模式呢?

是的,相当一部分场景是不须要进行设计模式的引入的,比方:业务逻辑简略,业务演进方向不明朗,或者就是一个不须要常常迭代的性能点。但当咱们遇到了简单问题设计的时候,就须要借助前人的教训了,而设计模式就是前人为咱们积淀总结的各种常见问题的解决方案。

那么多种设计模式,难道我须要全副零碎的学习实现一遍,都要闭着眼睛就能写进去吗?其实不必,这就跟排序算法一样,咱们只须要记住每种算法的适用范围和场景就能够了,在有须要的时候,再去深入研究就能够了。以下总结了各种设计模式对应的实用场景:

模式名称 实用场景
单例模式(Singleton) 无状态类应用单例模式能够节俭内存资源
工厂办法模式(Factory Method) 在不晓得具体实现细节的状况下创建对象的场景
形象工厂模式(AbstractFactory) 客户端与对象创立解耦,须要创立多个不同类型的对象的场景
建造者模式(Builder) 生成简单对象的场景
原型模式(Prototype) 疾速创立大量同类对象的场景
适配器模式(Adapter) 让两个不兼容的类一起工作的场景
桥接模式(Bridge) 将一个类的形象局部和实现局部独立扭转的场景
组合模式(Composite) 示意树形构造的场景
装璜模式(Decorator) 动静地为对象增加新职责的场景
外观模式(Facade) 为一个简单的子系统提供一个简略的接口的场景
亨元模式(Flyweight) 在多个中央共享大量细粒度对象的场景
代理模式(Proxy) 在拜访某个对象时减少额定管制的场景
模板办法模(TemplateMethod) 在不扭转算法构造的状况下重定义算法中的某些步骤的场景
策略模式(Strategy) 在不同状况下应用不同算法的场景
命令模式(Command) 反对命令的撤销和复原、提早调用或日志操作的场景
职责链模式(Chain of Responsibility) 在不明确指定接收者的状况下,向多个对象中提交一个申请的场景
状态模式(State) 依据对象的状态来扭转它的行为的场景。
观察者模式(Observer) 在对象之间涣散耦合的场景
中介者模式(Mediator) 在多个对象之间涣散耦合的场景
迭代器模式(Iterator) 为容器对象提供多种遍历形式的场景
访问者模式(Visitor) 在不扭转各元素的类的前提下定义对这些元素的新操作的场景
备忘录模式(Memento) 历史回放或者回滚等场景
解释器模式(Interpreter) 定义一个语言并为该语言实现一个解释器的场景

3.2 场景案例

为了让读者对设计模式有个更加直观平面的感知,接下来以理论案例为大家展示一下设计模式在理论场景的利用。案例蕴含了创立型,结构型,行为型各种模式类型里罕用的设计模式,比方:

•用工厂模式隔离业务实现;

•用策略模式消解业务流程分支;

•用模板办法模式提取业务分支公共流程;

•用建造者模式简化入参对象的构建难度;

•用代理模式横向扩大通用能力(日志,异样解决);

•用职责链模式对申请进行敏感词,防刷校验;

•用命令模式让指令领有了记忆;

中国有个古谚语:“一个和尚挑水吃,两个和尚抬水吃,三个和尚等水吃。”咱们就通过程序来模仿出家人的寺庙生存。

工厂模式

首先,这三个人是如何成为和尚的呢?

一号和尚(贫困潦倒型),出世在一个大山外头,父母怕他孤独,给他生了 5 个弟弟,在他 9 岁那年,凑巧家里闹了饥荒,为了吃上饭,进了寺庙,出了家;

二号和尚(穷途末路型),出世在一个湖泊旁边,因为生性正直,18 岁那年,走在街头,路见不平,三拳打死街上恶霸,为了赎罪,受了戒,坠入空门;

三号和尚(天选之子型),从小敏而好学,性格温厚,对佛学产生浓厚兴趣,13 岁那年,为了继承和光大佛法,断了尘缘,皈依佛门。

N 号和尚,……

每一个和尚的来历都不尽相同,但在当下喝不上水,这件事件上,都显得不重要。重要的是,只有凑足三个和尚,就会没水喝。那么寺庙如招收和尚?这里就能够用到工厂模式的思维。

    // 贫困潦倒产生的和尚过程:1. 大山里;2. 闹饥荒;3. 要吃饭;一号和尚 = HeShangFactoty.getOneHeshang("贫困潦倒型");
    // 穷途末路产生的和尚过程:1. 生性正直;2. 打死恶霸;3. 要赎罪;二号和尚 = HeShangFactoty.getOneHeshang("穷途末路型");
    // 天选之子产生的和尚过程:1. 敏而好学;2. 佛学感兴趣;3. 要宽广佛法;三号和尚 = HeShangFactoty.getOneHeshang("天选之子型");

以上示例想体现的是工厂模式能将简单的对象创立和应用进行了拆散设计。上面就以和尚吃水这件事件,用程序的形式具体展示工厂模式的实现思路。依照和尚的人数,别离有挑,抬,等三种实现形式。以下为根底代码实现:

public interface Waterable {Water getWater();
}

public class TiaoShui implements Waterable{
    public Water getWater(){System.out.println("先到山上来!");
        return "水是挑上来的!";
    }
} 

public class TaiShui implements Waterable{
    public Water getWater(){System.out.println("先到山上来!");
        return "水是抬上来的!";
    }
} 

public class DengShui implements Waterable{
    public Water getWater(){System.out.println("就坐在原地!");
        return "水是等不来的!";
    }
} 

具体应用

public class Factory {
    /**
     * 依照和尚数量生成取水对象
     *
     * @param heShangNum 和尚数量
     * @return
     */
    public static Waterable getWaterable(Integer heShangNum) {switch (heShangNum) {
            case 1:
                return new TiaoShui();
            case 2:
                return new TaiShui();
            case 3:
                return new DengShui();
            default:
                throw new RuntimeException("庙小,装不下那么多和尚!");
        }
    }
}

策略模式

依照不同的条件(人数),别离有几种获取水的办法:挑,抬,等。能够通过策略模式来实现,后面的实现形式其实就是策略模式和工厂模式的联合。咱们再看一下策略模式的具体应用形式如下:


    /**
     * 通过入参和尚人数,就能够动静扭转 Waterable.getWater()的取水模式
     * @param heShangNum
     * @return
     */
    public void getWater(Integer heShangNum) {Waterable waterable = Factory.getWaterable(heShangNum);
        Water water = waterable.getWater();// 取水}

1. 输出参数 1:挑水模式的实现(对应 Tiaoshui 实现类);

2. 输出参数 2:抬水模式的实现(对应 Taishui 实现类);

3. 输出参数 3:等不到水模式的实现(对应 Dengshui 实现类);

通过和尚人数,就能够动静取得对应的取水实现,即所谓的通过策略实现业务,对于应用方来说(主流程),无需关注取水的具体实现(解耦:业务流程稳定性的设计体现),新增取水形式时,只须要新增一个类实现就能够了,存量的实现和主流程都不会受到影响。

模板办法

咱们细化取水过程,取水过程个别须要三步:

1. 拿起工具(扁担或者木棍);

2. 到寺庙南面的小河边(步行);

3. 装满水带回寺庙(挑水,抬水,等水);

咱们能够将取水流程步骤进行模板化。

public interface Waterable {Water getWater();
}

public abstract class AbstractWaterable implements Waterable {
    @Override
    public Water getWater() {takeTool();
        toRiver();
        return moveWater();}
    /**
     * 拿起工具
     */
    protected abstract String takeTool();

    /**
     * 到河边去
     */
    protected String toRiver() {System.out.println("走过来!");
        return "步行";
    }

    /**
     * 将水带回来
     *
     * @return
     */
    protected abstract Water moveWater();}

个性化场景实现

public class TiaoShui extends AbstractWaterable {

    @Override
    protected String takeTool() {return "扁担";}

    @Override
    protected Water moveWater() {return "挑水";}
}

public class Taishui extends AbstractWaterable{
    @Override
    protected String takeTool() {return "木棍";}

    @Override
    protected Water moveWater() {return "抬水";}
}

public class DengShui extends AbstractWaterable{
    @Override
    protected String takeTool() {return "意念";}

    @Override
    protected String toRiver() {return "一动不动";}

    @Override
    protected Water moveWater() {return "无水";}
}

具体应用

    /**
     * 和尚取水: 实现一个和尚挑水喝,两个和尚抬水喝,三个和尚等水喝
     */
    public void fetchWater(){
        // 
        for (int heShangNum = 1; heShangNum < 4; heShangNum++) {Waterable waterable = Factory.getWaterable(heShangNum);
            Water water = waterable.getWater();}
    }

模板办法讲的是流程规范定义和能力复用。示例中,定义了取水的三个阶段,抉择工具,出行形式,搬运形式。单看出行形式中,【挑水】和【抬水】复用了模板办法里的通用实现,【等水】则个性化的重写了出行形式。

建造者模式

咱们取水须要一些工具,依照取水形式(挑,抬,等)能够分为扁担 + 木桶,木棍 + 木桶,意念(什么也不须要)等配备的组合形式。如何定义 getWater(ToolBox toolBox)的入参 ToolBox,使其可能依照对应取水形式匹配正确的配备组合呢?这里就能够应用建造者模式。

public class ToolBox {
    private final String bianDan;
    private final String muTong;
    private final String muGun;

    private ToolBox(TiaoBuilder builder){
        this.bianDan=builder.bianDan;
        this.muTong=builder.muTong;
        this.muGun = null;
    }
    private ToolBox(TaiBuilder builder){
        this.bianDan = null;
        this.muTong = null;
        this.muGun=builder.muGun;
    }
    private ToolBox(DengBuilder builder){
        this.bianDan = null;
        this.muTong = null;
        this.muGun=null;
    }
    public static class TiaoBuilder{
        private String bianDan;
        private String muTong;

        public TiaoBuilder setBianDan(String bianDan) {
            this.bianDan = bianDan;
            return this;
        }
        public TiaoBuilder setMuTong(String muTong) {
            this.muTong = muTong;
            return this;
        }
        public ToolBox build(){return new ToolBox(this);
        }
    }

    public static class TaiBuilder{
        private String muGun;
        private String muTong;

        public TaiBuilder setMuGun(String muGun) {
            this.muGun = muGun;
            return this;
        }
        public TaiBuilder setMuTong(String muTong) {
            this.muTong = muTong;
            return this;
        }
        public ToolBox build(){return new ToolBox(this);
        }
    }

    public static class DengBuilder{public ToolBox build(){return new ToolBox(this);
        }
    }

    // 省略 getter 办法
}

具体应用

ToolBox oneHeShangToolBox = new ToolBox.TiaoBuilder().setMuTong("小号木桶").setBianDan("小号扁担").build();
ToolBox twoHeShangToolBox = new ToolBox.TaiBuilder().setMuTong("大号木桶").setMuGun("长号木棍").build();
ToolBox threeHeShangToolBox = new ToolBox.DengBuilder().build();

建造者模式属于创立型设计模式,它能够将一个简单对象的构建与它的示意拆散,使得同样的构建过程能够创立不同的示意。

代理模式

为了激励多劳多得,庙里取水开始采纳积分机制,每取回来一桶水就要敲一下木鱼,打一次卡。这里就能够采纳代理模式。代理分为动态代理和动静代理,为了简略起见,这里就用动态代理来举例。

    public class WaterableProxy implements Waterable{
    /**
     * 被代理的原始对象
     */
    private Waterable waterable;
    
    public WaterableProxy(Waterable waterable) {this.waterable = waterable;}

    @Override
    public Water getWater() {Water water = waterable.getWater();
        // 加强的新性能,不论是挑水,抬水,等水,只有带回来水,就能够
        if(water != "无水"){System.out.println("我敲一下木鱼,打一次卡!");
        }
        return water;
    }
}

具体应用

    public void doProxy(){Waterable waterable = new Taishui();
        WaterableProxy proxyWaterable = new WaterableProxy(waterable);
        proxyWaterable.getWater();}

代理模式就是代理对象具备实在对象的性能,并代替实在对象实现相应操作,并可能在操作执行的前后,对操作进行加强解决。(通过代理拜访实在对象)

责任链模式

为了降级取水工具,将小木桶降级大金桶,寺庙决定对外提供烧香拜佛,诵经礼佛等增值服务。为了平安起见,寺庙引进了安检机制,流程是这样的:

•禁止携带宠物;

•衣着穿戴整齐;

•其它业障,最终解释权归寺庙;

public interface SecureFilter {void filter(Map context);
}

public class PetSecure implements SecureFilter{
    @Override
    public void filter(Map context) {if(context.containsKey("宠物")){throw new RuntimeException("请进来:禁止携带宠物进入!");
        }
    }
}

public class WearSecure implements SecureFilter{
    @Override
    public void filter(Map context) {if(context.containsKey("光膀子")){throw new RuntimeException("请进来:有伤风化者!");
        }
    }
}

public class OtherSecure implements SecureFilter{
    @Override
    public void filter(Map context) {if(context.containsKey("大声喧闹")){throw new RuntimeException("请进来:佛门乃喧扰之地!");
        }
    }
}

具体应用

/**
 * 安检责任链实现
 */
class SecureChain implements SecureFilter{
    // 注入 PetSecure,WearSecure,OtherSecure 等过滤器
    private List<SecureFilter> secureFilterList;
    /**
     * 进入寺庙,进行安检逻辑
     * @param context
     */
    @Override
    public void filter(Map context) {
        // 进行安检流程
        for (SecureFilter secureFilter : secureFilterList) {secureFilter.filter(context);
        }
        System.out.println("佛祖保佑,安检通过!");
    }
}

流程示意图

责任链模式个别和过滤器模式组合一起应用,即创立一个链条,通过这个链条解决的所有对象和数据别离进行顺次加工,每个环节负责解决不同的业务,环节间彼此独立解耦,同时能够复用。这种设计的奇妙之处在于能够链式调用,不同的过滤形式能够灵便的排序和组合。既能够应用单个过滤器进行解决,也能够间接增加一条责任链。

命令模式

寺庙里的和尚除了打水工作之外,还有很多工作要做,所有的工作安顿都是依照主持的指令来执行的,比方某日凌晨的工作安顿如下:

1. 一号和尚做早餐;

2. 二号和尚扫庭院;

3. 三号和尚敲古钟;

构造定义

public class Command implements Serializable {
    // 做早餐,清扫,敲钟等指令标识
    private OrderTypeEnum order;
    // 正向执行 OR 逆向回滚
    private Integer direction;
    // 省略 get 和 set 办法
}

// 指令动作执行器,每种指令对应一个实现
public interface OrderHandler {
 /**
     * 执行逻辑
     *
     * @param callContext
     * @return
     */
    PipeResult execute(CallContext callContext);
    /**
     * 反对的命令类型:做早餐,清扫,敲钟等命令标识
     *
     * @return
     */
    OrderTypeEnum getOrderType();}

// 指令类型管理器
public interface PipelineCmd {

    /**
     * 指令行定义
     *
     * @return
     */
    Command getCommand();

    /**
     * 执行逻辑
     *
     * @param pipeContext
     * @return
     */
    PipeResult execute(PipeContext pipeContext);

    /**
     * 如果能够吊销指令,则此办法应返回 true,否则返回 false
     *
     * @return
     */
    default boolean isReversible() {return true;}
}
 
 // 指令执行器管理器
 public interface CmdHandler {
    /**
     * 业务执行
     *
     * @param callContext
     * @return
     */
    PipeResult execute(CallContext callContext);

    /**
     * 业务回滚(只回滚以后指令)
     *
     * @param callContext
     * @return
     */
    PipeResult rollback(CallContext callContext);

    /**
     * 全副回滚
     *
     * @param pipeContext
     * @return
     */
    PipeResult rollbackAll(PipeContext pipeContext);
}

命令实现

public class ZhuChiCmd implements PipelineCmd {
    private Command command;
    private transient OrderHandler orderHandler;

    public StepCmd(Command command, OrderHandler orderHandler) {
        this.command = command;
        this.orderHandler= orderHandler;
    }

    @Override
    public PipeResult execute(PipeContext pipeContext) {return orderHandler.execute(new CallContext(command, pipeContext));
    }
    // 省略 get 和 set 办法
}
    
    
public class Breakfast implements OrderHandler {
 /**
     * 执行逻辑
     *
     * @param callContext
     * @return
     */
    PipeResult execute(CallContext callContext){System.out.println("做早餐啦!");
    }
    /**
     * 反对的指令类型:做早餐,清扫,敲钟等指令标识
     *
     * @return
     */
    OrderTypeEnum getOrderType(){return OrderTypeEnum.BREAKFAST;}

}

public class Clean implements OrderHandler {
 /**
     * 执行逻辑
     *
     * @param callContext
     * @return
     */
    PipeResult execute(CallContext callContext){System.out.println("清扫庭院啦!");
    }
    /**
     * 反对的指令类型:做早餐,清扫,敲钟等命令标识
     *
     * @return
     */
    OrderTypeEnum getOrderType(){return OrderTypeEnum.CLEAN;}

}

public class Ring implements OrderHandler {
 /**
     * 执行逻辑
     *
     * @param callContext
     * @return
     */
    PipeResult execute(CallContext callContext){System.out.println("敲钟啦!");
    }
    /**
     * 反对的命令类型:做早餐,清扫,敲钟等指令标识
     *
     * @return
     */
    OrderTypeEnum getOrderType(){return OrderTypeEnum.Ring;}

}

public class CmdFactory {
    private List<OrderHandler> orderHandlerList;

    /**
     * 获取指定指令条件的指令对象
     *
     * @param command
     * @return
     */
     public PipelineCmd getPipelineCmd(Command command) {for (OrderHandler orderHandler : orderHandlerList) {OrderTypeEnum orderTypeEnum = orderHandler.getOrderType();
            if (orderTypeEnum.equals(command.getOrder())) {return new ZhuChiCmd(command, orderHandler);
            }
        }
        throw new RuntimeException("对不起主持:没有多余的和尚来执行新命令了!");
    }
     /**
     * 获取给定指令的回滚操作指令对象
     *
     * @param command
     * @return
     */
    public PipelineCmd getRollbackPipelineCmd(Command command) {Command rollbackCommand = getRollbackCommand(command);
        return getPipelineCmd(rollbackCommand);
    }
}

具体应用

public class CmdHandlerImpl implements CmdHandler {
    private CmdFactory cmdFactory;

    @Override
    public PipeResult execute(CallContext callContext) {PipelineCmd pipelineCmd = cmdFactory.getPipelineCmd(callContext.getCommand());
        PipeResult pipeResult = pipelineCmd.execute(callContext.getPipeContext());
        return pipeResult;
    }

    @Override
    public PipeResult rollback(CallContext callContext) {Command rollbackCommand = cmdFactory.getRollbackCommand(callContext.getCommand());
        if (rollbackCommand == null) {return new PipeResult("不须要回滚");
        }
        PipelineCmd pipelineCmd = cmdFactory.getPipelineCmd(rollbackCommand);
        if (!pipelineCmd.isReversible()) {return new PipeResult("不反对回滚");
        }
        PipeResult pipeResult = pipelineCmd.execute(callContext.getPipeContext());
        return pipeResult;
    }

    @Override
    public PipeResult rollbackAll(PipeContext pipeContext) {
        // 命令执行备忘录模式对象, 这里不再开展
        Caretaker<Command> caretaker = pipeContext.getCaretaker();
        // 拿到上一步执行命令,顺次循环回滚
       Command command = caretaker.pop();
        while (command != null) {PipelineCmd pipelineCmd = cmdFactory.getRollbackPipelineCmd(command);
            if (pipelineCmd != null) {pipelineCmd.execute(pipeContext);
            }
            command = caretaker.pop();}
        return new PipeResult();}

}

命令模式将一个申请封装为一个对象,使收回的申请的对象和执行申请的对象宰割开。这两者之间通过命令对象进行沟通,这样不便将命令对象进行贮存、传递、调用、减少与治理。命令模式能够与备忘录模式组合应用,不便实现 Undo 和 Redo 操作。

3.3 实际心得

设计准则

具体蕴含繁多职责准则 SRP、开闭准则 OCP、里氏替换准则 LSP、依赖倒置准则 DIP、接口隔离准则 ISP、起码常识准则 LKP 等很多种,其外围还是围绕着低耦合,高复用,高内聚,易扩大,易保护开展的。

模式与准则

1. 设计准则是指导思想,设计模式是实现伎俩之一;

2. 设计准则在理论开发中并不能做到齐全恪守,往往是突破一些准则,恪守一些准则,来实现设计的合理性;(老本,性能)

3. 设计模式往往是问题解决方案的骨架,有时候能够当做开发标准和工作拆分执行落地的技术手段;

4. 一个设计模式,往往不仅仅采纳一种设计准则,而是一些设计准则的整合;

5. 设计模式不是变化无穷的,能够依据问题场景,输入新的模式;

6. 一个简单场景问题,有时候须要多种设计模式的组合;

7. 学设计模式,死记硬背是没用的,要从实际中习得;

8. 防止设计适度,使简略的问题复杂化。肯定要牢记简洁准则,设计模式是为了使设计简略,而不是更简单;

四 总结

本文从设计模式与编程语言的关系,设计模式与架构模式的区别,设计准则和设计模式的关系等几个维度进行了剖析和解答。对于设计模式应该如何学习和利用的问题,给出了学习意见和实际心得。当然,为了让设计模式更加的直观和平面,也花了大量篇幅在利用实际案例下面,次要是通过场景化的案例,以设计模式的形式给出解决方案,其中局部场景为了不便了解,将问题做了简化解决,但这不影响咱们去了解设计模式要解决的问题类型。冰冻三尺非一日之寒,滴水石穿非一日之功,心愿本文可能为你带来帮忙。

正文完
 0