欢迎光临
我们一直在努力

策略模式实战:用工厂加Spring消除冗长if-else

策略模式最常见的落地场景,就是一段代码里出现了越来越长的 if-else。

比如登录方式有账号密码、短信验证码、微信授权、QQ 授权;支付方式有支付宝、微信、银行卡;优惠计算有满减、折扣、会员价。每一种方式都是一套独立算法,但调用入口又是同一个。

一句话概括:策略模式把一组可替换的算法封装成独立策略类,业务流程只依赖统一接口,不再关心具体算法怎么实现。

从 if-else 开始看问题

先看一个登录例子:

public LoginResp login(LoginReq loginReq) {
if ("account".equals(loginReq.getType())) {
return accountLogin(loginReq);
} else if ("sms".equals(loginReq.getType())) {
return smsLogin(loginReq);
} else if ("we_chat".equals(loginReq.getType())) {
return weChatLogin(loginReq);
}

LoginResp resp = new LoginResp();
resp.setSuccess(false);
return resp;
}

这段代码短的时候还能接受,一旦登录方式继续增加,就会出现几个问题:

问题影响
分支越来越长 主流程越来越难读
新增方式要改核心方法 违反开闭原则
每个分支逻辑差异大 测试和维护成本变高
业务入口承担太多职责 登录流程和登录算法耦合

#mermaid-svg-BfFSSH1RUl698F9G{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BfFSSH1RUl698F9G .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BfFSSH1RUl698F9G .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BfFSSH1RUl698F9G .error-icon{fill:#552222;}#mermaid-svg-BfFSSH1RUl698F9G .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BfFSSH1RUl698F9G .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BfFSSH1RUl698F9G .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BfFSSH1RUl698F9G .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BfFSSH1RUl698F9G .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BfFSSH1RUl698F9G .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BfFSSH1RUl698F9G .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BfFSSH1RUl698F9G .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BfFSSH1RUl698F9G .marker.cross{stroke:#333333;}#mermaid-svg-BfFSSH1RUl698F9G svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BfFSSH1RUl698F9G p{margin:0;}#mermaid-svg-BfFSSH1RUl698F9G .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-BfFSSH1RUl698F9G .cluster-label text{fill:#333;}#mermaid-svg-BfFSSH1RUl698F9G .cluster-label span{color:#333;}#mermaid-svg-BfFSSH1RUl698F9G .cluster-label span p{background-color:transparent;}#mermaid-svg-BfFSSH1RUl698F9G .label text,#mermaid-svg-BfFSSH1RUl698F9G span{fill:#333;color:#333;}#mermaid-svg-BfFSSH1RUl698F9G .node rect,#mermaid-svg-BfFSSH1RUl698F9G .node circle,#mermaid-svg-BfFSSH1RUl698F9G .node ellipse,#mermaid-svg-BfFSSH1RUl698F9G .node polygon,#mermaid-svg-BfFSSH1RUl698F9G .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BfFSSH1RUl698F9G .rough-node .label text,#mermaid-svg-BfFSSH1RUl698F9G .node .label text,#mermaid-svg-BfFSSH1RUl698F9G .image-shape .label,#mermaid-svg-BfFSSH1RUl698F9G .icon-shape .label{text-anchor:middle;}#mermaid-svg-BfFSSH1RUl698F9G .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BfFSSH1RUl698F9G .rough-node .label,#mermaid-svg-BfFSSH1RUl698F9G .node .label,#mermaid-svg-BfFSSH1RUl698F9G .image-shape .label,#mermaid-svg-BfFSSH1RUl698F9G .icon-shape .label{text-align:center;}#mermaid-svg-BfFSSH1RUl698F9G .node.clickable{cursor:pointer;}#mermaid-svg-BfFSSH1RUl698F9G .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BfFSSH1RUl698F9G .arrowheadPath{fill:#333333;}#mermaid-svg-BfFSSH1RUl698F9G .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BfFSSH1RUl698F9G .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BfFSSH1RUl698F9G .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BfFSSH1RUl698F9G .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BfFSSH1RUl698F9G .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BfFSSH1RUl698F9G .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BfFSSH1RUl698F9G .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BfFSSH1RUl698F9G .cluster text{fill:#333;}#mermaid-svg-BfFSSH1RUl698F9G .cluster span{color:#333;}#mermaid-svg-BfFSSH1RUl698F9G div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BfFSSH1RUl698F9G .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BfFSSH1RUl698F9G rect.text{fill:none;stroke-width:0;}#mermaid-svg-BfFSSH1RUl698F9G .icon-shape,#mermaid-svg-BfFSSH1RUl698F9G .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BfFSSH1RUl698F9G .icon-shape p,#mermaid-svg-BfFSSH1RUl698F9G .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BfFSSH1RUl698F9G .icon-shape .label rect,#mermaid-svg-BfFSSH1RUl698F9G .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BfFSSH1RUl698F9G .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BfFSSH1RUl698F9G .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BfFSSH1RUl698F9G :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

account

sms

we_chat

qq

登录入口

type 是什么

账号密码登录

短信验证码登录

微信授权登录

QQ 授权登录

返回登录结果

策略模式就是要把这些分支拆出去。

策略模式的三个角色

策略模式通常有三个角色:

角色作用
抽象策略 定义所有策略都要实现的接口
具体策略 实现某一种具体算法
上下文 持有策略对象,并在合适时机调用

登录场景可以这样抽象:

public interface UserGranter {
LoginResp login(LoginReq loginReq);
}

账号登录策略:

@Component
public class AccountGranter implements UserGranter {
@Override
public LoginResp login(LoginReq loginReq) {
// 账号密码登录逻辑
return new LoginResp();
}
}

短信登录策略:

@Component
public class SmsGranter implements UserGranter {
@Override
public LoginResp login(LoginReq loginReq) {
// 手机验证码登录逻辑
return new LoginResp();
}
}

整体关系变成这样:

#mermaid-svg-0aFTplxiiy8D3yuw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0aFTplxiiy8D3yuw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0aFTplxiiy8D3yuw .error-icon{fill:#552222;}#mermaid-svg-0aFTplxiiy8D3yuw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0aFTplxiiy8D3yuw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0aFTplxiiy8D3yuw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0aFTplxiiy8D3yuw .marker.cross{stroke:#333333;}#mermaid-svg-0aFTplxiiy8D3yuw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0aFTplxiiy8D3yuw p{margin:0;}#mermaid-svg-0aFTplxiiy8D3yuw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0aFTplxiiy8D3yuw .cluster-label text{fill:#333;}#mermaid-svg-0aFTplxiiy8D3yuw .cluster-label span{color:#333;}#mermaid-svg-0aFTplxiiy8D3yuw .cluster-label span p{background-color:transparent;}#mermaid-svg-0aFTplxiiy8D3yuw .label text,#mermaid-svg-0aFTplxiiy8D3yuw span{fill:#333;color:#333;}#mermaid-svg-0aFTplxiiy8D3yuw .node rect,#mermaid-svg-0aFTplxiiy8D3yuw .node circle,#mermaid-svg-0aFTplxiiy8D3yuw .node ellipse,#mermaid-svg-0aFTplxiiy8D3yuw .node polygon,#mermaid-svg-0aFTplxiiy8D3yuw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0aFTplxiiy8D3yuw .rough-node .label text,#mermaid-svg-0aFTplxiiy8D3yuw .node .label text,#mermaid-svg-0aFTplxiiy8D3yuw .image-shape .label,#mermaid-svg-0aFTplxiiy8D3yuw .icon-shape .label{text-anchor:middle;}#mermaid-svg-0aFTplxiiy8D3yuw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0aFTplxiiy8D3yuw .rough-node .label,#mermaid-svg-0aFTplxiiy8D3yuw .node .label,#mermaid-svg-0aFTplxiiy8D3yuw .image-shape .label,#mermaid-svg-0aFTplxiiy8D3yuw .icon-shape .label{text-align:center;}#mermaid-svg-0aFTplxiiy8D3yuw .node.clickable{cursor:pointer;}#mermaid-svg-0aFTplxiiy8D3yuw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0aFTplxiiy8D3yuw .arrowheadPath{fill:#333333;}#mermaid-svg-0aFTplxiiy8D3yuw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0aFTplxiiy8D3yuw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0aFTplxiiy8D3yuw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0aFTplxiiy8D3yuw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0aFTplxiiy8D3yuw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0aFTplxiiy8D3yuw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0aFTplxiiy8D3yuw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0aFTplxiiy8D3yuw .cluster text{fill:#333;}#mermaid-svg-0aFTplxiiy8D3yuw .cluster span{color:#333;}#mermaid-svg-0aFTplxiiy8D3yuw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0aFTplxiiy8D3yuw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0aFTplxiiy8D3yuw rect.text{fill:none;stroke-width:0;}#mermaid-svg-0aFTplxiiy8D3yuw .icon-shape,#mermaid-svg-0aFTplxiiy8D3yuw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0aFTplxiiy8D3yuw .icon-shape p,#mermaid-svg-0aFTplxiiy8D3yuw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0aFTplxiiy8D3yuw .icon-shape .label rect,#mermaid-svg-0aFTplxiiy8D3yuw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0aFTplxiiy8D3yuw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0aFTplxiiy8D3yuw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0aFTplxiiy8D3yuw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

UserService

UserGranter

AccountGranter

SmsGranter

WeChatGranter

账号密码登录算法

短信验证码登录算法

微信授权登录算法

业务入口不再关心每种登录方式的细节,只关心拿到哪个策略。

策略模式为什么常和工厂一起用

策略模式解决“算法怎么封装”,但还有一个问题:运行时怎么找到对应策略。

如果你手动写:

if ("account".equals(type)) {
return new AccountGranter();
}

那只是把 if-else 从登录方法挪到了另一个地方。

更常见的做法是“策略 + 工厂 + Spring 容器”。所有策略交给 Spring 管理,工厂负责按照类型找到对应 Bean。

配置可以长这样:

login:
types:
account: accountGranter
sms: smsGranter
we_chat: weChatGranter

工厂维护一个策略池。这里建议用构造器注入,不要用 static 可变 Map,也不要在工厂里到处拿 ApplicationContext。原因是策略映射属于这个工厂对象的状态,启动时一次性构建并校验,后续只读会更稳。

@Component
public class UserLoginFactory {

private final Map<String, UserGranter> granterPool;

public UserLoginFactory(LoginTypeConfig loginTypeConfig,
Map<String, UserGranter> granterBeans) {
this.granterPool = loginTypeConfig.getTypes().entrySet().stream()
.collect(Collectors.toUnmodifiableMap(
Map.Entry::getKey,
entry -> {
UserGranter granter = granterBeans.get(entry.getValue());
if (granter == null) {
throw new IllegalStateException(
"missing login granter bean: " + entry.getValue());
}
return granter;
}));
}

public UserGranter getGranter(String grantType) {
return granterPool.get(grantType);
}
}

Map<String, UserGranter> 由 Spring 注入时,key 默认是 Bean 名称。上面这段代码会把配置里的业务类型,比如 account,映射到真正的策略 Bean,比如 accountGranter。如果配置写错,启动阶段直接失败,比等到用户登录时才报空指针更容易定位。

调用方只做一件事:

public LoginResp login(LoginReq loginReq) {
UserGranter granter = factory.getGranter(loginReq.getType());
if (granter == null) {
LoginResp resp = new LoginResp();
resp.setSuccess(false);
return resp;
}
return granter.login(loginReq);
}

#mermaid-svg-0MXG5dipfyFiATTe{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0MXG5dipfyFiATTe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0MXG5dipfyFiATTe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0MXG5dipfyFiATTe .error-icon{fill:#552222;}#mermaid-svg-0MXG5dipfyFiATTe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0MXG5dipfyFiATTe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0MXG5dipfyFiATTe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0MXG5dipfyFiATTe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0MXG5dipfyFiATTe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0MXG5dipfyFiATTe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0MXG5dipfyFiATTe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0MXG5dipfyFiATTe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0MXG5dipfyFiATTe .marker.cross{stroke:#333333;}#mermaid-svg-0MXG5dipfyFiATTe svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0MXG5dipfyFiATTe p{margin:0;}#mermaid-svg-0MXG5dipfyFiATTe .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0MXG5dipfyFiATTe .cluster-label text{fill:#333;}#mermaid-svg-0MXG5dipfyFiATTe .cluster-label span{color:#333;}#mermaid-svg-0MXG5dipfyFiATTe .cluster-label span p{background-color:transparent;}#mermaid-svg-0MXG5dipfyFiATTe .label text,#mermaid-svg-0MXG5dipfyFiATTe span{fill:#333;color:#333;}#mermaid-svg-0MXG5dipfyFiATTe .node rect,#mermaid-svg-0MXG5dipfyFiATTe .node circle,#mermaid-svg-0MXG5dipfyFiATTe .node ellipse,#mermaid-svg-0MXG5dipfyFiATTe .node polygon,#mermaid-svg-0MXG5dipfyFiATTe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0MXG5dipfyFiATTe .rough-node .label text,#mermaid-svg-0MXG5dipfyFiATTe .node .label text,#mermaid-svg-0MXG5dipfyFiATTe .image-shape .label,#mermaid-svg-0MXG5dipfyFiATTe .icon-shape .label{text-anchor:middle;}#mermaid-svg-0MXG5dipfyFiATTe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0MXG5dipfyFiATTe .rough-node .label,#mermaid-svg-0MXG5dipfyFiATTe .node .label,#mermaid-svg-0MXG5dipfyFiATTe .image-shape .label,#mermaid-svg-0MXG5dipfyFiATTe .icon-shape .label{text-align:center;}#mermaid-svg-0MXG5dipfyFiATTe .node.clickable{cursor:pointer;}#mermaid-svg-0MXG5dipfyFiATTe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0MXG5dipfyFiATTe .arrowheadPath{fill:#333333;}#mermaid-svg-0MXG5dipfyFiATTe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0MXG5dipfyFiATTe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0MXG5dipfyFiATTe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0MXG5dipfyFiATTe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0MXG5dipfyFiATTe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0MXG5dipfyFiATTe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0MXG5dipfyFiATTe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0MXG5dipfyFiATTe .cluster text{fill:#333;}#mermaid-svg-0MXG5dipfyFiATTe .cluster span{color:#333;}#mermaid-svg-0MXG5dipfyFiATTe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0MXG5dipfyFiATTe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0MXG5dipfyFiATTe rect.text{fill:none;stroke-width:0;}#mermaid-svg-0MXG5dipfyFiATTe .icon-shape,#mermaid-svg-0MXG5dipfyFiATTe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0MXG5dipfyFiATTe .icon-shape p,#mermaid-svg-0MXG5dipfyFiATTe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0MXG5dipfyFiATTe .icon-shape .label rect,#mermaid-svg-0MXG5dipfyFiATTe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0MXG5dipfyFiATTe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0MXG5dipfyFiATTe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0MXG5dipfyFiATTe :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

登录请求

读取 type

UserLoginFactory

策略池 Map

是否找到策略

返回登录失败

调用 UserGranter.login

返回登录结果

这样新增 QQ 登录时,不需要改 UserService:

  • 新增 QqGranter implements UserGranter
  • 加上 @Component
  • 配置里增加 qq: qqGranter
  • 策略模式适合哪些业务

    只要满足“同一入口,多套可替换规则”,就可以考虑策略模式。

    场景策略
    登录 账号、短信、微信、QQ
    支付 支付宝、微信、银行卡、余额
    优惠 满减、折扣、会员价、优惠券
    Excel 解析 xls、xlsx、csv
    运费计算 按重量、按地区、按会员等级
    消息发送 短信、邮件、站内信、企业微信

    #mermaid-svg-2hEjNYiRMkrQOP0b{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2hEjNYiRMkrQOP0b .error-icon{fill:#552222;}#mermaid-svg-2hEjNYiRMkrQOP0b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2hEjNYiRMkrQOP0b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2hEjNYiRMkrQOP0b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2hEjNYiRMkrQOP0b .marker.cross{stroke:#333333;}#mermaid-svg-2hEjNYiRMkrQOP0b svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2hEjNYiRMkrQOP0b p{margin:0;}#mermaid-svg-2hEjNYiRMkrQOP0b .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster-label text{fill:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster-label span{color:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster-label span p{background-color:transparent;}#mermaid-svg-2hEjNYiRMkrQOP0b .label text,#mermaid-svg-2hEjNYiRMkrQOP0b span{fill:#333;color:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b .node rect,#mermaid-svg-2hEjNYiRMkrQOP0b .node circle,#mermaid-svg-2hEjNYiRMkrQOP0b .node ellipse,#mermaid-svg-2hEjNYiRMkrQOP0b .node polygon,#mermaid-svg-2hEjNYiRMkrQOP0b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2hEjNYiRMkrQOP0b .rough-node .label text,#mermaid-svg-2hEjNYiRMkrQOP0b .node .label text,#mermaid-svg-2hEjNYiRMkrQOP0b .image-shape .label,#mermaid-svg-2hEjNYiRMkrQOP0b .icon-shape .label{text-anchor:middle;}#mermaid-svg-2hEjNYiRMkrQOP0b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2hEjNYiRMkrQOP0b .rough-node .label,#mermaid-svg-2hEjNYiRMkrQOP0b .node .label,#mermaid-svg-2hEjNYiRMkrQOP0b .image-shape .label,#mermaid-svg-2hEjNYiRMkrQOP0b .icon-shape .label{text-align:center;}#mermaid-svg-2hEjNYiRMkrQOP0b .node.clickable{cursor:pointer;}#mermaid-svg-2hEjNYiRMkrQOP0b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2hEjNYiRMkrQOP0b .arrowheadPath{fill:#333333;}#mermaid-svg-2hEjNYiRMkrQOP0b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2hEjNYiRMkrQOP0b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2hEjNYiRMkrQOP0b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2hEjNYiRMkrQOP0b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2hEjNYiRMkrQOP0b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2hEjNYiRMkrQOP0b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster text{fill:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b .cluster span{color:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2hEjNYiRMkrQOP0b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2hEjNYiRMkrQOP0b rect.text{fill:none;stroke-width:0;}#mermaid-svg-2hEjNYiRMkrQOP0b .icon-shape,#mermaid-svg-2hEjNYiRMkrQOP0b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2hEjNYiRMkrQOP0b .icon-shape p,#mermaid-svg-2hEjNYiRMkrQOP0b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2hEjNYiRMkrQOP0b .icon-shape .label rect,#mermaid-svg-2hEjNYiRMkrQOP0b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2hEjNYiRMkrQOP0b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2hEjNYiRMkrQOP0b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2hEjNYiRMkrQOP0b :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    统一业务入口

    策略选择

    策略 A

    策略 B

    策略 C

    统一返回结果

    策略模式不是为了消灭所有 if。如果只有两个分支,而且未来不会扩展,直接 if 更清楚。策略模式适合分支稳定成为“多种规则”的场景。

    常见坑

    坑一:策略类太碎

    不要把几行简单逻辑也拆成一堆策略类。如果业务没有扩展性,拆完只会增加跳转成本。

    坑二:策略选择逻辑仍然很复杂

    策略模式应该让选择逻辑变简单。如果选择策略本身要写几十行判断,那可能需要先整理业务规则,或者引入规则引擎、配置化路由,而不是继续堆策略。

    坑三:策略返回值不统一

    所有策略最好返回统一结果对象,否则调用方还要根据策略类型做二次判断,等于把复杂度又带回来了。

    #mermaid-svg-P6seDcVyQmsZhr94{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-P6seDcVyQmsZhr94 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-P6seDcVyQmsZhr94 .error-icon{fill:#552222;}#mermaid-svg-P6seDcVyQmsZhr94 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-P6seDcVyQmsZhr94 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-P6seDcVyQmsZhr94 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-P6seDcVyQmsZhr94 .marker.cross{stroke:#333333;}#mermaid-svg-P6seDcVyQmsZhr94 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-P6seDcVyQmsZhr94 p{margin:0;}#mermaid-svg-P6seDcVyQmsZhr94 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-P6seDcVyQmsZhr94 .cluster-label text{fill:#333;}#mermaid-svg-P6seDcVyQmsZhr94 .cluster-label span{color:#333;}#mermaid-svg-P6seDcVyQmsZhr94 .cluster-label span p{background-color:transparent;}#mermaid-svg-P6seDcVyQmsZhr94 .label text,#mermaid-svg-P6seDcVyQmsZhr94 span{fill:#333;color:#333;}#mermaid-svg-P6seDcVyQmsZhr94 .node rect,#mermaid-svg-P6seDcVyQmsZhr94 .node circle,#mermaid-svg-P6seDcVyQmsZhr94 .node ellipse,#mermaid-svg-P6seDcVyQmsZhr94 .node polygon,#mermaid-svg-P6seDcVyQmsZhr94 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-P6seDcVyQmsZhr94 .rough-node .label text,#mermaid-svg-P6seDcVyQmsZhr94 .node .label text,#mermaid-svg-P6seDcVyQmsZhr94 .image-shape .label,#mermaid-svg-P6seDcVyQmsZhr94 .icon-shape .label{text-anchor:middle;}#mermaid-svg-P6seDcVyQmsZhr94 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-P6seDcVyQmsZhr94 .rough-node .label,#mermaid-svg-P6seDcVyQmsZhr94 .node .label,#mermaid-svg-P6seDcVyQmsZhr94 .image-shape .label,#mermaid-svg-P6seDcVyQmsZhr94 .icon-shape .label{text-align:center;}#mermaid-svg-P6seDcVyQmsZhr94 .node.clickable{cursor:pointer;}#mermaid-svg-P6seDcVyQmsZhr94 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-P6seDcVyQmsZhr94 .arrowheadPath{fill:#333333;}#mermaid-svg-P6seDcVyQmsZhr94 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-P6seDcVyQmsZhr94 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-P6seDcVyQmsZhr94 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-P6seDcVyQmsZhr94 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-P6seDcVyQmsZhr94 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-P6seDcVyQmsZhr94 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-P6seDcVyQmsZhr94 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-P6seDcVyQmsZhr94 .cluster text{fill:#333;}#mermaid-svg-P6seDcVyQmsZhr94 .cluster span{color:#333;}#mermaid-svg-P6seDcVyQmsZhr94 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-P6seDcVyQmsZhr94 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-P6seDcVyQmsZhr94 rect.text{fill:none;stroke-width:0;}#mermaid-svg-P6seDcVyQmsZhr94 .icon-shape,#mermaid-svg-P6seDcVyQmsZhr94 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-P6seDcVyQmsZhr94 .icon-shape p,#mermaid-svg-P6seDcVyQmsZhr94 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-P6seDcVyQmsZhr94 .icon-shape .label rect,#mermaid-svg-P6seDcVyQmsZhr94 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-P6seDcVyQmsZhr94 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-P6seDcVyQmsZhr94 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-P6seDcVyQmsZhr94 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    策略模式落地

    统一接口

    统一入参

    统一返回值

    独立策略实现

    稳定策略选择方式

    面试怎么说

    可以这样回答:

    我在项目里用策略模式处理过多登录方式、多支付方式这类场景。它们都是同一个业务入口,但具体处理逻辑不同。如果直接写 if-else,新增一种方式就要修改核心业务方法。我们会定义一个统一策略接口,比如 UserGranter,每种登录方式实现自己的策略类,并交给 Spring 容器管理。再提供一个工厂,根据请求里的 type 从策略池中拿到对应策略并调用。这样新增策略时只需要新增实现类和配置,不需要改主流程代码。

    小结

    策略模式的价值不是“把 if-else 换成类”,而是把可变算法从稳定流程里拆出来。

    可以这样记:

    策略模式负责封装算法,工厂负责选择算法,Spring 负责管理算法对象。

    当一个业务入口后面挂着多套规则,并且这些规则还会继续扩展时,这个组合非常好用。

    赞(0)
    未经允许不得转载:171主机测评 » 策略模式实战:用工厂加Spring消除冗长if-else
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址