文章目录
课程来自:https://www.bilibili.com/video/BV1mc411h719
一、设计模式的概述
1.概述
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
2.正确使用设计模式具有以下优点
- 可以提高程序员的思维能力、编程能力和设计能力。
- 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
3.设计模式的基本要素
- 模式名称
- 问题
- 解决方案
- 效果
4.GoF23
(Gang of Four)一种思维、一种态度、一种进步
- 创建型模式
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。 - 结构型模式
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式 - 行为型模式
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。
二、OOP七大原则
- 开闭原则
对扩展开放,对修改关闭。
(尽量不修改原来的代码) - 里氏替换原则
继承必须确保超类所拥有的性质在子类中仍然成立。
(子类可以扩展父类的功能,尽量不要去修改父类的功能) - 依赖倒置原则
要面向接口,不要面向实现编程。
(抽象不依赖细节,细节依赖抽象) - 单一职责原则
控制类的粒度大小、将对象解耦、提高其内聚性。
(一个对象不应该承担太多的职责,一个方法只做一件事情;原子性) - 接口隔离原则
要为各个类建立他们需要的专用接口
(设计接口的时候,要保证接口的精简和单一) - 迪米特法则
只与你的直接朋友交谈,不跟“陌生人”说话。《程序员修炼之道》
(比如,A->B->C,通过B来转发,A不要去直接访问C) - 合成复用原则
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
( A类是B类的一个成员变量)
三、创建型模式
1、单例模式
2、工厂模式
1.作用
实现了创建者和调用者的分离
2.分类
-
简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
-
工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
-
抽象工厂模式:围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
3.理应用到的原则
开闭原则:一个软件的实体应当对扩展开放,对修改关闭
依赖倒转原则:要针对接口编程,不要针对实现编程
迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
4.本质
- 实例化对象不使用new,用工厂方法代替。
- 将选择实现类,创建对象统一管理和控制。从而将调用者和我们的实现类解耦。
3、抽象工厂模式
- 定义
抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类 - 适用场景
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
- 优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
- 缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度
UML类图
抽象工厂模式
工厂模式小结
-简单工厂模式(静态工厂模式):虽然某种程度上不符合设计原则,但实际使用最多!
- 工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展!
- 抽象工厂模式:不可以增加产品,可以增加产品族!(产品族就是生产产品的工厂)
应用场景:
- JDK中Calendar的getlnstance方法!
- JDBC中的Connection对象的获取!
- Spring中IOC容器创建管理bean对象!
- 反射中Class对象的newlnstance方法!
4、建造者模式
- 概念
建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来) - 例子
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、>发动机、方向盘等等))
- 不同的用法
- 上面示例是Builder模式的常规用法,导演类Director在Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
- 通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品
- 比如:麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。
- 优点:
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则"。
- 缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
角色分析
应用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
- 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
建造者与抽象工厂模式的比较
- 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
- 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!
5、原型模式
1.实现的方式
- 实体类实现Cloneable接口。
- 重写Cloneable接口中的clone方法。
2.问题:深克隆和浅克隆的问题
3.实现深克隆的方式
- 序列化和反序列化。
- 重写clone方法,把实体类的属性也进行克隆。
四、结构性模式
- 结构型模式的作用
从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。 - 结构型模式的分类
适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式
1、适配器模式
1.作用
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作!
2.角色分析
- 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
- 需要适配的类:需要适配的类或适配者类。
- 适配器:通过包装一个需要适配的对象,把原接口转换成目标对象!
3.对象适配器优点
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
4.类适配器缺点
- 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
5.适用场景
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
2、桥接模式bridge
- 作用
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。 - 问题
- 分析
分析:这个场景中有两个变化的维度:品牌,类型。
- 好处分析
- 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
- 劣势分析
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
- 最佳实践
- 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
- 场景
- Java语言通过Java虚拟机实现了平台的无关性。
- AWT中的Peer架构
- JDBC驱动程序也是桥接模式的应用之一。
3、代理模式
1.静态代理
- 角色分析
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人!
- 代理模式的好处
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 缺点
- 一个真实角色就会产生一个代理角色;代码量会翻倍一开发效率会变低!
2.动态代理
-
角色分析
- 动态代理和静态代理角色一样!
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理!
- 基于接口–JDK动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
-
动态代理的好处
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务!