设计模式之禅——设计模式(三)

组合模式(合成模式)
  • 定义:
    将对象合成树形结构以表示 整体-部分 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
  • 自我理解:
    用一系列的数据生成树形结构(整体-部分)。例如,给你公司的花名册,现在要将花名册以公司架构的形式展示。即,某部门,某部门下某些员工。
  • 类图:


    image.png
  • Component
    抽象组合对象,定义了所有组合对象的共有方法和属性。
  • Leaf
    叶子对象,类似于公司的基本员工,不管理任何人。
  • Composite
    树枝结构(branch),可以理解成管理层。方便管理,根节点也是它。其下有叶子对象。

Summary

  • 优点
    1、高层模块调用简单。
    只要找到一个其中的一个节点,可以找到root节点。不必关心整体结构。
    2、及诶单自由增加
    想增加branch就增加branch,想增加leaf就增加leaf。
  • 缺点
    1、例如上面的leaf 和composite 是一个具体的实现类,限制了接口的影响范围。
  • 场景
    1、维护和展示 部分-整体关系的场景。eg:树形菜单,文件和文件夹。
    2、从一个整体中能够独立出部分模块或者功能的场景。

扩展

组合模式有两种不同的实现:透明模式、安全模式。

  • 安全模式
    上面的类图就是安全模式。
  • 透明模式
    类图


    image.png

    个人理解:
    将树枝和叶结构统一成一个Component,然后根据其中的条件来判断是叶子结构还是树枝结构。比如:getChild(),返回一个迭代器,如果迭代器是null或者数量为0,那就说明这个结构是一个叶子结构。反之就是一个树枝结构。

观察者模式(发布订阅模型)
  • 定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所依赖于他的对象都会得到通知并被自动更新。
  • 简单理解:开关 与 灯的亮暗。灯是依赖于开关的,当开关的状态发生改变,灯的亮暗也随之发生改变。
  • 类图


    image.png
  • subject
    被观察者对象,上面的开关。该类的要求是能够动态的增减观察者,同时能够进行通知。
  • observer
    观察者,上面的灯,根据被观察者的行为,做出相应的操作。

Summary

  • 优点
    1、观察者和被观察者之间是抽象耦合
    2、建立一套触发机制
  • 缺点
    1、效率需要考虑。
  • 注意
    1、广播链(被观察者==》观察者==》观察者==》观察者)这种情况的时候,依赖严重不建议这样建立关系。建议最多只有两层关系(被观察者==》观察者==》观察者)。
    2、异步处理问题,单一对多观察的时候,考虑耗时,可能需要开异步。这个时候需要考虑队列以及线程安全。可以参考Message Queue,加深理解。
  • 扩展
    1、Observer 和 Observable Java中有现成的接口直接实现就可以工作了。
门面模式(外观模式)
  • 定义:
    要求一个子系统的外部与其内部的通行必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
  • 自我理解:
    门面可以理解为一个封装集合,他定义的固定的对外接口,其内部无论怎么变化,提供给外部的方法不能随意变动。简称金玉其外败絮其内。


    image.png

    image.png
  • Facade门面角色
    子系统的封装集合,没有实际的业务逻辑。只是一个委托类。
  • 子系统角色
    可以同时有个或者多个,每个子系统类是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另一个客户端而已。

Summary

  • 优点
    1、减少了系统的相互耦合
    因为将子系统封装了,所以外部访问不会深入到子系统中的具体方法,减少了耦合。
    2、提高了灵活性
    子系统随便更改,只要保证提供门面的方法不变,上级依赖稳定。
    3、提高安全性
    相当于增加了一道防火墙,防止直接访问子系统。
  • 缺点
    1、不符合开闭原则,对修改关闭,对扩展开放。门面出现问题之后,好像就玩不动了。这就是因为对修改关闭原因。
  • 场景
    1、为复杂模块或者子系统提供访问的接口
    2、子系统相对独立——外界对子系统的访问只要黑箱操作即可
    3、预防低水平人员带来的风险,即开发模块化,模块间依赖单一,别人水平低不会影响到你(其实你水平更低)。
  • 注意
    1、一个子系统可以有多个门面
    2、(重点)门面不参与子系统内的业务逻辑
    就是门面中的方法不处理业务逻辑。
    eg:
“public class Facade {
     //被委托的对象
     private ClassA a = new ClassA();
     private ClassB b = new ClassB();
     private ClassC c = new ClassC();
     //提供给外部访问的方法
     public void methodA(){
             this.a.doSomethingA();
     }
     
     public void methodB(){
             this.b.doSomethingB();
     }
     
     public void methodC(){
             this.a.doSomethingA();
             this.c.doSomethingC();
     }
}”

 

这种情况下,正确的做法是将A、C进行一次封装,生成新类,同事生成新方法。如下

“public class Context {
     //委托处理
     private ClassA a = new ClassA();
     private ClassC c = new ClassC();
     //复杂的计算
     public void complexMethod(){
             this.a.doSomethingA();
             this.c.doSomethingC();
     }
}”

“public class Facade {
     //被委托的对象
     private ClassA a = new ClassA();
     private ClassB b = new ClassB();
     private Context context = new Context();
     //提供给外部访问的方法
     public void methodA(){
             this.a.doSomethingA();
     }
     
     public void methodB(){
             this.b.doSomethingB();
     }
     
     public void methodC(){
             this.context.complexMethod();
     }
}”

 

蛋疼是蛋疼,设计思想就是这样的。

备忘录模式
  • 定义
    在不破坏封装性的前提下,捕获一个对象的内部状态,并在改对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
  • 自我理解
    打单机游戏的时候,经常用到的SL大法(Save-Load)。简单来说,将某一个状态保存,方便历史恢复。
  • 类图


    image.png
  • Originator
    数据源
  • Memento
    负责存储数据源的数据状态,生成备忘录。
  • Caretaker
    对备忘录进行管理的类。

拓展

  • Clone方式的备忘录
    记得之前的原型模式中用到的Clone,将当前的类进行Clone产生一个相同状态的类。
    类图


    image.png

操作
让originator 实现Clone接口,然后生成备忘录的时候,直接返回克隆对象给备忘录管理类。

注意:采用的是Clone接口,所以还是存在Clone 的弊端,如果成员变量有非原始数据类型的,需要进行深度拷贝。

  • 多状态备忘录模式
    背景
    当Originator属性成员过多的时候,在手动赋值还原的话容易出错,而且也是机械重复操作过多。为了解决这个问题,需要对bean的描述进行解析。
    类图


    image.png

    新增一个BeanUtil类进行属性数据的提取和还原。

注意
1、通过bean描述提取属性的时候,读取的时候是根据方法名开头以get来过滤的,所以,写代码的时候需要住,反之出现重复调用,行成死循环。
2、成员变量中,保存的是引用地址,所以会出现类似深度拷贝,和浅拷贝的问题。对于像list的非原始数据额类型,需要单独clone 赋值。
= 多备份的备忘录
背景
历史备份不仅可以有一份备份,也可以有多份备份。所以这种情况下,就需要使用多备份的备忘录。核心是通过Hashmap 生成关键字对应的备忘录,进行管理调用还原。
类图


image.png

优化
让备忘录实现一个空接口,之后以空接口作为上层代码的引用。反之数据暴露。

以上四种实现相关代码==》个人github

Summary

  • 应用场景
    需要保存和恢复的应用场景。入历史回滚。
    需要监控副本场景
    jdbc(数据库调用)的本质就是备忘录。
访问者模式
  • 定义:
    封装一些作用于某种数据结构中的各元素操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。
  • 自我理解
    留一个接口出来,用于扩展数该数据结构的元素操作。


    image.png
  • visitor
    抽象访问者,抽象接口或者抽象类,声明访问哪些元素。
  • ConcreteVisitor
    具体访问者实现类。
  • Element
    抽象元素,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义。
  • ConcreteElement
    具体元素,被操作的对象
  • ObjectStruture
    元素产生者,可以理解为元素的容器或者元素管理者。

Summary

  • 优点
    符合单一职责原则
    扩张性优秀、灵活性高
  • 缺点
    访问者能够关注到被访问者的内部细节,跟迪米特法则相违背。
    违背依赖倒置原则,因为访问者所依赖的 是具体的实现类而不是一个接口或者抽象类。
  • 场景
    1、需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作污染这些对象的类
    2、使用迭代器遍历对象,这个时候还需要做个性操作,这种情况就可以使用访问者模式。
  • 扩展
    1、统计功能,对需要计数操作的遍历的一种拖拽。
    2、多访问者,添加多个访问者。
    3、双分派
状态模式
  • 引子
    从电梯的状态操作开始说起,电梯有四种状态,停止、运行、开门、关门。同一种状态下有多中操作,但是这些操作是否被允许视具体情况而定。


    image.png

这种情况可以用根据每种情况然后用switch来搞定,如果状态很多的情况,可能代码量很大,而且逻辑很能比较混乱。这里就引出出了状态模式。平时写代码还是喜欢直接上switch,还是觉得用模式麻烦。

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

  • 自我理解
    还是看上面的引子吧,定义说得自己都云里雾里的。


    image.png
  • State ——抽象状态角色
    负责对象状态定义,并且封装环境角色以实现状态切换。

  • ConcreteState ——具体状态角色
    两个职责:
    1、状态变更
    2、行为管理

  • Context ——环境角色
    定义客户端需要的接口,并且负责具体状态的切换。

Summary

  • 优点
    结构清晰,清晰是清晰觉得麻烦好多
    遵循设计原则(废话)
    封装性非常好

  • 缺点
    当状态多、方法多的时候类非常庞大。

  • 场景
    1、行为会随着状态改变而改变的场景
    2、条件、分支判断语句的替代者

  • 注意
    状态数量最好不要超过5个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容