2020-03-17-笔记存档-设计模式

TemplateMethod:模板方法模式

背景:在一个完整的程序中,可以将代码(模块)分成稳定的和不稳定的(容易变化的),那么我们不需要每次都编写稳定的那一部分代码,而只需要将容易变化的那一部分代码(具体到类方法)重写,并将其嵌入到稳定部分,就可以实现一个较好的代码复用和管理(父类就相当于一个模板),其核心就是晚绑定.

应用:如javaweb中的servlet,我们只需继承一个类然后实现doGet/doPost(这两个方法最容易变化,所以非常适用于此模式),然后程序(web开发人员一般不可见)会执行我们编写的代码;还有Maven种的Junit测试方法,我们只需要编写测试的方法,而不用关心父类怎么调用

这种设计模式隐含了一种"不要调用我,让我来调用你"的反向控制结构,还是刚才的例子,我们不要来调用web核心程序,我们只要写好易变化的业务程序让web核心程序来调用就行了

很多情况下,一个完整的程序会由好几个模块构成,如一个业务流程模块,其中有几个方法需要具体的业务实现,那么编写具体业务的开发人员只需实现具体某个(些)业务的模块(一般要继承自业务流程模块),那么程序只需调用子类(继承了业务流程模块的类),就可以达到.


Strategy Pattern:策略模式

有些情况下,我们在不同的场景需要不同的算法,常见的如计算各国的货币的税务,我们不希望用过多的条件选择分支来进行不同国的税率计算,因为如果后面业务需求更改(如新增了一个国家,我们同时需要添加此国家的税率计算),会牵扯到我们原本的逻辑代码,那么这种情况下,会违背我们设计模式的开闭原则
使用策略模式可以使扩展性有很好的体现
同时也遵守了我们的开闭原则
对不同的需求算法实行不同的策略,我们可以将策略继续封装成一个上下文对象,将具体的算法策略和高层模块隔离开来
而对于包含了策略的业务方法,有点类似模板方法模式,比较明显的区别是策略模式一般只针对单个方法的进行实施不同策略,而模板则对方法间的逻辑性不是那么强,也不是针对解决业务扩展而实现其扩展性
其依赖关系可以这样表示Context->StrategyInterface
                ↑实现    ↑实现    ↑实现
            StrategyA...StrategyB...StrategyC
这里由Context来维护策略,我们要使用策略时只需调用Context中策略的方法即可
应用:我们使用的DBUtils就是使用了策略模式,我们使用query方法时要传入ResultSetHandler的子类以进行处理不同的结果集


Observer/Event:观察者模式


很多时候下,会有一种一对多的依赖的情况,其角色就是被观察者(主题)和观察者
我们有时需要主题对象进行某些操作(常见如数据更新等)的同时对主题所依赖的观察者(多个,但是无需知道具体有几个)进行通知,
通常即调用观察者的某个(些)方法,以达到通知观察者其主题已变更
举例来说如:微博的明星和粉丝,其明星即为主题,粉丝为观察者,那么经常地明星发布一条动态而粉丝就会收到一条动态提醒
还如:我们一个下载/加载进度条,也许加载的平台不一样,那么实现效果或方式就要不同,那么这里的进度就是主题,各个不同平台的进度条就是不同的观察者,我们可以对不同的观察者进行自定义接受通知消息的处理方式,而主题和观察者的耦合一般仅仅只有数据的耦合,而无需知道每一个观察者具体行为和数量.
所以观察者模式也被称为订阅/通知模式
应用:MVC中就用到了观察者模式,M为主题,V为观察者;而如前端框架中的Vue,其里面的v-model实现数据双向绑定中就用到了观察者模式
是基于事件的UI框架中非常常用的设计模式


装饰模式:Decorator Pattern


很多情况下我们要对一个基类的子类(可能会有多个)来进行功能的扩展,我们一般可以直接想到使用继承来扩展其功能,但是当基类的子类和要扩展的功能数量一多起来,那么子类的数量(组合情况很多)就会变得很庞大且不利于维护及以后的扩展
那么对于这种情况我们就最好使用多组合少继承,通过一层层的包装,来对子类进行功能的添加
这里装饰对象和真实对象应有统一的接口,这样可以实现多态
(抽象)装饰类应包含一个真实对象的引用
如我们使用的BufferedStream就是一个装饰类,其参数就是要传入一个流进行包装,但是里面扩展了一些基类所没有的方法,当然这只有不发送多态的情况下才能使用
同样,类也应该遵循开闭原则来进行编码
除了我们使用的输入输出流存在装饰类,Mybatis的插件的实现也是通过拦截器来实现功能的添加,其本质就是一个装饰类,使用了装饰模式

桥模式:Bridge Pattern

场景:很多时候,我们有一个业务(狭义上可以说是业务基类)有两个或者多个不同变化的维度,比如我们要实现的业务有两个不同的维度变化方向:
1.在不同平台上的连接,登录等操作会有不同的实现方式
2.在各个平台上都有基本操作的不同形式,如轻量版的登录注册和完全版的登录注册功能,其基本实现细节会有所不同

再如生活中我们的电子产品有平台和品牌这两个不同可以变化的维度,如果我们只单纯进行通过接口的实现类来进行扩展某一品牌的某一平台电子产品,那么我们创建这个类时会导致结构上的多个维度变化,品牌和平台的变化,这就违反了单一职责原则
那么我们这时不应该简单的将多个变化维度的方法都放入一个基接口中让子类来实现进行扩展,这时我们要将各个维度从接口中抽离出来,使其单独变化,而通过组合+继承的方式来实现两个维度的连接
因为这种方式将两个维度连接起来了,故称为桥模式

工厂方法模式:factory method 


很多情况下我们建议不直接使用new来创建对象,因为这导致了类与类之间的紧耦合关系,这对于后期的变化是非常不利的,这样我们就需要将使用者和具体对象隔离开来,而使用者只需依赖其抽象,将其创建的具体工作延迟到子类,而实现一种扩展而非更改的策略,较好的解决了这种紧耦合关系

抽象工厂模式:abstract factory


工厂模式的一个变种,和工厂模式不同的是对于一系列有紧密关系的对象,我们使用工厂模式不能很好地解决有紧密关系对象的绑定,而抽象工厂里的工厂就提供的是一个有紧密关系系列的对象的创建方法,实现了对一个系列多个紧密对象的创建,但是其优势也是其劣势所在,对于单独一个对象的创建抽象工厂无能为力,这是就要使用工厂方法模式


原型模式:prototype


原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,同样要求这些易变类有稳定的接口,他使得我们可以灵活的动态创建"拥有某些稳定接口"的新对象,我们只要注册一个新类对象(原型),然后在需要的地方Clone
可以使用某些框架中的序列化来实现深克隆

建造者模式:Builder


有时我们所要用的一个类是由比较复杂的算法和其子结构构成,即要分步骤构造一个复杂的对象,而其"步骤"是稳定的,而复杂对象的各个部分是经常变动的
其中Director负责其个部分的构建算法流程,指导复杂对象的创建(稳定)
如果我们所要构建不同的复杂对象,那么其子部件不尽相同(变化)

享元模式:FlyWeight


我们一直有个"一切皆对象的概念",但是很多情况下,一些细小且量多的对象有许多共同点,甚至就是相等的,那么我们完全无需重新创建一个与其一模一样的对象,因为不由分说地去创建对象的话,一旦对象创建多了(几千几万个),那么我们的内存会被占用过多,那么我们可以通过由一个工厂来维护一个数据结构(通常是map)来实现共享某些特征一致的对象,而非创建,通常key来标识特征是否一致
若一致,则直接返回该对象,否则创建一个对象并放入map中
应用:在java中的字符串常量池中就是运用了此模式,若我们要一个新的字符串当池子中已有,就直接返回而非创建,还有我们使用的线程池,连接池等(如Apache Commons Pool2中的),都因为新建一个此种类型的对象开销大,而采用内部维护一个有多个某类对象的池子,使用时只需返回而非创建,节省了开销.
但是我们要通过衡量我们的对象大小及其使用情况来衡量是否要采用此模式,因为实现此模式也有一定的开销,若实际使用时对象总开销不是很大,完全无需采用此模式,否则有可能还得不偿失

门面模式:facade:


一个经典的接口隔离模式,很多情况下,某些接口(类)之间的直接依赖会带来很多问题,甚至无法实现.我们可以添加一层间接(稳定)的接口来隔离本来互相紧密相关联的接口(类)是一种常见的解决方案
实际上,在现实生活中,尤其是计算机的发展领域中,从一开始人类直接和硬件打交道,到使用操作系统和更上层的软件,来隔离具体硬件和软件之间的耦合关系,达到用户不用了解各层模块间的细节就可以使用.
比如我们有一些电脑的组件,如cpu memory disk等模块,我们可以使用一个computer类来处理他们之间的关系,而不使其之间互相持有导致直接的依赖关系,那么我使用时也可以直接和computer打交道,而不用直接来操作这三者

代理模式:proxy


也是一个经典的接口隔离模式.在很多情况下,常常由于我们所要执行的方法及其所涉及到的对象由于安全,分布式等原因,不能直接由客户端来调用,甚至无法实现,所以我们常常需要在与实际主体对象间添加一层接口,以达到我们调用某些方法的目的,这个接口或类我们通常叫做代理,由于我们需要通过代理来访问主体类的方法,所以一般要求主体类和代理类要有一个同样的接口,而代理类也常常会有一个主体类的属性,以便代理其方法.
这样一来很容易实现代码透明,即客户端几乎无需太大变动,或者使用其他设计模式进行包装,那么同样是调用接口的方法,但是其内部我们可以是通过代理来实现调用的,而我们仍无需了解内部是怎样执行的
有静态代理和动态代理两种代理方式,其中动态代理最为常用,也经常是自动生成代理,无需我们进行完全配置代理.
其核心思想是也AOP编程中的核心内容,通过代理方法,来实现各种类型的通知
此模式的应用场景非常广泛,如Mybatis中实现拦截器插件,spring中AOP的实现,数据库连接池关闭处理等,都用到了代理模式,可以说随便一个技术框架都会用到代理模式

适配器模式:Adapter


在软件系统中,由于应用环境的变化,常常需要将一些现存的对象放到新环境中应用,但是新环境要求的接口是现存对象所不满足的,那么为了满足新的应用环境所要求的接口,我们常常在旧类与新环境中添加一层隔离接口,用于适配新环境,此层接口我们叫做适配器.
现实生活中,适配器充斥着我们的生活,如连接水管的两端会有所不同,但是通过中间这个部件使得水管能正常运转,中间这个部件就是我们所说的适配器.更常见的如usb转接口等各种电子元件间的接口适配,都会有一个适配器来使得不同接口的两端能够协同运作.
很多框架中都采用了此模式,因为产品的迭代和环境的不断更新,同时还要能使得旧接口(类)能原封不动地来适应新环境,那么我们就可以添加适配器来实现旧代码复用到新环境

中介者模式:Mediator


在软件构建当中,我们常常面临一种多个对象互相关联交互的情况,即可能出现两个对象互相依赖的情况,这时若遇到一些变更,这种直接的引用关系将会面临不断的变化
这时我们可以抽象出一个中介对象来管理对象间的关系,避免相互交互的对象之间的紧耦合引用关系
例如Vue中的双向绑定,我们不希望View和Model间直接引用,而是通过都依赖同一个中介者来进行解耦
实际Mediator应用千变万化,但是核心思想就是解耦网状对象关系网,而使得对象易维护

状态模式:State


在软件构建过程中,某些对象的状态发生改变,那么其行为也会随之变化,如文档的只读和可写状态所能进行的行为操作就完全不同,那么要在运行时透明地更改对象行为,同时不为对象操作和状态引入紧耦合,就可以使用此模式
此模式和策略模式非常相似,都是在不同的状态下进行不同的策略,只是这里突出了运行时状态的变化

组合模式:Composite Pattern


数据结构模式的一种,数据结构模式是指,一些组件内部具有特定的数据结构,如果让客户直接依赖这些特定的数据结构,那么会极大地破坏组件的复用,而且当数据结构进行变化或扩展时,客户也要进行相应的变化,这种紧耦合关系是我们所不希望产生的
此模式的两个角色:Leaf和Composite是内部结构所不同的组件,都实现了Component接口
我们不希望通过if else来进行对他们分别对待处理,那么我们就可以通过多态的方式来对他们统一处理
此模式采用树形结构来实现普遍存在的对象容器,从而将一对多的关系转为一对一的关系,使得用户可以一致的处理单个对象或对象容器,而无需关心他们内部的区别,也使得内部的数据接口没有暴露给用户

迭代器模式:Iterator


此模式也是数据结构模式的一种,其专门用于处理容器对象的遍历需求,由于不同容器的数据结构有所差异,那么遍历的方式有所差别,但是我们不希望用户来遍历的时候暴露其内部细节,否则会由于耦合度过高而导致用户不得不了解各种容器的数据结构细节.为了避免这种方式,我们提供统一的访问接口来暴露给用户使用,而其实现细节隐藏到内部.
一般有两个接口Iterable和Iterator,容器实现Iterable表示可以遍历,即可以获得迭代器,Iterator即定义迭代行为的接口,其实现类一般为容器的内部类

职责链模式:Chain of Responsibility


此模式使用了类似链表的数据结构,在将一系列接受者构建成一个链表一样的结构,对请求进行处理
在软件构建过程中,一个请求可能被多个对象处理,但是一个请求在运行时只有一个对象处理,如果显示地指定接受者,那么耦合度会大大提高
我们可以通过将各个对象串联起来,将请求一个一个往后传,直到有对象能处理此请求为止
本质就是一个有特殊功能的链表

命令模式:Command


此模式将"请求/代码"封装成对象,以便其成为一个可操作单元(Command),以便使用不同的请求、队列或者日志来参数化其他对象.有时当我们的接受者或命令(请求)较多的时候,我们不希望直接让调用者调用接受者,这样当命令扩展或变化起来一定程度上会违反开闭原则,不利于维护.并且当我们要进行一些特殊操作,如undo,redo等操作时,我们可以将一系列命令封装成可操作单元,放入栈等数据结构,结合如备忘录模式来进行实现操作场景的复原或复现.
其也被称为事务(transaction)模式,因为有的数据库底层事务操作就使用到了命令模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值