设计模式与原则
一、简介
设计模式代表了最佳实践。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式的类型
总共有23种设计模式,可以分为三大类:创建型模式creational patterns、结构型模式structural patterns、行为型模式behavioral patterns。
模式 | 包括 | 描述 |
创建型模式 |
工厂模式、抽象工厂模式、单例模式、 建造者模式、原型模式 |
提供了一种在创建对象的同时隐藏创建逻辑的方式, 而不是使用new运算符直接实例化对象。 这使得程序在判断针对某个给定实例需要创建哪些对象时 更加灵活。 |
结构型模式 |
适配器模型、桥接模式、过滤器模式、 组合模式、装饰器模式、外观模式、 享元模式、代理模式 |
关注类和对象的组合。继承的概念被用来组合接口和定义 组合对象获得新功能的方式。 |
行为型模式 |
责任链模式、命令模式、解释器模式、 迭代器模式、中介者模式、备忘录模式、 观察者模式、状态模式、空对象模式、 策略模式、模板模式、访问者模式 |
特别关注对象之间的通信。 |
二、具体各种设计模式
1.工厂模式 Factory Pattern
在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。
1.创建抽象基类和各子类,子类可扩展
2.创建工厂类,获得各子类的实例类对象
3.实际调用,调用的是工厂类
通过创建一个抽象基类,各种子类继承基类,实现具体的方法。在创建工厂类,不同的子类标识就返回不同的子类的实例类对象。实际调用的时候,实际上调用的是工厂类,通过不同的传参-子类标识,获得不同子类的实例类对象,进而实例类对象调用各自的函数。
2.抽象工厂模式 Abstract Factory Pattern
是围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。
接口是负责创建一个相关对象的工厂,不需要显示指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
1.创建接口,即某一种工厂,加上属于这个工厂的具体实体子类
2. 创建工厂抽象类,获取某个工厂类对象,一个工厂类一个方法
3.创建继承了工厂抽象类的具体某个工厂类的实体类,只用实现覆盖针对这个工厂的方法,通过参数返回这个工厂里面的具体实体类的对象
4.创建一个工厂创造器类,通过传递工厂标识信息来获得具体某个工厂对象。
5.使用时,通过工厂创造器先获得某个工厂对象,再通过传参获得这个工厂里面的具体实体类的对象,再通过实体类对象调用实体类的方法
3.单例模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。保证一个类仅有一个实例。
解决一个全局使用的类频繁地创建与销毁,判断系统是否已经有这个单例,有则返回,没有则创建。
注意:构造函数是私有的。
4.建造者模式
使用多个简单的对象一步一步构建成一个复杂的对象。
每一种事物都要创建一个接口;所有组合也要创建一个接口;每个类只会调用一个类的实例;
5.原型模式
用于创建重复的对象,同时又能保证性能。
实现一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
通过拷贝一个现有对象生成新对象。
浅拷贝,指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝的对象和源对象只是名称相同,但是它们共用一个实体。
深拷贝,对对象实例中字段引用的对象也进行拷贝。
6.适配器模式
适配器模式是作为两个不兼容的接口之间的桥梁。它结合了两个独立接口的功能。
适配器继承或依赖(推荐)已有的对象,实现想要的目标接口。
注意:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
7.桥接模式
用于把抽象化与实现化解耦,使得二者可以独立变化。
提供抽象化和实现化之间的桥接结构。
提供一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
关键代码:抽象类依赖实现类。
8.过滤器模式
这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。
它结合多个标准来获得单一标准。
9.组合模式
用于把一组相似的对象当做一个单一的对象。
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
创建了对象组的树形结构。
组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是终点对象(不再包含别的对象),也有可能是非终点对象(其内部还包含其他对象,或叫组对象),我们将对象称为节点,即一个根节点包含许多子节点,这些子节点有的不再包含子节点,而有的仍然包含子节点,以此类推。
10.装饰器模式
允许向一个现有的对象添加新的功能,同时又不改变其结构。
创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能,可叠加功能。
11.外观模式
它向现有的系统添加一个接口,来隐藏系统的复杂性,向客户端提供了一个客户端可以访问系统的接口。
涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
12.享元模型
主要用于减少创建对象的数量,以减少内存占用和提高性能。
提供了减少对象数量从而改善应用所需的对象结构的方式。
尝试重用现有的同类对象,如果未找到匹配的对象,则创建新的对象。
关键代码:用HashMap存储这些对象。
缺点:提高了系统的复杂性,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景:1、系统有大量相似对象;2、需要缓冲池的场景
注意事项:1、注意划分外部状态和内部状态,否则可能会引起线程安全问题;2、这些类必须有一个工厂对象加以控制。
13.代理模式
一个类代表另一个类的功能,创建具有现有对象的对象,以便向外界提供功能接口。
使用场景:按职责划分:1、远程代理。2、虚拟代理。3、Copy-on-Write代理。4、保护(Protect or Access)代理。5、Cache代理。6、防火墙(Firewall)代理。7、同步化(Synchronization)代理。8、智能引用(Smart Reference)代理。
注意事项:1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。2、和装饰器模式的区别:装饰器模式是为了增强功能,而代理模式是为了加以控制。
14.责任链模式
为请求创建一个接受者对象的链。
这种模式给予请求的类型,对请求的发送者和接受者进行解耦。
通过每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,以此类推。
使用场景:1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
15.命令模式
请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
16.解释器模式
提供了评估语言的语法或表达式的方式。
实现看一个表达式接口,该接口解释一个特定的上下文。
这种模式被用在SQL解析、符号处理引擎等。
17.迭代器模式
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
关键代码:定义接口:hasNext,next.
18.中介者模式
用来降低多个对象和类之间的通信复杂性。
该模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。
关键代码:对象之间的通信封装到一个类中单独处理。
降低了类的复杂度,将一对多转化成了一对一。
19.备忘录模式
保存一个对象的某个状态,以便在适当的时候恢复对象。
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
如何解决:通过一个备忘录类专门存储对象状态。
关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。
使用场景:1、需要保存/恢复数据的相关状态场景。2、提供一个可回滚的操作。
20.观察者模式
定义对象间的一种一对多的依赖关系,当一个对象(目标对象)的状态发生改变时,所有依赖于它的对象(观察者对象)都得到通知并被自动更新。
关键代码:在抽象类里有一个列表存放观察者们。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
21.状态模式
类的行为是基于它的状态改变的。
允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
使用场景:1、行为随状态改变而改变的场景。2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过5个。
22.空对象模式
一个空对象取代NULL对象实例的检查。Null对象不是检查空值,而是反应一个不做任何动作的关系。
这样的Null对象也可以在数据不可用的时候提供默认的行为。
23.策略模式
一个类的行为或其算法可以在运行时更改。
在策略模式中,创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象,该策略对象改变context对象的执行算法。
意图:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用if...else所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是它们的直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
注意事项:如果一个系统的策略多于4个,就需要考虑使用混合模式,解决策略类膨胀的问题。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
24.模板模式
一个抽象类公开定义了执行它的方法的方式/模板。
它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
25.访问者模式
使用一个访问者类,它改变了元素类的执行算法。
通过这种方式,元素的执行算法可以随着访问者改变而改变。
根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
26.MVC模式
Model-View-Controller(模型-视图-控制器)模式。这种模式用于应用程序的分层开发。
-Model模型:模型代表一个存取数据的对象。它可以带有逻辑,在数据变化时更新控制器。
-View视图:代表模型包含的数据的可视化。
-Controller控制器:作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
27.业务代表模式
用于对表示层和业务层解耦。它基本上是用来减少通信或对表示层代码中的业务层代码的远程查询功能。
在业务层中有以下实体:
-客户端Client:表示层代码可以是JSP、servlet或 UI Java代码。
-业务代表Business Delegate:一个为客户端实体提供的入口类,它提供了对业务服务方法的访问。
-查询服务LookUp Service:查找服务对象负责获取相关的业务实现,并提供业务对象对业务代表对象的访问。
-业务服务Business Service:业务服务接口。实现了该业务服务的实体类,提供了实际的业务实现逻辑。
28.组合实体模式
用在EJB持久化机制中。一个组合实体是一个EJB实体bean,代表了对象的图解。当更新一个组合实体时,内部依赖对象beans会自动更新,因为它们是由EJB实体bean管理的。
以下是组合实体bean的参与者:
-依赖对象 Dependent Object:依赖对象是一个持续生命周期依赖于粗粒度对象的对象。
-粗粒度对象 Coarse-Grained Object:该对象包含依赖对象。它有自己的生命周期,也能管理依赖对象的生命周期。
-组合实体 Composite Entity:它是主要的实体bean。它可以是粗粒的,或者可以包含一个粗粒度对象,用于持续生命周期。
- 策略 Strategies:策略表示如何实现组合实体。
29.数据访问对象模式
DAO模式用于把低级的数据访问API或操作从高级的业务服务中分离出来。
以下是数据访问对象模式的参与者:
-数据访问对象接口 Data Access Object Interface:该接口定义了在一个模型对象上要执行的标准操作。
-数据访问对象实体类 Data Access Object concrete class:该类实现了上述的接口。该类负责从数据源获取数据,数据源可以是数据库,也可以是XML,或者是其他的存储机制。
-模型对象/数值对象 Model Object/Value Object:该对象是简单的POJO,包含了get/set方法来存储通过使用DAO类检索到的数据。
30.前端控制器模式
用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。
该处理程序可以做认证、授权、记录日志,或者跟踪请求,然后把请求传送给相应的处理程序。
以下是这种设计模式的实体:
-前端控制器 Front Controller :处理应用程序所有类型请求的单个处理程序,应用程序可以是基于web的应用程序,也可以是基于桌面的应用程序。
-调度器 Dispatcher :前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
-视图 View:视图是为请求而创建的对象。
31.拦截过滤器模式
用于对应用程序的请求或响应做一些预处理/后处理。
定义过滤器,并在把请求传给实际目标应用程序之前应用在请求上。
过滤器可以做认证、授权、记录日志,或者跟踪请求,然后把请求传给相应的处理程序。
以下是这种设计模式的实体:
-过滤器 Filter:过滤器在请求处理程序执行请求之前或之后,执行某些任务。
-过滤器链 Filter Chain:过滤器链带有多个过滤器,并在Target上按照定义的顺序执行这些过滤器。
-Target:Target对象是请求处理程序。
-过滤管理器 Filter Manager:过滤管理器管理过滤器和过滤器链。
-客户端 Client:向Target对象发送请求的对象。
32.服务定位器模式
用在想使用JNDI(Java Naming and Directory Interface,Java命名和目录接口)查询定位各种服务的时候。
考虑到为某个服务查找JNDI的代价很高,服务定位器模式充分利用了缓存技术。
在首次请求某个服务时,服务定位器在JNDI中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。
以下是这种设计模式的实体:
-服务 Service:实际处理请求的服务。对这种服务的引用可以在JDNI服务器中查找到。
-Context/初始的Context:JNDI Context带有对要查找的服务的引用。
-服务定位器 Serviece Locator:服务定位器是通过JDNI查找和缓存服务来获取服务的单点接触。
-缓存 Cache:缓存存储服务器的引用,以便复用它们。
-客户端 Client:Client通过Service Locator调用服务的对象。
33.传输对象模式
用于从客户端向服务器一次性传递带有多个属性的数据。传输对象也被称为数值对象。
传输对象是一个具有getter/setter方法的简单的POJO类( 其中有一些属性及其getter setter方法的类,没有业务逻辑,有时可以作为VO(value -object)或dto(Data Transform Object)来使用.当然,如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection之类的方法),它是可以序列化的,所以它可以通过网络传输。它没有任何行为。
服务器端的业务类通常从数据库读取数据,然后填充POJO,并把它发送到客户端或按值传递它。
对于客户端,传输对象是只读的。客户端可以创建自己的传输对象,并把它传递给服务器,以便一次性更新数据库中的数值。
以下是这种设计模式的实体:
-业务对象 Business Object:为传输对象填充数据的业务服务。
-传输对象 Transfer Object:简单的POJO,只有设置、获取属性的方法。
-客户端 Client:客户端可以发送请求或者发送传输对象到业务对象,
代码实例:
https://github.com/xulei717/DesignPattern
参考:
https://www.runoob.com/design-pattern/design-pattern-tutorial.html
设计模式六大原则
1.单一原则 Single Responsibility Principle:
一个类只负责一项职责,尽量做到类的只有一个行为原因引发变化;
业务对象 BO Business Object、业务逻辑 BL Business logic拆分。
2.里氏替换原则 LSP liskov substitution principle:
子类可以扩展父类的功能,但是不能改变原有父类的功能;
目的:增强程序的简装性。
实际项目中,每个子类对应不同的业务含义,使父类作为参数,传递不同的子类完成不同的业务逻辑。
3.依赖倒置原则 dependence inversion principle:
面向接口编程,通过接口作为参数实现应用场景;
抽象就是接口或者抽象类,细节就是实现类。
含义:上层模块不应该依赖下层模块,两者应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
接口负责定义public属性和方法,并且申明与其他对象依赖关系,
抽象类负责公共构造部分的实现,
实现类准确的实现业务逻辑。
4.接口隔离 interface segregation principle:
建立单一接口;扩展为类也是一种接口,一切皆接口。
客户端不应该依赖它不需要的接口;
类之间依赖关系应该建立在最小的接口上。
接口的设计粒度越小,系统越灵活,但是灵活的同时结构复杂性提高,开发难度也会变大,维护性降低。
5.迪米特原则 law of demeter LOD:
最少知道原则,尽量降低类与类之间的耦合;
一个对象应该对其他对象有最少的了解。
6.开闭原则 open closed principle:
用抽象构建架构,用实现扩展原则;