设计模式
2023-05-09 14:41:10 62 举报
AI智能生成
设计模式
作者其他创作
大纲/内容
七大设计原则
(S)单一职责原则
一个类只负责一个功能领域中的相应职责
(O)开闭原则
对扩展开放,对修改关闭,多使用抽象类和接口
(L)里氏替换原则
基类可以被子类替换,使用抽象类继承,不使用具体类继承
(I)接口隔离原则
使用多个隔离的接口,比使用单个接口好,建立最小的接口
(D)依赖倒转原则
要依赖于抽象,不要依赖于具体,针对接口编程,不针对实现编程
迪米特法则
一个软件实体应当尽可能少地与其他实体发生相互作用,通过中间类建立联系
合成复用原则
尽量使用合成/聚合,而不是使用继承
设计模式
五个创建模式
0-2.工厂三兄弟
简单工厂模式
唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象
工厂方法模式
多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免了大量的if-else判断
抽象工厂模式
多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量
工厂模式就是为了方便创建同一接口定义的具有复杂参数和初始化步骤的不同对象。工厂模式一般用来创建复杂对象。只需用new就可以创建成功的简单对象,无需使用工厂模式,否则会增加系统的复杂度
3.单例模式
懒汉式
线程安全吗
如何写双重校验所
饿汉式
静态内部类
枚举
4.原型模式
当系统中需要大量创建相同或者相似的对象时,就可以通过“原型设计模式”来实现
原型模式的核心思想是,通过拷贝指定的“原型实例(对象)”,创建跟该对象一样的新对象。简单理解就是“克隆指定对象”
深拷贝
重写cloneable接口
通过序列化对象实现
浅拷贝
5.建造者模式
1.建造者模式定义:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
2.使用场景:当一个类的构造函数参数个数超过四个,而这些参数有些是可选参数,考虑使用建造者模式。
七个结构型模式
1.装饰模式
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能,好处在于,如果继承关系是纵向的,那么装饰类则是某个类横向的扩展,并不会影响继承链上的其他类
使用场景:<br>替代继承,扩展一个类的功能<br>动态的给一个对象添加功能,以及动态的撤销该功能<br>
优点:<br>动态扩展一个实现类的功能,在不需要添加功能的时候,可以撤销装饰。<br>装饰类和被装饰类模块间,通过抽象产生依赖,不会相互耦合<br>装饰模式替换继承,可以避免继承链的子类被影响<br>
2.代理模式
提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能
静态代理
代理与被代理类关系明确,易于阅读与调试
手动编写代理实现比较麻烦,不灵活;而且每个类都对应写一个代理类会造成代理类的数量过多
动态代理
jdk动态代理
Proxy 类中使用频率最高的方法是:newProxyInstance()
还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用
cglib代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类,可以用 CGLIB 动态代理机制来避免
需要引入cglib包或者spring-core(spring核心功能已经集成cglib)
在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。<br>你需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法
3.外观模式
Facade,也叫「门面模式」。<br>为现有的系统添加一个客户端可以访问的接口,来隐藏系统的复杂性。<br>使得调用复杂的子系统变得更加简单。因为 Facade 模式只包括应用真正关心的核心功能<br>
使用场景<br>1.为复杂的模块或子系统提供外界访问的模块。<br> 2.子系统相对独立。 <br>3.预防低水平人员带来的风险<br>
优点<br>1、松散耦合,减少系统相互依赖。 <br>2、提高灵活性,简单易用。 <br>3、提高了安全性<br>
缺点<br>不符合开闭原则,如果要改东西很麻烦,继承重写都不合适<br>
4.适配器模式
Adapter Pattern 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
使用场景<br>有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式<br>注意:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题
5.组合模式
用于把一组相似的对象当作一个单一的对象,依据树形结构来组合对象,用来表示部分以及整体层次,创建了对象组的树形结构
优点<br>一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码<br>更容易在组合体内加入新的对象,不会因为加入了新的对象而更改源代码,满足“开闭原则”<br>
缺点<br>设计较复杂,客户端需要花更多时间理清类之间的层次关系;<br>不容易限制容器中的构件;<br>不容易用继承的方法来增加构件的新功能<br>
6.桥接模式
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活<br>将抽象部分与实现部分分离,使它们都可以独立的变化<br>
使用场景<br>1.系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系<br>2.对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用<br>3.一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
7.享元模式
主要用于减少创建对象的数量,以减少内存占用和提高性能,运用共享技术有效地支持大量细粒度的对象<br>在有大量对象时,有可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建<br>
优点<br>大大减少对象的创建,降低系统的内存,使效率提高<br>
缺点:<br>提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。<br>
使用场景: <br>1、系统有大量相似对象。<br>2、需要缓冲池的场景。<br>
十一个行为模式
1.责任链模式
请求创建了一个接收者对象的链,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,并且沿着这条链传递请求,直到有对象处理它为止。
优点: <br>1、降低耦合度。它将请求的发送者和接收者解耦。 <br>2、简化了对象。使得对象不需要知道链的结构。 <br>3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 <br>4、增加新的请求处理类很方便。<br>
缺点: <br>1、不能保证请求一定被接收。 <br>2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 <br>3、可能不容易观察运行时的特征,有碍于除错<br>
2.命令模式
是一种数据驱动的设计模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令
使用场景:<br>认为是命令的地方都可以使用命令模式,比如: <br>1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。<br>
优点: <br>1、降低了系统耦合度。 <br>2、新的命令可以很容易添加到系统中去。<br>
缺点:<br>使用命令模式可能会导致某些系统有过多的具体命令类。<br>
3.解释器模式
提供了评估语言的语法或表达式的方式,这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等
使用场景:<br>1.可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 <br>2.一些重复出现的问题可以用一种简单的语言来进行表达。 <br>3.一个简单语法需要解释的场景。<br>
优点: <br>1、可扩展性比较好,灵活。 <br>2、增加了新的解释表达式的方式。 <br>3、易于实现简单文法。<br>
缺点: <br>1、可利用场景比较少。 <br>2、对于复杂的文法比较难维护。 <br>3、解释器模式会引起类膨胀。 <br>4、解释器模式采用递归调用方法。<br>
4.迭代器模式
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示,把在元素之间游走的责任交给迭代器,而不是聚合对象<br>JAVA 中的 iterator。<br>
优点:<br>1、它支持以不同的方式遍历一个聚合对象。 <br>2、迭代器简化了聚合类。 <br>3、在同一个聚合上可以有多个遍历。 <br>4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码<br>
缺点:<br>由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。<br>
5.中介者模式
提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,降低多个对象和类之间的通信复杂性,使代码易于维护
使用场景: <br>1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 <br>2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类<br>注意:不应当在职责混乱的时候使用<br>
优点: <br>1、降低了类的复杂度,将一对多转化成了一对一。<br>2、各个类之间的解耦。<br>3、符合迪米特原则。<br>
缺点:<br>中介者会庞大,变得复杂难以维护。<br>
6.备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态
使用场景: <br>1、需要保存/恢复数据的相关状态场景。 <br>2、提供一个可回滚的操作<br>
优点: <br>1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 <br>2、实现了信息的封装,使得用户不需要关心状态的保存细节<br>
缺点:<br>消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。<br>
7.观察者模式
义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
使用场景:<br>一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。<br>一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。<br>一个对象必须通知其他对象,而并不知道这些对象是谁。<br>需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。<br>注意:<br>1、JAVA 中已经有了对观察者模式的支持类。 <br>2、避免循环引用。 <br>3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。<br>
优点: <br>1、观察者和被观察者是抽象耦合的。 <br>2、建立一套触发机制。<br>
缺点: <br>1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 <br>2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 <br>3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。<br>
8.状态模式
对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者<br>注意:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。<br>
优点: <br>1、封装了转换规则。 <br>2、枚举可能的状态,在枚举状态之前需要确定状态种类。 <br>3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 <br>4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 <br>5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。<br>
优点: <br>1、封装了转换规则。 <br>2、枚举可能的状态,在枚举状态之前需要确定状态种类。 <br>3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 <br>4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 <br>5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。<br>
9.策略模式
类的行为或其算法可以在运行时更改。
使用场景: <br>1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 <br>2、一个系统需要动态地在几种算法中选择一种。 <br>3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。<br>注意:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题<br>
优点: <br>1、算法可以自由切换。 <br>2、避免使用多重条件判断。<br> 3、扩展性良好。<br>
缺点: <br>1、策略类会增多。 <br>2、所有策略类都需要对外暴露。<br>
10.模板方法模式
抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,<br>如抽象类中定义一个操作中的算法的骨架,而将一些步骤延迟到子类中<br>
使用场景: <br>1、有多个子类共有的方法,且逻辑相同。<br> 2、重要的、复杂的方法,可以考虑作为模板方法。<br>注意:为防止恶意操作,一般模板方法都加上 final 关键词。<br>
优点: <br>1、封装不变部分,扩展可变部分。 <br>2、提取公共代码,便于维护。 <br>3、行为由父类控制,子类实现<br>
缺点:<br>每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。<br>
11.访问者模式
使用一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变,<br>在数据基础类里面有一个方法接受访问者,将自身引用传入访问者<br>
优点<br>使得给结构稳定的对象增加新算法变得容易,提搞了代码的可维护性,可扩展性
缺点<br>太复杂,特别是伪动态双分派,不仔细理解很难想清楚。
模式辨析
抽象工厂模式与工厂方法模式的区别
工厂模式的核心是把一个实例化操作延迟到了子类,<br>即:由子类自行决定实例化哪个类<br>
抽象工厂是由工厂自行决定实例化哪个类
外观模式、桥接模式和适配器模式的区别
共同点<br>桥接和适配器都是让两个东西配合工作<br>
不同点<br>外观模式:已有接口,提供统一的客户端入口,隐藏系统的复杂性<br>适配器:改变已有的两个接口,让他们相容。<br>桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离<br>
桥接是先有桥,才有两端的东西,在桥好了之后,两边的东西还可以变化<br>适配是先有两边的东西,才有适配器<br>门面(外观)是对系统的"优化",<br>
需求程度<br>1. Facade的需求程度是 "中等" ,因为你不提供Facade程序照样能工作,只是不够好.<br>2. Adapter的需求程度是 "必须" ,因为你不这么做就不能工作,除非你自己从头实现一个.<br>3. Bridge的需求程度是 "一般" ,适合精益求精的人,因为你可以写三个程序给客户.<br>
出现时期<br>1. Facade出现在项目中期,再优化<br>2. Adapter出现在项目后期,大部分都有了,差的仅仅是接口不同<br>3. Bridge出现在项目前期,你想让你的系统更灵活,更cool
1. Facade很多时候是1:m的关系<br>2. Adapter很多是候是1:1的关系<br>3. Bridge很多时候是m:n的关系
模式并不很独立。<br>其实很多模式是配合使用的,而且在一定情况下可以用一个替换另一个.同一个需求,有可能当你思考的角度不同时,使用的模式就不同了
代理模式与装饰者模式的区别
相同:<br>都是增强被代理对象的功能。<br>
区别:<br>1.装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问<br>2.是否进行功能增强、何时进行功能增强,这个决策权交给谁是不同的。
重点在于构造Decorator的时候需要原obj参与,而构造Proxy的时候不可以也不需要拿到原obj,那么第三方程序员已经拿到obj的情况下,可以决定是什么情况下对这个obj进行增强,而代理就不行了,第三方程序员拿到的是已经被代理好的,功能已经增强。<br>如果是别人A已经给你一个类,你需要做好增强给第三方C使用增强的类,且C不可以直接使用未增强的类对象,那么你这个开发者应该使用代理模式,把你可以获得的类对象封装到自己的代理类;如果你希望把何时增强功能的这个决策权交给C,即C既可以使用增强的类对象,也可以使用不增强的对象,决定权在于C,那么这个时候你应该使用装饰器模式。
观察者模式和发布订阅模式的区别
相同:<br>1、都是用来处理消息对象通信的<br>2、都是基于事件机制的<br>3、都是有数据的生产方和消费方<br>4、都能实现一对多的消息机制<br>
区别:<br>1、观察者模式处理的消息,消息数据的生产方和消费方都要跟消息对象本身紧密关联;发布订阅模式跟消息对象本身没有紧密的关系。这一点再详细说明一点就是观察者模式是基于数据对象本身的,而发布订阅模式是基于主题的。<br>2、观察者模式不能跨进程网络通信;发布订阅模式支持跨进程网络通信,发布订阅模式里,发布者和订阅者,不是松耦合,而是完全解耦的<br>3.从使用层面上讲观察者模式,多用于单个应用内部发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件
状态模式与责任链模式的区别
状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用
状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变
代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责
状态模式与策略模式的区别
状态模式和策略模式的 UML 类图架构几乎完全一样,但两者的应用场景是不一样的
策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法
状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态
0 条评论
下一页