写点什么

原创 | 使用 JPA 实现 DDD 持久化 - O: 对象的世界 (1/3)

发布于: 2020 年 08 月 04 日
原创 | 使用JPA实现DDD持久化- O:对象的世界(1/3)







目前,面向对象的编程范式统治了编程语言的世界。除了纯粹支持结构式编程的C、纯粹支持函数式编程的Haskell、Erlang、Lisp、Clojure等语言之外,绝大多数现代编程语言如Java、C#、C++、Swift、Kotlin、Go、Scala等都是面向对象的。

1. 类、对象和值

面向对象的编程语言将类(Class)对象(Object)作为程序的基本构建块,应用程序就是由相互调用的多个对象结合而成。

1.1 对象

Grady Booch在他的名著《Object-Oriented Analysis and Design with Applications》中对对象给出了这样的定义:

一个对象是一个具有状态、行为和标识符的实体。结构和行为类似的对象定义在它们共同的类中。

  • 状态(State)

  • 行为(Behavior)

  • 标识符(Identity)

1.2 对象和值

对象的属性值可以是简单(例如矩形Rectangle对象的width属性值是一个简单的数字),也可以是对另一个外部对象的引用(例如账户Account对象的owner属性,引用了另一个外部对象:一个Owner类的实例)。数字、枚举、字符等等,都是简单值。字符串、枚举、日期等等数据类型,虽然在Java中以对象的形式实现,但本质上可以视为简单值。

因此对象的状态可以人为划分为两部分:一部分是由值类型的所有属性组成的内部状态,另一部分是由对象类型的所有属性组成的外部引用

1.3 类和对象

对象所拥有的属性和操作由对象所属的定义。对象是存在于时间和空间中的具体实体,而类仅代表一种抽象,即一个对象的本质类是对象的模板,对象是类的实例。类定义所有同类对象的共同特征,包括它的对象实例所能够拥有的属性和操作,而具体的对象实例持有自己特有的属性值。同一个类的所有对象拥有相同的属性集,但它们的属性值可以各不相同。

类之间可以存在继承关系。子类继承了超类的所有特征(属性和操作),同时可以再添加自己特有的属性和操作,还可以改变(不鼓励)或修饰超类的操作。所谓改变,是指在子类中完全重写操作的实现代码,将超类的代码覆盖掉。所谓修饰,是指子类的操作代码在调用超类操作代码之前/之后/前后,添加更多的代码。

有些编程语言允许子类继承多个超类,但Java只允许继承一个超类。

下面是一个账户(Account)类的类图:

@startuml
class Owner
abstract class Account {
- boolean locked
- int balance
+ boolean isLocked()
+ int getBalance()
+ void credit(int amount)
+ void debit(int amount)
+ void lock()
+ void unlock()
}
Account --> Owner
@enduml



在账户Account类中有三个私有的属性,代表它持有的两项数据:

  • locked: 账户是否已经被冻结

  • balance:账户的当前余额

  • owner:账户的持有人,指向Owner类的一个实例对象。

还有一些公开的操作:

  • isLocked():表明账户是否已被冻结

  • getBalance():获取账户的当前余额

  • credit():取出

  • debit():存入

  • lock():冻结账户

  • unlock():解冻账户

这些方法或者修改对象的状态(字段值),或者执行结果受对象的状态的影响。举例来说,lock()unlock()会修改locked字段的值,debit()credit()会修改balance字段的值,而credit()的执行结果受balance的值的影响,debit()credit()的执行结果都收到locked的值的影响。

package yang.yu.tdd.bank;
//被测对象
public class Account {
private boolean locked = false;
private int balance = 0;
private Owner owner;
public Account(Owner owner) {
this.owner = owner;
}
public boolean isLocked() {
return locked;
}
public int getBalance() {
return balance;
}
public void debit(int amount) {
if (locked) {
throw new AccountLockedException();
}
if (amount <= 0) {
throw new InvalidAmountException();
}
balance += amount;
}
public void credit(int amount) {
if (locked) {
throw new AccountLockedException();
}
if (amount <= 0) {
throw new InvalidAmountException();
}
if (amount > balance) {
throw new BalanceInsufficientException();
}
balance -= amount;
}
public void lock() {
locked = true;
}
public void unlock() {
locked = false;
}
}

1.4 数据只是实现业务规则的辅助手段

从对象的用户的角度来说,我们真正关注的是对象的行为(方法),而不是它持有的数据(属性值)。与数据相比,行为更加重要。数据存在的目的只是影响方法的执行结果。从上面的例子来看:我们根本不在乎Account对象中的balance字段的值是多少,甚至不关心Account类中是否存在balance字段,我们真正在乎的是:一个Account对象,无论历经多少次存取,只要未被冻结且取款总额不大于存款总额,就可以取款成功,否则取款失败。我们在Account类中定义balance字段的目的,只是为了实现这个业务规则的辅助手段。如果有其他方式可以达到同样的目标,我们完全可以不用在Account类中定义balance这个字段。可以说,以数据库为中心的增删改查开发范式在大多数情况下都是不合适的,以领域模型为中心的领域驱动设计才是更合适的开发范式。数据库是细枝末节,领域模型才是软件开发的核心。



详细内容请戳这里↓↓↓

原创 | 使用JPA实现DDD持久化- O:对象的世界(1/3)



这一节就讲到这里,下一节我们继续讲"O:对象的世界的第二部分"



如果觉得有收获,右下角点个【在看】鼓励一下呗!





发布于: 2020 年 08 月 04 日阅读数: 59
用户头像

高级架构师,技术顾问,交流公号:编程道与术 2020.04.28 加入

杨宇于2020年创立编程道与术,致力于研究领域分析与建模、测试驱动开发、架构设计、自动化构建和持续集成、敏捷开发方法论、微服务、云计算等顶尖技术领域。 了解更多公众号:编程道与术

评论

发布
暂无评论
原创 | 使用JPA实现DDD持久化- O:对象的世界(1/3)