建立函数模型的步骤和方法 数学建模的步骤过程( 二 )

【建立函数模型的步骤和方法 数学建模的步骤过程】这段代码最大的问题是他没有把本该拥有的领域知识记录在其中 。我来试着问你几个问题:
问:middle name可以为空吗?答1:不清楚 。也许需要查文档 。答2:也许可以吧?middle name可以为null 。
为可空类型建模在函数式编程语言中 。可空类型被定义为Option 。虽然null在ts中是合法的(注:咱们可以通过strictNullChecks来强致null检查) 。但是在函数式编程语言中 。你只能通过Option类型来表示可空类型 。
当领域专家告诉你middle name可以存在 。或者为空 。注意用词“或” 。说明咱们可以通过Union类型来为可空类型建模 。
type Option</t><t> = T | null
一个简单的Option其实就是一个或类型, 当然你可以使用一个更加复杂的Option实现, 不过不在咱们今天的讨论范围内 。经过改写后的代码变成了这样:
type CreditCard = { cardNo: string firstName: string middleName: Option<string> lastName: string contactEmail: Email contactPhone: Phone}避免基本类型偏执(Primitive Obsession)问:cardNo可以用string来表示吗?如果是 。它可以是任意字符串吗?firstName可以是任意长度的字符串吗?很显然 。你无法回答上面的问题 。源于这个模型并没有包含有此类领域知识 。
也许在编程语言里面 。cardNo可以用string表示 。但是cardNo在领域模型中 。string无法表示出cardNo的领域知识 。
cardNo是一个200打头的19位字符串 。name是一个不超过50位的字符串 。这样的领域信息可以通过type alias来实现:
type CardNo = stringtype Name50 = string...有了上面两个类型 。你就有机会通过定义函数的方式 。将cardNo业务规则包含在领域模型中 。
type GetCardNo = (cardNo: string) => CardNo
如果客户输入了一个20位的字符串 。函数GetCardNo返回什么?null?抛出异常?说实话函数式编程语言有比异常更加优雅的Error handling方式, 例如Either Monad或者Railway oriented programming 。本文虽然不包含这类话题 。但至少目前咱们可以用Option来表示这个函数签名:
type GetCardNo = (cardNo: string) => Option<cardno>
这个函数类型清晰的表示了整个考证过程 。客户输入一个字符串, 返回一个CardNo类型 。或者空 。
改写后的领域模型变成了这样:type CreditCard = { cardNo: Option<CardNo> firstName: Name50 middleName: Option<string> lastName: Name50 contactEmail: Email contactPhone: Phone}于是 。现在的代码拥有跟多的领域知识 。丰盛的类型还充当了单元测试的角色 。例如 。你永远都不可能把一个email赋值给contactPhone 。它们不是string, 它们代表不同的领域知识 。
领域模型的原子性和聚合性这个领域模型中的三个name可以分别改写吗?例如只改写middle name?如果不可以 。如何将这种原子性的改写知识包含在领域模型中?说实话咱们非常的容易就可以把Name和Contact两个类型分离出来并加以组合:
type Name = { firstName: Name50 middleName: Option<string> lastName: Name50}type Contact = { contactEmail: Email contactPhone: Phone}type CreditCard3 = { cardNo: Option<CardNo> name: Name contact: Contact}让错误的状态无法表示在领域建模过程中 。这是一条非常重要的原则 。用通俗的话可以理解为:你建立的领域模型应该有尽可能多的静态检查和约束 。让错误发生在编译时 。而不是运行时 。从而杜绝犯错误的机会 。其实整个领域建模都是在遵循这个原则 。例如上面的Email类型和Phone类型 。为什么不用string来表示呢?因为string给与的领域知识不够 。从而允许研究人员有了犯错误的机会 。
让咱们最后看一个例子 。用来探讨明这条原则如何被应用在领域建模中 。上面领域模型中有一个contact类型 。包含一个Email和Phone属性 。支付成功后 。系统可以通过这两个属性给客户发通知 。由此延伸出来这样一条规则:客户一定至少填写Email或者Phone来接受支付消息 。
首先 。上面的领域模型是不匹配这条业务规则的 。因为Email和Phone类型都是非空类型 。意味着这两个属性都应该是必填项 。
咱们能不能把它俩都改为Option类型呢?type Contact = { contactEmail: Option<Email> contactPhone: Option<Phone>}显然也不行 。说实话就是违反了让错误的状态无法表示(Make illegal states unrepresentable), 从而给与了代码犯错的机会 。你的领域模型表示出了一种非法的状态 。即Email和Phone都可以为空 。你也许会说我的xxService做了考证呢 。它俩绝对不会同时为空 。对不起 。咱们希望咱们的领域模型能够包含这种领域知识 。至于xxService 。跟领域模型无关 。到底能否将这一规则表示在领域模型中呢?答案是肯定的 。规则中有一个“或”字 。即咱们可以通过Or类型(union)来表示这种关系:

推荐阅读