设计模式与推荐系统:策略模式选择推荐算法、工厂模式创建推荐器、观察者更新推荐
推荐系统,说白了就是帮用户从海量信息里「挑出最想要的」。我做了这么多年后端,发现推荐系统的代码最容易变成一团乱麻——各种算法混在一起,新增一个推荐策略就要改一大片代码。今天咱们就用三个经典设计模式,把推荐系统拆得清清楚楚。
一、为什么推荐系统需要设计模式?
先说说我踩过的坑。几年前我接手一个电商推荐模块,代码里if-else嵌套了七八层,什么「协同过滤」「热门推荐」「新品推荐」全写在一个类里。每次产品经理说「加个新算法」,我都得把整个类翻一遍,生怕改漏了什么。后来我重构了这套代码,核心思路就一句话:把变化的部分封装起来,让稳定的部分保持不变。
推荐系统的变化点其实很明确:
- 算法:今天用协同过滤,明天可能换深度学习
- 推荐器创建:不同场景需要不同配置的推荐器
- 数据更新:用户行为变了,推荐结果要实时刷新
这三个变化点,正好对应三个设计模式:策略模式、工厂模式、观察者模式。咱们一个一个来看。
核心思想:用策略模式管理算法族,用工厂模式封装创建逻辑,用观察者模式解耦数据更新。三者配合,推荐系统就像搭积木一样灵活。
二、策略模式:让推荐算法可替换
策略模式,说白了就是把算法封装成独立的策略类。我习惯把每个推荐算法写成一个类,实现同一个接口。这样新增算法时,不用改现有代码,加个新类就行。
先看接口定义:
// 推荐策略接口
public interface RecommendationStrategy {
List<Product> recommend(String userId, int topN);
}
然后实现几个具体策略:
// 协同过滤策略
public class CollaborativeFilteringStrategy implements RecommendationStrategy {
@Override
public List<Product> recommend(String userId, int topN) {
// 实际项目中这里会调用协同过滤算法
System.out.println("使用协同过滤算法为用户 " + userId + " 推荐");
return Arrays.asList(new Product("商品A"), new Product("商品B"));
}
}
// 热门推荐策略
public class HotRecommendationStrategy implements RecommendationStrategy {
@Override
public List<Product> recommend(String userId, int topN) {
System.out.println("使用热门推荐算法为用户 " + userId + " 推荐");
return Arrays.asList(new Product("热门商品1"), new Product("热门商品2"));
}
}
// 基于内容的推荐策略
public class ContentBasedStrategy implements RecommendationStrategy {
@Override
public List<Product> recommend(String userId, int topN) {
System.out.println("使用基于内容推荐算法为用户 " + userId + " 推荐");
return Arrays.asList(new Product("内容相关商品A"), new Product("内容相关商品B"));
}
}
你可能会问:「策略怎么切换?」很简单,用上下文类来管理:
public class RecommendationContext {
private RecommendationStrategy strategy;
public void setStrategy(RecommendationStrategy strategy) {
this.strategy = strategy;
}
public List<Product> executeRecommend(String userId, int topN) {
if (strategy == null) {
throw new IllegalStateException("请先设置推荐策略");
}
return strategy.recommend(userId, topN);
}
}
调用的时候,根据场景动态切换:
RecommendationContext context = new RecommendationContext();
// 新用户用热门推荐
context.setStrategy(new HotRecommendationStrategy());
List<Product> result1 = context.executeRecommend("new_user_001", 10);
// 老用户用协同过滤
context.setStrategy(new CollaborativeFilteringStrategy());
List<Product> result2 = context.executeRecommend("old_user_002", 10);
我的经验:策略模式最爽的地方在于,你可以把算法配置写在数据库里。比如在后台管理页面勾选「新用户用热门推荐,老用户用协同过滤」,前端传个策略名称过来,代码里用反射或工厂创建对应的策略实例。这样产品经理自己就能调算法,不用每次都找开发改代码。
三、工厂模式:统一创建推荐器
策略模式解决了算法替换的问题,但推荐器的创建又成了新麻烦。每个推荐器可能需要不同的配置参数——协同过滤要配置相似度阈值,热门推荐要配置时间窗口。如果到处new对象,代码就散得到处都是。
这时候工厂模式就派上用场了。我一般用简单工厂或者工厂方法模式来封装创建逻辑。
// 推荐器产品接口
public interface Recommender {
List<Product> recommend(String userId, int topN);
}
// 具体推荐器实现
public class CollaborativeFilteringRecommender implements Recommender {
private double similarityThreshold;
public CollaborativeFilteringRecommender(double similarityThreshold) {
this.similarityThreshold = similarityThreshold;
}
@Override
public List<Product> recommend(String userId, int topN) {
System.out.println("协同过滤推荐器,相似度阈值:" + similarityThreshold);
// 实际推荐逻辑
return Arrays.asList(new Product("商品A"), new Product("商品B"));
}
}
public class HotRecommender implements Recommender {
private int timeWindowInHours;
public HotRecommender(int timeWindowInHours) {
this.timeWindowInHours = timeWindowInHours;
}
@Override
public List<Product> recommend(String userId, int topN) {
System.out.println("热门推荐器,时间窗口:" + timeWindowInHours + "小时");
return Arrays.asList(new Product("热门商品1"), new Product("热门商品2"));
}
}
然后创建工厂类:
public class RecommenderFactory {
public static Recommender createRecommender(String type, Map<String, Object> config) {
switch (type) {
case "collaborative":
double threshold = (double) config.getOrDefault("similarityThreshold", 0.7);
return new CollaborativeFilteringRecommender(threshold);
case "hot":
int window = (int) config.getOrDefault("timeWindowInHours", 24);
return new HotRecommender(window);
default:
throw new IllegalArgumentException("不支持的推荐器类型: " + type);
}
}
}
使用的时候,从配置文件或数据库读取参数:
// 假设从配置中心获取
Map<String, Object> config = new HashMap<>();
config.put("similarityThreshold", 0.8);
Recommender recommender = RecommenderFactory.createRecommender("collaborative", config);
List<Product> products = recommender.recommend("user_123", 10);
注意:工厂模式虽然好用,但别滥用。如果你的推荐器类型很少(比如就两三种),直接用if-else也没问题。我见过有人为了用工厂而用工厂,搞出五六层抽象,反而增加了理解成本。记住:设计模式是为了解决问题,不是为了炫技。
四、观察者模式:实时更新推荐结果
推荐系统有个典型场景:用户点了某个商品,或者收藏了某个类别,推荐结果应该立刻刷新。如果每次都要用户手动刷新页面,体验就很差。
观察者模式正好解决这个问题。用户行为作为「被观察者」,推荐器作为「观察者」。用户一有动作,推荐器自动更新。
// 观察者接口
public interface RecommendationObserver {
void update(String userId, UserAction action);
}
// 被观察者(用户行为事件源)
public class UserActionSubject {
private List<RecommendationObserver> observers = new ArrayList<>();
public void attach(RecommendationObserver observer) {
observers.add(observer);
}
public void detach(RecommendationObserver observer) {
observers.remove(observer);
}
public void notifyObservers(String userId, UserAction action) {
for (RecommendationObserver observer : observers) {
observer.update(userId, action);
}
}
// 用户触发行为时调用
public void userPerformedAction(String userId, UserAction action) {
System.out.println("用户 " + userId + " 执行了操作: " + action.getType());
notifyObservers(userId, action);
}
}
// 用户行为类
public class UserAction {
private String type; // "click", "favorite", "purchase"
private String productId;
public UserAction(String type, String productId) {
this.type = type;
this.productId = productId;
}
public String getType() { return type; }
public String getProductId() { return productId; }
}
推荐器实现观察者接口:
public class RealTimeRecommender implements RecommendationObserver {
private String name;
public RealTimeRecommender(String name) {
this.name = name;
}
@Override
public void update(String userId, UserAction action) {
System.out.println("[" + name + "] 收到用户 " + userId + " 的 " + action.getType() + " 行为");
System.out.println("正在重新计算推荐结果...");
// 实际项目中,这里会触发推荐算法的重新计算
}
}
组装起来:
UserActionSubject subject = new UserActionSubject();
RealTimeRecommender recommender1 = new RealTimeRecommender("协同过滤推荐器");
RealTimeRecommender recommender2 = new RealTimeRecommender("热门推荐器");
subject.attach(recommender1);
subject.attach(recommender2);
// 模拟用户点击商品
subject.userPerformedAction("user_001", new UserAction("click", "product_123"));
运行结果:
用户 user_001 执行了操作: click
[协同过滤推荐器] 收到用户 user_001 的 click 行为
正在重新计算推荐结果...
[热门推荐器] 收到用户 user_001 的 click 行为
正在重新计算推荐结果...
避坑指南:我曾经在观察者模式里犯过一个错——观察者太多,每次用户行为都触发全量更新,导致系统响应变慢。后来我加了事件过滤机制,只有特定类型的行为才触发更新。比如「点击」行为只更新协同过滤推荐器,「购买」行为才更新所有推荐器。这样既保证了实时性,又不会过度消耗性能。
五、三者如何协同工作?
这三个模式不是孤立的,它们可以完美配合。我画了一张图来说明它们的关系:
工作流程是这样的:
- 用户触发行为:点击、收藏、购买等,通过观察者模式通知推荐系统
- 策略选择:根据用户画像、场景等信息,用策略模式选择合适的推荐算法
- 工厂创建:用工厂模式创建对应的推荐器实例,注入配置参数
- 执行推荐:推荐器调用具体算法,生成推荐结果返回给用户
你看,三个模式各司其职:观察者模式负责「通知」,策略模式负责「选择」,工厂模式负责「创建」。它们组合起来,就是一个灵活、可扩展的推荐系统架构。
总结一下:设计模式不是银弹,但用在合适的地方确实能大幅提升代码质量。推荐系统这种「算法多变、配置复杂、需要实时响应」的场景,正好是策略模式、工厂模式、观察者模式的用武之地。下次你写推荐系统的时候,不妨试试这个组合拳。
公众号:蓝海资料掘金营,微信deep3321