设计模式笔记

1.类与类之间的关系

依赖,泛化,实现,关联,聚合,组合

1.1依赖关系(Dependence/Dependency)

只要是在类中用到了对方,那么他们之间就存在依赖关系,例如方法的返回类型,方法的参数类型,在方法中使用到的符号(虚箭头)。

1.2泛化关系(Ganeralization)

泛化关系也就是继承关系符号(带三角形的实线):

1.3实现关系(Realization)

实现关系就是依赖关系的特例,表示一个类实现了一个接口(interface)中定义的所有抽象方法。

1.4关联关系(Association)

类与类之间的关系,依赖关系的一种特例,通常通过成员变量来实现这种关系。

1.5聚合关系

关联关系的一种,聚合关系是整体和个体的关系,例如键盘、鼠标和键盘的关系。即聚合关系在关联关系的基础上,成员变量还是类的一部分。但是成员变量还可以独立访问。

1.6组合关系

关联关系的一种,整体和部分不能分开。和聚合关系的区别就在这里。

2.七大原则

2.1单一职责原则

  • 降低类的复杂度,一个类只负责一项职责
  • 提高类的可读性,可维护性
  • 降低更变引起的风险
  • 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以违反单一职责原则,只有类中方法数量足够少,可以在方法级别保持单一职责原则
java 复制代码
// 错误:一个类做两件事
class UserManager {
    void addUser(String name) { 
        System.out.println("添加用户:" + name); 
    }
    void sendEmail(String email) { // ❌ 发送邮件是另一件事
        System.out.println("发邮件到:" + email);
    }
}

// 正确:拆分成两个类
class UserService { // 只负责用户管理
    void addUser(String name) { 
        System.out.println("添加用户:" + name); 
    }
}

class EmailService { // 只负责邮件
    void sendEmail(String email) { 
        System.out.println("发邮件到:" + email);
    }
}

2.2接口隔离原则

  • 客户端不应该依赖他不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
java 复制代码
// 错误:胖接口
interface Worker {
    void work();
    void eat();
    void code(); // 工人不需要会写代码
}

class Programmer implements Worker { // 必须实现所有方法
    public void work() { System.out.println("工作"); }
    public void eat() { System.out.println("吃饭"); }
    public void code() { System.out.println("写代码"); }
}

class Chef implements Worker { // 厨师也要实现code(),不合理!
    public void work() { System.out.println("做菜"); }
    public void eat() { System.out.println("吃饭"); }
    public void code() { throw new UnsupportedOperationException(); } // 厨师不会写代码
}

// 正确:拆分接口
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Codable { void code(); }

class GoodProgrammer implements Workable, Eatable, Codable { // 程序员全要
    public void work() { System.out.println("工作"); }
    public void eat() { System.out.println("吃饭"); }
    public void code() { System.out.println("写代码"); }
}

class GoodChef implements Workable, Eatable { // 厨师不需要会写代码
    public void work() { System.out.println("做菜"); }
    public void eat() { System.out.println("吃饭"); }
}

2.3依赖倒转原则

高层模块不要直接依赖低层模块,都依赖抽象接口。

java 复制代码
// 错误的:高层依赖低层
class Dog {
    void bark() { System.out.println("汪汪"); }
}
class Person1 {
    private Dog dog = new Dog(); // 直接依赖具体的狗
    void callPet() { dog.bark(); }
}

// 正确的:都依赖抽象
interface Pet { void sound(); } // 抽象接口
class Dog2 implements Pet { public void sound() { System.out.println("汪汪"); } }
class Cat implements Pet { public void sound() { System.out.println("喵喵"); } }

class Person2 {
    private Pet pet; // 依赖抽象
    Person2(Pet pet) { this.pet = pet; } // 通过构造器注入
    void callPet() { pet.sound(); }
}

2.4里氏替换原则

  • 子类中尽量不要重写父类的方法
  • 在适当的情况下,可以通过聚合、组合、依赖代替继承
java 复制代码
// 错误的:子类改变了父类行为
class Rectangle {
    int width;
    int height;
    
    void setWidth(int w) { width = w; }
    void setHeight(int h) { height = h; }
    int getArea() { return width * height; }
}

class Square extends Rectangle { // 正方形继承长方形 ❌
    void setWidth(int w) { 
        width = w; 
        height = w; // 改变了父类的逻辑!
    }
    void setHeight(int h) {
        width = h;
        height = h; // 改变了父类的逻辑!
    }
}

// 正确:用接口或抽象类
interface Shape { // 抽象
    int getArea();
}

class GoodRectangle implements Shape {
    int width, height;
    public int getArea() { return width * height; }
}

class GoodSquare implements Shape {
    int side;
    public int getArea() { return side * side; }
}

2.5迪米特原则

  • 也叫最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管有多复杂,都尽量将逻辑封装在类的内部,对外除了提供的public方法,不对外泄露任何信息
java 复制代码
// 错误的:直接和陌生人说话
class A {
    B b = new B();
    C getC() { return b.getC(); } // A 通过 B 拿到了 C
    void doSomething() {
        getC().doWork(); // ❌ A 直接调用了 C 的方法,认识太多人了
    }
}

// 正确的:只和直接朋友说话
class GoodA {
    B b = new B();
    void doSomething() {
        b.doSomething(); // ✅ 我只告诉B做什么,不关心他怎么做的
    }
}

class GoodB {
    C c = new C();
    void doSomething() {
        c.doWork(); // B负责和C沟通
    }
}

class C {
    void doWork() { System.out.println("C在工作"); }
}

2.6合成复用原则

  • 尽量使用合成/聚合的方法,而不是使用继承
java 复制代码
// 错误的:过度使用继承
class Engine { void start() { System.out.println("引擎启动"); } }

class Car extends Engine { // ❌ 继承:汽车"是一个"引擎?不合理
    void drive() { 
        start(); // 继承引擎的方法
        System.out.println("汽车行驶");
    }
}

// 正确的:使用组合
class GoodCar {
    private Engine engine = new Engine(); // ✅ 组合:汽车"有一个"引擎
    
    void drive() {
        engine.start(); // 调用引擎的方法
        System.out.println("汽车行驶");
    }
    
    // 可以随时更换引擎
    void setEngine(Engine e) { this.engine = e; }
}

2.7开闭原则

当代码需要变化时,尽量通过扩展软件实体的行为来实现变换,而不是通过修改已有的代码来实现变化,简单来说,软件要像乐高积木一样,可以随意添加新功能,但不用修改原来的代码。

java 复制代码
// 扩展开放:新增支付方式不改原来的代码
interface Payment { // 抽象
    void pay();
}

class Alipay implements Payment { // 支付宝
    public void pay() { System.out.println("支付宝支付"); }
}

class WechatPay implements Payment { // 微信支付
    public void pay() { System.out.println("微信支付"); }
}

// 新增支付方式:不用改下面这个类
class BankPay implements Payment { // 新增银行支付
    public void pay() { System.out.println("银行支付"); }
}

class PaymentService {
    public void process(Payment payment) { // 依赖抽象
        payment.pay(); // 这里永远不用改
    }
}

3.单例模式

单例模式是一种创建型设计模式, 确保一个类只有一个实例, 并且自行实例化冰箱整个系统提供这个实例。

3.1懒汉式

java 复制代码
public class Singleton {
	private Singleton() {}
	private static Singleton instance = null;
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

减小项目内存开销,但是线程不安全。

java 复制代码
public class Singleton {
	private Singleton() {}
	private static Singleton instance = null;
	public static synchronized Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

线程安全,但是每次获取单例对象的时候,都需要进行synchronized判断,性能降低

3.2饿汉式

java 复制代码
public class Singleton {
	private Singleton() {}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance() {
		return instance;
	}
}

增加系统开销

3.3双检锁

java 复制代码
public class Singleton {
	private Singleton() {}
	private static Singleton instance = null;
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized(Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}				
			}
		}
		return instance;
	}
}

由于instance = new Singleton()在指令界面不是一个原子性操作,即先进行分配内存,再进行初始化对象,再将对象指向内存地址。由于jvm可能对指令进行重排序,会先进行初始化地址,再进行指令指向地址对象。此时另一个线程依然会创建Single对象。

java 复制代码
public class Singleton {
	private Singleton() {}
	private volatile static Singleton instance = null;
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized(Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}				
			}
		}
		return instance;
	}
}

推荐

3.4静态内部类

java 复制代码
public class Singleton {
	private static class SingleonHolder {
		private static final Singleton INSTANCE = new Singleton();
	}
	private Singleton() {}
	public static final Singleton getInstance() {
		return SingletonHolder.INSTANCE;
	}
}

能被反射破坏

3.5枚举类

java 复制代码
public enum Singleton {
	INSTANCE;
}
相关推荐
小林rr12 小时前
深入探索 C++:现代特性、工程实践与性能优化全解
java·c++·性能优化
专注数据的痴汉12 小时前
「数据获取」全国民用运输机场吞吐量排名(2006-2024)
java·大数据·服务器·数据库·信息可视化
悟空码字12 小时前
无缝集成指南,SpringBoot三步接入华为云短信服务
java·springboot·编程技术·后端开发·华为云短信
E_ICEBLUE13 小时前
【2026 最新教程】Java 自动化提取 PDF 表格:从文本到 Excel/CSV 的全场景实现
java·pdf·自动化
C雨后彩虹13 小时前
无向图染色
java·数据结构·算法·华为·面试
J_HelloWorld13 小时前
缺页中断:Java高性能存储的隐形推手
java·缺页中断
一代明君Kevin学长13 小时前
记录一个上手即用的Spring全局返回值&异常处理框架
java·网络·python·spring
悟空码字13 小时前
SpringBoot整合MyBatis-Flex保姆级教程,看完就能上手!
java·spring boot·后端
爬山算法13 小时前
Hibernate(43)Hibernate中的级联删除如何实现?
java·python·hibernate
J_liaty13 小时前
Java工程师的JVM入门教程:从零理解Java虚拟机
java·开发语言·jvm