摘要:按信息熵的觀點(diǎn)這種模式對(duì)熵的降低最大。但是,這將增加處理程序的復(fù)雜性,程序并不能輕松的自動(dòng)處理,或者要將多對(duì)多關(guān)聯(lián)分解成純粹的和有其他數(shù)據(jù)的兩種模式。模式是普遍存在的,從到。
4月份遺漏了一篇,這篇算是補(bǔ)充。
此文并非對(duì)設(shè)計(jì)模式的總結(jié),而是要談?wù)勔话愕木幋a風(fēng)格,找設(shè)計(jì)模式的朋友可以移步了。
什么是模式?我搜到一個(gè)簡(jiǎn)短的解釋?zhuān)耗J绞侵笍纳a(chǎn)經(jīng)驗(yàn)和生活經(jīng)驗(yàn)中經(jīng)過(guò)抽象和升華提煉出來(lái)的核心知識(shí)體系。
模式是多個(gè)生活、生產(chǎn)行為的歸納總結(jié),也就是解決某一類(lèi)問(wèn)題的方法論。從信息的角度來(lái)理解,模式是對(duì)信息減小信息熵的過(guò)程和結(jié)果。
比如一個(gè) APP 需要組織一個(gè)表單,表單里的部分元素如選項(xiàng)列表由服務(wù)器提供,假設(shè)是一個(gè)行業(yè)列表,通常可能定義一個(gè)列表:
[ {trade_id: 10, trade_name: "xdfsdf"}, ... ]
看上去接口提供的這個(gè)數(shù)據(jù)沒(méi)什么問(wèn)題。如果此時(shí)表單里還有選項(xiàng)列表,選擇城市,那你可能很自然想到 {city_id: 25, city_name: "wefel"} 。對(duì)于客戶(hù)端,問(wèn)題也不大,MVC 模式嘛,我在 Model 里分別指定不就行了。
嗯,你也許猜到我想說(shuō)什么了,沒(méi)錯(cuò),我想用一個(gè)可更簡(jiǎn)單的、可復(fù)用的結(jié)構(gòu)來(lái)進(jìn)行描述。如:
[ {id: 10, name: "xdfsdf"}, ... ]
甚至:
[ [10, "xdfsdf"], ... ]
這樣對(duì)此類(lèi)控件可以更容易的封裝使用。按信息熵的觀點(diǎn)這種模式對(duì)熵的降低最大。
但這種轉(zhuǎn)換您可能嗤之以鼻:『切,這算什么模式?』
我覺(jué)得當(dāng)開(kāi)始思考模式時(shí),就是在思考事物與事物間的關(guān)聯(lián),這才是一個(gè)程序員(Programmer)該做的事情;如果僅僅是為了寫(xiě)點(diǎn)代碼實(shí)現(xiàn)某個(gè)功能,那該叫編碼員(Coder)。
對(duì)照模式的解釋?zhuān)惴ㄒ彩悄J剑晃夷顣?shū)少,一直對(duì)算法掌握的比較弱,所以此文也就不班門(mén)弄斧了。在這里對(duì)僅對(duì)常規(guī)的 MIS(管理信息系統(tǒng))的接口數(shù)據(jù)結(jié)構(gòu)提出一些看法,主要針對(duì)數(shù)據(jù)的 CRUD(增刪改查)。
假設(shè)要為一家餐館做一個(gè)小型 App,需求是構(gòu)建菜譜,有單品,有套餐。單品的屬性有:名稱(chēng)、單價(jià)、簡(jiǎn)介、照片(多個(gè),文件、說(shuō)明),套餐有:名稱(chēng)、折扣價(jià)、簡(jiǎn)介、餐品(多個(gè))。繪制 ERM 如下:
單品和套餐通??梢猿霈F(xiàn)在一個(gè)搜索列表里,屬性基本一致,故放在同一張表里(模式出現(xiàn)了)。照片能夠復(fù)用,比如單品可以引用,某個(gè)套餐也可以用,甚至系統(tǒng)其他的地方也可以用(這就是模式),那就獨(dú)立出來(lái)好了(沒(méi)有 Product_id 而是用 Product_has_Picture 的多對(duì)多關(guān)系)。
下面我們開(kāi)始設(shè)計(jì) REST 接口:
GET /xxx/products Response Content: [ { id: "商品 ID", name: "名稱(chēng)", note: "簡(jiǎn)介", price: "價(jià)格, 格式: RMB ###.##", is_package: "是否是套餐", main_picture: "主要展示照片 URL" } ] GET /xxx/products/ID Response Content: { id: "Product ID", name: "名稱(chēng)", note: "簡(jiǎn)介", price: "價(jià)格, 格式: RMB ###.##", is_package: "是否是套餐", pictures: [ { id: "照片 ID", name: "名稱(chēng)", note: "簡(jiǎn)介" } ], products: [ { id: "商品 ID", name: "名稱(chēng)", note: "簡(jiǎn)介", price: "價(jià)格, 格式: RMB ###.##", main_picture: "主要展示照片 URL", is_free: "是否免費(fèi)附送" } ] } POST /www/products PUT /www/products/ID Request Content: { name: "名稱(chēng)", note: "簡(jiǎn)介", price: "價(jià)格, 格式: RMB ###.##", is_package: "是否是套餐", pictures: [ { Picture_id: "照片 ID", is_main: "0或1" } ] } DELETE /www/products/ID
看上去沒(méi)什么特殊的呀!細(xì)心的話也許發(fā)現(xiàn)了,GET 拿到的 pictures 列表與 POST、PUT 發(fā)送的 pictures 列表結(jié)構(gòu)不一致,這是根據(jù)我的框架能識(shí)別的模式所做的精簡(jiǎn),事實(shí)上 pictures 的完整結(jié)構(gòu)為:
Product_has_Picture: [ { Product_id: "商品ID", Picture_id: "照片ID", is_main: "是否是主照片", Picture: { id: "照片ID", name: "照片名稱(chēng)", note: "照片說(shuō)明" } } ]
在進(jìn)行商品的創(chuàng)建和修改時(shí),由于照片是從照片列表(接口)選擇的,故 Picture 層是不需要的。如果用類(lèi)似 Protobuf 的方式描述 Product 的數(shù)據(jù)結(jié)構(gòu),應(yīng)該是:
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 內(nèi)是單個(gè)的 Picture 對(duì)象,重復(fù)的是 Pictures 對(duì)象;如果僅僅按數(shù)據(jù)結(jié)構(gòu)越簡(jiǎn)單越好的模式來(lái)評(píng)判,假設(shè) Pictures 列表不需要 is_main 屬性,按第一個(gè)圖即為默認(rèn)圖的方式好了,也就是 Product 內(nèi)直接定義 repeated Picture pictures。那么,PUT Product 時(shí) pictures 可定義為一個(gè)由 ID 組成的列表甚至分隔符分隔的字符串。但是,這將增加處理程序的復(fù)雜性,程序并不能輕松的自動(dòng)處理,或者要將多對(duì)多關(guān)聯(lián)分解成純粹的和有其他數(shù)據(jù)的兩種模式。
這里涉及到另一個(gè)模式,我們的數(shù)據(jù)關(guān)聯(lián)方式到底有多少種,多數(shù)的 ORM 框架會(huì)給你這樣幾種:BLONGS_TO HAS_ONE HAS_MANY MANY_TO_MANY,但當(dāng)從 ERM 轉(zhuǎn)為類(lèi)圖的時(shí)候,你會(huì)發(fā)現(xiàn)其實(shí)只有一種關(guān)系即可完整覆蓋這 4 種關(guān)系,那就是 BELONGS_TO,在類(lèi)圖里叫 Dependency (個(gè)人覺(jué)得對(duì) UML 的 Association Aggregation 等其他名詞沒(méi)必要太較勁)。
為了顯式的申明當(dāng)前關(guān)聯(lián)查詢(xún)的表,確定關(guān)聯(lián)方向,保留 HAS_ONE、HAS_MANY 便于理解(其實(shí)是我嫌麻煩,Django 就做到了只要申明了 ForeignKey 就可以正、反雙向的關(guān)聯(lián)查詢(xún)),由此可得出 A MANY_TO_MANY C 可以分解為 A HAS_MANY B (by A_ID) BELONGS_TO C (by C_ID)。
當(dāng)理清楚了這個(gè)結(jié)構(gòu)模式時(shí),就可以編寫(xiě)程序『自動(dòng)』處理請(qǐng)求了,比如你給了 products 數(shù)據(jù) {product_id: 31, is_main: 1},就會(huì)去寫(xiě)入 Product_has_Picture 的關(guān)聯(lián)數(shù)據(jù), 外鍵 package_id 可以自動(dòng)代入。查詢(xún)商品列表時(shí)給了 products.product_id 就會(huì)提取含有某個(gè)(些)商品的套餐。
以此看來(lái),數(shù)據(jù)本身就成了『查詢(xún)語(yǔ)句』和『業(yè)務(wù)邏輯』。
說(shuō)完了結(jié)構(gòu)再說(shuō)說(shuō)行為。對(duì)于常規(guī)的 CRUD,我認(rèn)為從視覺(jué)操作上可以分解為兩個(gè)組件:列表、表單;列表對(duì)應(yīng)的接口有:獲取列表、更新;表單對(duì)應(yīng)的接口有:獲取信息、保存(增加、修改)。
獲取列表接口可涵蓋:分頁(yè)、排序、搜索(篩選)等;
列表的更新有:刪除、更新?tīng)顟B(tài)(批量更新某個(gè)字段的值);
在多年以前 WEB 開(kāi)發(fā)中頁(yè)面與頁(yè)面(組件)間的聯(lián)動(dòng)是靠顯式的告知回跳地址或用 Referer 來(lái)回到來(lái)源處,比如連貫的行為:打開(kāi)列表->添加商品->添加完成->跳回列表。
而獨(dú)立的組件模式,組件與組件之間的聯(lián)動(dòng)是采用事件來(lái)驅(qū)動(dòng),A組件完成后廣播自己完成的事件,哪個(gè)組件需要處理哪個(gè)組件監(jiān)聽(tīng)該事件就好了,簡(jiǎn)單總結(jié)為:誰(shuí)打開(kāi)、誰(shuí)負(fù)責(zé)。比如現(xiàn)有連貫操作:打開(kāi) A 列表->添加 A->進(jìn)入 A 表單->選擇 B->打開(kāi) B 列表->搜索 B->結(jié)果里沒(méi)有->添加 B->打開(kāi) B 表單->保存 B->返回 A 表單->自動(dòng)選中剛添加的 B->保存 A->刷新 A 列表。
這看上去好像很麻煩,用獨(dú)立組件的方式就很好理解了:
A 列表打開(kāi) B 選擇列表,此時(shí)監(jiān)聽(tīng) B 的選擇完成事件
B 選擇列表打開(kāi) B 表單,此時(shí)監(jiān)聽(tīng) B 的保存完成事件
當(dāng) B 列表監(jiān)聽(tīng)到打開(kāi)的 B 表單保存完成時(shí),觸發(fā) B 選擇完成事件
當(dāng) A 列表監(jiān)聽(tīng)到打開(kāi)的 B 列表選擇完成時(shí),將選中數(shù)據(jù)加入選項(xiàng)并選中
這種方式在 App 和 Web 的 Rich Client 開(kāi)發(fā)中其實(shí)很常見(jiàn)。此處表單的選擇是一個(gè)封裝了打開(kāi)、監(jiān)聽(tīng)、選中這些行為的控件;每個(gè)組件、控件都僅僅關(guān)心自己打開(kāi)的組件即可,并不需要關(guān)注完整的行為;其實(shí)平時(shí)工作中的人何嘗不是如此,問(wèn)題在于我們總是把問(wèn)題搞得很復(fù)雜,而覺(jué)得花時(shí)間去思考它們的共性是在浪費(fèi)時(shí)間。
創(chuàng)建一個(gè)訂單,挑選幾個(gè)商品。
我要的商品不存在,那就建一個(gè)好了。純舉例,哪有餐館沒(méi)菜顧客自己動(dòng)手的道理
利用模式,可以把一系列動(dòng)作、判斷用一個(gè)靜態(tài)的描述來(lái)定義,比如一組表單驗(yàn)證,一個(gè)表關(guān)聯(lián)關(guān)系描述。
而在行為模式上,將關(guān)注點(diǎn)從一系列動(dòng)作縮小到兩者之間關(guān)系的描述上。對(duì)程序復(fù)用和方便人操作識(shí)別都有價(jià)值。
模式是普遍存在的,從 HTTP 到 SQL。不是我們?nèi)鄙倌J剑翘嗟木蜁r(shí)間耗費(fèi)在了模式的轉(zhuǎn)換上,而忽略了模式與模式間轉(zhuǎn)換的模式的思考。
模式就是要建立起一個(gè)足夠『傻』的描述,如同《阿甘正傳》里甘去見(jiàn)珍妮時(shí)坐在長(zhǎng)凳上對(duì)路人說(shuō):
參考資料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.(媽媽常說(shuō)看一個(gè)人的鞋子你就可以知道他的很多事情。他們要去哪里,去過(guò)哪里。)
模式 http://www.baike.com/wiki/%E6%A8%A1%E5%BC%8F
信息熵 http://baike.baidu.com/view/401605.htm
《Google Protocol Buffers 入門(mén)》 http://shitouer.cn/2013/04/google-protocol-buffers-tutorial/
《UML類(lèi)圖符號(hào) 各種關(guān)系說(shuō)明以及舉例》 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
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/30259.html
摘要:按信息熵的觀點(diǎn)這種模式對(duì)熵的降低最大。但是,這將增加處理程序的復(fù)雜性,程序并不能輕松的自動(dòng)處理,或者要將多對(duì)多關(guān)聯(lián)分解成純粹的和有其他數(shù)據(jù)的兩種模式。模式是普遍存在的,從到。 4月份遺漏了一篇,這篇算是補(bǔ)充。 此文并非對(duì)設(shè)計(jì)模式的總結(jié),而是要談?wù)勔话愕木幋a風(fēng)格,找設(shè)計(jì)模式的朋友可以移步了。 什么是模式?我搜到一個(gè)簡(jiǎn)短的解釋?zhuān)耗J绞侵笍纳a(chǎn)經(jīng)驗(yàn)和生活經(jīng)驗(yàn)中經(jīng)過(guò)抽象和升華提煉出來(lái)的核...
摘要:而在模式中角色與角色的接口是相同的透明性。而在模式中與增加新功能相比它更注重通過(guò)設(shè)置代理人的方式來(lái)減輕本人的工作負(fù)擔(dān) 本文首發(fā)于泊浮目的專(zhuān)欄:https://segmentfault.com/blog... 前言 代理模式是在編程中非常常見(jiàn)的設(shè)計(jì)模式.筆者在面試的過(guò)程中也經(jīng)常會(huì)問(wèn)到相關(guān)的問(wèn)題,但是很多同學(xué)答的并不盡人意.在這篇文章中,筆者想和大家聊聊代理模式的應(yīng)用及一些實(shí)踐. Wha...
摘要:可水平擴(kuò)展,可以添加更多服務(wù)器來(lái)擴(kuò)展您的數(shù)據(jù)庫(kù)需要管理員是否開(kāi)發(fā)人員和管理員都可以使用適用場(chǎng)景會(huì)計(jì)師事務(wù)所和銀行,以及需要具有清晰架構(gòu)的結(jié)構(gòu)化數(shù)據(jù)的其他公司。 今天的主題是從MongoDB漫談數(shù)據(jù)庫(kù),在日常的項(xiàng)目中,我們一般都是使用的mysql作為數(shù)據(jù)庫(kù),但是一旦有問(wèn)題,又常常會(huì)聽(tīng)到類(lèi)似要不換成MongoDB試試的聲音,因此就讓我們這些小白來(lái)隨便聊聊數(shù)據(jù)庫(kù) 什么是數(shù)據(jù)庫(kù) 我們就用最簡(jiǎn)單...
摘要:在開(kāi)發(fā)設(shè)計(jì)中有一些常用原則或者潛規(guī)則,根據(jù)筆者的經(jīng)驗(yàn),這里稍微總結(jié)一下最最常用的,以饗讀者。是處理復(fù)雜性的一個(gè)原則。參考六大設(shè)計(jì)原則里氏替換原則奧卡姆剃刀如有問(wèn)題可以通過(guò)郵件微信聯(lián)系我。 在開(kāi)發(fā)設(shè)計(jì)中有一些常用原則或者潛規(guī)則,根據(jù)筆者的經(jīng)驗(yàn),這里稍微總結(jié)一下最最常用的,以饗讀者。 DRY 這里的DRY是Do Not Repeat Yourself的縮寫(xiě)。具體解釋參見(jiàn) ,嚴(yán)謹(jǐn)?shù)亩x是 E...
閱讀 3515·2021-11-15 11:38
閱讀 834·2021-11-08 13:27
閱讀 2245·2021-07-29 14:50
閱讀 2977·2019-08-29 13:06
閱讀 844·2019-08-29 11:22
閱讀 2416·2019-08-29 11:04
閱讀 3508·2019-08-28 18:23
閱讀 895·2019-08-26 13:46