设计模式与推荐系统:策略模式选择推荐算法、工厂模式创建推荐器、观察者更新推荐

推荐系统,说白了就是帮用户从海量信息里「挑出最想要的」。我做了这么多年后端,发现推荐系统的代码最容易变成一团乱麻——各种算法混在一起,新增一个推荐策略就要改一大片代码。今天咱们就用三个经典设计模式,把推荐系统拆得清清楚楚。

一、为什么推荐系统需要设计模式?

先说说我踩过的坑。几年前我接手一个电商推荐模块,代码里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 行为
正在重新计算推荐结果...

避坑指南:我曾经在观察者模式里犯过一个错——观察者太多,每次用户行为都触发全量更新,导致系统响应变慢。后来我加了事件过滤机制,只有特定类型的行为才触发更新。比如「点击」行为只更新协同过滤推荐器,「购买」行为才更新所有推荐器。这样既保证了实时性,又不会过度消耗性能。

五、三者如何协同工作?

这三个模式不是孤立的,它们可以完美配合。我画了一张图来说明它们的关系:

设计模式在推荐系统中的协同工作 用户行为(被观察者) 点击、收藏、购买等行为触发事件 观察者模式:UserActionSubject 通知所有观察者 策略选择(策略模式) 根据用户类型、场景动态选择推荐算法 RecommendationContext 管理策略切换 策略:协同过滤 | 热门推荐 | 基于内容 推荐器创建(工厂模式) RecommenderFactory 根据配置创建具体推荐器实例 封装创建逻辑,统一管理配置参数

工作流程是这样的:

  1. 用户触发行为:点击、收藏、购买等,通过观察者模式通知推荐系统
  2. 策略选择:根据用户画像、场景等信息,用策略模式选择合适的推荐算法
  3. 工厂创建:用工厂模式创建对应的推荐器实例,注入配置参数
  4. 执行推荐:推荐器调用具体算法,生成推荐结果返回给用户

你看,三个模式各司其职:观察者模式负责「通知」,策略模式负责「选择」,工厂模式负责「创建」。它们组合起来,就是一个灵活、可扩展的推荐系统架构。

总结一下:设计模式不是银弹,但用在合适的地方确实能大幅提升代码质量。推荐系统这种「算法多变、配置复杂、需要实时响应」的场景,正好是策略模式、工厂模式、观察者模式的用武之地。下次你写推荐系统的时候,不妨试试这个组合拳。


公众号:蓝海资料掘金营,微信deep3321