2933 字
15 分钟
设计模式笔记(七大原则、创建型模式)
2025-10-23
无标签

设计模式#

七大原则#

开闭原则

对于修改关闭,对扩展开放

我们现在有一个Human接口,有老师和学生对于Human的实现,后续如果需要拓展学生和老师,例如初中老师,高中学生,就可以继承于老师或者学生类,继承默认的学生、老师方法,有需要修改的直接重写方法或者增添方法即可。而无需修改老师和学生的实现类

依赖倒转原则

依赖倒转是开闭原则的基础,针对于接口进行编程

依赖抽象而不是具体的实现,可以减少类的耦合性,提升稳定性,提高代码的可读性和维护性,降低修改程序造成的风险

单一原则

一个类、接口、方法、只负责一项职责

降低程序复杂性,提高了可维护性,降低了变更带来的风险

例如我们有一个Java程序员继承自程序员抽象类,其中实现了吃饭的抽象方法,我们现在要改为点外卖,我们最好创建一个点外卖的吃饭类,其中包含了堂食和外卖两种方法,将点饭类放入程序员类中,由Java程序员来进行调用

接口隔离原则

用多个接口,而不是使用单一接口

尽量细化接口,其中的方法尽可能减少

符合低耦合设计思想,提高了可拓展性和可维护性

迪米特法则

最少知道原则:一个对象应该对于其他对象保持最少的了解,降低类和类之间的耦合性,强调之和朋友交流,不和陌生人说话

里氏替换原则

是继承复用的基石,也是对于开闭原则的补充

子类可以拓展父类的功能,但是不能修改父类原有的功能,

子类可以实现父类的抽象方法,但是不能覆盖原有的父类方法

对于子类的继承关系进行约束

增加程序健壮性

合成复用原则

尽量使用组合聚合的方式进行对象的使用,而不是使用继承关系达到软件复用的目的

可以使系统更加灵活,降低类与类之间的耦合度

设计模式#

设计模式的类型分为三种

创建型模式

隐藏了创建对象的过程,通过逻辑方法创建对象,而不是new关键字

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

结构型模式

主要关注类和对象的组合关系,集成的概念被用于组合接口和定义组合对象,从而获得新功能方式

适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式

行为型模式

主要关注对象之间的通信

责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式

工厂模式#

简单工厂#

是工厂方法模式的一种特殊实现,又称为静态工厂方法模式

有三个角色:抽象产品,具体产品,工厂类

具体产品

public class Fighter extends Weapon{
    @Override
    public void attack() {
        System.out.println("飞机攻击");
    }
}
public class Tank extends Weapon{
    @Override
    public void attack() {
        System.out.println("坦克攻击");
    }
}

抽象产品

public abstract class Weapon {
    public abstract void attack();
}

工厂类

public class WeaponFactory {
    public static Weapon get(String weaponType) {
        if ("Tank".equals(weaponType)) {
            return new Tank();
        } else if ("Fighter".equals(weaponType)) {
            return new Fighter();
        } else {
            System.out.println("创建失败");
            return null;
        }
    }
}

测试程序

public class Test {
    public static void main(String[] args) {
        Weapon tank = WeaponFactory.get("Tank");
        if (tank != null) {
            tank.attack();
        }
    }
}

优点:

客户只负责消费,工厂类负责生产,生产者和消费者分离,客户端不需要关注实现细节,只负责传入索要即可使用

缺点:

增加工厂产品,需要修改工厂类代码,违反OCP原则,另外工厂的责任大,发生问题的时候系统瘫痪,健壮性差

工厂方法模式#

一个产品对应一个工厂

解决了简单工厂模式中出现的OCP问题

有四个角色:抽象产品,具体产品(多个),抽象工厂类,具体工厂类(多个)

抽象工厂

public abstract class WeaponFactory {
    public abstract Weapon get();
}

具体工厂

public class TankFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return new Tank();
    }
}

测试程序

public class Test {
    public static void main(String[] args) {
        WeaponFactory factory = new TankFactory();
        Weapon tank = factory.get();
        tank.attack();
    }
}

添加新的产品的时候,只需要增加Weapon的实现类,以及对应武器的工厂实现类即可在不影响原有代码的基础上,通过对应的具体工厂创建武器的实例

缺点:类的复杂性增加,类的数量大

抽象工厂#

抽象工厂提供了一系列相关或相互依赖对象的接口

定义按钮和文本框的通用接口

interface Button {
    void render();
    void onClick();
}

interface TextBox {
    void render();
    void input(String text);
}

实现具体产品,例如Windows风格的按钮和文本框

class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("Windows 风格按钮渲染");
    }
    @Override
    public void onClick() {
        System.out.println("Windows 按钮点击");
    }
}

class WindowsTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("Windows 风格文本框渲染");
    }
    @Override
    public void input(String text) {
        System.out.println("Windows 文本框输入: " + text);
    }
}

定义一个抽象工厂接口,其中规定了返回两个组件接口的方法

interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}

继承接口实现方法,返回Window类型的各个组件

class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
    @Override
    public TextBox createTextBox() {
        return new WindowsTextBox();
    }
}

这样通过这个实现抽象工厂的Windows工厂实例去创建对应的各个组件了

定义客户端

我们对于这个工程再次进行封装,提取出抽象工厂作为接口,这样我们在传入工厂实例的时候就可以通过实例初始化各个组件了

public class Application {
    private final Button button;
    private final TextBox textBox;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        textBox = factory.createTextBox();
    }

    public void renderUI() {
        button.render();
        textBox.render();
    }

    public void simulateUserInput() {
        button.onClick();
        textBox.input("Hello, Abstract Factory!");
    }

    public static void main(String[] args) {
        // 使用 Windows 风格
        Application windowsApp = new Application(new WindowsFactory());
        windowsApp.renderUI();
        windowsApp.simulateUserInput();

}

后续我们扩展新的风格只需要:

1.创建Button和TextBox的实现类,2.新风格的工厂实例

在客户端中传入工厂实例即可

建造者模式#

通过静态内部类Builder链式的设置其各个属性,通过build方法返回实例对象

public class Student {
    private String name;
    private int age;
    public Student(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }
    public static class Builder{
        private String name;
        private int age;
        public Builder name(String name){
            this.name = name;
            return this;
        }
        public Builder age(int age){
            this.age = age;
            return this;
        }
        public Student build(){
            return new Student(this);
        }
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Main {
    public static void main(String[] args) {
        Student lory = new Student.Builder().
                age(18).
                name("lory").
                build();
        System.out.println(lory);
    }
}

将复杂对象的创建和属性分离,建造的过程和细节我们不需要知道,只需要通过构建者进行创建

原型模式#

用于创建重复的对象,我们需要保证创建对象的性能

原型设计模式是创建对象的最佳方式,通过克隆现有对象来创建新的对象,避免重复初始化成本,适合用于创建开销大,或需要动态配置对象的场景

public class Pig {
    private String name;
    private String ability;

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", ability='" + ability + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAbility() {
        return ability;
    }

    public void setAbility(String ability) {
        this.ability = ability;
    }
}

每一次创建新的对象都要重新new对象,设置参数

public class Main {
    public static void main(String[] args) {
        Pig pig1 = new Pig();
        pig1.setName("佩奇");
        pig1.setAbility("睡觉");
        System.out.println(pig1);
        Pig pig2 = new Pig();
        pig2.setName("乔治");
        pig2.setAbility("吃饭");
        System.out.println(pig2);
    }
}

现在我们通过原型设计模式对于这个类进行改造

实现Cloneable接口并实现其中方法

package com;

public class Pig implements Cloneable {
    private String name;
    private String ability;

    @Override
    protected Pig clone() {
        try {
            return (Pig) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", ability='" + ability + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAbility() {
        return ability;
    }

    public void setAbility(String ability) {
        this.ability = ability;
    }
}

使用原先的pig,clone浅拷贝一份对象出来

public class Main {
    public static void main(String[] args) {
        Pig pig1 = new Pig();
        pig1.setName("佩奇");
        pig1.setAbility("睡觉");
        System.out.println(pig1);
        Pig pig2 = pig1.clone();
//        Pig pig2 = new Pig();
        pig2.setName("乔治");
        pig2.setAbility("吃饭");
        System.out.println(pig2);
    }
}
NOTE

浅拷贝和深拷贝

场景推荐方式
对象的所有字段是基本类型不可变类(如 StringInteger浅拷贝(安全)
对象包含可变引用类型(如 ListMap、自定义类)深拷贝(避免共享问题)
需要极高的性能,且确定引用字段不会被修改浅拷贝(谨慎使用)

单例模式#

双重检查#

通过volatile和两次为null判断和synchronized关键字实现

public class Person {
    private static volatile Person person = null;

    private Person() {}

    public static Person getInstance() {
        if (person == null) {
            synchronized (Person.class) {
                if (person == null) {
                    person = new Person();
                }
            }
        }
        return person;
    }
}

volatile 的必要性

person = new Person();不是原子操作,它分为了三个部分

1.分配内存空间

2.初始化对象

3.将内存地址赋给person

如果没有进行volatile那么JMV可能会继续指令重排(1.3.2),导致其他线程没有拿到初始化的对象

确保一个线程对person的修改,对其他线程立即可见

静态内部类#

通过在单例类中,通过私有的静态内部类,创建单例对象

public class Person {

    private static class Inner {
        private static final Person person = new Person();
    }

    private Person() {}

    public static Person getInstance() {
        return Inner.person;
    }
}

这里使用Person中的Inner内部类的Inner.person时候才会进行加载Inner类,实现了懒加载

饿汉式#

public class Person {
    private static final Person person;
    static {
        person = new Person();
    }

    private Person() {}

    public static Person getInstance() {
        return person;
    }
}

枚举#

枚举方式实现单例模式,是最佳的实现方式,可以有效防止对于单例模式的破坏

public enum Person {
    INSTANCE;
    public static Person getInstance() {
        return INSTANCE;
    }
}
public enum Person {
    INSTANCE;

    private String name;

    // 枚举构造器(天生 private,不可 public/protected)
    Person() {
        this.name = "Default";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// 使用
Person.INSTANCE.setName("Alice");
System.out.println(Person.INSTANCE.getName()); // 输出 "Alice"

单例破坏#

序列化破坏

public class Main {
    public static void main(String[] args) throws Exception{
        Person p1 = Person.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sin"));
        oos.writeObject(p1);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("sin"));
        Person p2 = (Person) ois.readObject();
        System.out.println(p1.hashCode() + "\n" + p2.hashCode());
    }
}
  • Java 在反序列化 enum 时,不会调用构造方法,而是 直接返回 JVM 维护的唯一实例(类似 readResolve() 的逻辑)。
  • enum 的序列化仅存储 name,反序列化时根据 name 查找已有实例(Enum.valueOf

反射破坏

public class Main {
    public static void main(String[] args) throws Exception{
        Person p1 = Person.getInstance();
        Constructor<Person> constructor = Person.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Person p2 = constructor.newInstance();
        System.out.println(p1.hashCode() + "\n" + p2.hashCode());
    }
}

Java 不允许通过反射创建 enum 实例

  • enum 的构造器默认 private,且 JVM 进行严格限制
  • 即使 反射强行调用 enum 的构造器,也会抛出 IllegalArgumentException

克隆破坏

Person p1 = Person.getInstance();
Person p2 = p1.clone();  // ❌ 创建新对象,破坏单例

enum类的clone方法由final修饰,禁止重写

设计模式笔记(七大原则、创建型模式)
https://thrinisty.github.io/Blog/posts/设计模式笔记七大原则创建型模式/
作者
Thrinisty
发布于
2025-10-23
许可协议
CC BY-NC-SA 4.0