浅析Java设计模式【3.6】——模板方法

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Java常用设计模式,模板模式

1. 目录

temple

2. 概念

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

可以理解为 用抽象类存放相同逻辑,再声明一些抽象方法来胁迫子类实现剩余的逻辑。不同的子类实现不同抽象方法,即不同的剩余逻辑。

综述,它是一种自上而下的模式,类似于企业决策,决策者制定相关流程,执行层负责具体实现。

3. 应用场景

局部场景下,实现相同,但在个别细节上略有差异。

4. 优缺点

4.1. 优点

  • 实现代码复用率高
  • 局部实现带来的调整,对整体影响小

4.2. 缺点

  • 模板和子类的耦合性高,如要对模板中的算法骨架进行变更,会影响子类变化

5. 实现

这里用一个登录后的校验密码这个场景来举例。


public class UserService {

    public final boolean verifyPassWord(String userId,String password) {
        String userPassWord = getById(userId);
        return userPassWord.equalsIgnoreCase(password);
    }

    private String getById(String userId) {
        // TODO: 从数据库读取
        return null;
    }
}

因为数据库读取用户信息比较频繁而且属于IO操作,比较影响效率,我们很多时候把热点数据放到缓存,减少跟数据库的之间的IO操作。那用什么缓存实现?不同的场景有不同的选型,所以此处,我们只写出要缓存的定义,具体实现交由子类。

5.1. 抽象类


public abstract class UserService {

    /** 校验密码
     * @author <a href="https://github.com/rothschil">Sam</a>
     * @date 2022/8/5-12:58
     * @param userId
     * @param password
     * @return boolean
     **/
    public final boolean verifyPassWord(String userId,String password) {
        String userPassWord = getById(userId);
        return userPassWord.equalsIgnoreCase(password);
    }

    private String getById(String userId) {
        //  1、getByCache
        String userPassWord = getCache(userId);
        //  2、缓存没有,从数据库中获取
        if(StringUtils.isEmpty(userPassWord)){
            userPassWord =readDb(userId);
        }
        //  3、写入缓存
        if(StringUtils.isEmpty(userPassWord)){
            putCache(userId,userPassWord);
        }
        return userPassWord;
    }

    protected abstract String getCache(String userId) ;

    protected abstract String putCache(String userId,String userPassWord) ;

    private String readDb(String userId) {
        return "";
    }
}

小结:因为声明抽象方法,所以整个类也必须声明为抽象类。 重点在 getCache(String userId)putCache(String userId,String userPassWord) ,具体实现交由子类,子类具体如何实现,就由子类根据实际情况完善。

5.2. 子类实现

5.2.1. Redis实现


public class RedisService extends UserService {
    private RedisClient client = RedisClient.create("redis://localhost:6379");

    protected String getCache(String key) {
        try (StatefulRedisConnection<String, String> connection = client.connect()) {
            RedisCommands<String, String> commands = connection.sync();
            return commands.get(key);
        }
    }

    protected void putCache(String key, String value) {
        try (StatefulRedisConnection<String, String> connection = client.connect()) {
            RedisCommands<String, String> commands = connection.sync();
            commands.set(key, value);
        }
    }
}

5.2.2. SpringCache站点实现


public class SpringCacheService extends UserService {
    

    @Cacheable(key = "'key'")
    protected String getCache(String key) {

    }

    @CachePut(key = "'key'",value = "'value'",)
    protected void putCache(String key, String value) {
        
    }
}

6. 综述

模板设计模式核心思想是:父类定义骨架,子类实现某些细节。

在某些场景下为了防止子类重写父类的骨架方法,可以在父类中对骨架方法使用 final 。对于需要子类实现的抽象方法,一般声明为 protected ,使得这些方法对外部客户端不可见。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
31 17
|
18天前
|
算法 Java Linux
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
这篇文章介绍了如何使用Java的Graphics2D类在图片上合成另一个照片,并将照片切割成圆形头像的方法。
32 1
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
|
5天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
16 6
|
6天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
11 3
|
8天前
|
Java 大数据 API
别死脑筋,赶紧学起来!Java之Steam() API 常用方法使用,让开发简单起来!
分享Java Stream API的常用方法,让开发更简单。涵盖filter、map、sorted等操作,提高代码效率与可读性。关注公众号,了解更多技术内容。
|
6天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
9 2
|
6天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
8 1
|
6天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
16 1
|
6天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
12 1
|
6天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
16 1