策略模式感觉使用中还是很常见的,它属于行为型模式。具体的概念嘛什么的网上很多,而且网上都喜欢拿出行方式做例子,也不知道是不是出自同一篇文章。
个人使用下来感觉策略模式用来处理if、else或者swich、case这样的代码最合适了。拿我在实际中的使用举例子:当前的程序需求是接收到某个地方传过来的HttpRequest,然后需要根据目前设备的状态来选择不同的方式来处理并组成HttpResponse,最后返回Response,另外,设备的状态由设备上报,云端不参与(这个地方后面讲状态模式与策略模式的区别的时候会用到)。废话不多说,直接撸代码上来:
public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
if (log.isDebugEnabled()) log.debug("PhoneCallHandler Handle phone call start = " + new Date());
String userId = handlerInput.getRequestEnvelope().getContext().getSystem().getUser().getUserId();
String deviceId = handlerInput.getRequestEnvelope().getContext().getSystem().getDevice().getDeviceId();
if (log.isDebugEnabled()) log.debug("userId = " + userId + "deviceId = " + deviceId);
String userPhoneNumber = PhoneControllerUtil.searchPhoneNumberByUserId(userId);
//根据状态选择对应的策略
String callId = "";
PhoneStateEntity phoneStateEntity = PhoneControllerUtil.getPhoneState(userId, deviceId);
if (phoneStateEntity == null) {
stateStrategy = new IdleStateStrategy();
} else {
callId = phoneStateEntity.getCallId();
switch (phoneStateEntity.getState()) {
case "ACTIVE":
stateStrategy = new ActiveStateStrategy();
break;
case "TRYING":
stateStrategy = new TryingStateStrategy();
break;
case "OUTBOUND_RINGING":
stateStrategy = new OutboundRingingStateStrategy();
break;
case "INBOUND_RINGING":
stateStrategy = new InboundRingingStateStrategy();
break;
case "IDLE":
stateStrategy = new IdleStateStrategy();
break;
case "INVITED":
stateStrategy = new InvitedStateStrategy();
break;
}
}
//================End===================
//解析云端来的事件数据并分发给不同的策略
Slot slot = intentRequest.getIntent().getSlots().get("SlotName");
String value = slot.getValue();
if (log.isDebugEnabled()) log.debug("PhoneCallHandler value = " + value);
try {
JSONObject jsonObject = JSON.parseObject(value);
String name = null;
String namePinyin = null;
String targetPhoneNumber = null;
String operation = null;
String answer = jsonObject.getString("answer");
JSONObject parameterObject = jsonObject.getJSONObject("parameters");
if (parameterObject.containsKey("operation")) {
operation = parameterObject.getString("operation");
}
if (operation == null) operation = "PHONE_CALL";
ResponseBuilder builder = handlerInput.getResponseBuilder();
Optional<Response> optional = null;
switch (operation) {
case "PHONE_CALL":
PhoneCallParameter phoneCallParameter = new PhoneCallParameter();
phoneCallParameter.setAnswer(answer);
phoneCallParameter.setUserPhoneNumber(userPhoneNumber);
if (parameterObject.containsKey("name")) {
name = parameterObject.getString("name");
phoneCallParameter.setName(name);
}
if (parameterObject.containsKey("name_pinyin")) {
namePinyin = parameterObject.getString("name_pinyin");
phoneCallParameter.setNamePinYin(namePinyin);
}
if (parameterObject.containsKey("phone_number")) {
targetPhoneNumber = parameterObject.getString("phone_number");
phoneCallParameter.setTargetPhoneNumber(targetPhoneNumber);
}
optional = stateStrategy.handlePhoneCall(builder, phoneCallParameter);
break;
case "ACCEPT":
AcceptParameter acceptParameter = new AcceptParameter();
acceptParameter.setAnswer(answer);
acceptParameter.setCallId(callId);
optional = stateStrategy.handleAccept(builder, acceptParameter);
break;
case "REFUSE":
RefuseParameter refuseParameter = new RefuseParameter();
refuseParameter.setAnswer(answer);
refuseParameter.setCallId(callId);
optional = stateStrategy.handleRefuse(builder, refuseParameter);
break;
case "CALLING":
CallingParameter callingParameter = new CallingParameter();
callingParameter.setAnswer(answer);
optional = stateStrategy.handleCalling(builder, callingParameter);
break;
case "SET":
SetParameter setParameter = new SetParameter();
setParameter.setAnswer(answer);
optional = stateStrategy.handleSet(builder, setParameter);
break;
case "CHANGE":
ChangeParameter changeParameter = new ChangeParameter();
changeParameter.setAnswer(answer);
optional = stateStrategy.handleChange(builder, changeParameter);
break;
case "RECORD":
RecordParameter recordParameter = new RecordParameter();
recordParameter.setAnswer(answer);
optional = stateStrategy.handleRecord(builder, recordParameter);
break;
default:
optional = stateStrategy.handleDefault(builder, value);
break;
}
//================End===================
if (optional == null) {
log.error("PhoneCallHandler optional is null");
optional = builder.withShouldEndSession(true).build();
}
if (log.isDebugEnabled()) log.debug("PhoneCallHandler Handle phone call end = " + new Date());
return optional;
} catch (Exception e) {
log.error("PhoneCallHandler Handle phone error = " + e.toString());
e.printStackTrace();
return handlerInput.getResponseBuilder()
.withShouldEndSession(true)
.build();
}
}
从代码里看,整体结构分为两部分,第一部分是根据当前的状态选择对应的策略,第二部分是用策略去处理不同的事件。如果不用策略模式,那么代码的结构就会变为:在每一个事件里都需要判断用户的状态,然后做对应的处理,这样代码不仅臃肿,而且维护起来很困难。使用了策略模式之后,只需要把对应的数据传递给不同的策略,具体怎么处理完全由对应的策略决定,不需要自己再关心处理逻辑,降低了耦合;而且当需要扩展,例如扩展新的状态的时候,那么就只需要增加对应的策略即可,可扩展性提高。但是缺点也很明显:类变多,有多少个策略就需要有多少个类。
另外,这里可能会有人感觉疑惑(我当时就纠结了很久),就是既然是根据不同的状态去处理不同的业务,为什么不使用状态模式??这个地方其实是我想了很久才想到的,就是状态模式和策略模式到底有什么区别,网上搜了很多讲解,从各种讲解里来看,最大的区别就是状态模式里处理事件包含着状态的变化,但是策略模式不包含。从例子的角度来讲:策略模式以出行方式为例子,用户需要到达目的地,采用不同的出行方式,结果是;状态模式以桶放水为例子,在有水和没水的状态下处理方式不同。乍看之下感觉两者没什么区别,但是状态模式下有一点很容易忽略:桶在放水的过程中水会减少,很可能会有有水状态变为没水,此时会有状态的变化!!这里就是两者最大的区别。
回到代码本身,这里不用状态模式的原因也就很清楚了:本身是选择策略依托于当前的状态,程序状态的变化不是自身导致的(完全由客户端上报),所以采用了策略模式,而没有使用状态模式。
当然啦,这个部分完全是个人理解,理解的不对的地方还请大家指教。