Java设计模式:常用模式与应用

好嘞,各位观众,各位亲爱的码农朋友们,欢迎来到“Java设计模式:常用模式与应用”的脱口秀现场!我是你们的老朋友,码界段子手,今天咱们不聊八卦,只聊码,聊那些让你的代码像诗一样优雅的设计模式!

准备好了吗?让我们一起揭开设计模式的神秘面纱,让你的代码从此告别“屎山”的称号,走向高内聚、低耦合的康庄大道!🥳

开场白:代码的艺术与架构的灵魂

各位,写代码就像画画,有人画出来的是抽象派,让人看了半天不知道画的是啥;有人画出来的是印象派,朦朦胧胧,感觉还不错,但细节经不起推敲;还有人画出来的是写实派,栩栩如生,让人叹为观止!

而设计模式,就是那本教你如何画出写实派代码的秘籍!它不是让你照搬照抄,而是教你理解代码的本质,掌握架构的灵魂,让你的代码不仅能跑,还能跑得漂亮,跑得持久!

第一幕:单例模式 (Singleton Pattern) – 独一无二的“皇帝”

想象一下,你的系统中需要一个全局唯一的配置对象,或者一个数据库连接池。如果每次需要都创建一个新的,那资源岂不是要爆炸?💥 这时候,单例模式就闪亮登场了!

单例模式就像古代的皇帝,只能有一个,拥有至高无上的权力。它保证一个类只有一个实例,并提供一个全局访问点。

核心思想:

  • 私有构造器: 杜绝外部直接创建实例。
  • 静态实例: 内部维护一个静态的实例。
  • 静态方法: 提供一个全局访问该实例的方法。

代码示例 (懒汉式):

public class Singleton {

    private static Singleton instance; // 静态实例

    private Singleton() { // 私有构造器
        // 防止反射攻击,也可以在这里进行一些初始化操作
        if (instance != null) {
            throw new IllegalStateException("Singleton already initialized.");
        }
    }

    public static synchronized Singleton getInstance() { // 静态方法
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void doSomething() {
        System.out.println("Singleton is doing something...");
    }
}

优点:

  • 节省资源,避免重复创建实例。
  • 全局唯一访问点,方便管理和控制。

缺点:

  • 线程安全问题 (懒汉式需要加锁)。
  • 不易扩展。

应用场景:

  • 配置管理
  • 数据库连接池
  • 线程池
  • 日志记录器

第二幕:工厂模式 (Factory Pattern) – 批量生产的“流水线”

如果你需要创建多个相似的对象,但是它们的创建逻辑又不一样,难道你要写一堆 if-else 或者 switch-case 吗?🤯 这也太low了吧!这时候,工厂模式就来拯救你了!

工厂模式就像一个流水线,你只需要告诉它你需要什么产品,它就会帮你生产出来,而你不需要关心它是怎么生产的。

核心思想:

  • 定义一个工厂接口,用于创建对象。
  • 创建具体的工厂类,实现工厂接口,负责具体的对象创建逻辑。

代码示例 (简单工厂):

// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using Product A...");
    }
}

// 具体产品B
class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using Product B...");
    }
}

// 工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("Invalid product type.");
        }
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        productA.use(); // 输出: Using Product A...

        Product productB = SimpleFactory.createProduct("B");
        productB.use(); // 输出: Using Product B...
    }
}

优点:

  • 解耦:将对象的创建和使用分离。
  • 可扩展:易于添加新的产品类型。
  • 代码清晰:避免大量的 if-elseswitch-case

缺点:

  • 工厂类职责过重,违反单一职责原则(简单工厂)。
  • 增加代码复杂度。

应用场景:

  • 创建不同类型的对象。
  • 隐藏对象的创建逻辑。
  • 解耦对象的创建和使用。

第三幕:观察者模式 (Observer Pattern) – “订阅”你的世界

假设你订阅了一个公众号,一旦公众号发布了新的文章,你就会收到通知。这就是观察者模式的典型应用!

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生改变时,所有依赖它的观察者都会收到通知并更新自己。

核心思想:

  • 主题 (Subject): 维护一个观察者列表,并提供添加、删除观察者的方法。
  • 观察者 (Observer): 定义一个更新接口,当主题状态改变时,会调用该接口。
  • 具体主题 (ConcreteSubject): 维护主题状态,并在状态改变时通知观察者。
  • 具体观察者 (ConcreteObserver): 实现观察者接口,接收主题的通知并更新自己。

代码示例:

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体观察者
class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

// 主题接口
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers(String message);
}

// 具体主题
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    public void publishMessage(String message) {
        System.out.println("Subject published message: " + message);
        notifyObservers(message);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2");

        subject.attach(observer1);
        subject.attach(observer2);

        subject.publishMessage("Hello, observers!");
        // 输出:
        // Subject published message: Hello, observers!
        // Observer 1 received message: Hello, observers!
        // Observer 2 received message: Hello, observers!

        subject.detach(observer1);

        subject.publishMessage("Goodbye, observer 1!");
        // 输出:
        // Subject published message: Goodbye, observer 1!
        // Observer 2 received message: Goodbye, observer 1!
    }
}

优点:

  • 解耦:主题和观察者之间解耦,可以独立变化。
  • 可扩展:易于添加新的观察者。
  • 灵活:可以实现广播式的通知。

缺点:

  • 如果观察者过多,可能会影响性能。
  • 主题和观察者之间的依赖关系可能导致循环依赖。

应用场景:

  • 事件处理
  • 消息队列
  • GUI编程 (按钮点击事件)
  • 发布/订阅系统

第四幕:策略模式 (Strategy Pattern) – 算法界的“变形金刚”

想象一下,你需要实现一个排序算法,但是你有很多种排序算法可以选择,比如冒泡排序、快速排序、归并排序等等。难道你要写一堆 if-else 来判断使用哪种算法吗?😱 太麻烦了!这时候,策略模式就派上用场了!

策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

核心思想:

  • 策略接口 (Strategy): 定义一个算法接口。
  • 具体策略 (ConcreteStrategy): 实现策略接口,提供具体的算法实现。
  • 上下文 (Context): 维护一个策略对象,并提供一个执行算法的方法。

代码示例:

// 策略接口
interface SortingStrategy {
    void sort(int[] array);
}

// 具体策略 - 冒泡排序
class BubbleSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Bubble Sort...");
        // 冒泡排序算法实现
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

// 具体策略 - 快速排序
class QuickSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Quick Sort...");
        // 快速排序算法实现
        quickSort(array, 0, array.length - 1);
    }

    private void quickSort(int[] array, int low, int high) {
        if (low < high) {
            int pi = partition(array, low, high);
            quickSort(array, low, pi - 1);
            quickSort(array, pi + 1, high);
        }
    }

    private int partition(int[] array, int low, int high) {
        int pivot = array[high];
        int i = (low - 1);
        for (int j = low; j <= high - 1; j++) {
            if (array[j] < pivot) {
                i++;
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        int temp = array[i + 1];
        array[i + 1] = array[high];
        array[high] = temp;
        return (i + 1);
    }
}

// 上下文
class Sorter {
    private SortingStrategy strategy;

    public Sorter(SortingStrategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy;
    }

    public void sortArray(int[] array) {
        strategy.sort(array);
    }

    public void printArray(int[] array) {
        System.out.println(Arrays.toString(array));
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        int[] array = {5, 2, 8, 1, 9, 4};

        Sorter sorter = new Sorter(new BubbleSortStrategy());
        sorter.sortArray(array.clone());
        sorter.printArray(array.clone()); // Output: [1, 2, 4, 5, 8, 9]

        sorter.setStrategy(new QuickSortStrategy());
        sorter.sortArray(array.clone());
        sorter.printArray(array.clone()); // Output: [1, 2, 4, 5, 8, 9]
    }
}

优点:

  • 算法可替换:可以灵活地选择不同的算法。
  • 可扩展:易于添加新的算法。
  • 代码清晰:避免大量的 if-elseswitch-case

缺点:

  • 客户端需要知道所有策略。
  • 增加代码复杂度。

应用场景:

  • 支付方式选择 (支付宝、微信支付、银行卡支付)
  • 图片压缩算法选择 (JPEG、PNG、GIF)
  • 数据校验方式选择 (正则表达式、自定义校验)

第五幕:模板方法模式 (Template Method Pattern) – 写好的“剧本”

有时候,你需要定义一个算法的骨架,而将一些步骤延迟到子类中去实现。这时候,模板方法模式就非常适合你!

模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

核心思想:

  • 抽象类 (AbstractClass): 定义一个模板方法,该方法定义了算法的骨架,并调用一些抽象方法和具体方法。
  • 具体类 (ConcreteClass): 实现抽象方法,提供具体的算法步骤实现。

代码示例:

// 抽象类
abstract class AbstractClass {
    // 模板方法
    public void templateMethod() {
        step1();
        step2();
        step3();
        hook(); // 钩子方法
    }

    // 抽象方法,由子类实现
    protected abstract void step1();
    protected abstract void step2();

    // 具体方法,子类可以继承或重写
    protected void step3() {
        System.out.println("AbstractClass: Step 3");
    }

    // 钩子方法,子类可以选择性地实现
    protected void hook() {
        // Do nothing by default
    }
}

// 具体类A
class ConcreteClassA extends AbstractClass {
    @Override
    protected void step1() {
        System.out.println("ConcreteClassA: Step 1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClassA: Step 2");
    }
}

// 具体类B
class ConcreteClassB extends AbstractClass {
    @Override
    protected void step1() {
        System.out.println("ConcreteClassB: Step 1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClassB: Step 2");
    }

    @Override
    protected void hook() {
        System.out.println("ConcreteClassB: Hook");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        AbstractClass classA = new ConcreteClassA();
        classA.templateMethod();
        // 输出:
        // ConcreteClassA: Step 1
        // ConcreteClassA: Step 2
        // AbstractClass: Step 3

        AbstractClass classB = new ConcreteClassB();
        classB.templateMethod();
        // 输出:
        // ConcreteClassB: Step 1
        // ConcreteClassB: Step 2
        // AbstractClass: Step 3
        // ConcreteClassB: Hook
    }
}

优点:

  • 代码复用:将通用的算法骨架放在抽象类中。
  • 可扩展:子类可以灵活地实现算法的特定步骤。
  • 控制反转:父类控制算法的执行顺序。

缺点:

  • 增加了代码复杂度。
  • 抽象类和具体类之间存在耦合关系。

应用场景:

  • 数据库访问 (连接数据库、执行SQL、关闭连接)
  • HTTP请求处理 (建立连接、发送请求、接收响应、关闭连接)
  • 测试框架 (setUp、test、tearDown)

尾声:设计模式,代码的诗与远方

好了,各位观众,今天的“Java设计模式:常用模式与应用”脱口秀就到这里了。希望通过今天的讲解,大家能够对设计模式有更深入的了解,并在实际开发中灵活运用它们,让你的代码更加优雅、健壮、易于维护!

记住,设计模式不是银弹,不要为了用而用。只有在合适的场景下使用合适的设计模式,才能真正发挥它的威力!

最后,祝大家编码愉快,早日成为码界的大艺术家!再见!👋

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注