面试的时候,别人问起我工作这么多年了,有没有自己的一些沉淀和积累。其实高并发、分布式都是很响亮的,然而我却要讲讲用到的设计模式,也是一些细节。
- 装饰器(包装器)模式:
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,这样创建的开销很大,而且还会垃圾回收。具体等我找一下代码。