设计模式总结
简单工厂模式
通过简单的工厂类将对象实例的构建工作统一交给工厂类处理,从而避免在客户端因为构建实例而产生过于负责的逻辑代码.
优点:
判断逻辑提取到了工厂类中,减轻了客户端的工作,减少了客户端对具体产品的依赖.
缺点:
工厂模式类中需要逻辑判断(switch-case),新添加逻辑的时候需要更改工厂类,违反了开闭原则.switch可以通过反射的方式去除
工厂方法模式(Factory Method)
工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类.
优点:
相比于简单工厂模式,工厂方法模式通过对多态的应用成功的分解了”生成哪个类”的逻辑判断,拆解了switch的判断语句,使得代码有更高的可读性同时也更容易进行修改.
缺点:
工厂方法模式虽然使得代码可读性更高,但是判断重新抛给了客户端,增加了客户端的复杂度.同时,如果有新的逻辑依然需要对客户端进行修改,违反开闭原则.
抽象工厂模式(Abstract Factory)
抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类.
优点:
- 易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,使得改变具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置.
- 它让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂分离,不会出现在客户端代码中.
缺点:
增加新产品的时候需要同时增加一个抽象产品和数个具体产品,以及修改抽象工厂和具体工厂,涉及到改动太多,不利于维护.
改进:
利用发射将抽象工厂模式的工厂部分修改为简单工厂模式,通过反射来生成具体的产品.
策略模式
策略模式定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响使用算法的客户.
场景:
商场活动打折计费/类似的不同时间对应不同业务规则的业务场景
优点:
1.策略模式的算法的多个算法继承父类的时候,可以将各个算法公共的逻辑上提到父类,通过继承复用.
2.减少了算法类之间的耦合
3.方便算分类各自进行单元测试.
缺点:
单一的应用策略模式依然无法在做算法选择的时候避免switch条件判断,基本策略模式只是简单的把判断剥离了客户端,添加新的算法的时候依然违背开闭原则.需要与抽象工厂模式进行一起使用才能避免这种情况.
装饰者模式
装饰者模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更加灵活.
场景:
英雄联盟红蓝buff/系统添加新功能,需要向旧类添加新的代码以对原有类的核心职责或主要行为进行装饰(扩展).
优点:
将装饰代码从原有类中搬移出去,简化了原有类,避免原有类的业务逻辑过于复杂.
有效的把类的核心职责和装饰功能区分开,而且可以去除相关类中重复的装饰逻辑.
缺点:
部分情况下需要注意装饰顺序,尽量在使用装饰类相互独立的情况下使用装饰者模式.
代理模式(proxy)
代理模式(proxy),为其他对象提供一种代理以控制这个对象的访问.
场景:
1.远程代理,也就是为一个对象在不同的地址空间提供局部代表.这样可以隐藏一个对象存在于不同地址空间的事实(??)
2.虚拟代理,是根据需要创建开销很大的对象.通过它来存放实例化需要很长时间的真是对象.
3.安全代理,迎来控制真实对象访问时的权限.
4.智能指引,是指调用真实的对象时,代理处理另外一些事.
原型模式(Prototype)
原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
场景:
需要处理”复制”对象功能的场景,对象的所有信息都不需要改变,这时候”克隆”原型能共隐藏新对象的创建细节.
优点:
隐藏了具体的”创建”细节,对客户端友好.合理设计可能会降低对象初始化的资源消耗.
缺点:
对象内部有引用类型的时候,需要考虑深拷贝与浅拷贝的问题.
模板方法模式(TempleteMethod)
模板方法模式,定义一个操作中的算法骨架,而将一些步骤推迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定模式.
场景:
项目增删改查架构搭建
优点:
1.模板方法很好的体现了继承方式的应用.相同的情况父类处理,不同的情况子类特殊处理.提高了代码的复用又兼顾到特殊情况特殊处理.
2.在日常开发中可以通过模板方法管理架构,技术负责人定义公共的骨架,开发人员根据不同的业务逻辑来处理自己特殊的逻辑.
缺点:
因为父类一些方法采用了abstract修饰,使得子类即使没有特殊逻辑也必须重写这些方法.如果在业务逻辑特殊情况不是很多的时候,可以考虑不使用模板方法,而是子类单纯的重写父类的方法,通过单纯的继承来处理.
外观模式(Facade)
外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用.
场景:
- 在设计初期阶段,应该要有意识的将不同的两个层分离.
- 开发阶段,子系统往往因为不断的重构演化变得越来越复杂,只是会增加外观Facade可以提供一个简单的接口,减少他们之间的依赖.
- 在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,为新系统开发一个外观Facade类,用来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统和Facade对象交互,Facade与遗留代码交互所有复杂的工作.
建造者模式(Builder)
建造者模式(Builder),又称为生成器模式,将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示.
场景:
- 例如麦当劳汉堡的制作.客户作为客户端只需要告知服务员(指挥者)需要什么类型的汉堡,服务员按照流程组装各流水线员工(具体创建者)生产的汉堡组成部分(具体产品产品)
- 床架这模式是在(当创建复杂对象的算法(指挥者负责)应该独立于该对象的组成部分(具体产品)和以及他们的装配方法(具体建造者实现的抽象建造者定义的抽象方法))时使用的模式.
观察者模式(Observer)
观察者模式(Observer)又叫做发布-订阅模式(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,是他们能够自动更新自己.
场景:
- 当一个对象改变的时候需要改变其他对象,但是它并不知道具体需要改变多少个对象的时候应该考虑用观察者模式.
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,这时可以用观察者模式将这两者封装在独立的对象中,使他们各自独立的改变和复用.
优点:
观察者模式让耦合的双方都依赖于抽象,而不是依赖于具体,从而使各自的变化都不会影响另一边的变化.遵循了依赖倒转原则.
状态模式(State)
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来对象是改变了其类.
场景:
审批流程/当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式.
优点:
- 当控制一个对象状态转换的条件表达式过于负责的时候,把状态的判断逻辑转移到表示不同状态的一系列类当中,将复杂的判断逻辑简化.
- 将与特定状态的相关行为局部化,并且将不同状态的行为分割开来.
- 由于各个状态的逻辑分布到state的子类之间,减少了相互间的依赖.
适配器模式(Adapter)
适配器模式(Adapter),将一个类的接口转换成客户希望的另一个类的接口.Adapter模式使得原本由于接口不兼容而不能一起工作的类可以一起工作.
场景:
- 系统的数据和行为都正确,但接口不符时,可以考虑使用是适配器,目的是使控制范围之外的一个原有对象与某个接口匹配.
- 用于希望服用一些现存的类,但是接口又与复用环境要求不一致的情况.
- 使用一个已经存在的类,但是它的接口,也就是它的方法和你的要求不相同时
- 两个类所做的事情相同或相似,但是具有不同的接口.
- 双方都不太容易修改的时候.
适配器模式应该在无法重构或者重构代价太大的时候一如使用.在重构很容易的时候,重构优于引入适配器模式.
备忘录模式(Memento)
备忘录(Memento),在不破坏封装性的前提下,不活一个对象的内部状态,并在该对象之外保存这个状态.这样以后可以将该对象恢复到原先保存的状态.
场景:
- 游戏存档
- Memento 模式比较适用于功能比较复杂,但是需要维护或记录属性历史的类,或者是需要保存的属性只是众多属性众多一部分时,Originator 可以根据保存的Memento信息还原到前一状态.
优点:
备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来.
组合模式(Composite)
组合模式(Composite),将对象组合成树形结构以表示’部分-整体’的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性.
场景:
- 总公司子公司问题/父子菜单问题.
- 需求中是体现部分与整体层次的结构
- 希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象
优点:
组合模式让客户可以一致地使用组合机构和单个对象.
迭代器模式(Iterator)
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示.
场景:
当前java的所有容器类基本都实现了这种设计模式.
优点:
分离了集合对象的遍历行为,抽象出一个迭代器来负责,既可以做到不暴露集合内部结构,又可以让外部代码透明的访问集合内部的数据.
java的foreach循环最后就是通过迭代器实现的,foreach的写法只是语法糖.
单例模式(Singleton)
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点.
桥接模式(Bridge)
桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化.这里的抽象和实现分离,并不是指让抽象类与其派生类分离,而是指的是抽象类和它的派生类用来实现自己的对象
命令模式(Command)
命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求进行排队或者请求日志,以及支持可撤销操作.
优点:
- 很容易设计一个命令队列
- 在需要的情况下,可以较容易的将命令记入日志
- 允许接收请求的一方决定是否要否决请求
- 可以容易地实现对请求的撤销和重做
- 由于新建的具体命令类不影响其他类,因此增加新的具体命令类很容易.
职责链模式(Chain of Responsibility)
职责链模式(Chain of Responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象出处理它为止.
场景:
审批权限
优点:
- 接受者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构.结果是职责链可以简化对象的相互连接,它们仅需要保持一个指向其后继者的引用,而不需要保持它们所有的候选接受者引用.大大降低了耦合度.
- 可以随时地增加或者修改处理一个请求的结构,增强了给对象指派责任的灵活性.
缺点:
一个请求极有可能到了链的末端都得不到处理,或者因为没有正确的配置而得不到处理.
中介者模式(Mediator)
中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互.
场景:
- 一组对象以定义良好但是复杂的方式进行通信的场合
- 想定制一个分布在对个类中的行为,而又不想生成太多的类.
优点:
- Mediator(中介者类)的出现减少了各个Colleague(同事类)的耦合,可以独立的改变和复用各个Colleague类和Mediator类.
- 由于把对象如何进行协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自自身行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统.
缺点:
由于ConcreteMediator控制了集中化,于是就吧交互的复杂性变成了中介者的复杂性,这就使得中介者变得比任何一个ConcreteColleague都复杂.
解释器模式(Interpreter)
解释器模式(Interpreter),定义一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子.
场景:
- 自定义语法的语法解释.
- 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子.这样就可以构建一个解释器,该解释器通过解释这些句子来解释该问题.
- 当一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树
优点:
容易的改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或者扩展该文法.也比较容易实现文法,因为定义抽象语法树中的各个节点的类实现大体类似,这些类都易于直接编写.
缺点:
解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护.建议当文法非常复杂时,使用其他的技术如语法分析程序或者编译器生成器来处理.
访问者模式(Visitor)
访问者模式(Visitor),表示一个作用域某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用与这些元素的新操作.
场景:
访问者模式适用于数据结构相对稳定,又有易于变化的算法的系统.
优点:
- 访问者模式把数据结构和作用于结构上的操作(算法)之间的耦合解脱开,使得操作集合可以相对自由的演化.
- 访问者模式使得增减新的操作相对容易,因为增加新的操作就意味着增加一个新的访问者.
- 访问者将有关的行为集中到一个访问者对象中.
缺点:
访问者模式使得增加新的数据结构变得困难.
“大多数时候你并不需要访问者模式,但当你需要访问者模式时,那就是真的需要它了.”
享元模式(Flyweight)
享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象.
没搞明白.