设计模式简述

一,七大软件架构设计原则

1. 开闭原则

开闭原则Open-Closed Principle 简称OCP指的是一个软件实体如类,模块和函数应该对扩展开放对修改关闭。

强调用抽象构建框架,用实现扩展细节。

可以提高软件系统的可复用性和可维护性。

开关原则是面向对象设计中最基础的原则。它指导我们如何建立稳定灵活的系统,例如版本更新,我们可以尽可能不修改源码就可以增加新功能。

2. 依赖倒置原则

依赖倒置原则Dependence Inversion Principle简称DIP指设计代码结构时,高层模块不应依赖底层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。通过依赖倒置可以降低类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并降低修改代码带来的风险。

以抽象为基准比以细节为基准搭建起来的架构要稳定的多,因此大家在拿到需求后应该面向接口编程,按照先顶层后细节的顺序来设计代码结构。

3. 单一职责原则

单一职责原则Simple Responsibility Principle简称SRP,指不要存在一个以上导致类变更原因。简答的说就是一个类不要涉及两个需求。

4. 接口隔离原则

接口隔离原则Interface Segregation Principle简称ISP,就是指用多个专门的接口,而不是使用单一的总接口,客户端不应该依赖它不需要的接口。应当注意以下几点:

  1. 一个类对另一类的依赖应该建立在最小接口上
  2. 建立单一接口,不要建立庞大臃肿的接口
  3. 尽量细化接口,接口中的方法尽量少

接口隔离原则符合 高聚合,低耦合 设计思想,使得类具有很好的可读性,可扩展和可维护性。在设计接口时,要多花实践思考,要考虑业务模型,包括还要对以后可能发生变更的地方做一些预判。所以在实际开发中,我们对抽象,业务模型的理解时非常主要的。

5. 迪米特法则

迪米特法则又叫最少知道原则 Least Knowledge Principle简称LKP,指的是一个对象应该对其他对象保持最少的了解,尽量降低类之间的耦合。迪米特法则主要强迪奥只和朋友交流,不和陌生人说话。出现在成员变量,方法的输入和输出参数中 的类都可以被称为成员朋友类,而出现在放啊体内部的类不属于朋友类。

简单的理解可以这样理解,老板希望知道卖了多少商品,雇员去统计数量,而商品卖出的数量记在订单上,用接口的方法实现就是使老板只需要拥有雇员,让雇员去统计数量,而不是老板也需要拥有商品订单这个对象才能知道。

6. 里氏替换原则

里氏替换原则Liskov Substitution Principle简称LSP,就是指如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得T1定义的所有程序P在所有对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是T1的子类。

简单的理解就是所有父类能实现的地方子类也能实现,子类对象能够替换父类对象。

7. 合成复用原则

合成复用原则Composite/Aggregate Reuse Principle简称CARP,指尽量使用对象组合或对象聚合的方式实现代码复用,而不是用继承的关系达到代码复用。

继承又称为白箱复用,相当于把所有实现细节都暴漏给子类,组合/聚合又称为黑箱复用,对类意外的对象是无法获取实现细节的。

二,创建型设计模式

工厂模式的由来:原始社会中自给自足,现代社会却是产业链生产人类所需品,对于使用者来说,是越来越来简单了。这就类似代码,对象就是我们的所需品,而原始社会的自给自足固然可以满足我们的需求,但是背后却是大量重复复杂的工作,而对比现在社会,有人的地方就有商品的流通,人类生活所需品可以通过购买获得,极大的简化了生活形式。推演到代码中就是对于复杂对象而言,使用工厂化的形式生产,这样可以更加方便的调用和维护。在日常开发中,凡是需要生成复杂对象的地方,都可以尝试使用工厂模式来代替自给自足。

工厂模式按照实际业务场景来划分,有三种不同的实现方式,分别是简单工厂模式,工厂方法模式和抽象工厂模式。

1. 简单工厂设计模式

这个模式可以简单的理解为国家出面定制了车应该具有的功能和限制,即定义了汽车这个产品-接口car。而具体的生产车的企业会有自己的一套技术去实现它的子类-carPlayX(X代表不同车型)。接下来我们希望获得一个汽车car,这时候我们需要一个工厂来生产car这个工厂就是Factory,我们告诉工厂我们想要那种车型,工厂就会直接给我们生产那种车型。

简单工厂模式有三种组件,分别是简单工厂factory,抽象产品car,具体产品carPlayX.

值得一提的是简单工厂模式不在23种设计模式中。

简单工厂的有点是调用简单,职责分明,对于给定的信息可以方便的创建出相应的产品;但相应的缺点就很明显,那就是新增一种功能就要创建一个新的工厂类。违背了合成复用原则。

2. 工厂方法设计模式

工厂方法模式又叫多态性工厂模式,指顶一个创建对象接口,但由实现这个接口的类决定实例化那个类,工厂方法把类的实例化推迟到子类中进行了。

在工厂方法模型中,不再由单一的工厂类生产产品,而实由工厂类的子类实现具体产品,这句话的意思是职责分明,对比简单工厂模式而言,简单工厂模式强调的是模型,简单工厂提供推断模型来生产产品,而这些推断和生产产品都是在这个工厂中生产,这样对于越来越多的新增产品,这种形式就会显得很臃肿,且违背了开闭原则。而工厂方法模型指的是具体的工厂子类,你选择了工厂了进而选择了模型,模型是后面的事情,一个工厂只能生产一种模型,满足迪米特法则,依赖倒置原则和里氏替换原则。

工厂方法模型有四个组件,分别是抽象工厂,具体工厂,抽象产品,具体产品。

优点是增加新产品灵活,缺点是类的个数会变多,增加复杂度。

注意的是简单工厂模型和工厂方法模型都是需要客户端指定具体的产品类型的。而下面的抽象工厂模型是不需要客户端指定产品类型的。

3. 抽象工厂模式

抽象工厂的角色和工厂方法设计模式是一样的,但是和其却有一个最大的关注点,就是抽象工厂强调的是由一个工厂生产所有东西,即一个工厂生产所有产品,在其他的文献中也提到了产品族这个概念,产品族这个概念通俗的理解就是我由多少你需要的东西,结合工厂方法和简单工厂两种比较,会发现前面两种强调调用者选择模型,即产品类型,然后确定由那个工厂实现,最后才是由确定的工厂去生产。而抽象工厂强调的是调用者选择产品族,而抽象工厂中有现成的工厂可以直接给你生产。

更简单的理解,工厂方法类似零售,你买什么东西就去什么商店去买,而抽象工厂类似商场,这里什么商店都有,不用跑来跑去。

优点是增加新的产品族方便,缺点是扩展困难。

4. 单例模式

单例的关键是确保任何时刻都值能存在一个对象。应对得到是需要耗费大量资源进行创建的对象。

单例模式经典的有两种,即饿汉和懒汉模式,饿汉模式是在类加载的时候就初始化,因此不存在线程安全问题。但是饿汉模式有一个问题就是如果这个对象有很大的可能性用不到而且也会占用大量资源,但是仍和创建并一致持有这个大对象,这会导致资源浪费,因此就有了懒汉模式。

懒汉模式就是当使用的时候才去创建,但是这样就可能存在线程安全问题,即两个线程同时去请求创建这个对象的时候,可能会创建两个对象。

5. 原型模式

原型模式的核心在于复制原型对象,以系统中已存在的一个对象为原型,直接基于内存复制,不再需要经历初始化以及构造函数调用,性能提生许多。原型模式共有三个角色,分别是客户端,抽象原型(规定复制的接口),具体原型(被复制的对象)。

分析jdk浅克隆api带来的问题,在java提供的api中不需要手动创建抽象原型接口,因为java已经内置了cloneable抽象原型接口,自定的类型只需要实现该接口并重写clone方法即可完成本类的复制。通过查看jdk接口,可以发现cloneable接口是一个而空接口,java之所以提供cloneable接口,是为了在运行时通知java虚拟机可以安全的在改类上clone方法。如果该类没有是西安该接口,调用clone方法则会抛出clonenotsupportexception异常。

(1)     对任何对象而言,都有o.clone()!=o,换言之就是任何克隆对象和原对象都不是同一个对象。

(2)     对任何对象都有o.clone().getclass()==o.getclass()。换言之,克隆对象于被克隆对象的类型是一样的

(3)     如果equals方法定义得当,那么o.clone().equals(o)应当成立

我们在设计自定义类的clone方法时,应该遵守这三个条件,一般来说,这三个条件中的前两个是必需。Super.clone方法是直接从堆内存中以二进制的方式进行复制的,即重新分配一个内存块,因此效率极高。不会调用构造函数,即不需要经/历初始化,同样的引用对象也只是复制地址,这样原型对象和克隆对象就共享引用,这显然不合理,所以在使用原型模式的时候要慎重。

相应的深克隆就是重新创建里面的一切,这一版需要手动编写。

6. 构造者模式

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

不同的创建过程可以创建出不同表现形式的同类对象,比如你去买票,不同的选择会有不同等级的票,但是票还是票本身,再比如sql构造,调用不同的方法入processlike处理like,processbetween处理cetowween方法最后调用build即可得到想要的sql方法,再比如最简单的sqlsessionfactorybuilder的build方法会得到sqlsessionfactory对象。

三,结构性设计模式

1. 代理模式

当无法或不想直接引用某个对象或访问某个对象,或者访问存在困难时,可以通过代理对象来简介访问,使用代理对象主要有两个作用,一个是保护对象,另一个就是加强对象。

这种模式共有三种角色,抽象主题角色,主要职责是声明真实主题与代理的公共接口方法,该类可以是接口也可以是抽象类;真实主题角色,该类也被称为被代理类;代理主题角色,内部引用是真实对象。

代理模式分为静态代理和动态代理两种模式,静态代理就是最初的模式,但是针对大量的入婚介,静态代理就太麻烦了,所以就衍生了动态代理。

动态代理线程的api如jdk自带的代理和cglib

2. 门面模式

门面模式又叫做外观模式,整合复杂的内部系统,提供简洁通用的公共接口。

3. 装饰器模式

装饰器模式又叫做包装模式,Wrapper Pattern。指在不改变原有对象的基础上,动态的给一个对象添加一些额外的职责。就增加功能来说比直接生成子类更加灵活。

装饰器模式共有三个组件,分别是抽象接口;具体对象;抽象装饰器

感觉类似代理模式

4. 享元模式

面向对象技术可以很好的解决一些灵活性和可扩展性问题,但是在很多情况下需要在系统中新增类和对象的个数。当对象数量太多时,将导致系统运行代价过高,带来一系列的性能问题。享元模式正是为了解决这些问题诞生的。

享元模式又叫做轻量级模式,是对象池的一种实现,类似线程池,线程池可以避免不停的创建对象消耗性能。

享元模式共有三个角色,他们分别是抽象接口;具体享元角色;享元工厂。

核心思想是享元工厂中有一个map,可以记载具体享元对象。比方说,我们每次去医院排队都需要填写信息单等等操作,然后排队,但是如果我们把我们的信息单缓存在医院的前台,下次我们来的时候就可以直接取信息单然后排队,减少了一些重复的时间消耗。

5. 组合模式

组合模式的概述

我们主导古代的皇帝想要管理国家,是不可能直接管理到每一个老百姓的,因此设立了很多机构,如三省六部,这些机构下面又设置了很多小的组织,这些组织共同管理着国家。这就是组合模式。

组合模式又叫做部分-整体模式,它的宗旨是将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性。简单的说就是一个命令可以由最上层直接传输到最下层通过相同的接口以此来达到治理整个项目的目的。

就我个人的理解组合模式更实用于要求命令的彻底传达和结果一致性的场景。前提是组合对象和被组合对象都需要实现同样的接口或者统一的抽象父类

最典型的例子就是HashMap,Map是抽象构件,类似组合模式中的component,另外其中是包含一个内部接口的,这是为了方便容器内的叶子节点实现的部分,而hashmap是中间构件,即容器,只是它实现map接口的内容抽象到了abstractmap类中了,而node就是叶子节点了,它的功能由map中的静态entry接口定义。

 组合模式又分为安全组合模式和透明组合模式,这其中的区别是安全组合模式是把组合节点本身的一些方法延迟实现到组合节点中,而相反的透明模式就是组合节点和叶子节点完全实现一样的管理方法,这样的好处是处理简单,但是叶子节点也获得了一些不需要的方法。

6. 适配器模式

为了将两个本不适配的接口通过一个适配器来完美适配,简单的逻辑适配器实现不适配对象的接口,实现全部方法,但是实际执行操作的是它自己本身,只是将它自己本身包含在适配器中作为一个属性存在。

7. 桥接模式

桥接模式并不是适配器模式,高级的讲是通过一个抽象类将两个接口糅合到了一起,假设我们有两个需求,那么就需要两个接口,而如果两个接口有交集,我们更多的是想通过多重继承实现全部功能,构造一个全能的子类,但这样做会让这两个接口互相入侵,最好就是一方被入侵,但是如果使用一个桥接器,就是一个抽象类,这个抽象类包含了一个接口实例,调用该接口实例的方法写在了这个抽象类中,然后由子类继承这个抽象类,然后重写抽象类调用包含接口的方法,但是会使用super.xxx形式调用父类的方法,然后子类可以多种多样的实现,即另一个接口。

通俗的讲就是接口a的调用在一个抽象类中完成,接口b继承这个抽象类,b的子类在实现自己的方法时候也会调用抽象类父类的方法达到简介调用接口a的功能。有点抽象需要去买本书看才能看的清晰。

四,行为型设计模式

1. 委派方法模式

委派模式属于行为模式,不属于23种设计模式之一,简单的说就是将任务交给别人,只关注结果,属于一种特殊的静态代理,而代理模式代理对象和被代理对象都要参与其中的,但是委托模式,只是将结果交给另一个对象去执行然后回复结果即可。

最典型的就是jvm在加载类的时候的双亲委派模型,当一个类加载器加载对象的时候不是自己先去加载而是判断自己是否由父加载器,如果由先由父加载器去加载,如果父加载器加载不出来的时候会向上继续传递加载,如果父类确定自己无法加载会由子类去加载。盗个图

 

 

2. 模板方法模式

就是定好具体的流程步骤,然后在实际的执行过程中依次执行

3. 策略模式

简单的讲就是相同的接口,但是后面的实现确实不同,比如下单前选择是使用支付宝支付还是为微信支付还是银行卡支付,这就叫做策略模式,策略模式使用的就是面向对象的继承和多态机制。

4. 责任链模式

将请求任务传递给责任链中的每一个处理元素,由处理元素自己决定处理或者不处理。Jdk源码中的filter就是这样一种模式,可以过滤并处理http请求;netty中节点的串行化处理pipline也是采用了责任链模式

5. 迭代器模式

迭代器模式又叫做游标模式,迭代器模式的本质是把计合对象的迭代行为抽象到迭代器中,以此提供统一的访问接口。

可以在不暴漏集合内部的情况下,提供一种访问内部元素的方法。

6. 命令模式

命令模式是对命令的封装,每个命令都是一个操作,请求方发起一个操作即命令,接受方接收到请求然后执行,比如遥控器换台,饭店点饭都是命令模式。

命令模式一般包含四个角色,他们分别是私有的接受着角色;命令角色;具体命令角色;请求者角色。

具体命令角色会包含接收者角色,请求者角色发起命令请求,具体命令角色会调用内部的接收者角色进行执行操作。

7. 状态模式

简单的讲就是这个对象的行为受到自身一个状态属性的影响。

8. 备忘录模式

备忘录模式又叫做快照模式或者令牌模式,就是对对象本身进行备份,以便可以回退。

9. 中介者模式

中介者是指通过一个中介类封装两个对象的交互行为,使其复杂的调用过程简化为中介者的一个行为。

10. 解释器模式

定义一个方法对语义进行解释

11. 观察者模式

被观察者内部包含被观察者,当观察者一些行为被观察者感兴趣,则当触发这些事件时,去通知这些观察者,

12. 访问者模式

访问者模式是一种将数据结果和数据操作分离的操作,指封装一些作用于某种数据结构中的各元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。

访问者模式又被称为最复杂的设计模式,并且使用频率不高,设计模式的作者评价说在大多数情况下,你不需要使用访问者模式,但是一旦需要使用它,那就真的需要使用它了。

适用于内部数据结构类型固定 ,但是访问者变动大的情况,如医院的科室和来看病的病人。

访问者模式一共又5个角色,他们分别是抽象访问者;具体访问者;抽象元素;具体元素;结构对象

对应的抽象访问者对应的是看病的类型,元素对应的是科室;结构对象对应医院本身。

参考书籍为设计模式就该这样学

posted @ 2021-01-25 23:08  永不熄灭的火  阅读(183)  评论(0编辑  收藏  举报