Java设计模式
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。以下介绍Java的23种设计模式。
文章目录
一、七大原则:
1)单一职责原则:
定义:
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。
注意事项和细节:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性。
- 降低变更引起的风险。
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则: 只有类中方法数量足够少,可以在方法级别保持单一职责原则。
2)接口隔离原则
定义:
客户端不应该依赖它不需要的接口,即一一个类对另- -个类的依赖应该建立在最小的接口上。
3)依赖倒转原则
定义:
任务交给他们的实现类去完成。
基本介绍:
依赖倒转原则(Dependence Inversion Principle)是指:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心 思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的。在java中, 抽象 指的是接口或抽象类,细节就是具体的实现类。
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的
依赖倒转原则的注意事项和细节:
- 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
- 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
- 继承时遵循里氏替换原则。
4)里氏替换原则
定义:
- 如果对每个类型为T1的对象O1, 都有类型为T2的对象O2,使得以T1定
义的所有程序P在所有的对象O1都代换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。 - 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。.
00中的继承性的思考和说明:
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
- 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
解决方法:
- 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。
- 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替.
5)开闭原则
基本介绍:
- 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
- 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
6) 迪米特法则
基本介绍:
- 一个对象应该对其他对象保持最少的了解。
- 类与类关系越密切,耦合度越大。
- 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息。
- 迪米特法则还有个更简单的定义:只与直接的朋友通信。
- 直接的朋友:每个对象都会与其他对象由耦除关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量 的形式出现在类的内部。
7)合成复用原则
基本介绍:
原则是尽量使用合成/聚合的方式,而不是使用继承。
二、23种设计模式
1)创建型模式:
●单例模式
定义:
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,
对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
●抽象工厂模式
单例模式有八种方式:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
单例模式注意事项和细节说明:
1.单例模式保证了系统内存中该类只存在- 一个对象,节省了系统资源,对于一些需 要频繁创建销毁的对象,使用单例模式可以提高系统性能
2)当想实例化-一个单例类的时候,必须要记住使用相应的获取对象 的方法,而不是使用new。
3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数 据库或文件的对象(比如数据源、session工厂 ~等)
●原型模式
基本介绍:
- 原型模式(Prototype模式)是指: 用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
- 工作原理是:通过将-一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
- 形象的理解:孙大圣拔出猴毛,变出其它孙大圣。
深拷贝和浅拷贝:
浅拷贝:
- 对于 数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于 数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另-个对象的该成员变量值。
- 前面我们克隆羊就是浅拷贝。
- 浅拷贝是使用默认的clone()方法来实现sheep = (Sheep) super.clone();
深拷贝:
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
- 深拷贝实现方式1:重写clone方法来实现深拷贝
- 深拷贝实现方式2:通过对象序列化实现深拷贝
原型模式的注意事项和细节:
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
- 不用重新初始化对象,而是动态地获得对象运行时的状态。
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的编号,无需修改代码。
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意.
●建造者模式
基本介绍:
- 建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
- 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
建造者模式的四个角色:
- Product (产品角色) :一个具体的产品对象。
- Builder (抽象建造者) :创建一个Product对象的各个部件指定的接口。
- ConcreteBuilder (具体建造者) :实现接口, 构建和装配各个部件。
- Director (指挥者) :构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
建造者模式的注意事项和细节:
- 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户 使用不同的具体建造者即可得到不同的产品对象。
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.
- 抽象工厂模式VS建造者模式:
抽象工厂模式实现对产品家族的创建, 一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
●工厂模式
1.简单工厂模式:
基本介绍:
-
简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是
由一 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。 -
简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行 为(代码)。
-
在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会 使用到工厂模式。
2、简单工厂方法模式:
3、抽象工厂模式:
基本介绍:1)抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。 2)抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。 3)从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。 4)将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。 5)类图
2)结构型模式:
●适配器模式
基本介绍:
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
- 适配器模式属于结构型模式。
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。
Ⅰ、类适配器:
类适配器模式介绍:
基本介绍: Adapter类, 通过继承src类,实现dst类接口,完成src->dst的适配 。
Ⅱ、对象适配器:
对象适配器模式介绍:
- 基本思路和类的适配器模式相同,只是将Adapter类作修改, 不是继承src类,而是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src->dst的适配
- 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
- 对象适配器模式是适配器模式常用的一种
Ⅲ、接口适配器
接口适配器模式介绍:
- 一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
- 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求.
- 适用于一个接口不想使用其所有的方法的情况.
适配器模式的注意事项和细节:
- 三种命名方式,是根据src是以怎样的形式给到Adapter (在Adapter里的形式)来命名的。
- 类适配器:以类给到,在Adapter里,就是将src当做类,继承.
对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有.
接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现. - Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
- 实际开发中,实现起来不拘泥于我们讲解的三种经典形式.
●桥接模式
桥接模式的注意事项和细节:
- 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
- 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
- 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
- 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程.
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。
●装饰模式
装饰者模式定义:
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp).
- 这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会。
●组合模式
基本介绍:
- 组合模式(Composite Pattern) ,又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
- 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
- 这种类型的设计模式属于结构型模式。
- 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象.
组合模式的注意事项和细节:
- 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
- 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
- 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
- 需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式.
- 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式.
●外观模式
基本介绍:
- 外观模式(Facade) ,也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用.
- 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节.
●享元模式
基本介绍:
- 享元模式( Flyweight Pattern)也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象.
- 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避
免重新创建,如果没有我们需要的,则创建一个. - 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以
降低系统内存,同时提高效率. - 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式.
内部状态和外部状态:
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态.
- 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
- 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变.
- 外部状态指对象得以依赖的-一个标记,是随环境改变而改变的、不可共享的状态。
- 举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题.
注意事项:
●代理模式
代理模式的基本介绍:
- 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
- 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
- 代理模式有不同的形式,主要有三种静态代理、动态代理(JDK代理、接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)。
- 代理模式示意图
Ⅰ.静态代理:
静态代码模式的基本介绍:
5. 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类.
静态代理优缺点:
6. 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展.
7. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类.
8. 一旦接口增加方法,目标对象与代理对象都要维护.
Ⅱ.动态代理
动态代理模式的基本介绍:
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
- 动态代理也叫做: JDK代理、接口代理
JDK中生成代理对象的API
- 代理类所在包:java.ang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h )
Ⅲ.Cglib代理
Cglib代理模式的基本介绍:
- 静态代理和JDK代理模式都要求目标对象是实现-一个 接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理
- Cglib代理 也叫作子类代理,它是在内存中构建一- 个子类对象从而实现对目标对象功
能扩展,有些书也将Cglib代理归属到动态代理。 - Cglib是一个强大的高性 能的代码生成包,它可以在运行期扩展java类与实现java接口.它广 泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截.
- 在AOP编程 中如何选择代理模式:
1 .目标对象需要实现接口,用JDK代理
2 .目标对象不需要实现接口,用Cglib代理 - Cglib包的底 层是通过使用字节码处理框架ASM来转换字节码并生成新的类.
3)行为型模式
●模版方法模式
基本介绍:
- 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 简单说,模板方法模式定义-一个操作中的算法的骨架,而将一些步 骤延迟到子类中,使得子类可以不改变-一个 算法的结构,就可以重定义该算法的某些特定步骤
- 这种类型的设计模式属于行为型模式。
模板方法模式的注意事项和细节:
- 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时, 只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改.
- 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用.
- 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
- 该模式的不足之处:每-一个不同的实现都需要-个子类实现, 导致类的个数增加,使得系统更加庞大.
- 一般模板方法都加上final关键字,防 止子类重写模板方法.
- 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理.
●命令模式
基本介绍:
- 命令模式(Command Patterm) :在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个.
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计. - 命名模式使得请求发送者 与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
- 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作.
- 通俗易懂的理解,将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker是调用者(将军),Receiver是被调用者(士兵) ,
MyCommand是命令,实现了Command接口,持有接收对象
命令模式的注意事项和细节: - 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令.
- 容易实现对请求的撤销和重做.
- 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意.
- 空命令也是-种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来-定的麻烦。
- 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD (DOS命令)订单的撒销/恢复、触发-反馈机制.
●访问者模式
访问者模式基本介绍:
- 访问者模式(Visitor Pattern),封装- -些作用于某种数据结构的各元素的操作,它可以在不改变 数据结构的前提下定义作用于这些元素的新的操作。
- 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题.
- 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口.
- 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决.
访问者模式的注意事项和细节
➢优点
- 访问者模式符合单- -职责原则、让程序具有优秀的扩展性、灵活性非常高.
- 访问者模式可以对功能进行统一,可以做报表、UI、 拦截器与过滤器,适用于数据结构相对稳定的系统.
➢缺点 - 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难.
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.
●迭代器模式
基本介绍:
- 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式.
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
- 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。
➢优点
- 提供一个统一的方法遍历对象, 客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
- 隐藏了 聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
- 提供了一种设计思想, 就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话, 只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
- 当要展示- 组相似对象,或者遍历-组相同对象时使用,适合使用迭代器模式
➢缺点
每个聚合对象都要-个迭代器, 会生成多个迭代器不好管理类
●观察者模式
观察者模式的好处:
- 观察者模式设计后,会以集合的方式来管理用户(Observer), 包括注册,移除和通知。
- 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则。
●中介者模式
基本介绍:
- 中介者模式(MediatorPattern) ,用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互.
- 中介者模式属于行为型模式,使代码易于维护.
- 比如MVC模式, C (Controller控制器) 是M (Model模型)和V (View视图)的中介者,在前后端交互时起到了中间人的作用.
中介者模式的注意事项和细节:
- 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦.
- 减少类间依赖,降低了耦合,符合迪米特原则.
- 中介者承担了较多的责任,-旦中介者出现了问题,整个系统就会受到影响.
- 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意.
●备忘录模式
基本介绍:
- 备忘录模式(Memento Pattern) 在不破坏封装性的前提下,捕获-一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
- 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作.
- 备忘录模式属于行为型模式
备忘录模式的注意事项和细节:
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态.
- 实现了信息的封装,使得用户不需要关心状态的保存细节.
- 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意.
- 适用的应用场景: 1、后悔药。2、 打游戏时的存档。3、 Windows里的ctri + Z. IE中的后退。4、数据库的事务管理
- 为了节约内存,备忘录模式可以和原型模式配合使用.
●解释器模式(Interpreter模式)
基本介绍:
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器.
- 解释器模式(Interpreter Pattern) :是指给定一一个语言 (表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
- 应用场景
●应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
●一些重复出现的问题可以用一种简单的语言来表达
●一个简单语法需要解释的场景 - 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等.
解释器模式的注意事项和细节:
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性.
- 应用场景:编译器、运算表达式计算、正则表达式、机器人等
- 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.
●状态模式
基本介绍:
- 状态模式(State Pattern) :它主要用来解决对象在多种状态转换时,需要对外
输出不同的行为的问题。状态和行为是一–对应的,状态之间可以相互转换 - 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
状态模式的注意事项和细节:
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 方便维护。将容易产生问题的if else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句,而且容易出错.
- 符合“开闭原则”。容易增删状态
- 会产生很多类。每个状态都要-一个对应的类,当状态过多时会产生很多类,加大维护难度.
- 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式.
●策略模式
基本介绍:
- 策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.
- 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口) ;第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
策略模式的注意事项和细节:
- 策略模式的关键是:分析项目中变化部分与不变部分.
- 策略模式的核心思想是:多用组合/聚合少用继承:用行为类组合,而不是行为的继承。更有弹性.
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if.else if.else)
- 提供了可以替换继承关系的办法:策略 模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展.
- 需要注意的是:每添加一一个策略就要增加一一个类,当策略过多是会导致类数目庞大.
●职责链模式(责任链模式)
基本介绍:
- 职责链模式(Chain of Responsibility Pattern),又叫责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
- 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
- 这种类型的设计模式属于行为型模式
职责链模式的注意事项和细节:
- 将请求和处理分开,实现解耦,提高系统的灵活性.
- 简化了对象, 使对象不需要知道链的结构.
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNex()方 法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能.
- 调试不方便。 采用了类似递归的方式, 调试时逻辑可能比较复杂.
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器.
总结
以上内容为总结尚硅谷设计模式.
码字不易,确定不送个免费的赞再走.
收藏加关注,文章不丢失.