投票系統常面臨多種規則(單選、多選、權重、限時等)。本文採用策略模式將投票算法封裝為可互換策略,實現規則與業務流程解耦,支援運行時動態切換投票邏輯。
// 1. 策略接口:統一投票行為
public interface VoteStrategy {
/**
* 執行投票
* @param voterId 投票人ID
* @param optionId 選項ID
* @param context 上下文(如權重、時間等)
* @return 投票結果
*/
VoteResult vote(String voterId, String optionId, VoteContext context);
}
// 2. 上下文對象:攜帶動態參數
public class VoteContext {
private Map params = new HashMap<>();
public void put(String key, Object val) { params.put(key, val); }
public Object get(String key) { return params.get(key); }
}
// 3. 結果封裝
public class VoteResult {
private boolean success;
private String message;
private Map stats; // 實時統計
// getter/setter/builder 省略
}
// 單選策略:每人僅能投一票,覆蓋舊選擇
public class SingleChoiceStrategy implements VoteStrategy {
private final Map votes = new ConcurrentHashMap<>(); // voterId -> optionId
private final Map stats = new ConcurrentHashMap<>();
@Override
public VoteResult vote(String voterId, String optionId, VoteContext ctx) {
String prev = votes.put(voterId, optionId);
stats.computeIfAbsent(optionId, k -> new AtomicInteger()).incrementAndGet();
if (prev != null) {
stats.computeIfAbsent(prev, k -> new AtomicInteger()).decrementAndGet();
}
return VoteResult.success("單選投票成功", snapshot(stats));
}
}
// 多選策略:每人可投多選項,但每選項限一票
public class MultiChoiceStrategy implements VoteStrategy {
private final Map> votes = new ConcurrentHashMap<>(); // voterId -> Set
private final Map stats = new ConcurrentHashMap<>();
@Override
public VoteResult vote(String voterId, String optionId, VoteContext ctx) {
votes.computeIfAbsent(voterId, k -> ConcurrentHashMap.newKeySet()).add(optionId);
stats.computeIfAbsent(optionId, k -> new AtomicInteger()).incrementAndGet();
return VoteResult.success("多選投票成功", snapshot(stats));
}
}
// 權重策略:不同用戶票數權重不同(如 VIP 票值 2)
public class WeightedStrategy implements VoteStrategy {
private final Map userWeight; // 注入
private final Map stats = new ConcurrentHashMap<>();
public WeightedStrategy(Map userWeight) { this.userWeight = userWeight; }
@Override
public VoteResult vote(String voterId, String optionId, VoteContext ctx) {
int w = userWeight.getOrDefault(voterId, 1);
stats.computeIfAbsent(optionId, k -> new AtomicInteger()).addAndGet(w);
return VoteResult.success("權重投票成功,權重=" + w, snapshot(stats));
}
}
public class VoteService {
private final Map strategies = new HashMap<>();
private VoteStrategy currentStrategy;
public VoteService() {
// 註冊策略
strategies.put("single", new SingleChoiceStrategy());
strategies.put("multi", new MultiChoiceStrategy());
strategies.put("weighted", new WeightedStrategy(loadUserWeights()));
// 默認策略
currentStrategy = strategies.get("single");
}
public void setStrategy(String strategyKey) {
VoteStrategy s = strategies.get(strategyKey);
if (s == null) throw new IllegalArgumentException("未知策略: " + strategyKey);
this.currentStrategy = s;
}
public VoteResult vote(String voterId, String optionId, VoteContext ctx) {
// 可在此處添加通用校驗:投票時間、用戶狀態、頻率限制等
return currentStrategy.vote(voterId, optionId, ctx);
}
}
public class Main {
public static void main(String[] args) {
VoteService service = new VoteService();
// 單選模式
service.setStrategy("single");
service.vote("user1", "optionA", new VoteContext());
service.vote("user1", "optionB", new VoteContext()); // 覆蓋 A
// 切換多選模式
service.setStrategy("multi");
service.vote("user2", "optionA", new VoteContext());
service.vote("user2", "optionB", new VoteContext()); // 同時生效
// 權重模式
service.setStrategy("weighted");
service.vote("vip1", "optionA", new VoteContext()); // 權重 2
}
}
CompositeStrategy 組合多規則(如「限時+單選」)。| 維度 | 硬編碼 if/else | 策略模式 |
|---|---|---|
| 新增規則 | 修改核心類,風險高 | 新增類,零修改核心 |
| 測試 | 需測全組合 | 單獨測試每策略 |
| 運行時切換 | 困難 | setStrategy() 即時生效 |
| 代碼清晰度 | 邏輯糅雜 | 職責單一、易閱讀 |