设计模式之禅阅读笔记

单一职责原则:类的复杂性降低,可读性提高,可维护性提高,面向接口编程,可以考虑用组合模式。

用职责和变化原因来衡量接口和类设计得是否优良

实现类时不能过分细分类的职责,不然会有类的剧增等问题。

单一职责适用于接口、类和方法

 

继承好处:代码共享,提高代码重用性,提高代码可扩展性,提高产品和项目的开放性。

继承缺点:侵入性,降低代码灵活性,增强了耦合性。

里氏替换原则:只要父类能出现的地方子类就可以出现,而且替换成子类也不会产生任何错误和异常。但是反过来不一定。【向下转型是不安全的】。

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系来代替继承。

子类拥有父类的所有属性和方法,方法名相同,输入参数类型又不相同的,是重载(Overload),不是覆写(Override)。

想让子类的方法运行,要么覆写这个方法,要么重载这个方法,前提是扩大前置条件,即输入参数类型宽于父类覆盖范围。覆写或实现父类的方法时输出结果可以被缩小。

 

依赖倒置原则:【面向接口编程】高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖实现类;实现类依赖接口或抽象类。

定义变量必然要有类型,两种类型:表面类型(定义时候富裕类型)、实际类型(对象类型)。

遵循规则,但不是必须:

每个类尽量都有接口或抽象类,或者都具备;变量表面类型尽量是接口或者是抽象类;任何类都不应该从具体类派生;尽量不要覆写基类的方法;

接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。

 

接口隔离原则:Java中的类也是一种接口。

客户端不应该依赖它不需要的接口;类间的依赖关系应该建议在最小的接口上。

单一职责要求类和接口职责单一,是业务逻辑上的划分,接口隔离原则要求接口的方法尽量少。

遵循规则:接口尽量小;接口要高内聚;定制服务;接口设计是有限度的。

 

迪米特原则:一个对象应该对其他对象有最少的了解。

尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、package-private、protected等访问权限。谨慎使用Serializable。

类间解耦,弱耦合,类的复用率才可以提高。产生大量中转或跳转类,导致系统的复杂性提高。

 

开闭原则

软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

变化类型:逻辑变化;子模块变化;可见视图变化。

Keep the bar green to keep the code clean.

优势:对测试的影响;提高复用性;提高可维护性;面向对象开发要求。

遵循规则:抽象约束;元数据控制模块行为;制定项目章程;封装变化。

 

 

单例模式:确保某一个类只有一个实例,并且自行实例化并向系统提供这个实例。

优点:减少内存开支;减少系统性能开销;避免对资源多重占用;设置全局访问点,优化和共享资源访问。

缺点:一般没接口,扩展只能修改代码;对测试不利,与单一职责原则冲突。

 

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

优点:良好的封装性,代码结构清晰,扩展性优秀,屏蔽产品类。

1.缩小为简单工厂模式:一个模块仅需要一个工厂类。【相比来说,工厂类的扩展比较困难】

2.升级为多个工厂类:每一个具体的工厂都明确自己的职责,创建自己负责的产品类对象

3.替代单例模式:通过类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中对象唯一

4.延迟初始化:一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。

 

抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

优点:封装性;产品内约束为非公开状态。

缺点:产品族扩展困难。

 

软件开发过程中,如果相同一段代码复制过两次,就需要对设计产生怀疑。

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

基本方法尽量设计为protected类型,符合迪米特原则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类的访问权限。

优点:封装不变部分,扩展可变部分;提取公共部分代码,便于维护;行为由父类控制,子类实现。

 

建造者模式:讲一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

程序中尽量用this来调用。

优点:封装性;建造者独立,容易扩展;便于控制细节风险。

使用场景:

1.相同方法,不同执行顺序,产生不同的事件结果时。

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

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

建造者模式最主要的功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗来说就是零件的装配,顺序不同产生的对象也不同;而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是他关心的。

 

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

优点:职责清晰;高扩展性;智能化。

普通代理模式【调用者只知代理而不用知道真实的角色是谁】的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。

强制代理模式:从真实角色查找到代理角色,不允许直接访问真实角色,代理的管理已经由真实角色自己完成。

一个代理类可以代理多个真实角色,并且真实角色之间可以有耦合关系。

动态代理模式:在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。面向横切面编程。被代理类必须实现一个接口。

IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(cl【ClassLoader】, new Class[]{IGamePlayer.class},handler【InvocationHandler】)

 

 

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单程度仅次于单例模式和迭代器模式。clone()

优点:性能优良,内存二进制流的拷贝,要比直接new一个对象性能好,适合一个循环体内产生大量对象;逃避构造函数的约束。

使用场景:资源优化场景;性能和安全要求的场景;一个对象多个修改者的场景。

引用对象变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原型类型或不可变对象。

注意:要使用clone方法,类的成员变量上不要增加final关键字。

 

中介者模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立的改变他们之间的交互。

优点:减少类间依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然降低了类间的耦合。

缺点:中介者会膨胀得很大,而且逻辑复杂。

 

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

优点:类间解耦;可扩展性;结合其他模式,责任链模式实现命令解析任务,模板方法模式减少Command子类膨胀问题;日志回滚机制。

 

责任链模式:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,知道有对象处理它为止。

优点:请求和处理分开,两者解耦,提高系统灵活性。

缺点:性能问题;调试不方便。

 

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

优点:装饰类和被装饰类可以独立发展,而不会相互耦合;继承关系的一个替代方案;动态扩展实现类的功能。

缺点:多层装饰比较复杂。

 

策略模式:定义一组算法,将每个算法都封装起来,并且是它们之间可以互换。

优点:算法可以自由切换;避免使用多重条件判断;扩展性良好。

缺点:策略类数量增多;所有策略类都需要对外暴露、

策略枚举都是public、final、static的,扩展性受到一定约束。

 

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

优点:可以让两个没有任何关系的类一起运行;增加了类的透明性;提高了类的复用度;灵活性。

类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系。

 

迭代器模式:它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节【基本上已经没落】。

 

组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

优点:高层模块调用简单;节点自由增加。

使用场景:维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理;从一个整体中能够独立出部分模块或功能的场景。

 

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

优点:观察者和被观察者之间是抽象耦合;触发机制。

缺点:开发调试较复杂;顺序执行,一个观察者卡壳,会影响整体执行效率;多级触发效率不高。

使用场景:关联行为场景;事件多级触发场景;跨系统的消息交换场景。

它与责任链模式最大区别是观察者广播链在传播过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。

 

门面模式:外观模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。提供一个高层次的接口,使得子系统更易于使用。

优点:减少系统的相互依赖;提高灵活性;提高安全性。

使用场景:为一个复杂的模块或子系统提供一个供外界访问的接口;子系统相对独立——外界对子系统的访问只要黑箱操作即可;预防低水平人员带来的风险扩散。

一个子系统可以有多个门面;门面不参与子系统内的业务逻辑;

 

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

使用场景:需要保存和恢复数据的相关状态场景;提供一个可回滚的操作;需要监控的副本场景中。

注意事项:生命期【在 最近 的代码中使用,要主要管理它的生命周期,建议就要使用,不适用就要立刻删除其引用】;性能【一是控制不了备忘录建立的对象数量,二是大对象的建立是要消耗资源的】。

 

访问者模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

优点:符合单一职责原则;优秀的扩展性;灵活性高。

缺点:具体元素对访问者公布细节;具体元素变更比较困难;违背了依赖倒置原则。

使用场景:一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是说是用迭代器模式已经不能胜任的场景;需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作污染这些对象的类。业务规则要求遍历多个不同的对象。

主要功能:统计功能;多个访问者;双分派【双分派意味着得到执行的操作决定于请求的种类和两个接受者的类型】

 

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

约束条件:把状态对象声明为静态变量,有几个状态对象就声明几个静态变量;环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。

优点:结构清晰;遵循设计原则;封装性好。

缺点:类膨胀。

使用场景:行为随状态改变而改变;条件、分支判断语句的替代者。

 

解释器模式:按照规定语法进行解析的方案,项目中使用较少;给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

优点:简单语法分析工具,扩展性。

缺点:类膨胀;采用递归调用方法;效率问题。

使用场景:重复发生的问题;一个简单语法需要解释;

在项目中可以使用shell、JRuby、Groovy等脚本语言来替代解释器模式,弥补Java编译型语言的不足。

 

享元模式:池技术。使用共享对象可有效地支持大量的细粒度的对象。外部状态需要加上关键字。

不要有删除投产中代码的念头,如果方法或类确实不再使用,增加@Deprecated注解。

优缺点:大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分离出外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变。

使用场景:系统中存在大量的相似对象;细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关;需要缓冲池的场景。

我们使用享元模式时,对象中的享元对象尽量多,多到足够满足业务为止。

一个对象作为Map类的键值,一定要确保重写了equals和hashCode方法,否则会出现通过键值搜索失败情况。

对象池着重在对象的复用上,池中的每个对象是可替换的,主要解决复用。而享元模式主要解决对象的共享问题。

 

桥梁模式:将抽象和实现解耦,使得两者可以独立地变化。

优点:抽象和实现分离;扩充能力;实现细节对客户透明。

使用场景:不希望或不使用使用继承的场景:继承层次过渡、无法更细化设计颗粒等场景;接口或抽象类不稳定的场景;重用性要求较高。

 

总结与对比:

创建类模式大PK:单例模式【保持内存中只有一个对象】、原型模式【通过复制的方式产生一个新的对象】、工厂方法模式【整体对象的创建】、建造者模式【部件构建的过程、车间】、抽象工厂模式【工厂】

 

结构类模式大PK:适配器模式【修饰非血缘关系类,转化非相同接口的对象】、桥梁模式、组合模式、装饰模式【对类的功能进行加强或减弱,代理模式的特殊应用,包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类)】、门面模式、享元模式、代理模式【对代理过程的控制】

 

行为类模式大PK:责任链模式【基本上不改变消息对象,上下节点没有关系】、命令模式【算法解耦,需要接收者,适合解耦两个有紧耦合关系的对象场合或者多命令撤销的场景】、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式【达到状态切换行为也随之发生改变】、策略模式【算法完整性、封装性,适用于算法要求变换、算法之间没有交互】、模板方法模式、访问者模式

 

跨战区PK:创建类描述创建对象,行为类模式关注如何管理对象的行为,结构类模式则重于如何建立一个软件结构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值