Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

背景

面向对象设计领域的著作汗牛充栋,其中不乏大师精品。然而大部分研究对象(或者说描述对象)都是静态的模型,典型的如——设计模式、SOLID等等。

在开始介绍动态面向对象建模之前,我们先来给动态面向对象建模下个定义。

静态模型

动态模型

备注

Class

Object

一个Bird类是静态模型;一只麻雀是动态模型。后者考虑具体的这个实例的生命周期——创建、初始化、装配、解构、销毁等等。

Attribute

State

Bird类里面包含一个Eye属性;一只麻雀的左眼。麻雀睡觉的时候眼睛是不是要闭上?Bird类里面包含一个Eye属性;一只麻雀的左眼。麻雀眼睛是不是要闭上?

Method

Interactions

Bird累里面包含一个Fly方法;一只企鹅的Fly方法是应该抛出异常,还是实现为跳跃?

Class relationship

Object relationship

Bird类是Animal的子类;这只麻雀是另一只麻雀的妈妈。

Responsibility

Business Logic

Bird类负责鸟的鸣叫、飞行、睡眠等操作;一只麻雀睡觉的时候是不是要闭上眼睛。

掌握动态建模的必要性

每个人每天都在做动态建模,所有的设计最终都落实在动态建模上面,无论是有意识的还是无意识的。当你写下 new Order()的的时候你就在决定如何初始化一个订单的实例。这里面有多少个决策点?

  1. Order的属性是在构造函数传入还是用Setters传入?

  2. Order是自己创建还是由框架装配?

  3. Order是否是一个POJO?为什么?

  4. Order是否可以保存自己?Active Record vs Data Mapper

  5. Order是否是Immutable的?

动态建模本身的困难和教学上的困难

一个例子——武夷山茶铺

 根据以下规则计算商品总价:

  • 江浙沪100元以上免运费;

  • 大陆其他地区运费为价格的3%;

  • 澳大利亚加收商品税10%,运费为价格的5%;

  • 其他国际地区加收商品税17%,运费为价格的7%。

客户地址

商品税

订单价格

运费标准

江浙沪

0

¥100

免运费

江浙沪

0

¥99.99

3%

大陆其他地区

0

¥100

3%

大陆其他地区

0

¥99.99

3%

澳大利亚

10%

¥50

5%

其他国际地区

17%

¥50

7%

我们先来尝试做一次静态建模

Zenuml graph macro
uuid13348831-aeed-4304-be1d-d57e5ef22bb4
updatedAt2020-10-12T11:31:12Z

你可能注意到后面几个类我已经没有添加field和method了。因为我实在是不确定要加什么东西上去比较合适。

如果一个没有直观的办法被证明是错的,这一定不是一个好的设计,或者至少不是一个好的描述。好的设计激发问题、回答问题、促进沟通、迎接挑战。

进入正题来一次动态建模尝试

每个人打开 https://zenuml.com 自己尝试输入,不要复制粘贴。

第一步:设计最外层的接口

当你画出下面任何一个图的时候,你做了这么一件很重要的事情:

Tip

你有了一个,而且它有一个有用的方法

Zenuml sequence macro
uuidb8fe7af9-0347-418e-b091-c2a349b0248b
updatedAt2020-10-12T11:54:53Z
PriceCalculator.getTotalPrice(Order)
Zenuml sequence macro
uuid6e002f73-8fa2-4a0f-832d-c05291f488de
updatedAt2020-10-12T11:56:21Z
Order.getTotalPrice()

第二步:尝试实现

Zenuml sequence macro
uuid6f64d646-d947-447b-9b61-8f2c66d5be14
updatedAt2020-10-15T11:33:15Z
// `ShippingAddress: isJZH, isChina, isAu` <br>
// `Order` <br>
// FeeCalculator interface
// ShippingFeeCal & GstCal<br>

// FeeCalculator派生类
sfCal = new ShippingFeeCal()
// FeeCalculator派生类
gstCal = new GstCalculator()

priceCal = new TotalPriceCalculator(sfCal, gstCal)

"priceCal:TotalPriceCalculator".getTotal(Order) {
  // total = price <br>  
  feeCalculators = from(sfCal, gstCal)  
  forEach("cal as ICalculator in feeCalculators") {
    ICalculator.cal(Order.price, Order.shippingAddress)
  }
  // shippingFee = sfCal.cal(price, shippingAddress) {
  //   if (shippingAddress.isJZH && price > 100) {
  //     fee = Zero()
  //   } else if (shippingAddress.isJZH && price < 100) {
  //     fee = price.multily(0.03)
  //   } else {
  //     //...
  //   }      
  // }
  // // SRP = Single Responsibility Principle
  // gst = gstCal.cal(price, shippingAddress) {
  //   if (shippingAddress.isAu) {
  //     gst = price.multiply(0.10)
  //   } else if (!shippingAddress.isChina) {
  //     gst = price.multiply(0.17)
  //   }
  // }
  // return price + gst + shippingFee
}