设计模式
2025/10/9大约 5 分钟
设计模式
设计模式概述
设计模式:是前辈们对软件设计所遇到的问题和解决方案等经验的总结。是一些套路和模板。本质是面向对象原则设计原则的实际应用。
UML中的类图:用于描述类的内部构成和类之间的关系,系统分析和设计阶段的重要产物,是系统编码和测试的重要模式。
- 类的表示方法如下:
- 类和类之间的表示关系:
- 关联关系:一个类中包含了某个类,说明这两个类有关联,从包含类指向被包含类,实线实心三角箭头表示。
- 聚合关系:也是一种关联关系,表示一种类由另一种类聚合而成,由“大类”指向“小类”,比如学校指向老师,这里如果学校不存在了,但是老师依旧存在,使用实线空心菱形箭头表示。
- 组合关系:更强的一种关联关系,表示一种类由另一种类组合而成,由“大类”指向“小类”,比如头指向嘴,如果头不存在了嘴就一定也不存在了,使用实线实心菱形箭头表示。
- 依赖关系:表示某个类中某个方法依赖某个类,比如Driver的drive(Car car),需要依赖Car类,由Driver指向Car,使用虚线箭头表示
- 继承关系:比较好理解,使用实线空心三角箭头表示,由子类指向父类
- 实现关系:使用虚线空心三角箭头表示,由实现类指向接口
软件设计原则:
- 开闭原则:对扩展开放,对修改关闭。使用接口和抽象类实现比较好。
- 里氏替换原则:任何基类(父类)出现的地方,子类一定可以出现,子类可以扩展父类的功能,但是不能改变父类原有的功能,也就是说在该位置 子类能够完全替换父类(父类原有的功能没有改变)。子类继承父类时,除了添加新方法外,尽量不要重写父类的方法.
- 依赖倒置原则:高层模块不依赖于低层模块,高层模块和低层模块都依赖于抽象;抽象不依赖于细节,细节应该依赖于抽象。
- 接口隔离原则:客户端不应该被迫依赖于其不使用的方法(比如A类有方法1和方法2,B类继承了A类,但是B类只使用方法1,不需要方法2,则违背了接口隔离原则)
- 迪米特法则(最少知识原则):如果两个实体无须直接通信,那么不应当发生直接的相互调用,可以通过第三方转发该调用。
- 合成复用原则:优先考虑使用聚合或者组合等关联关系来实现,其次才考虑使用继承关系来实现(如果是继承复用会产生很多类,如果用聚合复用则不会产生多个子类)
- (单一职责原则?)
创建者模式
用于描述“怎么创建对象”,它的主要特点是“将对象的创建和使用分离”。五种:单例模式、原型模式、工厂方法模式、抽象工厂模式、建造者模式。
单例模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了唯一的对象访问方式,可以直接访问,不需要实例化该类对象。
单例的两种类
- 单例类:只能创建一个实例的类
- 访问类: 使用单例类
单例模式的两种实现
- 饿汉式:类加载时就会导致该实例对象被创建
实现方式:静态变量的方式、静态代码块方式、枚举(枚举最简单是线程安全的且只会装载一次,JVM底层实现不会被破坏)
因为类加载时就会创建对象,所以不管你用不用该对象,都会存在于内存中,导致内存浪费。 - 懒汉式:类加载不会导致该实例对象被创建,而是首次使用该对象时创建。
实现方式:双重检查锁,静态内部类。
在getInstance方法中实例化对象,这样就有一个问题,在写操作时如果有大量请求,不加锁可能会导致实例多个对象,可以给方法加上synchronized关键字。但是实际上大部分操作是读操作,给方法加锁会导致性能低下,那么我们可以优化为双重检查锁。双重检查锁要使用volatile关键字,防止JVM指令重排出现空指针。
存在的问题 - 破坏单例模式(除了枚举):
序列化反序列化
使用输入流读取并序列化多个对象,这些对象不是同一个
解决方法:在单例类中写readResolve()方法,反序列化时会自动调用该方法,将该方法的返回值直接返回反射
创建Class和Constructor对象,通过newInstance()方法反射创建的对象也不是同一个对象
结构型模式
用于描述如何将类和对象通过一种布局组成更大的结构。七种:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式。
行为型模式
用于描述类和对象之间怎样相互协作共同完成单个对象无法完成的任务,以及如何分配职责。十一种:模板方法模式、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式。