设计模式小结

设计模式

7 大原则

单一职责: 每个类只负责一个职责(或每个方法)
接口隔离: 一个类对另一个类的依赖应建立在最小的接口上
依赖倒转: 高层模块不应依赖低层模块, 二者都应该依赖接口而非细节. 细节依赖抽象, 面向接口编程
里式替换: 子类应该做到可以替换父类, 及子类应尽量不重写父类方法.
开闭原则: 对提供者而已可以修改, 对使用者而言不需要修改(即代码兼容性), 尽量使用扩展增加功能, 而非修改原有类
迪米特法则: 一个对象应该对其他对象保持最小了解(最少知道原则)
合成复用原则: 一个类使用另一个类的代码(方法), 尽量使用合成, 而不是继承

创建型

单例模式

原理: 确保一个类只有一个实例,并提供该实例的全局访问点。

饿汉式:
    静态常量
    静态代码块
懒汉式:
    直接判断(线程不安全)
    方法加 synchronized(线程安全, 效率低)
    判断后再同步(错误写法)
    双重判断(if-同步-if) (推荐写法)
    匿名静态内部类 (简单, 推荐)
    枚举(简单, 但对象方法写在枚举中, 略有不适)
    
示例:
    java.lang.Runtime#getRuntime()
    java.awt.Desktop#getDesktop()

原型模式

原理: 使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象.
示例: Java 的 Object 对象的 clone 方法, java.util.Arrays.ArrayList#toArray()

浅拷贝: 仅对基础类型及字符串类型的字段拷贝值
深拷贝: 同时对引用类型(如数组,对象) 也进行拷贝

深拷贝实现:
1.重写 clone, 一一处理每个引用对象(调用对象的 clone), 麻烦, 且若对象之间关系复杂, 其中一个未实现深拷贝则导致 bug.
2.利用序列化和反序列化, 如 Json, 或 Java 自带的序列化方式(二进制)

创建者模式(生成器模式)

原理:
    封装一个对象的构造过程,并允许按步骤构造.
    若对象的生成过于复杂(字段极多且赋值还有依赖关系, 需要顺序调用), 则可将赋值过程封装成一个build(), 并放到一个 Builder 类中. 此类对外提供各个字段的赋值方法并先保存起来, 直到调用 build(), 此方法返回对象实例. 
    使用此模式, 调用者无需关注构建过程, 只需设置自己想要的值, 然后调用 build() 即可得到对象实例. 且若增加或修改字段, 构造过程变化, 调用者无感知, 无需修改代码. 符合开闭原则.

示例: StringBuilder, 一些框架的 ConfigurationBuilder(如 xmpp), 用于构建配置.

简单工厂模式

原理:
    在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
    此模式可避免多个调用者创建对象时判断创建哪个子类的重复代码, 且若多一个子类, 调用者无需修改代码.
    
示例: Spring ApplicationContext 的 getBean 方法.

工厂方法模式

原理:
    定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
    此模式解决了简单工厂每增加一个子类需要修改工厂类的问题.
    此模式存在问题, 若新增一个子类, 需同时新增一个子类工厂, 系统复杂性更高.

示例: Calendar, NumberFormat

抽象工厂模式

原理:
    提供一个接口,用于创建 相关的对象家族.
  同上, 由子类工厂决定创建哪些对象.
    此模式是工厂方法的升级版, 不同之处在于它同时创建多个种类的对象(工厂类具有多个方法).
    此模式将一个对象家族的新建集合到一个工厂类创建管理, 这些对象家族相互之间一般有关联, 在创建时就可以处理这些关联. 且对于 2 个子类工厂, 一般可以无缝切换, 使得修改代码极为方便(即换一个子类工厂).
    此模式在新增一个对象家族的成员时非常麻烦(即所有工厂类需要新增一个方法), 但再新增一类对象家族时比较简单(即新增一个子类工厂).

结构型

适配器模式

原理:
    把一个接口转换成另一个用户需要的接口.
    定义一个类, 实现用户需要的接口, 并聚合一个需要转换的接口对象, 在重写的方法(用户需要的方法)中调用聚合的对象的方法, 若需要返回值, 且返回值类型不一致, 则还需要在方法中处理一番, 然后返回. 这个过程叫做适配.这个类叫做适配器类.
    使用此模式可对一些老旧接口适配兼容.
    
示例: java.util.Arrays#asList() 将数组适配成 List, Spring MVC的 HandlerAdapter

装饰者模式

原理:
    将一个或多个功能(方法)动态的新增到一个类中.
    把需要新增功能类称为 A,定义一个类B,实现A的上层接口, 并聚合一个A 的实例对象, B类实现的接口中, 对其他不关心的方法直接调用聚合的对象的方法. 对于关心的方法则可以在调用前后进行加料处理(如一个方法返回一个数, 可以在原来的返回值上乘以 2), 同时, B类也可以新增一些其他方法, 这些方法就是多出的功能. B类就是装饰者类, A就是被装饰类.
    此模式的优点是, 装饰类也可以当做被装饰类, 然后再来一层装饰, 可以无限的装饰.
    
示例: java IO 流

代理模式

原理:
    控制其他对象的访问(方法级), 将一些前置或后置的处理, 通过代理对象注入到目标对象的方法前后. 面向切面编程.

类型:
    静态代理: 定义一个代理类实现目标对象的上层接口, 并聚合一个目标对象, 重写方法时将前置后置处理加上.
    动态代理: 
        JDK 动态代理: 需要目标对象有上层接口(自然接口内的方法才可以代理) 
            使用java.lang.reflect.Proxy#getProxyClass
        CGLIB 动态代理: 是个类就行. 实现原理是 ASM 框架动态生成目标对象类的子类字节码, 然后通过反射生成代理对象.

示例: Spring AOP

桥接模式

原理:
    将抽象与实现分离开来,使它们可以独立变化。
    桥接的含义是, 一个桥, 放在哪里都有桥的 2 边, 桥的 2 边可以变化, 但桥始终不变. 此处, 桥代表一个操作(如手机上运行软件), 2 边代表 一个操作的 2 个维度(如手机和软件). 同时, 桥接后的操作也可以视为一个维度, 与另一个维度桥接(如手机上运行软件和人这 2 个维度, 可以进行桥接, 组成 3 维度嵌套桥接).
    
示例: JDBC 获取连接, 获取连接是一个维度, 数据库是一个维度, 数据库有多个, 所以这是一个数据库维度变化, 另一维度不变的桥接模式.

享元模式

原理:
    利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
    如常见的 线程池, 常量池等, 使得对象的获取速度加快.
    
示例: java.lang.Integer#valueOf() java.lang.Boolean#valueOf()

组合模式

原理:
    将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
    一般需要部分和整体具有一定的相似度, 才能对其进行抽象.
    对部分/整体进行抽象, 得出一个公共抽象类或接口, 再实现类中根据具体角色做不同处理. 

示例:
    javax.swing.JComponent#add(Component) 
  java.util.Map#putAll(Map)
  java.util.List#addAll(Collection)
  java.util.Set#addAll(Collection)

外观模式

原理:
    提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用.

行为型

职责链(责任链)模式

原理:
    使多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止, 从而避免请求的发送者和接收者之间的耦合关系。
    
示例:
    javax.servlet.Filter#doFilter()
    netty 的 Handler Chain

观察者模式

原理:
    定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
    主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。

示例: 
    swing 的事件监听(按钮事件, 鼠标事件)
    JS 的 事件监听

状态模式

原理:
    允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
    状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为.

策略模式

原理:
    定义一系列算法,封装每个算法,并使它们可以互换。
    策略模式可以让算法独立于使用它的客户端。
    策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法.

示例: java.util.Comparator#compare() javax.servlet.http.HttpServlet

模板方法模式

原理:
    定义算法框架,并将一些步骤的实现延迟到子类。
    通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。

示例: java.util.Collections#sort()

命令模式

原理:
    将一个对象(命令接收者)的每个操作拆分到每一个命令类中, 再使用一个命令管理类来管理这些命令. 使得命令可以放入队列中有序执行, 且可以统一记录命令的操作日志, 还可以支持撤销操作(每个命令都实现对应的撤销即可).
    此模式的好处是, 若将命令抽象为几个标准的命令(如开,关), 然后管理多个命令接收者(如灯,电视机,空调)的操作, 可使新增命令接收者变得简单, 即扩展性好.
    
    又称万能遥控器.

中介模式

原理:
    集中相关对象之间复杂的沟通和控制方式。降低子系统之间的耦合.
    类似一个消息收发中心, 负责字系统的消息中转, 使得子系统之间可以进行一定的交互.
    
示例: 线程池管理者线程和要执行的任务.

备忘录模式

原理:
    在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
    如对游戏的当前状态进行一个保存, 然后在后续游戏中死亡后可以读取这个状态重新开始.

访问者模式

原理: 
    为一个对象结构(比如组合结构)增加新能力。
    使用访问者模式可实现重载的动态绑定(即伪双分派), 效果与重载方法内使用 instanceof 是一样的, 但使用访问者模式, 可扩展性更好.

迭代器模式

原理:
    提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。

示例: java.util.Iterator

解释器模式

原理:
    为语言创建解释器,通常由语言的语法和语法分析来定义。

示例: EL 表达式, Freemaker模板

空对象模式

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