《设计模式之禅》总结

转自https://blog.csdn.net/clivially/article/details/78437846

阅读设计模式有很多时日了,差不多两个月时间,以下为个人理解。其中有部分内容为书中原文,或者例子,有些是自己的所感所想,理解浅显,谨以此记录自己的学习经历

设计模式观后感

1.六项基本原则:

1.1单一职能原则:

每一个类实现的功能和作用要单一,比如实体类实现的是单纯的属性和get,set方法,是为了能生成一个纯净的类。实现逻辑操作的要重新生成一个类,不要在实体类中给出复杂业务逻辑的操作。调用到业务逻辑的服务操作也要重新生成一个类,边界尽量清晰。

1.2.里氏替换原则:

  子类可以继承父类的私有方法以外的所有方法和非私有的属性,重写可以覆盖掉父类中同名同参数的方法。

   子类必须完全实现父类的方法。

   子类可以有自己独立的属性和方法。

   覆盖或实现父类的方法时输入参数可能会被放大。(如果子类给的参数范围大于父类,不会被执行到,要求子类给参数类型必须等于父类)。

   覆盖或者实现父类的方法时输出可以被缩小范围。(父类的返回参数类型必须大于子类)

1.3.依赖倒置的原则

  使用接口,就是面向接口编程。

1.4.接口隔离

  接口实现的作用越简单越好,最好是只针对某一项相同对象的。

1.5.迪米特法则:

  类之间的调用,最好不要知道被调用者中其他信息,只要知道对应的接口即可。具体如何实现不需要知道,或者越少越好。

扫描二维码关注公众号,回复: 3476596 查看本文章

1.6.开闭原则:

使用extends(继承)的方法实现原有的类的方法以及扩展其中的应用,应用去系统升级,替换实现类即可,不需要太多变动。

2.设计模式的真面目

2.1单例模式

  切入故事:唯一的皇帝

这种设计模式,要求程序运行时候,不管是什么情况,只允许出现同一个对象。

所以这个单例模式的example建立时候要保证,构造方法为空,不能通过new的方法生成一个另外的对象。

对象声明的时候可以定义为final类型的,不能被修改,实现方法要加synchronized关键字,可以防止并发。

2.2工厂模式

切入故事:女娲造人的故事

这种设计模式,可以不用知道需要的对象是怎么生成的,只要找到对应的factory接口,然后,给出想要的对象的名称,就可以生成一个需要的对象。

2.3抽象工厂

切入故事:女娲造人的故事续

这种设计模式,需要的某种对象,可以找到某对象的抽象Factory,就可以生成一个需要的对象,而且,可以对对象的某些属性和方法提出具体的要求,弊端是,会生成大量的抽象factory.

2.4 模板方法

切入故事:制造悍马

定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法结构即可重定义该算法的某些特定步骤。

为防止恶意操作,一般模板方法上都会加上final关键字不允许被覆盖。

抽象模板中的基本方法计量设计为protected类型法则,不需要暴露的属性尽量不要暴露为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。

封装不变部分,扩展可变部分。

提取公共部分代码,便于维护。

行为由父类控制,子类实现。

(使用场景为:

多个子类有公有的方法,并且实现逻辑基本相同时。

重要,复杂核心的算法可以把核心算法设计为模板方法,周边的相关细节由子类实现。)

例子:上家公司工作时调用某一系统核心的接口全部是通过同一个模板去调用的,子类只是把接口参数制造和返回结果接受重写了下。

2.5 建造者模式

切入故事:变化是永恒的

建造者模式又称为生成器模式,定义为:讲一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。

使用建造者模式可以使客户端不必知道产品内部组成的细节。建造者独立,容易扩展,便于控制细节风险。

使用场景:1.相同的方法,不同的执行顺序,产生不同的结果时,可以采用建造者模式。

         2.多个部件或者零件,都可以装配到一个对象中,但是产生的运行结果又不相同时候,可以使用该模式

         3.产品类非常复杂。或者产品类的调用顺序不同产生不同的效果。

         4.在对象创建过程中使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到时,可以采用建造者模式封装该对象的创建过程。

它更关注的是零件类型和装配顺序。

2.6代理模式

切入故事:事务,日志等

代理模式有两种,静态代理和动态代理,动态代理运用非常广泛,比如java中Spring引用类的反射,使用动态代理的方式实现了动态代理。动态代理现在有一个流行名称—AOP,核心就是动态代理机制。

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

一般而言,我们管切入到指定类指定方法的代码片段称为切面.

而切入到哪些类、哪些方法则叫切入点.

有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。

这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

代理模式应用非常广泛,大到系统框架,企业平台,小到代码片段,事务处理,稍不留意就用到代理模式。理解AOP时,Aspect切面, JoinPoint切入点,Advice通知,Weave织入要深入理解。

2.7原型模式

切入故事:拷贝

原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可以更好地提现特点。

使用场景:

资源优化场景(类初始化需要消化非常多的资源,资源包括数据和硬件资源)

性能和安全要求的场景

一个对象多个修改者的场景

需要注意的是:构造函数不会被执行。

2.8中介者模式

切入故事:进销存管理是这个样子么?

中介者模式也叫调停模式,一个对象要和N个对象交流,很混乱,需要加入一个中心,让所有类和中心有一个交互,中心决定如何处理。

使用场景:调度中心,MVC的框架中(C相当于中介者,减少了M和C之间的耦合)。

媒体网关,中介服务,

中介者让各个系统之间可以独立,同时又可以交互。

2.9命令模式

切入故事:项目经理的故事

命令模式是一个高内聚的模式,定义为,将一个请求封装成一个对象,从而让你用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

有三种角色,Receive接收者角色,Command命令角色,Invoke调用者角色,Invoke调用者和Receive接收者分开了,扩展性也有很好保障。

类似于目前系统中的底层应用,schedule-app,接受到消息后,消费,按照命令把指定消息发给其他系统,同时如果返回失败,可以重试,重复操作。如果有消息是可以回滚操作,那么就是完整使用了命令模式。类似于数据的操作,数据库做某些操作时候,收到对应的sql,执行,失败后可以回滚。(问题,这里的理解是不是不合理?)

2.10责任链模式

切入故事:三从四德

书中用到了一个例子,古代的三从四德,未嫁从父,既嫁从夫,夫死从子。要求古代女性的生活琐事全部要请示到他人,依赖他人。

例子中建立的几个类,其中父,夫,子有共同的字段,女性类塞值时候,给某个参数赋值,判断是某一参数,就执行某项操作。可以用case的形式,去区分。

责任链模式最显著的优点就是将请求和处理分开,请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌,两者解耦。

避免超长链的出现

2.11装饰模式

切入故事:罪恶的成绩单

简单描述:就是一个成绩单不好的情况下,做的一些其他工作,比如说告诉家长,最高分才**分,然后告诉家长自己多少分,可以免除惩罚。

装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比子类更为灵活。

装饰类和被装饰类可以独立发展,而不会互相耦合。

装饰模式是继承关系的一个替代方案。

装饰模式可以动态地扩展一个实现类的功能。

2.12策略模式

切入故事:刘备江东娶妻,赵云容易么

解决问题的方案是,诸葛亮给了3个锦囊妙计。

第一步,第二步,第三步最终解决问题。

策略模式,定义一组算法,将各个算法都封装起来,并使各个算法之间可以互相交换。

策略枚举是一个很好,方便的模式,但是它受到枚举类型的限制。

比如四项运算,使用if判断出运算类型,可以使用对应的方法去做处理。

2.13适配器模式

切入故事:业务发展-上帝才能控制

类似一个遥控器可以控制很多电器一样。

Java中最常见的DTO就是一种适配类型的类。

描述:将一个类的接口变换成客户端所期待的另一个接口,从而使原来因接口不匹配而无法在一起工作的两个类能够在一起工作。

2.14迭代器模式

切入故事:整理项目信息

描述:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。

典型的就是java中的迭代子,解决遍历问题

Java自带的容器足够使用,不需要自己创建迭代器。

2.15组合模式

切入故事:人事架构

数据结构中的二叉树

描述:组合模式又叫合成模式,有时称之为部分-整体模式,主要是用来描述部分与整体之间的关系。将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

根节点和叶子节点

2.16观察者模式

切入故事:韩非子身边的卧底是谁派过来的

观察者模式也叫作发布订阅模式,定义对象间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

eureka服务(服务发现)

文件系统/ATM取钱/广播收音机

2.17门面模式

切入故事:我要投递信件

写信的过程:先写信,后装入信封,封好,再投递到信箱。

门面模式又叫外观模式,是一种比较常用的封装模式,定义:要求一个子系统的外部和其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。

减少系统的相互依赖,提高灵活性,提高安全性。

上家公司使用的网关和平台,调用方式全部是一样的,调用的服务名和操作名去区分调用的具体方法,但是入口接口是只有一个可以通过,校验通过后才能分发到其他接口上使用,保证了调用的安全性,统一性。有一套统一的解析接口数据,验证规范。

2.18备忘录模式

切入故事:如此追女孩子,你还不乐

原始状态的保留和恢复非常重要。每次尝试失败后都要恢复到原始状态。

备忘录模式提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序世界中真是可行。

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

通俗的说,备忘录模式就是一个对象的备份模式,提供一种程序数据的备份方法。

使用场景:如word中的ctrl+Z组合,退回按钮等等。

可以使用双接口设计,两个接口,一个正常接口,实现必要的业务逻辑,叫做宽接口。一个空接口,什么方法都么,目的是提供给子系统的模块访问,这个叫窄接口。

不要再for循环中使用到备忘录模式,会产生大量的数据。

2.19访问者模式

切入故事:员工的隐私何在

之前有例子组合模式和迭代器模式,能够把一个公司的组织机构所有员工的信息。看看是否有员工“有人去世还拿退休金”,“拿高工资不干活的尸位素餐”等,现在需要把这些信息统计成报表,让领导看看有多严重。

实际项目中,一个项目,多个访问者情况非常多。

一个对象结构包含很多对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是说用迭代器模式已经不能胜任的情景。

*业务规则要求遍历多个不同的对象。这本身也是访问者模式的出发点。

统计功能。多个访问者。双分派。

Example:思考一下添加不同类型商品的购物车,当点击结算的时候,它计算出所有不同商品需付的费用。现在,计算逻辑即为计算这些不同类型商品的价格。

个人理解,访问模式,对象使用时候,使用一个方法把自己传递回去,其他使用者就可以获悉该对象的所有的非private的对象了。

2.20状态模式

切入故事:电梯

特定的状态:敞们状态,闭门状态,运行状态,停止状态。

当一个对象内在状态改变时,允许其改变行为,这个对象看起来像改变了类。

状态模式的核心是封装,状态的变更引起了行为的变更。

上家公司有一个使用状态机流转的方案,当时的数据区分了很多很多的状态,包括中间态和终态,状态流转时候,是在某种特定场景下,再上一个事件和下一个事件和本事件的场景一致时,才允许流转到其他状态。保证数据操作的幂等性。

2.21解释器模式

切入故事:四则运算

按照特定的语法进行解析的方案。给定一种语言,定义其文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

就像JDK能把java语言按照语法语义,转换成机器可以理解的二进制流,处理后,返回给我们我们理解的数据流。

2.22享元模式

切入故事:内存溢出,司空见惯

就是一种资源共享的连接池,资源池的理念。代表有Tomcat的连接池。

2.23桥梁模式(这部分的理解是从网上摘下来,因为我自己也不是很理解,具体位置当时并没有记录,对原作者表示歉意)

切入故事:

定义:桥梁模式又叫桥接模式,是将抽象和实现解耦,使得两者可以独立的变化。

桥梁模式在Java应用中的一个非常典型的例子就是JDBC驱动器。JDBC为所有的关系型数据库提供一个通用的界面。一个应用系统动态地选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。

抽象角色可以针对任何数据库引擎发出查询指令,因为抽象角色并不直接与数据库引擎打交道,JDBC驱动器负责这个底层的工作。由于JDBC驱动器的存在,应用系统可以不依赖于数据库引擎的细节而独立地演化;同时数据库引擎也可以独立于应用系统的细节而独立的演化。两个独立的等级结构如下图所示,左边是JDBC API的等级结构,右边是JDBC驱动器的等级结构。应用程序是建立在JDBC API的基础之上的。

  应用系统作为一个等级结构,与JDBC驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥梁模式的例子。

  JDBC的这种架构,把抽象部分和具体部分分离开来,从而使得抽象部分和具体部分都可以独立地扩展。对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植;对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序。

3.JDK中引用的设计模式

这部分的理解也是从网上摘下来,是为了自己理解使用,具体位置当时并没有记录,对原作者表示歉意

在JDK(Java Development Kit)类库中,开发人员使用了大量设计模式,正因为如此,我们可以在不修改JDK源码的前提下开发出自己的应用软件,研究JDK类库中的模式实例也不失为学习如何使用设计模式的一个好方式。

3.1创建型模式:

(1) 抽象工厂模式(AbstractFactory)

• Java.util.Calendar#getInstance()

• java.util.Arrays#asList()

• java.util.ResourceBundle#getBundle()

• java.NET.URL#openConnection()

• java.sql.DriverManager#getConnection()

• java.sql.Connection#createStatement()

• java.sql.Statement#executeQuery()

• java.text.NumberFormat#getInstance()

• java.lang.management.ManagementFactory (所有getXXX()方法)

• java.nio.charset.Charset#forName()

• javax.xml.parsers.DocumentBuilderFactory#newInstance()

• javax.xml.transform.TransformerFactory#newInstance()

• javax.xml.xpath.XPathFactory#newInstance()

(2) 建造者模式(Builder)

• java.lang.StringBuilder#append()

• java.lang.StringBuffer#append()

• java.nio.ByteBuffer#put() (CharBuffer, ShortBuffer,IntBuffer,LongBuffer, FloatBuffer 和DoubleBuffer与之类似)

• javax.swing.GroupLayout.Group#addComponent()

• java.sql.PreparedStatement

• java.lang.Appendable的所有实现类

(3) 工厂方法模式(FactoryMethod)

• java.lang.Object#toString() (在其子类中可以覆盖该方法)

• java.lang.Class#newInstance()

• java.lang.Integer#valueOf(String) (Boolean, Byte,Character,Short, Long, Float 和 Double与之类似)

• java.lang.Class#forName()

• java.lang.reflect.Array#newInstance()

• java.lang.reflect.Constructor#newInstance()

(4) 原型模式(Prototype)

• java.lang.Object#clone() (支持浅克隆的类必须实现java.lang.Cloneable接口)

(5) 单例模式 (Singleton)

• java.lang.Runtime#getRuntime()

• java.awt.Desktop#getDesktop()

3.2结构型模式:

(1) 适配器模式(Adapter)

•java.util.Arrays#asList()

•javax.swing.JTable(TableModel)

•java.io.InputStreamReader(InputStream)

•java.io.OutputStreamWriter(OutputStream)

•javax.xml.bind.annotation.adapters.XmlAdapter#marshal()

•javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

(2) 桥接模式(Bridge)

• AWT (提供了抽象层映射于实际的操作系统)

•JDBC

(3) 组合模式(Composite)

•javax.swing.JComponent#add(Component)

•java.awt.Container#add(Component)

•java.util.Map#putAll(Map)

•java.util.List#addAll(Collection)

•java.util.Set#addAll(Collection)

(4) 装饰模式(Decorator)

•java.io.BufferedInputStream(InputStream)

•java.io.DataInputStream(InputStream)

•java.io.BufferedOutputStream(OutputStream)

•java.util.zip.ZipOutputStream(OutputStream)

•java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()

(5) 外观模式(Facade)

•java.lang.Class

•javax.faces.webapp.FacesServlet

(6) 享元模式(Flyweight)

•java.lang.Integer#valueOf(int)

•java.lang.Boolean#valueOf(boolean)

• java.lang.Byte#valueOf(byte)

•java.lang.Character#valueOf(char)

(7) 代理模式(Proxy)

• java.lang.reflect.Proxy

•java.rmi.*

3.3行为型模式:

(1) 职责链模式(Chain ofResponsibility)

•java.util.logging.Logger#log()

•javax.servlet.Filter#doFilter()

(2) 命令模式(Command)

• java.lang.Runnable

• javax.swing.Action

(3) 解释器模式(Interpreter)

• java.util.Pattern

• java.text.Normalizer

• java.text.Format

• javax.el.ELResolver

(4) 迭代器模式(Iterator)

• java.util.Iterator

• java.util.Enumeration

(5) 中介者模式(Mediator)

• java.util.Timer (所有scheduleXXX()方法)

• java.util.concurrent.Executor#execute()

• java.util.concurrent.ExecutorService (invokeXXX()和submit()方法)

• java.util.concurrent.ScheduledExecutorService (所有scheduleXXX()方法)

•java.lang.reflect.Method#invoke()

(6) 备忘录模式(Memento)

•java.util.Date

•java.io.Serializable

•javax.faces.component.StateHolder

(7) 观察者模式(Observer)

•java.util.Observer/java.util.Observable

•java.util.EventListener (所有子类)

•javax.servlet.http.HttpSessionBindingListener

•javax.servlet.http.HttpSessionAttributeListener

•javax.faces.event.PhaseListener

(8) 状态模式(State)

•java.util.Iterator

•javax.faces.lifecycle.LifeCycle#execute()

(9) 策略模式(Strategy)

• java.util.Comparator#compare()

• javax.servlet.http.HttpServlet

• javax.servlet.Filter#doFilter()

(10) 模板方法模式(Template Method)

•java.io.InputStream, java.io.OutputStream, java.io.Reader和java.io.Writer的所有非抽象方法

•java.util.AbstractList, java.util.AbstractSet和java.util.AbstractMap的所有非抽象方法

•javax.servlet.http.HttpServlet#doXXX()

(11) 访问者模式(Visitor)            

•javax.lang.model.element.AnnotationValue和AnnotationValueVisitor

•javax.lang.model.element.Element和ElementVisitor

•javax.lang.model.type.TypeMirror和TypeVisitor

猜你喜欢

转载自blog.csdn.net/huyang0304/article/details/82559653