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

36 篇文章 2 订阅
36 篇文章 1 订阅

在这里插入图片描述
在这里插入图片描述

目前,面向对象的编程范式统治了编程语言的世界。除了纯粹支持结构式编程的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)

    对象的状态包括这个对象的所有属性(Property,通常是静态的),以及每个属性的当前值(通常是动态的)。

    例如矩形(Rectangle)这个具有*长(length)宽(width)*两个属性。某一个长3米,宽2米的矩形是一个具体对象。这个矩形的长度和宽度就是它的当前状态。

    对象所拥有的属性由它所属的**类(Class)**定义,通常是静态的,这意味着在对象的生命周期中一般不会动态添加新的属性(但是有些编程语言支持动态添加属性)。而对象的属性值由对象而不是类持有,在对象的生命周期中一般是可以改变的(例如员工的职位这个属性,在他的职业生涯中是会改变的),所以说属性值通常是动态的。当然也有一些属性值在对象的生命周期中不会改变,例如订单对象的订单号,这时它就是静态的。

    系统中的所有对象都封装了某种状态,系统的所有状态都由对象所封装。

    Java中,属性以实例字段(Instance Field)的形式存在,一般是private的,可以(但不必须)通过publicgetter/setter方法暴露为外部可见的属性。如果只有setter方法,就是只读属性;如果只有getter方法,就是只写属性;两者都有,就是可读写属性。

  • 行为(Behavior)

    行为是对象在状态改变和消息传递方面的动作和反应的方式。换言之:对象的行为代表了它外部可见的活动。

    对象的行为包括这个对象的所有操作(Operation)。一个操作代表了一个类提供给它的对象的一种服务。一个操作是某种动作,一个对象调用另一个对象定义的操作,目的是获得反应(获得操作的返回值或发生副作用——修改对象的内部状态)。

    Java中,操作用**方法(Method)**的形式来声明。方法是类的成员函数,可以包含0到多个形式参数。当一个对象调用另一个对象的方法时,我们说一个对象向另一个对象传递了一个消息,消息内容包括了方法名和参数列表。

    方法的执行结果由方法的参数和对象的状态共同决定。例如账户Account类的取款方法credit(int amount),其执行结果既受到方法参数取款金额amount的影响,又受到Account对象的当前余额属性balance影响。因此可以说:

    **一个对象的行为是它的状态以及施加在它上面的操作的函数。**操作(方法)执行结果由操作的参数值和对象的当前状态(属性值)共同决定。

    **一个对象的状态代表了它的行为的累积效果。**例如账户的余额和冻结状态代表了对该账户对象多次调用存款、取款、冻结和解冻操作之后的总结果。

  • 标识符(Identity)

    Khoshafina和Copeland提出了这样的标识符定义:

    标识符是对象的一个属性,它区分这个对象与其他所有对象。

    每个对象的唯一标识符(不一定是名称)是在整个对象的生命周期中都被保持的,即使对象的状态改变了,只要标识符不变,就还是原来的对象。

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:对象的世界的第二部分"。

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值