深入架构剖析 博客点赞逻辑 strategy 策略模式 策略接口 上下文 具体策略 项目实战

目录

点赞策略上下文

策略上下文代码详解

[1. 策略模式概述](#1. 策略模式概述)

[2. 核心组件](#2. 核心组件)

[3. 代码解读](#3. 代码解读)

[LikeStrategyContext 类](#LikeStrategyContext 类)

[LikeTypeEnum 枚举](#LikeTypeEnum 枚举)

[LikeStrategy 接口](#LikeStrategy 接口)

具体策略类

[4. 如何使用这个设计](#4. 如何使用这个设计)

[5. 优点](#5. 优点)

[6. 总结](#6. 总结)

具体代码实现

定义枚举类

从控制层传入参数到上下文

[在策略上下文进行详解 传到策略接口的实现类](#在策略上下文进行详解 传到策略接口的实现类)

[Strategy 策略接口](#Strategy 策略接口)

[StrategyImpl 策略接口实现类](#StrategyImpl 策略接口实现类)

[难点 likeStrategyMap](#难点 likeStrategyMap)

详细解释

[1. Map的键:策略名称](#1. Map的键:策略名称)

[2. Map的值:具体策略对象](#2. Map的值:具体策略对象)

[3. Spring的依赖注入](#3. Spring的依赖注入)

[4. 执行点赞策略](#4. 执行点赞策略)

总结


点赞策略上下文

package com.ican.strategy.context;

import com.ican.enums.LikeTypeEnum;
import com.ican.strategy.LikeStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * 点赞策略上下文
 *
 * @author Dduo
 */
@Service
public class LikeStrategyContext {

    @Autowired
    private Map<String, LikeStrategy> likeStrategyMap;

    /**
     * 点赞
     *
     * @param likeType 点赞类型
     * @param typeId   类型id
     */
    public void executeLikeStrategy(LikeTypeEnum likeType, Integer typeId) {
        likeStrategyMap.get(likeType.getStrategy()).like(typeId);
    }
}

策略上下文代码详解

1. 策略模式概述

策略模式是一种行为设计模式,它允许你定义一系列算法(策略),并将每个算法封装在独立的类中,让它们可以互换。策略模式使得算法可以独立于使用它的客户而变化。

2. 核心组件

  • Context(上下文) :通常是策略模式中的上下文,负责与策略交互。在你的代码中,LikeStrategyContext是上下文类。
  • Strategy(策略接口) :定义了策略的共同接口。在你的代码中,LikeStrategy是策略接口,具体的点赞策略会实现这个接口。
  • ConcreteStrategy(具体策略) :实现了具体算法的类。在你的代码中,具体的点赞策略类应该实现LikeStrategy接口,并提供具体的like实现。

3. 代码解读

LikeStrategyContext

这个类充当了策略模式中的 Context,它依赖于具体的点赞策略来完成点赞操作。

java


复制代码
@Service
public class LikeStrategyContext {
    @Autowired
    private Map<String, LikeStrategy> likeStrategyMap;

    public void executeLikeStrategy(LikeTypeEnum likeType, Integer typeId) {
        likeStrategyMap.get(likeType.getStrategy()).like(typeId);
    }
}
  • likeStrategyMap 是通过 Spring 的依赖注入机制注入的,它是一个 Map,键是策略名称,值是实现了 LikeStrategy 接口的具体点赞策略对象。
  • executeLikeStrategy 方法根据 likeType(点赞类型)从 likeStrategyMap 中获取对应的 LikeStrategy 实现,并执行其 like 方法。
LikeTypeEnum 枚举

这个枚举类定义了不同的点赞类型和对应的策略。

java


复制代码
public enum LikeTypeEnum {
    PHOTO("photoLikeStrategy"),
    COMMENT("commentLikeStrategy");

    private final String strategy;

    LikeTypeEnum(String strategy) {
        this.strategy = strategy;
    }

    public String getStrategy() {
        return strategy;
    }
}
  • LikeTypeEnum 定义了两个类型(如:PHOTOCOMMENT),并且每个类型对应一个具体的策略名称(如 photoLikeStrategycommentLikeStrategy)。
  • 通过 getStrategy 方法,LikeTypeEnum 能告诉 LikeStrategyContext 使用哪种具体的点赞策略。
LikeStrategy 接口
java


复制代码
public interface LikeStrategy {
    void like(Integer typeId);
}
  • LikeStrategy 是策略接口,定义了 like 方法,具体的点赞策略需要实现该接口,并提供具体的点赞逻辑。
具体策略类

例如,PhotoLikeStrategyCommentLikeStrategy 类应该实现 LikeStrategy 接口,并根据不同的策略来实现点赞逻辑。

java


复制代码
public class PhotoLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 执行照片点赞的具体逻辑
    }
}

public class CommentLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 执行评论点赞的具体逻辑
    }
}

4. 如何使用这个设计

  • 在应用中,当用户点击点赞时,LikeStrategyContext 会根据传入的 likeType(例如,PHOTOCOMMENT)来选择合适的点赞策略。
  • 通过 likeStrategyMap.get(likeType.getStrategy()),代码能够动态地选择策略,调用其 like 方法,实现不同的点赞行为。

5. 优点

  • 扩展性 :当需要新增点赞类型时,只需增加新的 LikeStrategy 实现类和 LikeTypeEnum 中的枚举项,而无需修改现有代码。
  • 解耦 :点赞的具体实现被封装在不同的策略类中,LikeStrategyContext 类无需知道具体的点赞细节,只负责调用相应的策略。
  • 灵活性 :可以根据不同的 likeType 来选择不同的策略,易于扩展和维护。

6. 总结

这个代码使用了策略模式来实现点赞功能的灵活扩展,不同类型的点赞行为通过不同的策略类实现,LikeStrategyContext 则负责选择合适的策略并执行。这个设计使得点赞功能的扩展更加清晰和易于管理。

具体代码实现

定义枚举类

三种点赞类型

文章 评论 说说

package com.ican.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 点赞类型枚举
 *
 * @author Dduo
 */
@Getter
@AllArgsConstructor
public enum LikeTypeEnum {

    /**
     * 文章
     */
    ARTICLE("文章", "articleLikeStrategyImpl"),

    /**
     * 评论
     */
    COMMENT("评论", "commentLikeStrategyImpl"),

    /**
     * 说说
     */
    TALK("说说", "talkLikeStrategyImpl");

    /**
     * 点赞类型
     */
    private final String likeType;

    /**
     * 策略
     */
    private final String strategy;

}

从控制层传入参数到上下文

以点赞文章举例

    /**
     * 点赞文章
     *
     * @param articleId 文章id
     * @return {@link Result<>}
     */
    @SaCheckLogin
    @ApiOperation(value = "点赞文章")
    @AccessLimit(seconds = 60, maxCount = 3)
    @SaCheckPermission("blog:article:like")
    @PostMapping("/article/{articleId}/like")
    public Result<?> likeArticle(@PathVariable("articleId") Integer articleId) {
        // 策略模式 调用策略上下文 传入参数
        likeStrategyContext.executeLikeStrategy(LikeTypeEnum.ARTICLE, articleId);
        return Result.success();
    }

传入的是枚举类中的数值

传入的是 ID

在策略上下文进行详解 传到策略接口的实现类

三个不同的类中的点赞方法

都调用了这个上下文里面的策略方法

executeLikeStrategy

package com.ican.strategy.context;

import com.ican.enums.LikeTypeEnum;
import com.ican.strategy.LikeStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * 点赞策略上下文
 *
 * @author Dduo
 */
@Service
public class LikeStrategyContext {

    // 策略模式使得算法可以独立于使用它的用户而变化

    @Autowired
    private Map<String, LikeStrategy> likeStrategyMap;

    /**
     * 点赞
     *
     * @param likeType 点赞类型
     * @param typeId   类型id
     */
    public void executeLikeStrategy(LikeTypeEnum likeType, Integer typeId) {
        likeStrategyMap.get(likeType.getStrategy()).like(typeId);
    }
}

我们通过likeType(枚举值 点赞类型)从 likeStrategyMap 中获取对应的 LikeStrategy 实现,并执行其 like 方法。

Strategy 策略接口

实现了一个点赞的方法

package com.ican.strategy;

/**
 * 点赞策略
 *
 * @author Dduo
 */
public interface LikeStrategy {

    /**
     * 点赞
     *
     * @param typeId 类型id
     */
    void like(Integer typeId);
}

StrategyImpl 策略接口实现类

直接重写方法即可

package com.ican.strategy.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ican.constant.RedisConstant;
import com.ican.entity.Talk;
import com.ican.mapper.TalkMapper;
import com.ican.service.RedisService;
import com.ican.strategy.LikeStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 说说点赞策略
 *
 * @author Dduo
 */
@Service("talkLikeStrategyImpl")
public class TalkLikeStrategyImpl implements LikeStrategy {

    @Autowired
    private RedisService redisService;

    @Autowired
    private TalkMapper talkMapper;

    @Override
    public void like(Integer talkId) {
        // 查询说说
        Talk talk = talkMapper.selectOne(new LambdaQueryWrapper<Talk>()
                .select(Talk::getId)
                .eq(Talk::getId, talkId));
        Assert.notNull(talk, "说说不存在");

        //  public static final String USER_TALK_LIKE = "user_talk_like:";

        // 用户id作为键,说说id作为值,记录用户点赞记录
        String userLikeTalkKey = RedisConstant.USER_TALK_LIKE + StpUtil.getLoginIdAsInt();

        // 判断是否点赞
        if (redisService.hasSetValue(userLikeTalkKey, talkId)) {
            // 已经点赞 即将取消点赞 取消点赞则删除用户id中的说说id
            redisService.deleteSet(userLikeTalkKey, talkId);
            // 说说点赞量-1
            redisService.decrHash(RedisConstant.TALK_LIKE_COUNT, talkId.toString(), 1L);
        } else {
            // 未点赞 即将进行点赞 点赞则在用户id记录说说id
            redisService.setSet(userLikeTalkKey, talkId);
            // 说说点赞量+1
            redisService.incrHash(RedisConstant.TALK_LIKE_COUNT, talkId.toString(), 1L);
        }

    }

}

难点 likeStrategyMap

难点无非就是 如何找到对应的策略 执行特定的策略

这边使用的是 likeStrategyMap

likeStrategyMap 是一个 Map 类型的变量,它存储了策略模式中所有可用的点赞策略。这个 Map 通过 Spring 的依赖注入机制(@Autowired)自动填充。在这个设计中,likeStrategyMap 的键是策略的名称(字符串),而值是实现了 LikeStrategy 接口的具体策略对象。

详细解释

1. Map的键:策略名称

likeStrategyMap 的键是一个字符串,代表了不同的点赞策略的名称。例如,可能的策略有"照片点赞策略"、"评论点赞策略"等。

java


复制代码
private Map<String, LikeStrategy> likeStrategyMap;

这个 Map 通过 Spring 自动注入,它的键值对是:

  • 键(String) :一个策略名称,通常在 LikeTypeEnum 枚举中定义,例如 PHOTO 对应的策略名称是 "photoLikeStrategy"COMMENT 对应的策略名称是 "commentLikeStrategy"
  • 值(LikeStrategy) :一个实现了 LikeStrategy 接口的具体策略实例,具体实现了点赞的行为。
2. Map的值:具体策略对象

likeStrategyMap 的值是 LikeStrategy 接口的实现类。每个实现类封装了不同的点赞逻辑,具体的操作可能是对数据库的修改,或者对其他业务逻辑的处理。

例如,PhotoLikeStrategyCommentLikeStrategy 实现了 LikeStrategy 接口,分别处理照片和评论的点赞:

java


复制代码
public class PhotoLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 执行对照片的点赞逻辑
    }
}

public class CommentLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 执行对评论的点赞逻辑
    }
}
3. Spring的依赖注入

Spring 会根据 @Autowired 注解,自动填充 likeStrategyMap。在启动时,Spring 会扫描所有的 LikeStrategy 实现类,并将它们注册到 likeStrategyMap 中。这个过程会通过 Spring 的 @Service@Component 注解完成。

例如,假设你在 @Service 注解的类中定义了 PhotoLikeStrategyCommentLikeStrategy

java


复制代码
@Service("photoLikeStrategy")
public class PhotoLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 照片点赞逻辑
    }
}

@Service("commentLikeStrategy")
public class CommentLikeStrategy implements LikeStrategy {
    @Override
    public void like(Integer typeId) {
        // 评论点赞逻辑
    }
}

通过这种方式,likeStrategyMap 会被自动填充为:

java


复制代码
{
    "photoLikeStrategy" -> PhotoLikeStrategy 实例,
    "commentLikeStrategy" -> CommentLikeStrategy 实例
}
4. 执行点赞策略

LikeStrategyContext 中,executeLikeStrategy 方法根据传入的 LikeTypeEnum 类型获取对应的策略名称:

java


复制代码
public void executeLikeStrategy(LikeTypeEnum likeType, Integer typeId) {
    likeStrategyMap.get(likeType.getStrategy()).like(typeId);
}

这里的 likeType.getStrategy() 返回的是一个字符串(例如 "photoLikeStrategy""commentLikeStrategy"),likeStrategyMap.get(likeType.getStrategy()) 就会找到对应的策略对象(例如 PhotoLikeStrategyCommentLikeStrategy)。

然后调用这个策略的 like(typeId) 方法来执行相应的点赞逻辑。

总结

  • likeStrategyMap 是一个 Map,存储了所有的点赞策略,键是策略名称(字符串),值是实现了 LikeStrategy 接口的具体策略对象。
  • 通过 Spring 的自动注入机制,likeStrategyMap 会被自动填充,键值对的映射关系基于策略的名称。
  • executeLikeStrategy 方法中,根据 LikeTypeEnum 提供的策略类型,动态选择相应的策略对象执行点赞操作。

这种设计使得策略的选择非常灵活,可以根据需要添加新的点赞策略,而不需要修改原有的

相关推荐
蒜蓉大猩猩1 小时前
Node.js - 模块化与包管理工具
后端·架构·node.js
五行星辰1 小时前
Servlet与JSP:Java的秘密花园入口
java·开发语言·servlet
代码驿站5202 小时前
Scala语言的软件工程
开发语言·后端·golang
Code花园2 小时前
Objective-C语言的多线程编程
开发语言·后端·golang
扶离_flee2 小时前
麦田物语学习笔记:背包物品选择高亮显示和动画
笔记·学习
Rverdoser2 小时前
接口项目架构流程图-thinkphp6-rabbitmq
开发语言·microsoft·ruby
大丈夫立于天地间2 小时前
OSPF - 特殊报文与ospf的机制
网络·网络协议·学习·算法·智能路由器·信息与通信
Jelena技术达人2 小时前
利用 Python 爬虫获取 1688 关键字 API 接口
开发语言·爬虫·python
graceyun2 小时前
C语言初阶习题【23】输出数组的前5项之和
c语言·开发语言·算法
~Yogi3 小时前
NLP学习
人工智能·学习·自然语言处理