工厂模式:
它属于创建型模式,用于解决类的实例化问题,我们在使用工厂模式的时候不用去理解工厂中的代码,只需要将我们的参数传进去,最终获取到我们想要的实体即可。(单一职责原则,依赖反转原则)
2.策略模式
他属于行为模式,用于解决如何封装可供复用的算法或者行为。
使用工厂模式的时候用于消除客户端的条件语句,不会出现很多if判断,但是策略模式的if判断会出现在客户端,所以很多时候策略模式会与工厂模式混合使用。
3.单例模式:
也是对象实例化问题,用于解决一个对象只能实例化一次。可以使用内部静态类方式书写,每当第一次使用内部静态类时创建对象。
4.代理模式:可用于以下场景
远程调用代理:在分布式系统中,我们经常会调用其他系统的服务。通过代理模式,可以对客户端代码隐藏远程调用的细节;
虚代理:有一个典型的场景,加载一个包含大量大 size 图片的页面时,为了更好的用户体验,可以通过图片代理类先把图片的位置占好,保证排版的正确。当滚动到某个图片位置的时候才去加载图片;
保护代理:当委托对象需要访问权限控制时,可以通过代理类来控制权限进行保护;
智能指引:为委托对象增加一层控制。比如记录访问次数,当为 0 的时候,可以释放掉。第一次引用一个对对象时,把它装入内存。访问委托对象前,检查是否已经有其他访问已经锁定了它,以确保其他对象不能改变它。
代理模式在客户端代码和真正的逻辑代码中引入了一层代理,这样做有很多好处:代理模式
隐藏逻辑的真正实现对象。上面的例子中,如果卖房人身份特殊,那么通过代理人来卖房,可以不让买房人接触到自己;
隐藏委托类的某些行为,在代理类认为应该触发时再触发;
代理类可以为委托类的行为附加一些逻辑处理,例如上例中的退税。
代理类和委托类实现同一个接口,因此只能面向接口代理;
代理类和委托类实现同一个接口。即使代理类只想代理某个行为,也需要实现接口所有方法;
代理类和委托类需要一一对应。如果你有段逻辑需要对所有的方法都附加上,静态代理是无法实现的。
5.适配器模式
优点:
不需要修改现有的接口和实现,就能复用已有的类;
灵活度高,可以在接口不变的情况下,兼容多种不同的类。
适用场景:
想要使用一个已有的类,但是此类的接口并不符合使用方要;
多个类做的事情相同或者类似,但是各自接口又不同。调用方希望统一接口。
第一个场景可以认为是亡羊补牢。由于种种原因造成系统接口不同,但功能却类似。此时很可能我们不能直接修改已经存在的接口,我们只能通过适配器模式去适配这个接口。
第二个场景其实也很常见。比如我们开发一个比价网站,需要从不同网站抓取同类商品的价格,然后按照自己系统的数据结构保存。不同网站抓取到的数据肯定是不同的,可能字段名不一样,也可能数据结构都不同。但是最终都要保存为同样的数据结构,此时就需要适配器来做转换。
6.装饰着模式:
动态的为对象添加额外职责:通过组合不同装饰类,非常灵活的为对象增加额外的职责;
避免子类爆炸:当不同的特性组合,构成不同的子类时,必然造成子类爆炸。但通过装饰者灵活组合,可以避免这个问;
分离核心功能和装饰功能:核心业务保留在 Component
的子类中。而装饰特性在 Decorator
的实现类中去实现。面对装饰特性的变化,实现了开闭原则,只需要增加装饰实现类;
很方便的重复添加特性:我想要一套两个鸡蛋,双份辣条的煎饼。是不是只需要多装饰一次就可以了?就是这么简单。
由于不是通过继承实现添加职责,所以被装饰后的对象并不能通过对象本身就能了解其特性。而需要分析所有对其装饰过的对象;
装饰模式会造成有很多功能类似的小对象。通过组合不同的装饰实现,来达成不同的需求。这样对于不了解系统的人,比较难以学习。过多的装饰类进行装饰,也稍显繁琐。
装饰者模式适用场景
使用装饰者模式,有以下几种情况:
需要一个装饰的载体。不能将全部特性都放在装饰类中。换句话讲得有个装饰主体,核心特性在主体对象中实现。例如浏览器窗口,不管是加边框还是滚动条,都是基于窗口的;
有多种特性可以任意搭配,对主体进行扩展。并且你想以动态、透明的方式来实;
不能以生成子类的方式扩展。可能有两种情况,一是对大量子类带来的类爆炸有所顾虑。二是类定义被隐藏,或者不能用于生成子类。
7.模板方法模式:
分离了算法中变和不变的部分。不变的部分定义在父类的模版方法中。变的部分通过子类实现。不变的算法部分可以被充分复用。当变的部分有新需求时,可以定义新的子类。从而实现了开闭原则。
模版意味着死板,我们设定好模版就必须按照模版的一、二、三步来执行。如果我们想调换顺序,或者增加几步就很难做到。除非定义新的模版。或者很小心的改动已有模版,避免影响现有程序逻辑。但这已经违反了开闭原则。
模版方法适用场景
如果我们发现一系列的算法,主干一样,只是在局部的实现上有区别。此时我们可以考虑使用模版方法。把算法主干及不变的部分提炼出来,在父类中实现。抽象出变化部分的方法,交由不同的子类自己去实现。
8.观察者模式:
1、目标对象状态的变化,不需要观察者真的一直观察。当存在大量观察者时,如果所有的观察者都去轮询状态,那么系统资源的消耗极大。而观察者模式避免了这种情况;
2、观察者模式支持广播,状态变化时,目标对象的所有观察者都会得到通知;
3、符合开闭原则,目标对象依赖的是观察者的接口,可以很方便的对观察者进行扩展,而不需要修改已有观察者。反过来观察者也是依赖的目标接口。
1、观察者模式中,观察者接口限定了方法签名。有一定的局限性。
观察者模式适用场景
1、抽象模型可以分为两个部分,一部分行为取决于另外一部分状态的变化。并且你想让这两部分各自独立。每部分都可以独自使用和复用;
2、一个对象的变化,需要通知其他对象,并且有多少对象需要通知并不清楚
3、你想让通知与被通知双方松耦合。
观察者模式最大的优点就是把目标和观察者解耦,观察者根据目标对象状态的变化作出响应。而目标者可以把自己状态变化广播给所有注册的观察者。实际使用中有推/拉两种模型。
在推模型中,目标对象会把状态改变相关的所有信息推送出去,信息的量有可能会很大。
在拉模型中,目标对象只推送出最核心的信息,比如变化的数据 id。
观察者收到消息后再决定如何处理,比如查询与变化相关的自己感兴趣的数据。推模型,目标对象需要知道所有观察者对数据的需求。而拉模型效率会比较差,观察者收到消息后,还需要自己再去获取改变的内容。关于推拉模型总结如下:
9.抽象工厂模式:
抽象工厂模式用来解决产品族的实例化问题
分离了产品类和客户端类:客户端只依赖抽象的产品接口。此外,如何生产产品被封装在工厂内部;
方便切换产品族:客户端代码只需要初始化一次工厂实现。这意味着在切换产品族的时候,只需要修改一行代码,换一个工厂实现即可;
保证产品的一致性:使用抽象工厂,可以保证你从相同工厂生产的产品都属于同一个产品族。不会出现椅子是现代简约风格,而桌子是欧式风格的情况。
添加新的产品时,改动较多。例子从两个维度定义产品,一是不同产品,比如桌子、椅子。另外是不同族,例如现代简约和欧式。使用抽象工厂,优化了产品族,也就是第二个维度变化的难度。但是当添加新的产品时改动就会比较多。比如我们要添加一个新的产品是电视柜。那么需要修改抽象工厂,添加生产电视柜的方法。此外,有几种工厂的实现,我们就需要修改几个类,添加具体的生产实现。
抽象工厂适用场景
你的系统中,需要使用不同产品族中的某一个产品族来操作。 比如说DB源。如果想切换DB,只需要切换DB源即可,其他代码基本上不需要改动;
你的系统中,需要保证某些产品的一致性。 比如操作系统的外观,当切换到夜间模式时,所有的组件都会换为夜间模式风格。
抽象工厂可以做到一组产品的使用和生产相分离。通过抽象工厂模式,我们切换一组产品族的,只需要更换抽象工厂实现即可。由于产品生产被分离出去,所以添加新的产品族完全通过扩展来实现的。很好的实现了开闭原则。如果你要生产的产品很多,而且是一个产品族。并且面临不同产品族切换的情况。那么可以考虑通过抽象工厂来实现。
https://www.imooc.com/wiki/Designlesson/abstractfactory.html
根据此文章进行的学习总结