4月份遗漏了一篇,这篇算是补充。html
此文并不是对设计模式的总结,而是要谈谈通常的编码风格,找设计模式的朋友能够移步了。git
什么是模式?我搜到一个简短的解释:模式是指从生产经验和生活经验中通过抽象和升华提炼出来的核心知识体系。程序员
模式是多个生活、生产行为的概括总结,也就是解决某一类问题的方法论。从信息的角度来理解,模式是对信息减少信息熵的过程和结果。github
好比一个 APP 须要组织一个表单,表单里的部分元素如选项列表由服务器提供,假设是一个行业列表,一般可能定义一个列表:web
[ {trade_id: 10, trade_name: "xdfsdf"}, ... ]
看上去接口提供的这个数据没什么问题。若是此时表单里还有选项列表,选择城市,那你可能很天然想到 {city_id: 25, city_name: "wefel"}
。对于客户端,问题也不大,MVC 模式嘛,我在 Model 里分别指定不就好了。算法
嗯,你也许猜到我想说什么了,没错,我想用一个可更简单的、可复用的结构来进行描述。如:设计模式
[ {id: 10, name: "xdfsdf"}, ... ]
甚至:服务器
[ [10, "xdfsdf"], ... ]
这样对此类控件能够更容易的封装使用。按信息熵的观点这种模式对熵的下降最大。数据结构
但这种转换您可能嗤之以鼻:『切,这算什么模式?』框架
我以为当开始思考模式时,就是在思考事物与事物间的关联,这才是一个程序员(Programmer)该作的事情;若是仅仅是为了写点代码实现某个功能,那该叫编码员(Coder)。
对照模式的解释,算法也是模式;我念书少,一直对算法掌握的比较弱,因此此文也就不班门弄斧了。在这里对仅对常规的 MIS(管理信息系统)的接口数据结构提出一些见解,主要针对数据的 CRUD(增删改查)。
假设要为一家餐馆作一个小型 App,需求是构建菜谱,有单品,有套餐。单品的属性有:名称、单价、简介、照片(多个,文件、说明),套餐有:名称、折扣价、简介、餐品(多个)。绘制 ERM 以下:
单品和套餐一般能够出如今一个搜索列表里,属性基本一致,故放在同一张表里(模式出现了)。照片可以复用,好比单品能够引用,某个套餐也能够用,甚至系统其余的地方也能够用(这就是模式),那就独立出来好了(没有 Product_id 而是用 Product_has_Picture 的多对多关系)。
下面咱们开始设计 REST 接口:
GET /xxx/products Response Content: [ { id: "商品 ID", name: "名称", note: "简介", price: "价格, 格式: RMB ###.##", is_package: "是不是套餐", main_picture: "主要展现照片 URL" } ] GET /xxx/products/ID Response Content: { id: "Product ID", name: "名称", note: "简介", price: "价格, 格式: RMB ###.##", is_package: "是不是套餐", pictures: [ { id: "照片 ID", name: "名称", note: "简介" } ], products: [ { id: "商品 ID", name: "名称", note: "简介", price: "价格, 格式: RMB ###.##", main_picture: "主要展现照片 URL", is_free: "是否免费附送" } ] } POST /www/products PUT /www/products/ID Request Content: { name: "名称", note: "简介", price: "价格, 格式: RMB ###.##", is_package: "是不是套餐", pictures: [ { Picture_id: "照片 ID", is_main: "0或1" } ] } DELETE /www/products/ID
看上去没什么特殊的呀!细心的话也许发现了,GET 拿到的 pictures 列表与 POST、PUT 发送的 pictures 列表结构不一致,这是根据个人框架能识别的模式所作的精简,事实上 pictures 的完整结构为:
Product_has_Picture: [ { Product_id: "商品ID", Picture_id: "照片ID", is_main: "是不是主照片", Picture: { id: "照片ID", name: "照片名称", note: "照片说明" } } ]
在进行商品的建立和修改时,因为照片是从照片列表(接口)选择的,故 Picture 层是不须要的。若是用相似 Protobuf 的方式描述 Product 的数据结构,应该是:
message Picture { required int323 id = 1; required string name = 2; required string note = 3; } message Product { required int32 id = 1; required string name = 2; optional string note = 3; required float price = 4; optional bool is_package = 5; message Pictures { required Picture picture = 1; optional bool is_main = 2 [default = 0]; } repeated Pictures pictures = 6; message Products { required Product product = 1; optional bool is_main = 2 [default = 0]; } repeated Products products = 7; }
能够看出 Pictures 内是单个的 Picture 对象,重复的是 Pictures 对象;若是仅仅按数据结构越简单越好的模式来评判,假设 Pictures 列表不须要 is_main 属性,按第一个图即为默认图的方式好了,也就是 Product 内直接定义 repeated Picture pictures
。那么,PUT Product 时 pictures 可定义为一个由 ID 组成的列表甚至分隔符分隔的字符串。可是,这将增长处理程序的复杂性,程序并不能轻松的自动处理,或者要将多对多关联分解成纯粹的和有其余数据的两种模式。
这里涉及到另外一个模式,咱们的数据关联方式到底有多少种,多数的 ORM 框架会给你这样几种:BLONGS_TO HAS_ONE HAS_MANY MANY_TO_MANY
,但当从 ERM 转为类图的时候,你会发现其实只有一种关系便可完整覆盖这 4 种关系,那就是 BELONGS_TO
,在类图里叫 Dependency
(我的以为对 UML 的 Association Aggregation
等其余名词不必太较劲)。
为了显式的申明当前关联查询的表,肯定关联方向,保留 HAS_ONE、HAS_MANY 便于理解(实际上是我嫌麻烦,Django 就作到了只要申明了 ForeignKey 就能够正、反双向的关联查询),由此可得出 A MANY_TO_MANY C
能够分解为 A HAS_MANY B (by A_ID) BELONGS_TO C (by C_ID)
。
当理清楚了这个结构模式时,就能够编写程序『自动』处理请求了,好比你给了 products 数据 {product_id: 31, is_main: 1}
,就会去写入 Product_has_Picture 的关联数据, 外键 package_id 能够自动代入。查询商品列表时给了 products.product_id 就会提取含有某个(些)商品的套餐。
以此看来,数据自己就成了『查询语句』和『业务逻辑』。
说完告终构再说说行为。对于常规的 CRUD,我认为从视觉操做上能够分解为两个组件:列表、表单;列表对应的接口有:获取列表、更新;表单对应的接口有:获取信息、保存(增长、修改)。
获取列表接口可涵盖:分页、排序、搜索(筛选)等;
列表的更新有:删除、更新状态(批量更新某个字段的值);
在多年之前 WEB 开发中页面与页面(组件)间的联动是靠显式的告知回跳地址或用 Referer 来回到来源处,好比连贯的行为:打开列表->添加商品->添加完成->跳回列表。
而独立的组件模式,组件与组件之间的联动是采用事件来驱动,A组件完成后广播本身完成的事件,哪一个组件须要处理哪一个组件监听该事件就行了,简单总结为:谁打开、谁负责。好比现有连贯操做:打开 A 列表->添加 A->进入 A 表单->选择 B->打开 B 列表->搜索 B->结果里没有->添加 B->打开 B 表单->保存 B->返回 A 表单->自动选中刚添加的 B->保存 A->刷新 A 列表。
这看上去好像很麻烦,用独立组件的方式就很好理解了:
这种方式在 App 和 Web 的 Rich Client 开发中其实很常见。此处表单的选择是一个封装了打开、监听、选中这些行为的控件;每一个组件、控件都仅仅关心本身打开的组件便可,并不须要关注完整的行为;其实平时工做中的人未尝不是如此,问题在于咱们老是把问题搞得很复杂,而以为花时间去思考它们的共性是在浪费时间。
建立一个订单,挑选几个商品。
我要的商品不存在,那就建一个好了。纯举例,哪有餐馆没菜顾客本身动手的道理
利用模式,能够把一系列动做、判断用一个静态的描述来定义,好比一组表单验证,一个表关联关系描述。
而在行为模式上,将关注点从一系列动做缩小到二者之间关系的描述上。对程序复用和方便人操做识别都有价值。
模式是广泛存在的,从 HTTP 到 SQL。不是咱们缺乏模式,而是太多的精力和时间耗费在了模式的转换上,而忽略了模式与模式间转换的模式的思考。
模式就是要创建起一个足够『傻』的描述,如同《阿甘正传》里甘去见珍妮时坐在长凳上对路人说:
Mama always said "There's an awful lot you can tell about a person by their shoes." Where they're going. Where they've been.(妈妈常说看一我的的鞋子你就能够知道他的不少事情。他们要去哪里,去过哪里。)
模式 http://www.baike.com/wiki/%E6%A8%A1%E5%BC%8F
信息熵 http://baike.baidu.com/view/401605.htm
《Google Protocol Buffers 入门》 http://shitouer.cn/2013/04/google-protocol-buffers-tutorial/
《UML类图符号 各类关系说明以及举例》 http://www.cnblogs.com/duanxz/archive/2012/06/13/2547801.html
HongsCORE for Javascript https://github.com/ihongs/HongsCORE/tree/develop/hongs-web/web/common