建造者模式是一种创建类的模式,它尽可能的保证代码的复用性,而且可读性也非常好(最终产生对象的过程对调用方是透明的)。
很多朋友应该都做过微信公众号或者支付宝服务窗的消息推送,下面是微信公众平台给的消息示例:
{
"touser":"OPENID",
"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
"url":"http://weixin.qq.com/download",
"miniprogram":{
"appid":"xiaochengxuappid12345",
"pagepath":"index?foo=bar"
},
"data":{
"first":{
"value":"恭喜你购买成功!",
"color":"#173177"
},
"keynote1":{
"value":"巧克力",
"color":"#173177"
},
"keynote2":{
"value":"39.8元",
"color":"#173177"
},
"keynote3":{
"value":"2014年9月22日",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
具体参数可在公众平台开发者文档查看。现在的问题是如何设计消息模版呢?我身边有的同事是用字符串替换,有的对各个模版建立一个类,这里我不往深里讨论方案对优劣,本节结束后读者们可以尝试做下对比。下面我给出一种建造者模式的做法,创建builder类如下(简便起见,去掉了miniprogram参数):
# -*- coding: utf-8 -*-
from collections import OrderedDict
import json
# 模版中“data”节点的各个元素的数据结构
class Metadata:
def __init__(self, value, color):
self.value = value
self.color = color
# 微信消息的建造器
class MessageBuilder:
__contentDict = OrderedDict() # 定义整个模版的数据结构,保持添加的顺序
__dataDict = OrderedDict() # 定义data节点的数据结构,保持添加的顺序
__dataNoteNext = 1 # data节点要添加的下一个元素的序号
def __init__(self, touser, template_id, url):
self.__contentDict['touser'] = touser
self.__contentDict['template_id'] = template_id
self.__contentDict['url'] = url
self.__contentDict['data'] = self.__dataDict
def add_first_data(self, value, color):
data = Metadata(value, color)
self.__dataDict['first'] = data
return self
def add_remark_data(self, value, color):
data = Metadata(value, color)
self.__dataDict['remark'] = data
return self
def add_note_data(self, value, color):
data = Metadata(value, color)
self.__dataDict['keynote' + str(self.__dataNoteNext)] = data
self.__dataNoteNext += 1
return self
def build(self):
# 为打印出来看的方便,这里将json序列化后的结果缩进2个空格,并且不把中文转为unicode
return json.dumps(self.__contentDict, default=lambda o: o.__dict__, indent=2, ensure_ascii=False)
有两点要说明下:
建造者有了,下面我们来生成两条消息。前几天我在丰巢快递柜寄了票件,下图是我收到的其中两个微信通知:
我们模拟作如上两条微信消息:
if __name__ == '__main__':
order_builder = MessageBuilder('user111111', 'template_id_order', '') \
.add_first_data('您的寄件订单已经生成啦!请打包好您的包裹前往柜机投递。', '#173177') \
.add_note_data('03226580', '#173177') \
.add_note_data('10(小格);14(中格);18(大格);', '#173177') \
.add_note_data('顺丰速运', '#173177') \
.add_remark_data('“一分钱寄全国”优惠券以放入到你的账户,在柜机支付时记得使用哦。', '#173177')
print('生成下单通知微信消息')
print(order_builder.build())
print()
send_builder = MessageBuilder('user222222', 'template_id_send', 'http://balabala') \
.add_first_data('丰巢已收到您的包裹,并已通知快递员来收取啦!', '#173177') \
.add_note_data('顺丰速运', '#173177') \
.add_note_data('422154541545', '#173177') \
.add_note_data('某地址某地址某地址某地址', '#173177') \
.add_note_data('2017-07-31 11:20', '#173177') \
.add_remark_data('点击详情查看物流进度', '#173177')
print('生成投递微信消息')
print(send_builder.build())
运行结果:
生成下单通知微信消息
{
"touser": "user111111",
"template_id": "template_id_order",
"url": "",
"data": {
"first": {
"color": "#173177",
"value": "您的寄件订单已经生成啦!请打包好您的包裹前往柜机投递。"
},
"keynote1": {
"color": "#173177",
"value": "03226580"
},
"keynote2": {
"color": "#173177",
"value": "10(小格);14(中格);18(大格);"
},
"keynote3": {
"color": "#173177",
"value": "顺丰速运"
},
"remark": {
"color": "#173177",
"value": "“一分钱寄全国”优惠券以放入到你的账户,在柜机支付时记得使用哦。"
}
}
}
生成投递微信消息
{
"touser": "user222222",
"template_id": "template_id_send",
"url": "http://balabala",
"data": {
"first": {
"color": "#173177",
"value": "丰巢已收到您的包裹,并已通知快递员来收取啦!"
},
"keynote1": {
"color": "#173177",
"value": "顺丰速运"
},
"keynote2": {
"color": "#173177",
"value": "422154541545"
},
"keynote3": {
"color": "#173177",
"value": "某地址某地址某地址某地址"
},
"remark": {
"color": "#173177",
"value": "点击详情查看物流进度"
},
"keynote4": {
"color": "#173177",
"value": "2017-07-31 11:20"
}
}
}
本人经验,这类情形下,建造者模式使得代码更加清晰易懂,读者有没有一点点启发呢?