设计模式实战

简介: **设计模式的应用与案例**设计模式是解决常见软件设计问题的最佳实践,有助于提升代码质量和可维护性。有效利用设计模式的步骤包括:理解业务需求、识别问题、选择合适模式、学习研究和适时调整。在实际工作中,例如,通过结合工厂模式和策略模式,解决了多端页面配置筛选逻辑,避免接口爆炸;使用模板方法模式,将复杂业务逻辑拆分为可复用步骤,提高了代码扩展性。设计模式虽好,但应适度,避免过度复杂化。

一、设计模式的使用场景

设计模式(Design Patterns)是在软件开发中经过验证的最佳实践,用于解决常见的设计问题。它们提供了一种可复用的解决方案,可以帮助开发人员提高代码质量、可维护性和可重用性。那么在在工作中,如何有效利用设计模式帮我们解决问题呢?我觉得主要有以下几点:

  1. 理解业务需求:在开始编码之前,深入了解业务需求是非常重要的。这将帮助我们确定哪些设计模式最适合当前的问题。
  2. 识别问题:在开发过程中,时刻关注代码中可能出现的重复、冗余或难以维护的部分。这些问题通常是需要应用设计模式的候选者。
  3. 选择适当的设计模式:根据识别到的问题,拟合设计模式的解决方式,在脑子中模拟改写后的业务代码,大致逻辑通顺后就可以尝试改写了。有的一直设计模式可能还不好解决,那就要先拆解一部分,分批解决,因为工程是有时间、成本要求的,不可能一直等我们苦思冥想和尝试。而且也不要试图一次性将所有代码都改造成使用设计模式。相反,应该逐步改进代码,逐步引入设计模式。这样可以确保代码始终保持在一个稳定且可维护的状态。
  4. 学习和研究:不断学习新的设计模式,研究它们是如何解决特定问题的。这可以通过阅读书籍、在线教程、博客文章和开源代码库来实现。这样可以让我们脑子里有个设计模式的概念,尽快的能拟合业务场景找到合适的设计模式,这样才保证能在工作中用起来。
  5. 注意事项:设计模式并不是银弹,我们应该根据具体情况和需求进行选择和调整。不要盲目追求使用设计模式,而是要在理解它们的优缺点和项目排期的基础上做出决策。

二、案例

在之前的工作经历中我有过多次使用设计模式的经历,下面我讲述两个分别是使用工厂和策略两种设计模式和模版方法的案例,方便大家理解。

1、策略和工厂模式

1)背景

当时面临的问题是有多种类型的 app,app 上有不同的页面,不同页面根据不同的业务或者标识去查询配置的不同的页面,但是我们页面的数据逻辑又在其他系统,我们只能在自己系统内完成这样的筛选逻辑,在当时要么根据不同的客户端开放不同的接口,要么根据不同的业务类型开放不同的接口,那这样肯定会随着端的增多或者业务类型的增多会产生接口爆炸的情况,要么就是在一个接口内完成大量的判断来完成这样的逻辑。

2)引入设计模式

那有没有可能借助设计模式来解决呢?当时我就在寻找相关的案例,最后觉得工厂模式和策略模式的结合是很好的方式,简单工厂模式的作用是提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程,只需要知道具体产品类所对应的参数即可。这样不正是拟合了根据上送和客户端参数创建多端工厂的场景吗?而策略模式是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。那也正好拟合了我们根据上送的业务类型和页面类型寻找不同处理类的场景,所以我就在项目中着手使用设计模式来解决这样的问题。

  • 客户端
@SpringBootTest
public class ApplicationTestBizPage {
    @Autowired
     HandlerFeComponentFactory handlerFeComponentFactory;
    @Test
    public void test() {
        List<FeComponent> feComponentList = handlerFeComponentFactory.doWork("alipay", "home",  "biz");
        System.out.println("-----------------");
        System.out.println(feComponentList);
    }
}
  • 创建工厂
@Component
public class HandlerFeComponentFactory {
    @Autowired
    private List<HandlerFeComponentService> handlerFeComponentServices;
  
    public List<FeComponent> doWork(String app, String page, String bizType) {
        for (HandlerFeComponentService handlerFeComponentService : handlerFeComponentServices) {
            if (handlerFeComponentService.isSupport(app)) {
                return handlerFeComponentService.doWork(app, page,  bizType);
            }
        }
        return new ArrayList<>();
    }
}
  • 客户端接口
public interface HandlerFeComponentService {
    boolean isSupport(String app);
    List<FeComponent> doWork(String app, String page, String bizType);
  • 支付宝实现
@Component
public class AliappHandlerFeComponentServiceImpl implements HandlerFeComponentService {
    @Autowired
    private Map<String, AbstractAliappHandlerFeComponent> aliappHandlerFeComponentMap;
    @Override
    public boolean isSupport(String app) {
        return "alipay".equals(app);
    }
    @Override
    public List<FeComponent> doWork(String app, String page, String bizType) {
        String bizPage = String.format("%s:%s:%s", app, page, bizType);
        AbstractAliappHandlerFeComponent aliappHandlerFeComponent = aliappHandlerFeComponentMap.get(bizPage);
        if (Objects.nonNull(aliappHandlerFeComponent)) {
            return aliappHandlerFeComponent.doWorkForAliapp(app, page);
        }
        return null;
    }
  • 业务抽象类定义
public   abstract class AbstractAliappHandlerFeComponent {
    abstract List<FeComponent> doWorkForAliapp(String app, String page) ;
}
  • 业务抽象类实现
@Component("alipay:home:biz")
public class TestBizPage extends AbstractAliappHandlerFeComponent {
    @Override
    List<FeComponent> doWorkForAliapp(String app, String page) {
        List<FeComponent> feComponentList = new ArrayList<>();
        FeComponent feComponent = new FeComponent();
        feComponent.setMessage("alipay:home:biz");
        feComponentList.add(feComponent);
        return feComponentList;
    }
}
  • 实体类
public class FeComponent {
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    private  String message;
    @Override
    public String toString() {
        return "FeComponent{" +
                "message='" + message + '\'' +
                '}';
    }
}

从上面可以看出,调用者仍然只需要传入自己的参数,在寻找具体处理类和方法的路径都被包装在工厂方法和 springbean 和具体类的映射关系处理中。以后增加了新的客户端百度,只需要新增一个 BaiduService 和处理类即可。

2、模版方法

还有一次经历是是使用模版方法完成业务逻辑的组件化。

1)背景

之前一个业务逻辑有很多个版本,我还需要再增加2个版本,全部流程大致有15步流程,在不同的版本可能会有缺少不同的步骤,所以之前的逻辑就有在不同步骤中有各自版本的判断导致很难清晰的看出每个版本自己的逻辑是什么,导致我很难加自己的逻辑,也很不利于排查问题。

2)引入设计模式

那怎样解决这样一个问题呢?我是结合模版方法的模式来重构了一遍代码,我们先看下模版方法模式的定义,模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。它主要优点包括:

  • 封装不变部分:将算法的核心结构和不变部分封装在基本方法中,使得子类可以根据需求对可变部分进行扩展,而不影响不变部分。
  • 代码复用:抽象类中包含的基本方法可以避免子类重复实现相同的代码逻辑,提高了代码的复用性。
  • 更好的扩展性:由于具体实现由子类来完成,可以方便地扩展新的功能或变更实现方式,同时不影响模板方法本身。

这不正好拟合我遇到问题场景吗。之后我将业务逻辑中的步骤梳理出来,放到一个抽象类中,公共部分在抽象类中实现,不同版本的内容定义成抽象方法,由各个版本的子类完成实现,由传入的参数来和子类形成一个映射关系。这样我增加自己的两个版本,只需要增加两个子类即可,每个版本的逻辑也清晰可见,减少了排查的难度。我也来展示一个简化版本根据的 demo,方便大家理解的。

// 抽象类定义了算法的骨架  
public abstract class AbstractClass {  
  
    // 模板方法  
    public final void templateMethod() {  
        step1();  
        step2();  
        hook1(); // 钩子方法,子类可以选择是否覆盖  
        step3();  
        hook2(); // 另一个钩子方法  
    }  
  
    // 具体步骤,由抽象类实现  
    protected void step1() {  
        System.out.println("执行步骤1");  
    }  
  
    protected void step2() {  
        System.out.println("执行步骤2");  
    }  
  
    protected void step3() {  
        System.out.println("执行步骤3");  
    }  
  
    // 钩子方法,默认实现为空  
    protected void hook1() {  
    }  
  
    // 另一个钩子方法,默认实现为空  
    protected void hook2() {  
    }  
}  
  
// 子类实现了抽象类,并重写了某些步骤  
public class SubClass extends AbstractClass {  
  
    // 重写钩子方法  
    @Override  
    protected void hook1() {  
        System.out.println("执行钩子方法1");  
    }  
  
    // 重写另一个钩子方法  
    @Override  
    protected void hook2() {  
        System.out.println("执行钩子方法2");  
    }  
}  
  
// 客户端代码  
public class Client {  
  
    public static void main(String[] args) {  
        AbstractClass obj = new SubClass();  
        obj.templateMethod();  
    }  
}

在这个示例中,AbstractClass 是一个抽象类,它定义了业务的全部落哦,包括三个具体步骤(step1(), step2(), step3())和两个钩子方法(hook1(), hook2())。

SubClass 是 AbstractClass 的一个子类,它选择性地重写了两个钩子方法,但没有改变算法的骨架。

Client 类是客户端代码,它创建了一个 SubClass 的实例,并调用了模板方法templateMethod()。

当运行这个示例时,输出将是:

执行步骤1

执行步骤2

执行钩子方法1

执行步骤3

执行钩子方法2

模板方法还是比较常见的,大家可以看看 jdk 中的 map 实现和 springboot 的启动流程中都有体现的。

需要注意的是,虽然设计模式在软件开发中很有用,但过度使用或不当地使用它们也可能导致代码过度复杂化和难以理解。那怎样定义过度使用呢,遵循事不过三的原则即可,只要重复的代码或者方法没有超过三个的时候就不用优化。希望大家可以在实际业务中灵活运用。

目录
相关文章
|
4月前
|
设计模式 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 设计模式与实战应用
【4月更文挑战第30天】本文探讨JavaScript设计模式在提升开发效率和代码质量中的关键作用。涵盖单例、工厂、观察者、装饰器和策略模式,并通过实例阐述其在全局状态管理、复杂对象创建、实时数据更新、功能扩展和算法切换的应用。理解并运用这些模式能帮助开发者应对复杂项目,提升前端开发能力。
56 0
|
4月前
|
设计模式 存储 uml
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
56 1
|
17天前
|
设计模式 C# 开发者
C#设计模式入门实战教程
C#设计模式入门实战教程
|
24天前
|
前端开发 开发者 开发框架
JSF与Bootstrap,打造梦幻响应式网页!让你的应用跨设备,让用户爱不释手!
【8月更文挑战第31天】在现代Web应用开发中,响应式设计至关重要,以确保不同设备上的良好用户体验。本文探讨了JSF(JavaServer Faces)与Bootstrap框架的结合使用,展示了如何构建响应式网页。JSF是一个基于Java的Web应用框架,提供丰富的UI组件和表单处理功能;而Bootstrap则是一个基于HTML、CSS和JavaScript的前端框架,专注于实现响应式设计。通过结合两者的优势,开发者能够更便捷地创建自适应布局,提升Web应用体验。然而,这种组合也有其局限性,如JSF组件库较小和较高的学习成本等,因此在选择开发框架时需综合考虑具体需求和应用场景。
30 0
|
24天前
|
设计模式 前端开发 开发者
Angular携手Material Design:探索设计模式下的UI组件开发之道——从按钮到对话框的全面实战演示
【8月更文挑战第31天】在现代Web应用开发中,Angular框架结合Material Design设计原则与组件库,显著提升了用户界面的质量与开发效率。本文通过具体代码示例,详细介绍如何在Angular项目中引入并使用Material Design的UI组件,包括按钮、表单和对话框等,帮助开发者快速构建美观且功能强大的应用。通过这种方式,不仅能提高开发效率,还能确保界面设计的一致性和高质量,为用户提供卓越的体验。
19 0
|
1月前
|
设计模式 存储 Java
掌握Java设计模式的23种武器(全):深入解析与实战示例
掌握Java设计模式的23种武器(全):深入解析与实战示例
|
3月前
|
设计模式 算法 Java
Java中的设计模式:实战案例分享
Java中的设计模式:实战案例分享
|
3月前
|
设计模式 存储 前端开发
【设计模式】MVC与MVVM详尽解读与实战指南
【设计模式】MVC与MVVM详尽解读与实战指南
625 0
|
4月前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
59 1
|
4月前
|
设计模式 Java 数据库连接
JAVA设计模式解析与实战
本文探讨了Java中的常见设计模式,包括单例模式、工厂模式和观察者模式。单例模式确保类只有一个实例,常用于管理资源;工厂模式通过抽象工厂接口创建对象,降低了耦合度;观察者模式实现了一对多的依赖关系,当主题状态改变时,所有观察者都会收到通知。理解并运用这些设计模式能提升代码的复用性、可扩展性和可维护性。