设计模式:
纸上得来终觉浅,绝知此事要躬行
设计原则
1) 单一职责原则
com.zh.designpatterns.design_principles.demo01_single_principle
概念:对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。 当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2 (例如交通工具跑,分为天上,地上。不能统一)
单一职责原则:核心思想,让类足够简单
1.降低了类的复杂度,一个类或者多个类只负责一项职责
2.提高类的可读性,可维护性
3.降低变更引起的风险
4.在逻辑足够简单时,才可以在代码级别违反单一职责原则,类中方法足够少时,可以在方法级别保持单一职责原则
2) 接口隔离原则
com.zh.designpatterns.design_principles.demo02_interface_segregation_principle
客户端不应该依赖它不需要的接 口,即一个类对另一个类的依赖 应该建立在最小的接口上(例如A只要接口123,C只要接口145方法。A依赖B,C依赖D。将接口拆分,B只实现1和2。不要4和5。D同理)
3) 依赖倒转(倒置)原则
com.zh.designpatterns.design_principles.demo03_dependence_inversion
(案例:用户接受消息,原先需要新加微信,邮件方法。耦合太高。抽取一个接受信息的接口,微信和方法都去实现接口。用户传入接口参数。可扩展性搞。接口原则理解依赖抽象,面向接口编程)
依赖倒转原则(Dependence Inversion Principle)是指:
-
高层模块不应该依赖低层模块,二者都应该依赖其抽象
-
抽象不应该依赖细节,细节应该依赖抽象
-
依赖倒转(倒置)的中心思想是面向接口编程
-
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的 多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象 指的是接口或抽象类,细节就是具体的实现类
-
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的 任务交给他们的实现类去完成
依赖关系传递的三种方式和应用案例
依赖倒转原则的注意事项和细节
-
低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
-
变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在 一个缓冲层,利于程序扩展和优化
-
继承时遵循里氏替换原则
4) 里氏替换原则
com.zh.designpatterns.design_principles.demo04_liskov_substitution_principle
(父类,funA减法,子类重新funA实际实现为加法。如果B需要使用A类的方法,可以使用组合关系)
子类可以扩展父类的功能,但不能改变父类原有的功能。
里氏替换原则是讲,继承实际上让两个类耦合性增强了,在适当情况,可以通过聚合,组合,依赖来解决问题。
5) 开闭原则(OCP)
com.zh.designpatterns.design_principles.demo05_open_close_inversion
(绘图类,每次创建方法,画一个形状就需要增加一个方法一个类。修改->绘图类,接受一个接口,具体的形状实现接口,体会使用方和提供方)
-
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
-
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
3)可提高代码的可复用性,可维护性以及测试时只需要对扩展的代码进行测试就行。
6) 迪米特法则(最少知道原则)
com.zh.designpatterns.design_principles.demo06_demeter_inversion
(两个学院的员工信息打印,打印方法只需要在自己的学院进行实现,对外提供一个public的公共方法)
-
一个对象应该对其他对象保持最少的了解,类与类关系越密切,耦合度越大,只与直接的朋友通信,核心是降低类之间的耦合
-
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系, 我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量 的形式出现在类的内部
7) 合成复用原则
com.zh.designpatterns.design_principles.demo07_composite_reuse_principle
原则是尽量使用合成/聚合的方式,而不是使用继承
设计模式
设计模式分为三种类型,共23种
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
1.创建型模式
1.1单例模式(常用)
单例模式注意事项和细节说明
-
单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需 要频繁创建销毁的对象,使用单例模式可以提高系统性能
-
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使 用new
-
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数 据库或文件的对象(比如数据源、session工厂等)
1) 饿汉式(静态常量) (可使用,可能内存浪费)
2) 饿汉式(静态代码块) (可使用,可能内存浪费)
3) 懒汉式(线程不安全) (不推荐)
4) 懒汉式(线程安全,同步方法) (不推荐)
5) 懒汉式(线程安全,同步代码块) (不推荐)
6) 双重检查 (推荐)
7) 静态内部类(推荐)
8) 枚举(也ok)
1.2抽象工厂模式(常用)
com.zh.designpatterns.design_patterns.demo02_factory
简单工厂模式 s01_simple_factory
案例背景(来源尚硅谷): 创建披萨案例:要便于披萨种类的扩展,要便于维护
-
披萨的种类很多(比如 GreekPizz、CheesePizz 等)
-
披萨的制作有 prepare,bake, cut, box
-
完成披萨店订购功能。
原始方案,当新增扩展种类披萨时,需要修改使用方,使用方多了要改很多地方,包一层,通过简单工厂模式进行创建。
- 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族 中最简单实用的模式
2) 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
- 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式
工厂方法模式 s02_factory_method
案例背景(尚硅谷): 披萨项目新的需求:客户在点披萨时,可以点不同地点不同口味的披萨,比如 北京的奶酪pizza、 北京的胡椒pizza 或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
思路1:使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、 LDPizzaSimpleFactory 等等.需要非常多的地理位置的工厂。从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2:使用工厂方法模式
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方 法模式将对象的实例化推迟到子类
抽象工厂模式 s03_abstract_factory
-
抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
-
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
-
从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
-
将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以 根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展。
1.3原型模式
com.zh.designpatterns.design_patterns.demo03_prototype
基本数据类型值传递可以cp,对含有引用类型的需要深拷贝,引用类型地址传递。
深拷贝实现方式1:重写clone方法来实现深拷贝
深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
ClassPathXmlApplicationContext , " scope=“prototype” 使用原型模式
1.4建造者模式(常用)
-
建造者模式,是一种对象构建模式。将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
-
建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节.
工厂是创建一批对象,建造者是创建一个对象的流程
建造者模式的四个角色
-
Product(产品角色): 一个具体的产品对象。
-
Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口/抽象类。
-
ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
-
Director(指挥者):构建一个使用Builder接口的对象。主要是用于创建一个复杂的对象。主要有两个作用,
一是:隔离了客户与对象的生产过程,
二是: 负责控制产品对象的生产过程
1.5工厂模式
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
2.结构型模式
2.1适配器模式(常用)
类适配器模式
-
适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同 工作。其别名为包装器(Wrapper)
-
适配器模式属于结构型模式
-
主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
类适配器
对象适配器
接口适配器
springmvc- HandlerAdapter 使用的适配器
2.2桥接模式(常用)
理解手机案例即可
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),传统方式需要扩展手机样式类,品牌类。每个类型下都要有对应品牌的手机。扩展性很差。违反单一原则
使用桥接模式修改
-
桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
-
是一种结构型设计模式
-
Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展
桥接模式类原图:
- Client类:桥接模式的调用者
2)抽象类(Abstraction):维护了Implementor/即它的实现类Concretelmplementor…二者是聚合关系,Abstraction充当桥接类
3)RefinedAbstraction:是 Abstraction抽象类的子类
-
Implementor:行为实现类的接口
-
ConcretelmplementorA/B :行为的具体实现类
6)从UML图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系
桥接模式的注意事项和细节
-
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实 现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
-
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部 分由具体业务来完成。
-
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
-
桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层, 要求开发者针对抽象进行设计和编程
-
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局 限性,即需要有这样的应用场景。
桥接模式其它应用场景
-
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥 接模式尤为适用.
-
常见的应用场景: -JDBC驱动程序 -银行转账系统
转账分类: 网上转账,柜台转账,AMT转账
转账用户类型:普通用户,银卡用户,金卡用户…
-消息管理
消息类型:即时消息,延时消息 消息分类:手机短信,邮件消息,QQ消息…
2.3装饰模式(常用)
jdk中inputstream案例使用到,com.zh.designpatterns.design_patterns.demo07_decorator
案例背景:
星巴克咖啡订单项目(咖啡馆):
-
咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack(蒸馏)、LongBlack(美式)、Decaf(无因)
-
调料:Milk、Soy(豆浆)、Chocolate
-
要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
-
使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖 啡+调料组合
方案1类会爆增
方案2可控制类的暴增,但新增调料时候,需要维护的代码非常多
装饰者模式定义
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
装饰者模式原理
- 装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component) // 被装饰者
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
-
Component 主体:比如类似前面的Drink
-
ConcreteComponent和Decorator
ConcreteComponent:具体的主体, 比如前面的各个单品咖啡
Decorator: 装饰者,比如各调料.
- 在如图的Component与ConcreteComponent之间,如果 ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来, 抽象层一个类。
2.4组合模式
com.zh.designpatterns.design_patterns.demo08_composite
jdk案例:hashmap
案例背景:看一个学校院系展示需求 编写程序展示一个学校院系结构:要在一个页面中展示出学校的院系 组成,一个学校有多个学院,一个学院有多个系。如下
-----------------清华大学-----------------
-----------------计算机学院-----------------
计算机科学与技术
网络工程
软件工程
-----------------信息工程学院-----------------
通信工程
信息工程
-----------------计算机学院-----------------
计算机科学与技术
网络工程
软件工程
Component:它是一个抽象角色,为要组合的对象提供统一的接口。
Leaf:在组合中表示子节点对象,叶子节点不能有子节点。
Composite:定义有枝节点的行为,用来存储部件,实现在Component接口中的有关操作
-
组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑 它是节点还是叶子
-
对应的示意图
2.5外观模式
com.zh.designpatterns.design_patterns.demo09_facade
java: MyBatis 中的Configuration 去创建MetaObject
背景案例: 组建一个家庭影院: DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能
其过程为: 直接用遥控器:统筹各设备开关,开爆米花机,放下屏幕,开投影仪,开音响,开DVD,选dvd,去拿爆米花,调暗灯光,播放,观影结束后,关闭各种设备
1.传统方式
传统方式解决影院管理问题分析
-
在ClientTest 的main方法中,创建各个子系统的对象,并直接去调用子系统(对象) 相关方法,会造成调用过程混乱,没有清晰的过程,不利于在ClientTest 中,去维护对子系统的操作
-
解决思路:定义一个高层接口对外接口,屏幕内部子类实现细节提供给访问者。
外观模式原理类图
原理类图的说明(外观模式的角色)
-
外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当 子系统对象
-
调用者(Client): 外观接口的调用者
-
子系统的集合:指模块或者子系统,处理Facade 对象指派的任务,他是功能的实际提供者
2.6享元模式
“享”就表示共享,“元”表示对象
com.zh.designpatterns.design_patterns.demo10_flyweight
背景案例:
1.需求:不同的用户以不同的方案展示内容(客户A: 新闻发布 客户B:博客发布 客户C:微信公众号发布)
传统方案: 1) 直接复制粘贴一份,然后根据客户不同要求,进行定制修改 2) 给每个网站租用一个空间
存在问题(使用享元模式解决):
-
相当于同一个网站有很多实例,造成服务器的资源浪费
-
解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、 数据库空间等服务器资源都可以达成共享,减少服务器资源
-
对于代码来说,由于是一份实例,维护和扩展都更加容易
基本介绍
-
享元模式(Flyweight Pattern)也叫蝇量模式: 运用共享技术有效地支持大量细粒度的对象
-
常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
-
享元模式能够解决重复对象的内存浪费的问题, 当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
-
享元模式经典的应用场景就是池技术了,String常 量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
享元模式的原理类图
对原理图的说明-即(模式的角色及职责)
1)FlyWeight 是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态和内部状态(后面介绍)的接口
或实现
2) ConcreteFlyWeight是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
- UnSharedConcreteFlyWeight是不可共享的角色,一般不会出现在享元工厂。
- FlyWeightFactory享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法
内部状态和外部状态
属性,内部状态为不可变的,外部状态为经常变的。棋子颜色是内部,位置是外部。
案例解决方案:
享元模式在jdk中的应用: JDK-Interger
2.7代理模式(常用)
com.zh.designpatterns.design_patterns.demo11_proxy
代理模式的基本介绍
-
代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的 功能操作,即扩展目标对象的功能。
-
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
-
代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代 理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于 动态代理的范畴) 。
-
代理模式示意图
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一 起实现相同的接口或者是继承相同父类
-
优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
-
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
-
一旦接口增加方法,目标对象与代理对象都要维护
具体要求
-
定义一个接口:ITeacherDao
-
目标对象TeacherDAO实现接口ITeacherDAO
-
使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
-
调用的时候通过调用代理对象的方法来调用目标对象.
-
特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
动态代理
-
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
-
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
-
动态代理也叫做:JDK代理、接口代理
Cglib代理
-
代理的对象是个单独类,没有实现接口-使用Cglib代理
-
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展
-
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
-
在AOP编程中如何选择代理模式: 1. 目标对象需要实现接口,用JDK代理 2. 目标对象不需要实现接口,用Cglib代理
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。
3.行为型模式
3.1模版方法模式(常用)
案例背景(来源尚硅谷):
编写制作豆浆的程序,说明如下:
-
制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
-
通过添加不同的配料,可以制作出不同口味的豆浆
-
选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
模板方法模式的原理类图
模板方法模式的钩子方法
-
在模板方法父类中可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
-
还是用上面做豆浆的例子来讲解,比如,还希望制作纯豆浆,不添加任何的配料。可以使用钩子
spring-ioc中的模板方法使用, Spring IOC容器初始化时运用到的模板方法模式
3.2命令模式
com.zh.designpatterns.design_patterns.demo13_commond
背景: 对一套智能家电,安装同一个app进行控制所有。
基本介绍
-
命令模式:在软件设计中,需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,只需在程序运行时指定具体的请求接收者即可
-
命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
-
在命名模式中,会将一个请求封装为一个对象,以便使用不同参 数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
-
通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色: 将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。 Invoker是调用者(将军),Receiver是被调用者(士兵), MyCommand是命令,实现了Command接口,持有接收对象
-
Invoker 是调用者角色
-
Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
-
Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
-
ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execut
命令模式在Spring框架JdbcTemplate使用到了命令模式
3.3访问者模式
com.zh.designpatterns.design_patterns.demo14_visitor
背景案例(来源尚硅谷):
将观众分为男人和女人以及少儿等,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如成功、失败等)
访问者模式原理类图:
-
Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
-
ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
-
ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
-
Element 定义一个accept 方法,接收一个访问者对象 5) ConcreteElement 为具体元素,实现了accept 方法
3.4迭代器模式(常用)
com.zh.designpatterns.design_patterns.demo15_iterator
背景: 编写程序展示一个学校院系结构:要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。一个用数组存,一个用list存,针对不同的集合类型进行遍历
迭代器模式基本介绍
-
如果集合元素是用不同的方式实现的,有数组,还有java的集合类, 或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可使用迭代器模式解决。
-
迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素, 不需要知道集合对象的底层表示,即:不暴露其内部的结构。
原理图:
-
Iterator :迭代器接口,是系统提供,含义hasNext, next, remove
-
Concretelterator:具体的迭代器类,管理迭代
-
Aggregate :一个统一的聚合接口,将客户端和具体聚合解耦
-
ConcreteAsgreage:具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历
集合 -
Client :客户端,通过Iterator和 Aggregate依赖子类
案例背景实现类图:
ArrayList迭代器结构图:
角色分析说明
1、内部类Itr 充当具体实现迭代器Iterator 的类, 作为ArrayList 内部类
2、List 就是充当了聚合接口,含有一个iterator() 方法,返回一个迭代器对象
3、ArrayList 是实现聚合接口List 的子类,实现了iterator()
4、Iterator 接口系统提供
5、迭代器模式解决了不同集合(ArrayList ,LinkedList) 统一遍历问题
3.5观察者模式(常用)
com.zh.designpatterns.design_patterns.demo16_observer
背景: 天气预报项目需求,要求:
-
气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如 发布到自己的网站或第三方)。
-
需要设计开放型API,便于其他第三方也能接入气象站获取数据
-
提供温度、气压和湿度的接口
-
测量数据更新时,要能实时的通知给第三方
普通方案:
普通方案存在问题:
-
其他第三方接入气象站获取数据的问题
-
无法在运行时动态的添加第三方 (新浪网站)
-
违反ocp原则=>观察者模式
观察者模式原理
观察者模式类似订牛奶业务
-
奶站/气象局:Subject
-
用户/第三方网站:Observer
Subject:登记注册、移除和通知
-
registerObserver 注册
-
removeObserver 移除
-
notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用 户来取,也可能是实施推送,看具体需求定
Observer:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject, 依赖的对象为Observer,Subject通知Observer变化,比如这里的奶站是 Subject,是1的一方。用户时Observer,是多的一方。
解决方式:
观察者模式的好处
-
观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除 和通知。
-
这样,增加观察者(这里可以理解成一个新的公告板),就不需要去修改 心类WeatherData不会修改代码,遵守了ocp原则
Jdk应用的源码Observable 应用了观察者模式
3.6中介者模式
类和类之间的交互,全由中介第三者类去做处理。类和类之间不要有太多的耦合
com.zh.designpatterns.design_patterns.demo17_mediator
背景: 智能家庭项目:
-
智能家庭包括各种设备,闹钟、咖啡机、电视机、窗帘等
-
主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起->咖啡机开始做咖啡->窗帘自动落下->电视机开始播放
传统方案:
传统问题分析
-
当各电器对象有多种状态改变时,相互之间的调用关系会比较复杂
-
各个电器对象彼此联系,你中有我,我中有你,不利于松耦合.
-
各个电器对象之间所传递的消息(参数),容易混乱
-
当系统增加一个新的电器对象时,或者执行流程改变时,代码的可维护性、扩展性都不理想
中介者模式基本介绍
-
中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。 中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立 地改变它们之间的交互
-
中介者模式属于行为型模式,使代码易于维护
-
比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中 介者,在前后端交互时起到了中间人的作用
中介者原理类图:
对原理类图的说明-即(中介者模式的角色及职责)
- Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
2)Colleague是抽象同事类
-
ConcreteMediator具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理
HashMap,并接受某个同事对象消息,完成相应的任务 -
ConcreteColleague具体的同事类,会有很多,每个同事只知道自己的行为,而不了解其他同事类的行为(方法),
但是他们都依赖中介者对象
案例解决思路:
1、创建ConcreMediator对象
2、创建各个同事类对象,比如:Alarm . CoffeeMachine.TV…
3、在创建同事类对象的时候,就直接通过构适器,加入到colleagueMap
4、同事类对象,可以调用sendMessage ,最终会去调用ConcreteMediator的getMessage方法
5、getMessage会根据接收到的同事对象发出的消息来协调调用其它的同事对象完成任务
6、可以看到getMessage是核心方法,完成相应任务
3.7备忘录模式
通过状态类保存对象的某个时刻状态,然后直接通过状态类获取当时的状态。
com.zh.designpatterns.design_patterns.demo18_memento
案例背景(来源尚硅谷):
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态
传统模式:
-
一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不 利于管理,开销也很大.
-
传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但这就暴露了对象内部的细节
备忘录模式的原理类图:
对原理类图的说明-即 (备忘录模式的角色及职责)
-
originator : 对象(需要保存 状态的对象)
-
Memento : 备忘录对象,负责 保存好记录,即Originator内部 状态
-
Caretaker: 守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
-
说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要HashMap<String, 集合>
3.8解释器模式
了解。。。。
com.zh.designpatterns.design_patterns.demo19_Interpreter
案例背景(来源尚硅谷):
通过解释器模式来实现四则运算,如计算a+b-c的值,具体要求
-
先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复
-
在分别输入a ,b, c, d, e 的值 ,最后求出结果
传统问题:
-
编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
-
问题分析:如果加入新的运算符,比如 * / ( 等等,不利于扩展,另外让一个方法来 解析会造成程序结构混乱,不够清晰
-
解决方案:可以考虑使用解释器模式, 即:表达式 -> 解释器(可以有多种) -> 结果
解释器原理类图:
对原理类图的说明-即(解释器模式的角色及职责)
-
Context:是环境角色,含有解释器之外的全局信息.
-
AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
-
TerminalExpression:为终结符表达式,实现与文法中的终结符相关的解释操作
-
NonTermialExpression:为非终结符表达式,为文法中的非终结符实现解释操作.
5)说明:输入Context he TerminalExpression信息通过Client输入即可
Spring框架中 SpelExpressionParser就使用到解释器模式
解释器模式的注意事项和细节
-
当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
-
应用场景:编译器、运算表达式计算、正则表达式、机器人等
-
使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用 方法,将会导致调试非常复杂、效率可能降低
3.9状态模式(常用)
com.zh.designpatterns.design_patterns.demo20_state
案例背景:
完成APP抽奖活动 具体要求如下:
-
假如每参加一次这个活动要 扣除用户50积分,中奖概率是10%
-
奖品数量固定,抽完就不能 抽奖
-
活动有四个状态: 可以抽奖、 不能抽奖、发放奖品和奖品 领完
-
活动的四个状态转换关系图
状态模式的原理类图:
-
Context 类为环境角色, 用于维护State实例,这个实例定义当前状态
-
State 是抽象状态角色,定义一个接口封装与Context 的一个特点接口相关行为
-
ConcreteState 具体的状态角色,每个子类实现一个与Context 的一个状态相关行为
解决方案:
3.10策略模式(常用)
com.zh.designpatterns.design_patterns.demo21_strategy
案例背景:
- 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等,显示鸭子的信息,传统方案
问题:其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,不正确
-
继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
-
为了改进1问题,我们可以通过覆盖fly 方法来解决 => 覆盖解决
策略模式基本介绍 :
-
策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
-
这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来; 第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合, 少用继承(客户通过组合方式使用策略)。
原理类图:
案例解决方案:
JDK的 Arrays 的Comparator就使用了策略模式
策略模式的注意事项和细节
-
策略模式的关键是:分析项目中变化部分与不变部分
-
策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的 继承。更有弹性
-
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只 要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if…else)
-
提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得 你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
-
需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
3.11职责链模式(常用)
com.zh.designpatterns.design_patterns.demo22_responseibility
案例背景:
学校OA系统的采购审批项目:需求是
-
采购员采购教学器材
-
如果金额 小于等于5000, 由教学主任审批 (0 <= x <= 5000)
-
如果金额 小于等于10000, 由院长审批 (5000 < X <= 10000)
-
如果金额 小于等于30000, 由副校长审批 (10000 < X <= 30000)
-
如果金额 小于等于30000, 由校长审批 (30000 < X)
传统方式if多,一旦金额改变或者新加审批人,导致修改的东西非常多,耦合性太强。
职责链模式基本介绍 :
当前处理不了就找下一个接受者,看下一个接受者是否能处理。
-
职责链模式(Chain of Responsibility Pattern), 又叫责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的 发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接 收者的引用。如果一个对象不能处理该请求, 那么它会把相同的请求传给下一个接收者,依此类推。
职责链模式的原理类图:
职责链模式, 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
对原理类图的说明-即(职责链模式的角色及职责)
-
Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外Handler
-
ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链
-
Request ,含义很多属性,表示一个请求
案例解决方案;
SpringMVC-HandlerExecutionChain 类就使用到职责链模式
职责链模式的注意事项和细节
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般 通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值, 超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪 等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
学习路径:https://space.bilibili.com/302417610/,如有侵权,请联系q进行删除:3623472230
) 如果金额 小于等于30000, 由副校长审批 (10000 < X <= 30000)
- 如果金额 小于等于30000, 由校长审批 (30000 < X)
传统方式if多,一旦金额改变或者新加审批人,导致修改的东西非常多,耦合性太强。
职责链模式基本介绍 :
当前处理不了就找下一个接受者,看下一个接受者是否能处理。
-
职责链模式(Chain of Responsibility Pattern), 又叫责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的 发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接 收者的引用。如果一个对象不能处理该请求, 那么它会把相同的请求传给下一个接收者,依此类推。
职责链模式的原理类图:
职责链模式, 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
[外链图片转存中…(img-tmHWIURf-1670857830221)]
对原理类图的说明-即(职责链模式的角色及职责)
-
Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外Handler
-
ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链
-
Request ,含义很多属性,表示一个请求
案例解决方案;
[外链图片转存中…(img-07ZhgBeO-1670857830221)]
SpringMVC-HandlerExecutionChain 类就使用到职责链模式
职责链模式的注意事项和细节
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般 通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值, 超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪 等审批流程、Java Web中Tomcat对Encoding的处理、拦截器