设计模式

软件设计原则

开闭原则

对扩展开放,对修改关闭
适合用抽象类和接口实现

里氏代换原则

  • 任何基类出现的地方,子类一定可以出现
  • 子类可扩展但不能改变父类原有功能,就是尽量不重写父类方法

反例
假设有个长方形类,正方形类继承长方形类,同时自己内部重写了长和宽的 setter 方法,之后假设有个 resize 方法拓宽长方形,长方形实例没
问题,但是正方形实例 resize 方法就会同时拓宽长和宽,违反里氏代换原则
解决方法
定义一个四边形接口,正方形类和长方形类都实现这个接口,这样长方形和正方形就没有继承关系了,符合里氏代换原则

依赖倒置原则

  • 高层模块不应该依赖低层模块,两者都应该依赖抽象
  • 抽象不应该依赖细节,细节应该依赖抽象(细节就是具体实现类)

反例
定义一个电脑类,电脑有 CPU,内存,硬盘等,电脑直接依赖希捷硬盘,英特尔 CPU,金士顿内存条,之后如果用别的牌子的设备就要对电脑类的 setter 方法里面的类 进行修改
解决方法
定义 CPU,内存,硬盘等接口,所有牌子的设备实现对应种类的接口,电脑类的 setter 方法传入接口,这样电脑类就只依赖抽象,不依赖具体实现类,符合依赖倒置原则

接口隔离原则

  • 客户端不应该被迫依赖他不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上
  • 就假设一个类实现了接口,接口有方法 1 和方法 2,但这个类并不需要方法 2,所以应该给两个方法分别都写成接口,类需要哪个就实现哪个接口

反例
假设有个安全门接口,有三个功能防盗、防火、防水,A 类安全门功能都需要直接实现就行,但是 B 类安全门只需要防盗防火功能,他不需要实现防水,被迫依赖了
解决方法
给三个功能全抽成接口,A 类安全门实现三个接口,B 类安全门只实现两个接口

迪米特法则(最少知识原则)

只与你的直接朋友交谈,不和陌生人说活
其含义是:如果两个软件实体无需直接通信,那么就不应当发生直接的相互调用,而是通过第三方转发

例子
假设有明星类、粉丝类、公司类
建立一个经纪人类。前三个类都是经纪人类的成员变量,分别有对这三个类的方法,就能降低耦合度

合成复用原则

指尽量使用组合、聚合的方式,其次才考虑继承
组合和聚合就是使用成员变量的方式
组合是强依赖,由整体对象作为部分创建出来,整体对象销毁时也随之销毁(独占)
聚合是弱依赖,由外部创建后引入进来(共享)

单一职责原则

一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分

创建者模式

单例模式

该类负责创建自己的对象,同时确保只有单个对象被创建,并提供一个访问其唯一对象的方式

单例设计模式分类两种:

饿汉式:类加载的时候创建对象
懒汉式:类加载的时候不创建对象,而是第一次调用的时候创建对象

饿汉式单例实现方式

  • 静态成员变量
    • private Singleton () {};// 私有构造方法,不写的话会用默认构造器,默认是 public 的
    • private static Singleton instance = new Singleton();
  • 静态代码块
    • private Singleton () {};
    • private static Singleton instance;
    • static {instance = new Singleton();}

懒汉式单例实现方式

懒汉式实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 私有构造方法,防止外部调用构造器创建对象
// 必须显示写出来,不然会用默认构造器,默认是 public 的,能被外部调用
private Singleton () {};

// 声明Singleton类型的变量instance
private static Singleton instance;// 只是声明没有赋值

// 对外提供访问方式,用synchronized修饰,保证多线程安全
// 用static修饰,使这个方法为静态的,属于这个类本身而不需要new 对象来调用
public static synchronized Singleton getInstance() {
// 判断instance是否为空,如果为null,说明还没有创建过对象
// 如果没有就创建一个对象并返回,如果有直接返回
if (instance == null) {
instance = new Singleton();
}
return instance;
}

双重检查锁方式
读不加锁提升性能
但是有可能出现空指针问题,因为 JVM 的优化和指令重排序
用 volatile 修饰单例,保证可见性和有序性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 私有构造方法
private Singleton () {};

// volatile修饰确保可见性和有序性
private static volatile Singleton instance;

public static Singleton getInstance() {
// 第一次判断,如果instance不为null,不需要抢占锁,直接返回
if (instance != null) {
synchronized (Singleton.class) {
// 第二次判断
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

静态内部类方式(常用)
由于 JVM 在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只实施例换一次,并严格保证实例化顺序
在没加锁的情况下保证线程安全,并且没有任何性能影响和空间浪费

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 私有构造方法
private Singleton () {};

// 定义一个静态内部类
private static class SingletonHolder {
// 在内部类中声明并初始化外部类的对象
// 用final修饰,防止被修改,提高可读性
// final 修饰的字段必须在声明时或构造函数中初始化,所以之前的懒汉式不能用
// // 只会在类加载的时候new一次,后续直接返回
private static final Singleton INSTANCE = new Singleton();
}

// 提供公共的访问方法
public static class SingletonHolder {
// 静态内部类只有在被调用的时候才会被加载一次
// 静态内部类本身就是懒加载,所以静态内部类中的字段也是懒加载
return SingletonHolder.INSTANCE;
}

枚举方式(最推荐)(属于饿汉式,在不考虑内存浪费的时候最推荐)
线程安全,而且只会加载一次,而且是所有单例实现里面唯一不会被破坏的

1
2
3
public enum Singleton {
INSTANCE;// 这就完事了
}

设计模式
http://www.981928.xyz/2025/12/08/设计模式/
作者
981928
发布于
2025年12月8日
许可协议