目录
点赞策略上下文
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
定义了两个类型(如:PHOTO
和COMMENT
),并且每个类型对应一个具体的策略名称(如photoLikeStrategy
和commentLikeStrategy
)。- 通过
getStrategy
方法,LikeTypeEnum
能告诉LikeStrategyContext
使用哪种具体的点赞策略。
LikeStrategy
接口
java
复制代码
public interface LikeStrategy {
void like(Integer typeId);
}
LikeStrategy
是策略接口,定义了like
方法,具体的点赞策略需要实现该接口,并提供具体的点赞逻辑。
具体策略类
例如,PhotoLikeStrategy
和 CommentLikeStrategy
类应该实现 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
(例如,PHOTO
或COMMENT
)来选择合适的点赞策略。 - 通过
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
接口的实现类。每个实现类封装了不同的点赞逻辑,具体的操作可能是对数据库的修改,或者对其他业务逻辑的处理。
例如,PhotoLikeStrategy
和 CommentLikeStrategy
实现了 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
注解的类中定义了 PhotoLikeStrategy
和 CommentLikeStrategy
:
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())
就会找到对应的策略对象(例如 PhotoLikeStrategy
或 CommentLikeStrategy
)。
然后调用这个策略的 like(typeId)
方法来执行相应的点赞逻辑。
总结
likeStrategyMap
是一个Map
,存储了所有的点赞策略,键是策略名称(字符串),值是实现了LikeStrategy
接口的具体策略对象。- 通过 Spring 的自动注入机制,
likeStrategyMap
会被自动填充,键值对的映射关系基于策略的名称。 - 在
executeLikeStrategy
方法中,根据LikeTypeEnum
提供的策略类型,动态选择相应的策略对象执行点赞操作。
这种设计使得策略的选择非常灵活,可以根据需要添加新的点赞策略,而不需要修改原有的