簡單而有效的深入設計模式

每一個階段,我對設計模式都有不同的理解。

隨著對函數式編寫的熱愛,對關系型數據庫和文檔型數據庫的對比,我對設計模式又有了新的感覺。

我覺得范例總是最有效的說明,假設我們以一個餐館來作為對象(我的英文不怎么樣,就不要計較拼寫了):

Eatery (餐館類)

首先,有了一個餐館類,然后加上餐館提供的接口,餐館就可以工作了。目前,先提供:

    milk()                    // 來份牛奶
    rice()                    // 來份炒飯

當然,還得有餐館建立的時間、城市、老板、雇員、價格...于是還得加上下面的:

    build_date             // 建立日期
    city                      // 所在城市
    boss_a                  // 老板a
    employee_a           // 雇員a
    employee_b           // 雇員b
    employee_c           // 雇員c
    price_milk              // 牛奶價格
    price_rice              // 炒飯價格
    menu_milk             // 牛奶菜單
    menu_rice             // 炒飯菜單

另外,客人進來餐館,還要報價不是,所以,還得加上:

    get_price()            // 報價    

如果要做成程序,還得報告建立的日期、城市...所以,還得加上:

    get_build_date()        // 報告建立日期
    get_city()                 // 報告所在城市
    get_boss()                // 報告有哪幾個老板
    get_employee()          // 報告有哪幾個雇員
    get_price()                // 報告菜品價格
    get_menu()               // 報告菜單上的菜

先大致如此吧,于是,得到了一個可以使用的餐館了:

Eatery:

    build_date               // 建立日期
    city                        // 所在城市
    boss_a                   // 老板a
    employee_a             // 雇員a
    employee_b             // 雇員b
    employee_c             // 雇員c
    price_milk                // 牛奶價格
    price_rice                // 炒飯價格
    menu_milk               // 牛奶菜單
    menu_rice               // 炒飯菜單

    get_build_date()       // 報告建立日期
    get_city()                // 報告所在城市
    get_boss()               // 報告有哪幾個老板
    get_employee()         // 報告有哪幾個雇員
    get_price()               // 報告菜品價格
    get_menu()              // 報告菜單上的菜

    milk()                      // 來份牛奶
    rice()                      // 來份炒飯

目前,餐館應該是這么運作的:

告訴我餐館建立日期 get_build_date()

告訴我餐館所在城市 get_city()

告訴我餐館菜單上的菜 get_menu()

告訴我餐館菜單上的價格 get_price()

給我來一個牛奶 milk()

給我來一份炒飯 rice()

現在,先不管面向對象,先來想想關系型數據庫,要做個餐館的數據庫該怎么打草稿?

table_eatery:   起個名字, 從餐館的基礎來開始吧

    id                           // 現在當然只有一個餐館
    build_date                // 建立日期
    city                        // 所在城市

是不是應該是這樣的?

我們總是保持基礎的那個表格盡可能簡單高效.

這份表格告訴我們 : 餐館建立日期和所在城市,還有通過id號能獲取到這個餐館。

就這么多!

如果想知道餐館的老板怎么辦?

簡單!

建個表格,show me the info:

table_boss:

    id
    boss_name
    eatery_id

應該是這樣的。

通過id號取到某個老板,然后還有一個關聯的餐館號碼eatery_id.

老板肯定是某個餐館的老板,

老板boss_a當然是eatery_id號碼這個餐館的老板。

更多的先不討論了。

因為,這里,

有意思的地方已經出來了。

table_eatery和table_boss全面的揭示了面向對象的本質:

擴展和關聯

我們先建立起基礎:eatery,然后呢?

當我們需要新的東西的時候,擴展eatery。

怎么擴展呢?

通過建立一個新的表格。

那新的表格table_boss怎么跟table_eatery建立聯系呢?

通過在新擴展出來的table_boss塞入eatery_id來指向table_eatery。

乍一看,是不是就是繼承呢?

先弄出個table_eatery對象,然后繼承這個對象,在上邊再弄上個boss_name。

不過,仔細觀察 ,當然知道不是繼承。 (而且,都知道,多數時候繼承不是好的面向對象)

這里是"組合"。

在新的對象里放入舊有對象的一個引用,然后,成了。

objectA   --->    objectB   [放入objectA的一個引用]

以此類推,我們加深下關系型數據庫表格的建立。

建立一個雇員表格:

    id
    employee_name
    eatery_id

建立一個餐館菜單:

    id
    menu_item
    menu_price
    eatery_id

...

這其實就是面向對象的過程。

所以,對于程序來說,設計這個餐館,可以這樣面向對象:

Eatery (build_date, city)

    build_date             // 建立日期
    city                      // 所在城市
    get_build_date()     // 報告建立日期
    get_city                // 報告所在城市

Boss (eatery)

    name                    // 老板名字
    set_name              // 任職老板
    get_name              // 當前老板的名字

Employee (eatery)

    names                   // 雇員們的名字
    add_name              // 增加一個雇員
    remove_name         // 開除一個雇員
    get_name(i)           // 報告第幾號雇員的名字
    get_names()          // 報告所有的雇員名字列表

Food (name, price)

    name                    // 菜的名字
    price                    // 菜的價格      
    get_name()           // 報告菜的名字
    get_price()           // 報告菜的價格

Menu (eatery)

    foods                   // 菜列表
    add_food(food)      // 加入菜
    remove_food(food) // 刪除菜
    get_food_price(i)   // 報告第幾號菜的價格
    get_food_prices     // 報告所有菜的價格
    get_food_names    // 報告所有菜的名字
    get_food_name(i)  // 報告第幾號菜的名字

Cook (name, food_names)

    name                    // 廚師名字
    food_names           // 廚師擅長做的菜名
    get_name()           // 報告廚師的名字
    get_food_names()  // 報告廚師擅長做的菜名列表
    make(food_name)   // 廚師制作名為food_name的菜

Cooklist (eatery)

    cooks                  // 餐館entery雇傭的廚師名字列表
    add_cook(cook)     // 增加一個廚師
    get_cook_names()  // 報告所有的餐館廚師名字
    get_cook_name(i)   // 報告第幾號廚師的名字

OK. 餐館構造完畢,現在,開始運營:

var entery_pie = new Eatery('2014-5-1', 'Beijing');  // 2014年5月1日,在北京建立餐館pie

entery_pie.get_build_date();  // => 2014-5-1

entery_pie.get_city();  // => Beijing


var boss = new Boss(eatery_pie);  // 為餐館pie指認老板

boss.set_name('Tom');  // 老板是Tom

 

var employee = new Employee(eatery_pie);  // 為餐館pie雇傭員工

employee.add_name('Lili');  // 雇傭Lili

employee.add_name('Lina');  // 雇傭Lina

employee.add_name('Jerry');  // 雇傭Jerry

employee.get_names();  // => Lili,Lina,Jerry

employee.get_name(1);  // => Lina

 

var menu = new Menu(eatery_pie);  // 為餐館pie開設菜單

menu.add_food(new Food('milk', '12.00¥'));  // 加入價格12.00¥的牛奶

menu.add_food(new Food('rice', '26.00¥'));  // 加入價格26.00¥的炒飯

menu.get_food_names();  // => 牛奶,炒飯

menu.get_food_prices();  // => 12.00¥,26.00¥

menu.get_food_name(1);  // => 炒飯

 

var cooklist = new Cooklist(eatery_pie);  // 為餐館pie增加廚師

cooklist.add_cook(new Cook('Daxiong', ['milk', 'rice', 'tomoto'])); // 增加一個會做牛奶、炒飯、西紅柿的廚師

cooklist.add_cook(new Cook('Xiaoxiao', ['milk', 'rice']));  // 增加一個會做牛奶、炒飯的廚師

cooklist.get_cook_name(1).make('milk');  // Xiaoxiao廚師來一份牛奶

cooklist.get_cook_name(0).make('tomoto');  // Daxiong廚師來一份西紅柿

這就是我現在思想中的面向對象,更可能簡單的模型,更多的擴展和關聯。

在很長時間之前,我收集到的信息告訴我,盡可能的封裝,并且 a.get().method() 表現的不夠隱蔽。

如果有a.get().nethod(),應該盡可能換成a.method()來隱藏。

然而,現在我卻感覺到這樣教條式的做法,在許多時候是多余和低效的。

比起把許多小對象裝起來,扔到一個大對象里封裝,我現在更喜歡建立一大堆的小對象,然后分批指派責任。

從許多方面來看,簡化代碼行數的外觀模式都是反模塊化的,只用一個對象來操縱一大堆函數。

事實上,底層要通過層層傳遞,才能夠到達真正實現功能的地方。

這既是低效的,在編寫擴展的時候而且很困難。

如果你寫完一個夠大的對象,當需要增加內容的時候,就會對這個對象的關聯“鏈”一籌莫展。

而打散的小對象群,更貼近函數式的風格,只需要指定源對象的引用,就可以擴展ta,而且是采用組合而不是繼承。


而且,在某些方面,你會發現這其實就是函數式.

一個數據結構Eatery,以及一大堆圍繞數據結構Eatery的函數群:

eatery

fn1(eatery, arg1, arg2, ...)

fn2(eatery, arg1, arg2, ...)

fn3(eatery, arg1, arg2, ...)

...

好處是,非常易于擴展(增加功能,增加內容,增加關聯,...)和修改.

壞處是? e, ... ,相比較傳統的OO教學,會有一大堆“小星星”散亂在宇宙世界里,變得不那么透明,但是依然可以設定一個邊界來限定這個外層。

但是,我覺得,好處是絕對的,因為這樣更符合函數的特性:

    輸入一個值,show me the result

    fn1(eatery_pie, arg1, arg2) => 餐館pie的信息 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容