设计模式该怎么说

前言

设计模式算是高频的面试题了,基本在工作的前几年,只要想说设计,必定会谈到设计模式。我理解的原因是业务设计通常难以描述,普通CURD开发也很难有设计亮点,大部分情况下更是难以参与亮点。技术设计更是扯淡,没到一定深度,就是单纯的方案整合商罢了,更难说。因此设计模式相对于大部分人来说,是一个体现自己与众不同的银弹,在短时间内,能让面试官能GET到你想描述的东西,不至于出现各说各的情况。本文也是博主对设计模式的一些简单理解,聊一聊常见的问题,写得比较简略,后续会追加更新。博主现在工作9116,已经快不行了,坚持下更新,不能忘了自己的初衷。

设计模式有哪些?

一些常见的设计模式及其在Spring框架中的使用场景:

  • 单例模式(Singleton Pattern):在Spring框架中,许多核心组件(如ApplicationContext)都是以单例模式创建和管理的。通过使用单例模式,可以确保在整个应用程序中只有一个实例,并提供全局访问点。
  • 工厂模式(Factory Pattern):在Spring框架中,BeanFactory和ApplicationContext充当了工厂的角色,负责创建和管理对象实例。通过配置和使用不同类型的工厂,可以实现对象的灵活创建和解耦。
  • 观察者模式(Observer Pattern):Spring框架中的事件机制就是基于观察者模式实现的。通过定义事件、监听器和发布者,可以实现模块之间的解耦和事件的触发与处理。
  • 代理模式(Proxy Pattern):Spring框架中的AOP(面向切面编程)就是基于代理模式实现的。通过使用动态代理技术,可以在不修改原有代码的情况下,为目标对象添加额外的功能,如事务管理、日志记录等。
  • 模板方法模式(Template Method Pattern):在Spring框架中,JdbcTemplate就是使用了模板方法模式。通过定义模板方法和钩子方法,可以将通用的数据库操作逻辑封装在模板类中,而将具体的实现延迟到子类中。
  • 策略模式(Strategy Pattern):Spring框架中的BeanPostProcessor就是使用了策略模式。通过实现BeanPostProcessor接口,可以在Spring容器实例化和初始化Bean的过程中插入自定义的逻辑。
  • 外观模式(Facade Pattern):在Spring框架中,ApplicationContext充当了外观角色,提供了统一的接口,隐藏了底层复杂的配置和实现细节,简化了系统的使用和调用。
  • 责任链模式(Chain of Responsibility Pattern):该模式将请求的发送者和接收者解耦,通过将多个对象组成链条,依次处理请求,直到找到合适的处理者。这样可以避免请求发送者与接收者之间的直接耦合,提高代码的灵活性和可扩展性。在Java中,Servlet过滤器就是一种责任链模式的应用。
  • 建造者模式(Builder Pattern):该模式通过将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式适用于创建复杂的对象,且对象的构建过程需要多个步骤或者可配置选项。在Java中,StringBuilder和DocumentBuilder就是建造者模式的应用。
  • 原型模式(Prototype Pattern):该模式通过复制现有对象来创建新对象,而无需显式地使用new操作符。原型模式适用于创建成本较高的对象,或者对象的创建过程比较复杂的情况。在Java中,Object类的clone()方法就是原型模式的应用。
  • 享元模式(Flyweight Pattern):该模式通过共享对象来减少内存使用和提高性能。它适用于存在大量相似对象的场景,通过共享相同的状态,减少了对象的数量和内存占用。在Java中,String常量池就是享元模式的应用。
  • 桥接模式(Bridge Pattern):该模式将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式适用于需要在多个维度上扩展的情况,通过将抽象和实现分离,可以灵活地组合不同的抽象和实现。在Java中,JDBC(Java Database Connectivity)就是桥接模式的应用。
  • 组合模式(Composite Pattern):该模式将对象组织成树形结构,使得用户可以以相同的方式处理单个对象和组合对象。组合模式适用于处理具有层次结构的对象,通过统一的接口,简化了对单个对象和组合对象的操作。在Java中,AWT和Swing中的组件树就是组合模式的应用。
  • 状态模式(State Pattern):该模式允许对象在内部状态改变时改变它的行为。通过将状态封装成独立的类,使得对象的行为可以根据状态的改变而变化,避免了大量的条件语句。在Java中,线程的不同状态(如新建、运行、等待等)就可以使用状态模式来实现。

策略模式和工厂模式的区别

arduino 复制代码
public class PaymentContext {
    private PaymentStrategy paymentStrategy;
    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    public void doPayment(double amount) {
        paymentStrategy.pay(amount);
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用策略模式选择支付策略
        PaymentContext paymentContext = new PaymentContext(new CreditCardPaymentStrategy());
        paymentContext.doPayment(100.0);
        // 使用策略模式切换支付策略
        paymentContext = new PaymentContext(new PayPalPaymentStrategy());
        paymentContext.doPayment(200.0);
    }
}

策略模式关注的是如何在运行时动态地选择算法或行为。它将一组算法封装到独立的策略类中,并使这些策略类可以互相替换。客户端代码可以根据需要选择合适的策略对象,而不必关心具体的实现细节。

typescript 复制代码
public class ProductA extends Product {
    public void use() {
        System.out.println("Using Product A");
    }
}
public class ProductB extends Product {
    public void use() {
        System.out.println("Using Product B");
    }
}

public class ProductFactory {
    public static Product createProduct(String type) {
        if (type.equals("A")) {
            return new ProductA();
        } else if (type.equals("B")) {
            return new ProductB();
        }
        return null;
    }
}
public class Main {
    public static void main(String[] args) {
        // 使用工厂模式创建产品对象
        Product productA = ProductFactory.createProduct("A");
        productA.use();
        Product productB = ProductFactory.createProduct("B");
        productB.use();
    }
}

工厂模式关注的是如何创建对象的过程。它提供了一个通用的接口或抽象类来创建对象,而不需要直接暴露对象的创建逻辑。客户端代码只需要通过工厂来获取所需的对象,而不需要关心对象的具体创建方式。

总结

策略模式合理利用多态特性,生成一个接口,传入不同的实现类来动态处理不同的逻辑。普通工厂模式,则是利用工厂类隐藏对象创建逻辑,通过输入不同的参数生成不同的对象。

装饰模式和代理模式的区别

java 复制代码
// 抽象组件接口
interface Component {
    void operation();
}

// 具体组件类
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("执行具体组件的操作");
    }
}

// 装饰器类
abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

// 具体装饰器类
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("执行具体装饰器A的操作");
    }
}

class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("执行具体装饰器B的操作");
    }
}

// 使用装饰器模式
public class DecoratorPatternExample {
    public static void main(String[] args) {
        // 创建具体组件对象
        Component component = new ConcreteComponent();
        // 使用具体装饰器A装饰具体组件
        Component decoratedComponentA = new ConcreteDecoratorA(component);
        decoratedComponentA.operation();
        System.out.println("==============");
        // 使用具体装饰器B装饰具体组件
        Component decoratedComponentB = new ConcreteDecoratorB(component);
        decoratedComponentB.operation();
    }
}

定义了一个抽象组件接口Component,具体组件类ConcreteComponent实现了该接口。然后,我们定义了一个抽象装饰器类Decorator,它也实现了Component接口并持有一个Component对象。具体装饰器类ConcreteDecoratorA和ConcreteDecoratorB分别继承了Decorator类,并在其operation()方法中添加了额外的操作。

typescript 复制代码
// 抽象主题接口
interface Subject {
    void request();
}

// 实际主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("执行实际主题的请求");
    }
}

// 代理类
class Proxy implements Subject {
    private Subject realSubject;

    public Proxy(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 在实际主题对象的请求前后执行一些额外操作
        System.out.println("执行代理的前置操作");
        realSubject.request();
        System.out.println("执行代理的后置操作");
    }
}

// 使用代理模式
public class ProxyPatternExample {
    public static void main(String[] args) {
        // 创建实际主题对象
        Subject realSubject = new RealSubject();

        // 创建代理对象
        Subject proxy = new Proxy(realSubject);

        // 通过代理对象进行请求
        proxy.request();
    }
}

定义了一个抽象主题接口Subject,实际主题类RealSubject实现了该接口。然后,我们定义了一个代理类Proxy,它也实现了Subject接口并持有一个实际主题对象。在代理类的request()方法中,我们可以在实际主题对象的请求前后执行一些额外的操作。

总结

装饰器模式和代理模式都会对原有接口再次进行实现,但有所不同的是,装饰器在不修改原有代码,通过新增继承子类的方式拓展原有方法,从而在运行时动态地添加额外功能。而代理模式则是通过实现类获取到实际对象,直接控制其访问来进行拓展。

工作中是怎么使用的?

拒绝叠叠乐!我用设计模式重构核心项目

我在项目中主要用到了模板模式,怎么用的呢?我在做订单交付实时计算流的重构时,参考了Flink的Sink端,将整个Sink当成一个模板类,提供三个抽象方法,分别是数据准备、数据计算和最后的处理方法,还有一个普通流程方法,负责串联前面三个方法。

模板方式给我重构时带来了什么好处呢?首先是将原先一个大的计算流程拆分成十个小的计算流,分别用模板类套上,由此可以梳理出数据准备、数据计算和数据处理三块。第一个好处就是这样细分下来就很清晰明了,相比原先,我能知道各个模块有没有重复查询数据库或者接口的操作,如果有的话就合并重复的查询。第二个好处就是经过梳理后,需要入库处理的数据可以在最后阶段去做统一批量入库,避免计算中途频繁修改数据做成一个大事务。

总的来说,模板模式在这次重构过程中,起到的作用是辅助我在重构过程中清晰梳理了整个计算流程。同时梳理后的代码相对来说很好做性能优化,比如批量处理,多线程优化。

写在最后

我自己对于设计模式其实不太会用,尽管我自己写了不少框架方面的东西,一些SDK,但是也是无意间用到,目前用的最多的会是代理、门面、单例这样的基础款。因为框架大部分比较固化,没有提供足够的扩展性,所以我没有使用到一些框架设计中常用的设计模式,比如spring里用到的那些。

最近的状态很差啊,前几天有个同事加班加的不行了,去医院检查了,博主年轻倒还能扛,但是没加班费没调休到11点确实伤害太大了。各方面的压力都有吧,但是我还算乐天,之前情感上有些问题,可能是我冲动了,还好我朋友们一顿话聊,让我重拾冷静。有一个好对象着实不易,哈哈,我说话很直,有的人会喜欢,有的人会觉得我不会说话。我之前一直以为我改了,但是现在看来没有改,所以接下来,我会继续成长,做一个好人,自己好大家都好,希望大家身体健康,有自己喜欢的和喜欢自己的人,下一篇博客见!

相关推荐
JaguarJack5 分钟前
使用 PHP 和 WebSocket 构建实时聊天应用 完整指南
后端·php
kylezhao201926 分钟前
C#23种设计模式-单例模式(Singleton)详解与应用
单例模式·设计模式·c#
CodeSheep38 分钟前
中国四大软件外包公司
前端·后端·程序员
千寻技术帮39 分钟前
10370_基于Springboot的校园志愿者管理系统
java·spring boot·后端·毕业设计
风象南39 分钟前
Spring Boot 中统一同步与异步执行模型
后端
聆风吟º41 分钟前
【Spring Boot 报错已解决】彻底解决 “Main method not found in class com.xxx.Application” 报错
java·spring boot·后端
乐茵lin1 小时前
golang中 Context的四大用法
开发语言·后端·学习·golang·编程·大学生·context
会员果汁1 小时前
5.设计模式-工厂方法模式
设计模式·工厂方法模式
步步为营DotNet1 小时前
深度探索ASP.NET Core中间件的错误处理机制:保障应用程序稳健运行
后端·中间件·asp.net
@zulnger1 小时前
python 设计模式
设计模式