设计模式
设计模式的目的:重用性、可读性、可扩展性、可靠性、高内聚低耦合
一、七大原则
单一职责原则(Signle Responsibility Principle)
对一个类只负责一项职责,如果一个类A有两个不同职责:职责1,职责2。当职责1需求发生变更而改变类A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。
注意事项
1)降低类分复杂度,一个类只维护一项职责
2)提高类的可读性,可维护性
3)降低变更引起的风险
4)只有逻辑足够简单 ,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
*
接口隔离原则 (Interface Segregation Principle)
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。用不上的方法,把大的接口拆成若干个小的接口
*
依赖倒转(倒置)原则 (Dependence Inversion Principle)
1)高层模块不应该依赖底层模块,二者都应该依赖其抽象
2)抽象不应该依赖细节,细节应该依赖抽象
3)依赖倒转(倒置)的中心思想是**面向接口编程 **
4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。抽象为基础搭建的架构比以细节为基础的架构要稳定得多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
注意事项
1)底层模块尽量都要有接抽象类或接口,或者两者都有,程序稳定性更你好
2)变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利用程序扩展和优化
3)继承是遵循里氏替换原则
**总结:**和开闭原则很像
*
里氏替换原则(Liskov Substitution Principle)
OO中的继承性的思考和说明
1)继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然他不强制要求所有的子类必须遵循这些契约,但是如果子类这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
2)继承再给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须要考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都可能产生故障。
基本介绍
1)如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成了o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使其子类的对象。
2)在使用继承时,遵循里氏替换原则,在子类中尽可能不要重写父类的方法
3)里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
**总结:**子类不修改基类的方法,要用的话,尽可能重写方法实现
*
开闭原则ocp(Open Closed Principle)
1)一个软件实体如类,模块和函数对应对扩展开放(对提供方),对修改关闭(使用方) 。用抽象构建框架,用实现扩展细节。
2)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
3)编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
**总结:**在修改提供方代码时如果需要使用方变动很大的话,就要违背了开闭原则
*
迪米特法则(Demeter Principle)
基本介绍
1)一个对象应该对其他的对象保持最少的了解。
2)类与类关系越密切,耦合度越大。
3)又叫最少知道原则,一个类对自己依赖的类知道的越少越好。也就是说,对于依赖的类不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供public方法,不对外泄露任何信息。
4)只与直接的朋友通信。
5)**直接的朋友:**每个对象都会有其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多,依赖,关联,组合,聚合等。其中我们称出现成员变量,方法参数,方法返回值中的类为直接朋友,而出现在局部变量中的类不是直接朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
注意事项
1)核心是降低类之间的耦合。
2)由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。
**总结:**有A和B两个类,A在实现某个方法是,通过B类的方法返回一个类C,这时候C的出现违背了迪米特法则。
*
合成复用原则(Composite Reuse Principle)
基本介绍
1)原则就是使用合成/聚合的方式,而不是用继承。
*
设计原则核心思想
1)找出应用中可能需要变化之外,把它们独立出来,不要和那些不需要变化的代码混在一起。
2)针对接口编程,而不是针对实现编程
3)为了交互对象之间的松耦合设计而努力
二、UML类图
依赖(Dependency):
1)只要在类中用到对方,那么他们之间就存在依赖关系。如果没有对方,连编译都过不了
2)可以是类的成员属性
3)可以是方法的返回类型
4)可以是方法接收的参数类型
5)可以是类中使用的
*
泛化(继承 Generalization):
1)就是继承的关系,依赖关系的特例
2)如果A类继承了B类,我们就说A和B存在泛化关系
*
实现(Realization):类实现接口的关系
1)就是一个类实现了接口,依赖关系的特例
*
关联(Association)
1)关联关系是类与类之间的联系,依赖关系的特例
2)具有导航性:双向关系或单向关系
*
聚合(Aggregation):一个类调用使用另一个类,通过Set赋值
1)表示的是整体和部分的关系,整体与部分可以分开。聚合关系是关联关系的特例,所以它具有关联的导航性与多重性。如:一台电脑有键盘,显示器,鼠标等组成;组成电脑的各个配件可以从电脑上分离出来的,可使用带空心菱形的实现来表示
*
组合(Composite):一个类调用另一个类,直接在成员变量中new了另一个类
1)如果我们认为键盘,显示器,鼠标不可分离的,则升级为组合关系。
2)表示的是整体和部分的关系,整体与部分不可以分开。例如:人与身份证和脑袋,脑袋就是人的组合,省份证是人的聚合。但是如过程序中Person定义了对IDCard进行级联删除,即删除Person时连同IDCard一起删除,那么IDCard和Person就是组合了。
三、设计模式
创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)
单例模式
- 所谓的单例模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法。
- 有八种实现方式
1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步方法)
5)懒汉式(线程安全,同步代码块)
6)双重检查
7)静态内部类
8)枚举
使用场景
需要频繁的进行创建和销毁的对象、创建对象时消耗过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
工厂模式
1)简单方法模式
实现:定义了一个创建对象的抽象方法,由父类决定要实例化的类。通过父类的方法去创建需要的对象
2)工厂方法模式
实现:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
3)抽象工厂模式
-
实现:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
-
抽象工厂可以将简单工厂和工厂方法进行整合。
-
从设计层面看,抽象工厂就是简单工厂的改进(称为进一步的抽象)
-
将工厂抽象成两层,AbsFatory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂变成了工厂簇,更利于代码的维护和扩展。
4)小结
-
将实例化对象的代码提取出来,放到一个类中统一维护和管理,达到和主项目的依赖关系的解耦。从而提高项目的扩展性和维护性。
-
设计模式的依赖抽象原则
- 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
- 不要让类继承具体的类,而是继承抽象类或者是实现interface(接口)。
- 不要覆盖基类中已经实现的方法。
-
JDK源码:Calendar cal = Calendar.getInstance();
原型模式
1)基本介绍
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型模式是一种创建型设计模式,允许一个对象在创建另一个可定制的对象,无需知道如何创建细节。
- 原理:通过一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实施创建,即对象.clone()。
2)浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
- 浅拷贝是使用默认的clone()方法实现
3)深拷贝
- 复制对象的所有基本类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,知道该对象可达的所有对象。也就是说深拷贝要对整个对象进行拷贝
- 实现方式1:重写clone方法来实现
- 实现方式2:通过对象序列化实现(推荐)
4)注意事项
- 创建新的对象比较复杂时,可以利用原型模式简化对象创建过程,同时能提高效率
- 不用重新初始化对象,而是动态的获取对象运行状态
- 如果原始对象发生变化,其他的克隆对象也会发生变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
- Spring的getBean就应用了原型模式
建造者模式
1)基本介绍
-
建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个过程的不同实现方法可以构造出不同表现(属性)的对象。
-
建造者模式是一步一步创建一个复杂的对象,它允许用户指通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构建细节。
-
建造中的模式的四个角色:
- Product(产品角色):一个具体的产品对象。
- Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
- ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
- Director(指挥者):构建一个使用Builder接口的对象。他主要是用于创建一个复杂的对象。主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
2)注意事项
-
客户端不知道产品内部组成的细节,将产品本事身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
-
每一个具体建造者都相对独立,,而与其他的具体建造者无关,因此可以很方便的替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
-
==可以更加精细地控制产品的过程。==将复杂的产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
-
==增加新的具体建造者无须修改原有类库的代码,==指挥者类针对抽象建造者类编程,系统扩展方便,符合"开闭原则"。
-
建造者模式所传间的产品一般具有较多的共同点,其组成部分相似,==如果产品之间的差异性很大,则不适合使用建造者模式,==因此使用范围受到一定的限制。
-
如果产品内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得庞大,因此在这种情况下,要考虑是否选择建造者模式
-
抽象工厂VS建造者模式
抽象工厂模式对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要按照指定的蓝图建造产品,他的主要目的是通过组装零配件而产生的一个新产品。
-
JDK中StringBuilder就是建造者模式
适配器模式
1)基本介绍
- 适配器模式将某个类的接口转换陈客户端期望的另一个接口表示,主要的目的是兼容性,让原本因为接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
2)工作原理
- 适配器模式:将一个类的接口转换成另一个接口,让原本接口不兼容的类可以兼容
- 从用户的角度看不到被适配者,是解耦的
- 用户调用适配器转化出来的目标接口方法,适配器在调用被适配者的相关接口方法
- 用户收到反馈结果,感觉只是和目标接口交互
3)类适配器模式
- Adapter,通过继承src类,实现dst类接口,完成src->dst的适配。
- 注意事项
- Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定的局限
- src类的方法在Adapter中会暴露出来,也增加了使用成本
- 由于继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了
4)对象适配器模式
- 介绍
- 基本思路和类的适配器模式相同,只是将Adapter类做修改,不是继承src类,而是持有src类的实例,已解决兼容性的问题。即:持有src类,实现dst类的接口,完成src->dst的适配。
- 根据"合成复用原则",在系统中尽量使用关联关系来替代继承关系
- 对象适配器模式是适配器常用的一种
- 注意事项
- 对象适配器和类适配器其实算是一中思想,只不过是实现方式不同。根据合成复用原则,使用组合来替代继承,所以他解决了类适配器必须继承src的局限问题,也不再要求dst必须是接口。
- 使用成本更低,更灵活
5)接口适配器模式
- 基本介绍
- 适配器又叫缺省适配器模式
- 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法实现需求。
- 适用于一个接口不想使用其所有的方法的情况。
6)注意事项
- 类适配器:以类给到,在Adapter里,就是将src当作类,继承
- 对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
- 接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
- Adapter模式最大的作用还是将原来不兼容的接口融合在一起工作
- 实际开发中,实现起来不拘泥与我们讲解的三种经典形式
- 在SpringMvc中获取handle就是用的适配器
桥连模式
1)基本介绍
- 桥接模式是指:将实现与抽象放在两个不同的类层次中,是两个层次可以独立改变。
- 是一种结构想设计模式
- 桥接模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。他的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保证各个部分的独立性以及应对他们的功能扩展,个人理解行为实现是去使用抽象层
2)注意事项
- 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
- 对于系统的高层部门,只需要知道抽象部分和实现部分的接口就可以了,其他的部分有具体业务来完成
- 桥接模式可以替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
- 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联建立在抽象层,要求开发者针对抽象进行设计和编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度(抽象、实现),因此其使用范围有一定的局限性,即需要由这样的应用场景。
3)应用场景
- 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
- 常见场景
- JDBC驱动
- 银行转账系统
- 转账分类:网上转账,柜台转账,AMT转账
- 转账用户类型:普通用户,银卡用户,金卡用户
- 消息管理
- 消息类型:即时消息,延时消息
- 消息分类:手机短信,邮件消息,QQ消息
装饰模式
1)基本介绍
-
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更具有弹性,装饰着模式也体现了开闭原则(ocp)。
-
JDK的io中用了装饰者
组合模式
1)基本介绍
- 又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示"整体-部分"的层次关系。
- 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
- 这种类型的设计模式数据结构型模式
- 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
2)原理类图
3)应用场景
- JDK的Map就是组合的模式
4)注意事项
- 简化客户端操作,客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题
- 具有较强的扩展性。当我们更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动
- 方便构建复杂的层次结构,客户端不用理会里面的组成细节,容易添加节点或叶子从而创建出更复杂的树形结构
- 需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式
- 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如有很多方法和属性都不一样,不适合使用组合模式。
外观模式
1)基本介绍
- 也叫过程模式,外观模式为子系统中的一组接口提供一个一致的界面,一模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
- 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,是的调用端只需跟这个接口发生调用,而无需关系这个子系统的内部细节。
2)原理类图
3)应用场景
- mybatis的configuration
4)注意事项
- 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
- 外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
- 通过合理的使用外观模式,可以帮我们更好的划分访问的层次
- 当系统需要进行分层设计时,可以考虑使用Facade模式
- 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑开发一个新的Facade类,来提供一六系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。
- 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好,要以让系统有层次,利于维护为目的。
享元模式
1)基本介绍
- 享元模式(FlyWeight Pattern)也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象
- 常用于系统底层开发,解决系统的性能问题。向数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我么需要的,则创建一个
- 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新的对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。
- 享元模式经典的应用场景就是池技术了,String常量池/数据库连接池/缓冲池等等都是享元模式的应用,享元模式时池技术的重要实现方式。
2)内部状态和外部状态
- 享元模式提出了两个要求:细粒度和共享对象。这里涉及到的内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
- 内部状态指的是对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
- 外部状态指的是对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态
- 例子:围棋理论上有361个空位可以放在棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子就可以减少到只有两个实例,这样就很好的解决了对象的开销问题。
3)原理类图
4)应用场景
- JDK源码中Integer
5)注意事项
- 在享元模式这样理解,"享"就是共享,"元"表示对象
- 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
- 用唯一标识码判断,如果内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
- 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
- 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的
- 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
- 享元模式经典的使用场景是需要缓冲池的场景
代理模式
1)基本介绍
-
代理模式:作为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象,这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
-
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
-
代理模式有不同的形式,主要有三种,静态代理,动态代理和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,它是属于动态代理的范畴)。
-
代理模式示意图
2)静态代理
-
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
-
具体要求
-
定义一个接口:ITeacherDao。
-
目标对象TeacherDAO实现接口ITeacherDAO。
-
使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO。
-
调用的时候通过调用代理对象的方法来调用目标对象。
-
特别提醒:代理对象与目标对象要实现相同的接口,通过调用相同的方法来调用目标对象的方法。
-
优缺点
- 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
- 一旦接口增加方法,目标对象与代理对象都要维护
-
静态代理类图
3)动态代理
-
基本介绍
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API,动态的在内存中构建对象
- 动态代理也叫做:JDK代理、接口代理
-
JDK中生成代理对象的API
- 代理类所在的包:java.lang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接口三个参数,完整的写法是:static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
-
动态代理类图
4)Cglib代理
-
基本介绍
- 静态代理和JDK代理模式都要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何接口,这个时候可使用目标对子类来实现代理。
- Cglib代理也叫做子类代理,她是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,如SpringAOP,实现方法拦截。
- 在AOP编程中如何实现代理模式:对象需要实现接口,用JDK代理;目标对象不需要实现接口,用Cglib代理。
- Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
-
cglib代理类图
5)代理模式的变体
- 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问
- 缓存代理:当请求图片文字等资源时,先到缓存代理去取,如果取到资源ok,如果取不到资源,再到公网或者数据库取,然后缓存
- 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
- 同步代理:主要使用在多线程编程中,完成多线程间同步工作。
模板方法模式
1)基本介绍
- 模板方法(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 简单来说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。
- 这种类型的设计模式属于行为型模式。
2)原理类图
3)模板方法中的钩子方法
- 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为"钩子"。
- 还是用上面的豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造。
4)应用场景
- SpringIOC容器初始化
5)注意事项
- 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
- 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用
- 即统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由于类提供部分步骤的实现。
- 该模式的不足之处:每个不同的实现都要一个子类实现,导致类的个数增加,使系统更加庞大
- 一般模板方法都加上final关键字,防止子类重写模板方法
- 模板方法使用场景:当要完成某个过程,该过程要执行一系列步骤,这一系列步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。
命令模式
1)基本介绍
- 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是那个,我们只需要在程序运行时指定具体的请求接受者即可,此时,可以使用命令模式来进行设计
- 命令模式使得请求发送者与请求接受者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦
- 在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同请求(即命名),同时命令模式也支持可撤销的操作。
- 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色,将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象。
2)原理类图
3)应用场景
- Spring中的JdbcTemplate
4)注意事项
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者是要调用命令对象的execute方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接受者执行请求的动作,也就是说:"请求发起者"和"请求执行者"之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令
- 容易实现对请求的撤销和重做
- 不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用的时候注意
- 空命令也是一种设计模式,他为我们省去了判空的操作。在上面的实例中,如果没有空命令,我们没按下一个按键都要去判空,这给我们编码带来一定的麻烦。
- 命令模式的经典场景:界面的一个按钮都是一条命令、模拟CMD、订单的撤销/恢复、触发-反馈机制
,将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象。
2)原理类图
[外链图片转存中...(img-6ZVTbgso-1728288695893)]
3)应用场景
- Spring中的JdbcTemplate
4)注意事项
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者是要调用命令对象的execute方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接受者执行请求的动作,也就是说:"请求发起者"和"请求执行者"之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令
- 容易实现对请求的撤销和重做
- 不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用的时候注意
- 空命令也是一种设计模式,他为我们省去了判空的操作。在上面的实例中,如果没有空命令,我们没按下一个按键都要去判空,这给我们编码带来一定的麻烦。
- 命令模式的经典场景:界面的一个按钮都是一条命令、模拟CMD、订单的撤销/恢复、触发-反馈机制
本文通过尚硅谷视频学习进行部分总结,用作个人笔记。