简单而有效的深入设计模式

作者: Tulayang | 来源:发表于2014-09-12 16:36 被阅读355次

每一个阶段,我对设计模式都有不同的理解。

随着对函数式编写的热爱,对关系型数据库和文档型数据库的对比,我对设计模式又有了新的感觉。

我觉得范例总是最有效的说明,假设我们以一个餐馆来作为对象(我的英文不怎么样,就不要计较拼写了):

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的信息 

相关文章

网友评论

    本文标题:简单而有效的深入设计模式

    本文链接:https://www.haomeiwen.com/subject/dnaytttx.html