设计模式该怎么说

前言

设计模式算是高频的面试题了,基本在工作的前几年,只要想说设计,必定会谈到设计模式。我理解的原因是业务设计通常难以描述,普通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点确实伤害太大了。各方面的压力都有吧,但是我还算乐天,之前情感上有些问题,可能是我冲动了,还好我朋友们一顿话聊,让我重拾冷静。有一个好对象着实不易,哈哈,我说话很直,有的人会喜欢,有的人会觉得我不会说话。我之前一直以为我改了,但是现在看来没有改,所以接下来,我会继续成长,做一个好人,自己好大家都好,希望大家身体健康,有自己喜欢的和喜欢自己的人,下一篇博客见!

相关推荐
coderWangbuer21 分钟前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
攸攸太上27 分钟前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志30 分钟前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
sky丶Mamba1 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
刷帅耍帅2 小时前
设计模式-桥接模式
设计模式·桥接模式
千里码aicood2 小时前
【2025】springboot教学评价管理系统(源码+文档+调试+答疑)
java·spring boot·后端·教学管理系统
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
MinBadGuy3 小时前
【GeekBand】C++设计模式笔记5_Observer_观察者模式
c++·设计模式
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
刷帅耍帅3 小时前
设计模式-生成器模式/建造者模式Builder
设计模式·建造者模式