- 00.设计模式分类汇总
- 01.创建型设计模式
- 02.结构型设计模式
- 03.行为型设计模式
- 04.面向对象设计原则
- 05.项目重构的演进
- 06.面向对象设计思想
- 07.更多内容推荐
23种设计模式 & 描述 & 核心作用 | 包括 |
---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 |
工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 |
适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是“类或对象之间的交互”问题 |
责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
- 05.结构型:静态代理模式思想
- 06.结构型:动态代理模式思想
- 07.结构型:适配器设计模式
- 08.结构型:装饰者设计模式
- 09.结构型:外观设计模式
- 10.结构型:桥接设计模式
- 11.结构型:组合设计模式
- 12.结构型:享元模式设计思想
- 13.行为型:观察者模式
- 14.行为型:策略者模式
- 15.行为型:模版设计模式
- 16.行为型:迭代器设计模式
- 17.行为型:中介者设计模式
- 18.行为型:解释器设计模式
- 19.行为型:命令设计模式
- 20.行为型:责任链设计模式
- 21.行为型:备忘录设计模式
- 22.行为型:访问者设计模式
- 23.行为型:状态设计模式
- 01.面向对象六大原则
- 02.单一职责原则详解
- 03.开闭原则详细介绍
- 04.里氏替换原则介绍
- 05.接口隔离原则介绍
- 06.依赖倒置原则介绍
- 07.迪米特原则介绍
- 08.代码保持简单原则
- 09.避免代码重复原则
模块 | 描述 | 备注 |
---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | GitHub |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | YCBlogs |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | 设计模式 |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | Java高级 |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | 网络协议 |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | 计算机基础 |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | C编程 |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | C++编程 |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | Leetcode |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 | Android |
01.单例模式基础介绍
对于系统某些需求来说,保证一个实例很重要,比如文件管理系统,ID生成器等。为了保证实例只能被创建一次,因此这才有了单例模式!
单例模式特点,构造私有,单例类只有一个,反序列化不会重新构建对象,通过静态返回单例对象。
使用场景:应用中只存在一个实例,比如账号系统,数据库等。思考几个问题:为何使用单例,它存在什么问题,跟静态类有何区别,是否有替代方案?
02.单例模式设计思考
为何要使用单例?一个类只允许创建一个对象(或者实例),表示全局唯一的类,比如Android中数据库,所有数据操作都是指向一个数据库!
03.单例模式实现方式
如何实现单例:构造必须私有,避免外部创建;要考虑线程安全问题,避免多线程下创建多个对象;是否支持延迟加载;性能
- 方式1: 熟悉单例模式各自的优缺点和使用场景。
- 方式2: 饿汉式实现方式。
- 方式3: 懒汉式实现方式。
- 方式4: 双重DCL校验模式。这种用的最为广泛!
- 方式5: 静态内部类方式。
- 方式6: 枚举方式单例。
- 方式7: 容器实现单例模式。
有什么优点:1.提供全局访问【共享】;2.只有一个对象【对于高频率比较好】;3.使用简单。缺点也很明显:1.拓展难;2.指责不清晰;3.滥用单例导致对象状态丢失。
04.单例模式有那些不友好
单例对OOP不友好。单例违背了面向对象设计思想,不能搞封装,继承,多态等。如果强行实现面向对象,则会让人感到奇怪!
对代码类之间的依赖和可读性要注意。避免单例中内容太过于庞大,代码逻辑复杂导致维护比较难。
对代码拓展不友好。对可测试不够友好。不支持有参数的构造。
01.工厂模式设计
工厂模式分类,大概分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。一般情况下,使用简单工厂,工厂方法场景稍多。
工厂模式思考,主要要搞清楚应用场景。什么时候该用工厂模式?相对于直接 new 来创建对象,用工厂模式来创建究竟有什么好处呢?
思考一个题目,你要在店里买各种咖啡,而这些咖啡中有的加冰,有的加奶,有的加糖。比如美式咖啡,拿铁咖啡,摩卡咖啡,巴西咖啡等等!用简单工厂,工厂方法,抽象工厂分别实现。
02.简单工厂介绍
一句话概括简单工厂:可以根据参数的不同返回不同类的实例。
简单工厂模式包含如下角色:抽象产品角色,具体产品角色,工厂角色。
简单工厂实现:
- 创造抽象产品角色,比如总结出很多咖啡都可能会,加糖,加冰,加奶,因此把它抽成抽象方法。
- 具体产品角色,这块有:美式咖啡类,拿铁咖啡类,摩卡咖啡等,这些都是具体的咖啡产品。
- 工厂角色。负责创建不同咖啡,为了简便高效地分配咖啡,设了根据类型(比如
american
,latte
)来提供不同的咖啡。
03.工厂方法介绍
工厂方法模式:定义一个创建对象的接口(指的是抽象工厂),让子类(具体工厂)决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法实现:
- Product:抽象产品。这个和简单工厂模式代码一样,忽略!
- ConcreteProduct:具体产品。这个和简单工厂模式代码一样,忽略!
- Factory:抽象工厂。这块定义咖啡工厂接口,通过该接口子类可以自己实现咖啡创建。
- ConcreteFactory:具体工厂。这里主要是创建美式咖啡工厂类,创建拿铁咖啡工厂类。
04.抽象工厂介绍
抽象工厂模式:抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级(即,同种的产品)的产品,而抽象工厂模式可生产多个等级(即,多种产品)的产品。
抽象工厂实现:
- AbstractFactory:抽象工厂。这个时候则需要抽象出生产甜品,生产咖啡的接口。
- ConcreteFactory:具体工厂。分别实现生产甜品的具体工厂,生产咖啡的具体工厂。
- AbstractProduct:抽象产品。这里把甜点抽象成一个产品,把咖啡抽象成一个产品。
- Product:具体产品。创建具体的美式咖啡,拿铁咖啡产品。创建具体的提拉米苏甜点,抹茶慕斯甜点。
01.建造者模式介绍
- 构造复杂对象的困境:通常使用构造函数或setter方法来设置对象的属性。但是,当对象具有许多属性或配置选项时,构造函数或setter方法的参数列表可能会变得冗长且难以维护。
- 建造者模式由来:在创建具有多个可选参数或配置选项的复杂对象,将对象的构建过程与其表示分离,使得构建过程更加灵活和可扩展。
- 建造者模式定义:建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
- 建造者模式场景:某个产品构建属性很多,或者属性之间存在依赖关系,或者同一个类可以创建不同的产品。
- 建造者模式思考:这种模式并不难,主要是理解应用场景,为什么要引入它?可以用set方式替换吗?
01.原型模式介绍
原型模式由来:看一个例子——邮件。由于邮件对象包含的内容较多(如发送者、接收者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,则可以快速给不同的人发邮件【可以修改复制后的邮件内容】。
原型模式定义:提供了一种灵活的方式来创建对象,通过复制现有对象来创建新对象,从而避免了昂贵的对象创建过程。该模式的核心思想是通过复制现有对象来创建新对象,而不是通过实例化类来创建。
原型模式在以下情况下特别有用:
- 当对象的创建过程比较复杂或耗时时,可以通过复制现有对象来提高性能。
- 当需要创建多个相似对象,但又不希望与它们的具体类耦合时,可以使用原型模式。
01.静态代理模式基础
由来和背景:在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。比如以下场景就适合用代理模式
- 个人,通过中介来找房东发布的租房。
- 乘客,通过火车站来购买铁路局的车票。
- 客户,通过VPN代理来翻墙查阅国外资料。
一句话概括就是:代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
静态代理使用场景:监控、统计、鉴权,日志等场景。附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发!
02.静态代理原理与实现
实现模式
- 首先创建一个接口(代理都是面向接口的),
- 创建具体实现类来实现这个接口,
- 创建一个代理类同样实现这个接口,不同之处在于具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
举一个实际案例
- 定义用户找房子的需求接口
- 创建用户找房子具体实现类
- 创建一个代理中介,委托中介去找房子
03.静态代理分析
代理模式包含如下角色:
- Subject: 抽象主题角色
- Proxy: 代理主题角色
- RealSubject: 真实主题角色
04.代理模式优势
- 使用静态代理来降低耦合。比如,邮件发送的接口 MailSender,以及一个实现该接口的具体类 RealMailSender。可以在不修改 RealMailSender 类的情况下,为邮件发送操作添加额外的功能。这样,RealMailSender 类和 MailSenderProxy 类之间的耦合度降低,客户端只需要与 MailSender 接口进行交互,而不需要关心具体的实现类。
- 静态代理可以用于保护真实对象的使用权限。在真正需要显示图像时,真实对象才会被加载和显示,从而保护了真实对象的使用权限。
05.静态代理不足
- 静态代理缺乏灵活说明
- 代码复用难,会导致代码庞大
- 难以动态添加功能
- 静态代理无法满足多态性
01.为何要动态代理
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
02.动态代理的概念
动态代理是指:代理类是在运行期间生成的,也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
动态代理定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
03.动态代理的实现
代理模式的角色分四种:
- 主题接口:Subject,是委托对象和代理对象都共同实现的接口,即代理类的所实现的行为接口。
- 目标对象:ReaSubject,是原对象,也就是被代理的对象。
- 代理对象:Proxy,是代理对象,用来封装真是主题类的代理类。
- 客户端:使用代理类和主题接口完成一些工作。
04.动态代理案例
组成元素:
- 抽象类接口
- 被代理类(具体实现抽象类接口的类)
- 动态代理类,实际调用被代理类的方法和属性
动态代理有两种实现方式:
- 基于接口动态代理。一种常见的动态代理方式,它通过实现Java的Proxy类和InvocationHandler接口来实现。
- 基于类动态代理。另一种常见的动态代理方式,它使用第三方库或框架来实现。在Java中,常用的类动态代理库包括CGLIB。
05.动态代理思想和原理
动态代理实现机制,主要是通过关键类Proxy
和InvocationHandler
来实现的。
- 创建
InvocationHandler
处理器对象,主要作用是通过实现方法,调用从代理类到委托类的分派转发。 - 通过
Proxy
创建代理对象,传入指定 ClassLoader 对象和一组 interface 来创建动态代理类,里面实现是通过反射去动态创建对象。
01.适配器模式基础
什么叫做适配器模式?用来做适配,将不兼容的接口转化成兼容的接口,例子就是 USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。
简单来说,用于事后补救措施,项目代码后期,想让不想关的类,变成可以一起工作。可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式思考?不要强行适配,设计前期就要考虑到兼容性,以减少对适配器依赖!
02.适配器模式实现
适配器模式两种实现方式:
- 类的适配器。通过继承来实现
- 对象的适配器。通过组合来实现
03.适配器模式分析
类适配器是一种结构型设计模式,它允许将一个类的接口转换为另一个客户端所期望的接口。通过继承原始类和实现目标接口,类适配器使得原始类的接口与目标接口兼容。
对象适配器是适配器模式的一种变体,它通过组合(而不是继承)原始类和目标接口来实现适配器功能。在对象适配器中,适配器类持有一个原始类的实例,并实现目标接口,通过调用原始类的方法来实现目标接口的方法。
04.适配器应用解析
类适配器:现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
对象适配器:有一个MediaPlayer接口,它可以播放mp3格式的文件。现在,我们想扩展这个功能,使其也可以播放其他格式的文件,比如vlc和mp4。
05.应用场景分析
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
- 封装有缺陷的接口设计。
- 设计统一多个类的接口。
- 替换依赖的外部系统。
- 兼容老版本接口。
- 适配不同格式的数据。
01.装饰者模式基础
给一个类或者对象增加行为:1.继承,子类通过重写父类方法修改行为。2.关联机制,将一个类的对象嵌入另一个对象中用于行为拓展。
装饰者模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰者模式适用于以下场景:
- 动态地给一个对象添加额外的功能,而不需要修改其原始类的代码。
- 需要扩展一个类的功能,但是使用继承会导致类的数量急剧增加。
- 需要在不影响其他对象的情况下,对对象的功能进行动态组合和排列。
02.装饰者模式实现
比如:有一个基本的咖啡对象,可以动态地添加不同的配料(如牛奶和糖)。在不改变对象情况下,对行为进行拓展!
- 咖啡A,加牛奶和半糖
- 咖啡B,加牛奶和全糖
- 咖啡C,加牛奶和多糖
- 咖啡D,不加牛奶和半糖
在装饰模式中的角色有:
- ● 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
- ● 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
- ● 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- ● 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
03.装饰者实例演示
半透明的装饰者模式是介于装饰者模式和适配器模式之间的。适配器模式的用意是改变所考虑的类的接口,也可以通过改写一个或几个方法,或增加新的方法来增强或改变所考虑的类的功能。大多数的装饰者模式实际上是半透明的装饰者模式,这样的装饰者模式也称做半装饰、半适配器模式。
04.装饰器模式场景
装饰者模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。
IO流中装饰者模式结构如下所示:
- 抽象构件(Component)角色:由InputStream扮演。这是一个抽象类,为各种子类型提供统一的接口。
- 具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。
- 抽象装饰(Decorator)角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。
- 具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。
05.装饰器模式分析
01.外观模式基础
外观模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,外观模式被引入。
定义是:门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
主要解决的问题
- 降低客户端与复杂子系统之间的耦合度。
- 简化客户端对复杂系统的操作,隐藏内部实现细节。
02.外观模式实现
假设有一个电脑系统,包含了多个子系统,如音乐功能、视频功能和联网功能。可以打开或者关闭某功能。
实现方式
- 创建外观类:定义一个类(外观),作为客户端与子系统之间的中介。提供Facade接口,抽象出通用方法打开和关闭。
- 封装子系统操作:外观类将复杂的子系统操作封装成简单的方法。三个子系统类(Subsystem):音乐功能、视频功能和联网功能
比如该案例中:
- 针对音乐,视频,联网抽象出通用功能,定义成接口【打开和关闭】。充分体现出面向对象中抽象的设计思想!
- 针对音乐,视频,联网各个子系统之间的交互,将他们的逻辑封装到外观类中,充分体现出封装的原则!
03.外观实例演示
有个需求:我们知道在UI开发中,可以绘制很多图像。比如可以绘制圆形,绘制矩形,绘制椭圆形,绘制……来达到绘制显示的效果。
05.外观模式分析
外观模式优点:1.简化接口;2.降低耦合;3.提高灵活性;4.方便维护
外观模式缺点:1.当添加新的子类系统,可能需要修改外观类,或者破坏开闭原则;
使用建议:建议1不要通过外观类给子类系统加入新的行为
01.桥接模式基础
桥接模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,桥接模式被引入。
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
主要解决的问题:桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展。
02.桥接模式实现
假设有一个几何形状Shape类,从它能扩展出两个子类:圆形Circle和方形Square。对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色Red和蓝色Blue的形状子类。
桥接模式实现如下所示:
- 创建一个抽象类 Shape,它有一个抽象方法 draw()。
- 抽象化角色的子类,创建两个实现了 Shape 接口的具体类:Circle 和 Square。
- 创建一个桥接类 Color,它也实现了 Shape 接口,并持有一个 Shape 类型的引用。
- 在主函数中测试这个桥接模式。
03.桥接实例演示
如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度,包括:支付渠道和支付方式。
不使用设计模式来模拟实现不同模式的支付场景。不使用设计模式缺点:维护和扩展都会变得非常复杂,需要修改原来代码,风险较大。
以后若增加支付渠道或修改支付方式,比如增加了京东支付,抖音支付,或者增加一个微信刷掌支付,则成本比较高,不利于后边的扩展和维护。
桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。
01.组合模式基础
组合模式定义 :将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
主要解决的问题 :1.简化树形结构中对象的处理,无论它们是单个对象还是组合对象。2.解耦客户端代码与复杂元素的内部结构,使得客户端可以统一处理所有类型的节点。
02.组合模式实现
组合模式包含如下角色:
- 组件(Component): 定义了组合中所有对象的通用接口,可以是抽象类或接口。它声明了用于访问和管理子组件的方法,包括添加、删除、获取子组件等。
- 叶子节点(Leaf): 表示组合中的叶子节点对象,叶子节点没有子节点。它实现了组件接口的方法,但通常不包含子组件。
- 复合节点(Composite): 表示组合中的复合对象,复合节点可以包含子节点,可以是叶子节点,也可以是其他复合节点。它实现了组件接口的方法,包括管理子组件的方法。
- 客户端(Client): 通过组件接口与组合结构进行交互,客户端不需要区分叶子节点和复合节点,可以一致地对待整体和部分。
04.组合实现方式
组合模式分为透明式的组合模式和安全式的组合模式。这两种类型的主要区别在于抽象构件(Component)角色上的差别。
- 透明式的组合模式:在透明式的组合模式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。
- 安全式的组合模式:在安全式的组合模式中,将管理子构件的方法移到树枝构件中,抽象构件只定义树枝构件和树叶构件所共同的方法。避免了透明式的组合模式的空实现或抛异常问题。
01.享元模式基础介绍
享元模式由来,主要是解决对象太多创建的问题:
- 享元模式通过共享技术实现相同或相似对象的重用。
- 在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
**享元模式(Flyweight Pattern)**的定义:
运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。
工作中遇到场景:缓存,字符串常量池,线程池,连接池。