面試的時候,別人問起我工作這麼多年了,有沒有自己的一些沉澱和積累。其實高併發、分佈式都是很響亮的,然而我卻要講講用到的設計模式,也是一些細節。
- 裝飾器(包裝器)模式:
public class ResponseMapWrapper {
private static Logger logger = LoggerFactory.getLogger(ResponseMapWrapper.class);
private static final String STATUS = "status";
private static final String STATUS_TEXT = "statusText";
private static final String DATA = "data";
public static Map<String, Object> build(InfoCode infoCode, Object data) {
Map<String, Object> map = new HashMap<>();
map.put(STATUS, infoCode.getStatus());
map.put(STATUS_TEXT, infoCode.getMsg());
if (data != null) {
map.put(DATA, data);
}
logger.info(JSON.toJSONString(map));
return map;
}
public static Map<String, Object> build(ResponseCode responseCode) {
Map<String, Object> map = new HashMap<>();
map.put(STATUS, responseCode.getCode());
map.put(STATUS_TEXT, responseCode.getReasonPhrase());
logger.info(JSON.toJSONString(map));
return map;
}
}
InfoCode是enum類型,比如PARAMS_ERROR(40000, "參數錯誤"),這樣在Controller裏就能按照統一的格式返回,return ResponseMapWrapper.build(InfoCode.SUCCESS, vo);
- 枚舉策略 + 簡單工廠模式:
最典型的就是微信公衆號開發中的event/msg了。從解析xml到對接具體的業務,event包括關注取消關注、掃碼、點擊菜單等,而msg包括了文本、圖片、語音等等,種類繁多,所以很適合枚舉策略。
接口:
public interface IEventStrategy {
public String dealEvent(Element root);
}
實現:
public enum EventStrategy implements IEventStrategy {
// 關注事件 || 掃描帶參數二維碼, 用戶未關注時,進行關注後的事件推送
subscribe {
@Override
public String dealEvent(Element root) {
return SubscribeStrategy.dealEvent(root);
}
},
// 取消關注事件
unsubscribe {
@Override
public String dealEvent(Element root) {
return UnSubscribeStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 點擊菜單拉取消息時的事件
CLICK {
@Override
public String dealEvent(Element root) {
return ClickStrategy.dealEvent(root);
}
},
// 自定義菜單 —— 點擊菜單跳轉鏈接時的事件
VIEW {
@Override
public String dealEvent(Element root) {
return ViewStrategy.dealEvent(root);
}
},
// 模版消息發送任務完成後的事件
TEMPLATESENDJOBFINISH {
@Override
public String dealEvent(Element root) {
return TemplateSendStrategy.dealEvent(root);
}
}
}
具體的策略,以關注爲例:
public class SubscribeStrategy {
private static final Logger logger = LoggerFactory.getLogger(SubscribeStrategy.class);
/*
* ToUserName 開發者微信號 FromUserName 發送方帳號(一個OpenID) CreateTime 消息創建時間 (整型) MsgType 消息類型,event Event
* 事件類型,subscribe(訂閱)、unsubscribe(取消訂閱)
*/
public static String dealEvent(Element root) {
WxUserService wxUserService = (WxUserService) SpringContextUtils.getBeanById("wxUserService");
NodeList toUserName = root.getElementsByTagName("ToUserName");
NodeList fromUserName = root.getElementsByTagName("FromUserName");
NodeList createTime = root.getElementsByTagName("CreateTime");
String developerId = toUserName.item(0).getTextContent();
String openId = fromUserName.item(0).getTextContent();
String _createTime = createTime.item(0).getTextContent();
NodeList eventKey = root.getElementsByTagName("EventKey");
String _eventKey = "";
if (eventKey != null) {
_eventKey = eventKey.item(0).getTextContent();
}
logger.info("SubscribeStrategy toUserName: {}; fromUserName: {}; createTime: {}; eventKey: {}",
developerId, openId, _createTime, _eventKey);
UserInfo userInfo = wxUserService.getUserInfo(openId);
// 以unionId爲Key
String unionid = userInfo.getUnionid();
RedisUtils redisUtils = (RedisUtils) SpringContextUtils.getBeanByClass(RedisUtils.class);
if (unionid == null) {
logger.warn("weclome get unionid failed, userOpenId : {}", openId);
} else {
// 存儲用戶信息
redisUtils.setStrNX(openId, unionid);
userInfo.setSubscribe(1);
redisUtils.setPOJO(unionid, userInfo);
}
// 以下與具體業務對接
@TODO
}
對外調用是通過簡單工廠提供的方法:
public class EventStrategyFactory {
public static IEventStrategy getDealer(String event) {
// 按事件發生概率由高到低排序
switch (event) {
// 自定義菜單 —— 點擊菜單拉取消息時的事件
case "CLICK":
return EventStrategy.CLICK;
// 自定義菜單 —— 點擊菜單跳轉鏈接時的事件
case "VIEW":
return EventStrategy.VIEW;
// 關注事件 || 掃描帶參數二維碼, 用戶未關注時,進行關注後的事件推送
case "subscribe":
return EventStrategy.subscribe;
// 模版消息發送任務完成後的事件
case "TEMPLATESENDJOBFINISH":
return EventStrategy.TEMPLATESENDJOBFINISH;
// 取消關注事件
case "unsubscribe":
return EventStrategy.unsubscribe;
default:
return null;
}
}
}
入口,xml節點是event時,不關心具體的event名稱,EventStrategyFactory.getDealer(_event)從工廠拿到策略,然後啓用該策略。
if ("event".equals(_msgType)) {
NodeList event = root.getElementsByTagName("Event");
String _event = event.item(0).getTextContent();
IEventStrategy eventStrategy = EventStrategyFactory.getDealer(_event);
if(eventStrategy != null){
replyMsg = eventStrategy.dealEvent(root);
}
}
- 享元模式:
複用map,不要每次都new一個map,這樣創建的開銷很大,而且還會垃圾回收。具體等我找一下代碼。