设计模式觉醒系列(02)这几种设计模式很简单实用 | 相信你肯定见过

本文涉及的产品
云原生网关 MSE Higress,422元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 本文介绍了设计模式中的模板方法模式、外观模式和单例模式。模板方法模式通过父类定义算法框架,子类实现具体步骤,提高代码复用性和扩展性;外观模式提供高层次接口隐藏子系统复杂性,降低耦合度;单例模式确保一个类只有一个实例,适用于资源开销敏感场景。文中结合实战demo及Tomcat源码应用详细解析了这三种模式的实现与优点,并附带Spring中单例模式的应用示例。

读书笔记:“幸福并非瞬间发生,他与运气、概率无关,用钱买不到,也不能倚仗权势巧取豪夺。它不受外在事物操纵,而取决于我们对外在事物的阐释”。这句话触动很大,很多时候的精神内耗,无妄的想象揣测导致我们对外在的看法变得消极。阿德勒心理学提出的目的论,引导我们的就是改变自己对外在事物的阐释,从而做到他者信赖、他者贡献,主动去改变自己,也可以改变世界,建立维护双向的良好关系。


一、前言背景

二、模板方法模式

2.1 实战demo-场景

2.2 模板方法模式的优点

2.3 tomcat 源码应用模板模式

三、外观模式(门面模式)

3.1 实战demo-场景

3.2 外观模式的优点

3.3 tomcat 源码应用外观模式

四、单例模式

4.1 实战demo-场景

4.1.1 线程不安全-懒汉单例

4.1.2 线程安全-懒汉-单例模式

4.1.3 线程安全-饿汉-单例模式

4.2 单例模式优点

4.3 Spring 源码应用单例模式


一、前言背景

   在上一篇系列文章《设计模式觉醒系列(01)设计模式的基石 | 六大原则的核心是什么?》,我们已经详细分享了设计模式的六大原则,以及总结设计模式在研发设计过程中的核心作用。可以说,设计模式就是专门帮我们解决设计问题的经典方案,也是帮助研发人员提高代码可复用性、可维护性、可扩展性、可阅读性的内功心法。

   设计模式总共有20多种,如果一篇一篇来写,可以写很长时间,读者也可以订阅关注很长时间。实际而言,很多设计模式都是基于六大基本原则衍生出来,都有共性,为了减少阅读疲劳,以及帮助有缘同学尽快掌握了解齐全这20多种设计模式,后续每篇文章,我们尽量至少分享2个设计模式,期望10篇左右分享完毕。今天我们一次分享三个最简单实用、又非常接地气的设计模式,相信你一定都见过、用过。

   附带多说一句,每个设计模式并非完美,都有优缺点。没有绝对的完美解决方案,唯有合适的业务场景应用合适的设计模式,争取最佳实践效果。

二、模板方法模式

   模板方法模式(Template Method Pattern),英文原义是:定义一个算法的框架,将一些步骤的实现,延迟到子类中去。在模板方法模式里,父类定义了一个模板方法(它真的就是一个方法method()),然后在这个方法里包含了一系列的操作,比如按顺序调用方法a、b、c、d。而其中的某些操作父类并没有实现,并定义为抽象方法,让子类去实现。

   比如以下非常简单的demo,定一个美好一天的抽象类,里面有个模板方法start(),模板方法里按顺序调用了起床、刷牙、吃早餐的方法。

   其他三个方法,父类已经实现起床方法getup()+一个吃早餐方法eatBreakfast(),而刷牙方法延迟给子类去实现。


// 抽象美好一天类
abstract class AbstractNiceDay {
    // 模板[方法],定义一天的开始,需要起床,刷牙,吃早餐
    public final void start() {
        getup();
        brushtooth();
        eatBreakfast();
    }
    // 起床,公共方法,父类已经实现好
    private void getup() {
        System.out.println("拉丁解牛说技术,每个人早上都会起床");
    }
    // 刷牙,抽象方法,由子类实现,有一些人可能不爱刷牙,比如小朋友
    protected abstract void brushtooth();
    
    // 吃早餐,公共方法,父类已经实现好
    private void eatBreakfast() {
        System.out.println("拉丁解牛说技术,每个人早上都吃早餐");
    }

   具体应用,如果小火、小美他们都开启美好的一天,就分别继承这个相信这个AbstractNiceDay父类,实现里面刷牙方法就可以复用父类的模板方法start()正式开启快乐的一天。这个很直观,大家都能理解,也就不用画图说明。

2.1 实战demo-场景

   假如我们需要设计开发一个文档生成器,文档生成包括新建具体文档、文档增加水印标签、保存文档三个步骤。


package lading.java.designpattern.templatemethod;
/**
 * 业务场景:文档生成器,支持PDF,word,excel,csv,ppt等文件
 * 文档处理器-抽象类
 * 包含三个步骤,
 * 1、生成具体文档
 * 2、文档加水印
 * 3、保存文档
 */
public abstract class AbstractDocumentGenerator {
    //定义模板方法,按顺序生成文档
    public final void generateDocument() {
        createDocument();
        addTarget();
        saveDocument();
    }
    //子类自己实现
    protected abstract void createDocument();
    //文档加水印,已实现
    protected void addTarget() {
        System.out.println("给文档内容加拉丁解牛说技术的水印或者标识");
    }
    protected void saveDocument() {
        System.out.println("保存文档到固定文档目录/file/ladingjieniu/doc");
    }
}
package lading.java.designpattern.templatemethod;
/**
 * 业务场景:文档生成器,支持PDF,word,excel,csv,ppt等文件
 * 文档处理器-抽象类
 * 包含三个步骤,
 * 1、生成具体文档
 * 2、文档加水印
 * 3、保存文档
 */
public abstract class AbstractDocumentGenerator {
    //定义模板方法,按顺序生成文档
    public final void generateDocument() {
        createDocument();
        addTarget();
        saveDocument();
    }
    //子类自己实现
    protected abstract void createDocument();
    //文档加水印,已实现
    protected void addTarget() {
        System.out.println("给文档内容加拉丁解牛说技术的水印或者标识");
    }
    protected void saveDocument() {
        System.out.println("保存文档到固定文档目录/file/ladingjieniu/doc");
    }
}

2.2 模板方法模式优点

   子类可以在严格遵守父类(或者说算法)模板结构的前提下,重新自定义实现具体的某些步骤。这里充分体现了六大核心原则的开闭原则(OCP),对具体步骤实现修改开放,对模板规则框架修改封闭。

   此外也提高代码的复用性,子类复用父类已实现的部分步骤方法,减少代码编写。

   以及也提高代码可扩展性。

2.3 tomcat 源码应用模板模式

    在tomcat源码里,javax.servlet.http.HttpServlet 类的service方法,也是应用了模板方法。service()方法就是个模板方法,他会根据不同请求类型调用相应的处理方法。而里面调用的具体方法,比如doGet、doPost,这些方法在子类可以自定义去实现。源码:

三、外观模式(门面模式)

   外观模式(facade),也叫门面模式。它的定义是:一个子系统的外部与其内部的通信必须通过一个统一的对象进行。具体是:外观模式为子系统的一组接口提供一个高层次的接口(IFacade,或者定一个xxFacade外观类)。这样可以隐藏子系统的复杂性,使得子系统更易于使用。当客户端调用子系统时候,只需要通过这个高层次的外观接口(Facade类)就可以调用子系统的功能,从而达到客户端与子系统解耦。

   比如说一会3.1例子demo的订单系统,订单系统有支付、库存、物流分配三个子系统功能,外部第三方平台需要调用我们订单系统下定单。我们可以定义一个专门处理订单的OrderFacade类,封装好三个功能,供外部客户端去调用。

3.1 实战demo-场景

   具体如下,通过OrderFacade类封装好底层内部的支付、物流、库存管理功能。外部client只需要调用OrderFacade类就可以实现下单功能。


package lading.java.designpattern.facade;
/**
 * 物流分配子模块
 */
public class LogisticsService {
    public void assignExpress(){
        System.out.println("说技术拉丁分配物流公司接单安排派送");
    }
}
package lading.java.designpattern.facade;
/**
 * 支付服务模块
 */
public class PayService {
    public void processPay(){
        System.out.println("拉丁处理订单支付扣款事宜");
    }
}
package lading.java.designpattern.facade;
/**
 * 库存管理模块
 */
public class StoreService {
    public void deductProd(){
        System.out.println("ladingjieniu 减少产品库存");
    }
}
package lading.java.designpattern.facade;
/**
 * 内部订单系统门面-外观类
 */
public class OrderFacade {
    //内置相关子类功能
    private LogisticsService logisticsService = new LogisticsService();
    private PayService payService = new PayService();
    private StoreService storeService = new StoreService();
    public void processOrder() {
        payService.processPay();
        storeService.deductProd();
        logisticsService.assignExpress();
    }
    /**
     * 客户端调用下单
     *
     * @param args
     */
    public static void main(String[] args) {
        OrderFacade facade = new OrderFacade();
        facade.processOrder();
    }
}

3.2 外观模式的优点

   外观模式非常像公司内部的门户,通过提供高层次的外观模式类,隐藏子系统的交互细节,有效降低客户端与内部模块的耦合度。

   由于解耦,子系统的修改,不影响外观类,也不影响客户端的调用,提高代码的可扩展性、可维护性。

   外观模式充分体现了依赖倒转原则(细节依赖抽象,降低模块依赖)和迪米特法则(最少知道,一个类对另一个类知道越少越好)的思想。

3.3 tomcat 源码应用外观模式

   在tomcat源码里,RequestFacade就是外观模式的经典标准应用。RequestFacade实现了HttpServletRequest接口,底层封装了Request对象,客户端ApplicationDispatcher在应用Request时候,就不需要关系Request的底层逻辑,通过RequestFacade类就可以调用,实现解耦。

   此外tomcat里的ResponseFacade、StandardSessionFacade都是用了外观模式。

四、单例模式

   单例模式,singleton pattern,原义很简单:确保一个类只有一个实例,并自行实例化给整个系统使用。这个特点决定里单例模式适合在对资源开销敏感、要求资源共享复用场景下应用,比如缓存、静态资源。

   单例模式有几种实现方式,一个是懒汉模式,等需要用到实例的时候再去实例化,这种模式需要注意线程安全。另一个是饿汉模式,在类加载的时候就实例化对象,这种是线程安全的。接下来对这两种模式的三个案例提供demo分享。

4.1 实战demo-场景

4.1.1 线程不安全-懒汉单例


package lading.java.designpattern.singleton;
/**
 * 并发不安全,单例模式
 */
public class UnsafeSingleton {
    private static UnsafeSingleton singleton;
    //避免外部构造多个对象
    private UnsafeSingleton() {
    }
    //仅该方法可以获得实例
    public static UnsafeSingleton getSingleton() {
        //多个线程同时执行到这部分,就会出现多个实例
        if (singleton == null) {
            singleton = new UnsafeSingleton();
        }
        return singleton;
    }
}

4.1.2 线程安全-懒汉-单例模式


package lading.java.designpattern.singleton;
/**
 * 懒汉单例,双重检查,线程安全的单例模式
 */
public class SafeSingletonLazy {
    private static volatile SafeSingletonLazy singleton;
    //私有,防止外部实例化
    private SafeSingletonLazy() {
    }
    public static SafeSingletonLazy getSingleton() {
        if (singleton == null) {
            synchronized (SafeSingletonLazy.class) {
                if (singleton == null) {
                    singleton = new SafeSingletonLazy();
                }
            }
        }
        return singleton;
    }
}

4.1.3 线程安全-饿汉-单例模式

package lading.java.designpattern.singleton;
/**
 * 饿汉-单例模式,线程安全
 * 单例对象在加载的时候就实例化好该实例,线程安全
 */
public class SafeSingletonHungry {
    private static final SafeSingletonHungry singletonHungry = new SafeSingletonHungry();
    //私有化,防止外部实例化
    private SafeSingletonHungry() {
    }
    public static SafeSingletonHungry getInstance() {
        return singletonHungry;
    }
}

4.2 单例模式优点

   通过确保一个类在整个应用程序生命周期里只有一个实例对象,直接好处就是减少创建对象的开销,为系统节省资源。

4.3 Spring 源码应用单例模式

   我们熟悉的spring的IOC容器的实例管理有经典实践,@Bean默认就是单例模式,也可以通过@Scope("prototype")进行修改。


推荐阅读拉丁解牛相关专题系列(欢迎交流讨论):

1、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

2、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

3、JVM进阶调优系列(1)类加载器原理一文讲透

4、JAVA并发编程系列(13)Future、FutureTask异步小王子

5、MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

目录
打赏
0
2
2
0
106
分享
相关文章
设计模式觉醒系列(03)创建型模式的5个设计模式 | 一口气讲全讲透
本文详细介绍了设计模式中的创建型模式,包括建造者模式、原型模式、单例模式、工厂方法模式和抽象工厂模式。创建型模式关注对象的创建过程,隐藏了创建细节,以提高代码的可维护性和可扩展性。通过具体的实战demo和应用场景分析,展示了每种模式的特点和优势。例如,建造者模式适用于复杂对象的分步骤构建;原型模式通过复制对象实现高效复用;单例模式确保全局唯一实例;工厂方法模式和抽象工厂模式则提供了灵活的对象创建机制,支持多类型产品族的生产。这些模式在实际开发中能够简化客户端代码,提升系统灵活性和复用性。
设计模式觉醒系列(01)设计模式的基石 | 六大原则的核心是什么?
本文介绍了设计模式的六大原则,包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)、依赖倒置原则(DIP)和迪米特法则。通过具体案例分析了每个原则的应用场景及优势,强调了这些原则在提升代码可维护性、可复用性、可扩展性和降低耦合度方面的重要作用。文章指出,设计模式的核心在于确保系统模块间的低耦合高内聚,并为后续深入探讨23个经典设计模式打下基础。
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
202 11
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
设计模式:工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。
|
2月前
|
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
96 40
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
76 19
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
67 12
「全网最细 + 实战源码案例」设计模式——工厂方法模式
简单工厂模式是一种创建型设计模式,通过一个工厂类根据传入参数创建不同类型的产品对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。适用于创建对象种类较少且调用者无需关心创建细节的场景。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。
61 15

相关实验场景

更多