关于ddd:领域驱动设计DDD前夜面向对象思想

33次阅读

共计 5380 个字符,预计需要花费 14 分钟才能阅读完成。

面向对象

面向对象是一种对 世界 了解和形象的办法。那么 对象 是什么呢?
对象是对世界的了解和形象,世界又代称为 万物 。了解世界是比较复杂的,然而世界又是由 事物 组成的。
正是这样的一种关系,意识事物是极其重要的。那什么是 事物 呢?
事物:由 两个方面组成。事即事件,物即物体,那什么是事件?什么是物体呢?

  • 意志的行为 是为事。
  • 存在的所有是为物,物体又是由属性组成的。

一个事物就是本有属性和行为的组合。因为 对象 是对 事物 的了解和形象,所以对象就是对一个事物的属性和行为的了解和形象。正是这样的一种关系,面向对象 就是对一个事物的 属性和行为 的了解和形象的办法。
了解对象以及形象“对象”就是在了解和形象事物的属性和行为。

属性和操作

面向对象的外围是 对象 ,对象是由 属性 办法 组合而成的。在应用面向对象进行剖析、设计、编码的时候,你 首先 应该想到的是 属性 办法 组合造成的对象。在须要组合的时候就不应该呈现只蕴含属性的对象或者只蕴含办法的对象。

  • 何时须要属性和办法组合的对象呢?
  • 何时只须要蕴含属性的对象呢?
  • 何时只须要蕴含办法的对象呢?

事物由事件和物体组成。事件是行为,物体是属性。

  • 当你 须要 形象一个事物的 事件和物体 时就须要属性和办法的组合。
  • 当你只 须要 形象一个事物的 物体 时就只须要 属性
  • 当你只 须要 形象一个事物的 事件 时就只须要 办法

对象建模

在数据库系统中,它们关怀的是事物中的 物体 ,所以在形象事物时它们只形象了事物中的属性。在利用零碎中,它们关怀的是表白事物的三种形式(属性和办法的组合、只蕴含属性、只蕴含办法),所以在形象事物时须要思考你须要那种形式。
只有须要形象事物(事件和物体)中的属性,也就是物体的这部分,那有可能是须要长久化的。只有须要长久化,通常是保留到关系型数据库中,在关系型数据库中的表(Table)基本上是与面向对象中的对象(Object)的属性是一一对应的。
因为数据库中的表只形象了事物中的属性,所以它有可能是不残缺的。就形象事物的属性来说仍然有两种:只形象事物的属性、形象事物的属性和办法的组合。
正是数据库中 的这种形象造成了数据模型,它比照对象模型是不残缺,所以在面向对象分析(OOA)时肯定要采纳对象(事物)形象而不是数据(属性、物体)形象。
举个例子:
简略金融账户(Account)
属性有:账号(id)、余额(balance)、状态(status)
操作有:开户(open)、登记(close)、存钱(credit)、取钱(debit)。
数据模型的只须要设计字段(fields)和关联关系,所以上面的 SQL 根本已实现。

create table account
(id      integer, balance integer, status  integer);

如果把上述 SQL 转换成 Java 的对象的话,失去将是一个用面向对象设计的数据模型,而不是残缺的对象模型。这种模型在 Java 开发中十分广泛,这是数据模型思维所导致的后果。

@Getter
@Setter
public class Account {private int id; private int balance; private AccountStatus status;}

如果应用对象模型的思维来设计模型,从接口上来看,他应该是这样的:

public interface Account {int getId();
 int getBalance();
 AccountStatus getStatus();
 void open();
 void close();
 void credit(int amount);
 void debit(int amount);}

如果 Account 接口合乎金融账户的设计,那么 Account 最简略地实现应该如下:

@Getter
public class Account {
 private int id; private int balance; private AccountStatus status;
 public void open() { this.status = AccountStatus.OPENED;}
 public void close() { this.status = AccountStatus.CLOSED;}
 public void credit(int amount) {this.balance += amount;}
 public void debit(int amount) {this.balance -= amount;}}

这是从两个建模的角度来比照对象模型和数据模型的不同,上面咱们还要从残缺地执行流程来比照。

Account Credit

首先是应用数据模型所设计的时序图,因为数据模型下的 Account 不蕴含业务逻辑,所有的业务逻辑都在 AccountService 中,所以通常称为业务逻辑服务(层)或者事务脚本。如图下:

应用 Java 代码的实现:

public class AccountService {
 private final AccountRepository accountRepository;
 public AccountService(AccountRepository accountRepository) {this.accountRepository = accountRepository;}
 public Account creditAccount(int accountId, int amount) {var account = this.accountRepository.findById(accountId) .orElseThrow(() -> new AccountException("The Account was not found")); if (AccountStatus.OPENED != account.getStatus()) {throw new AccountException("The Account is not open"); } account.setBalance(account.getBalance() + amount); return this.accountRepository.save(account); }}

当初咱们要应用对象模型的思维进行设计时序图

应用 Java 代码的实现:

public class AccountService {
 private final AccountRepository accountRepository;
 public AccountService(AccountRepository accountRepository) {this.accountRepository = accountRepository;}
 public Account creditAccount(int accountId, int amount) {var account = this.accountRepository.findById(accountId) .orElseThrow(() -> new AccountException("The Account was not found")); account.debit(amount); return this.accountRepository.save(account); }}

在 AccountService 的 creditAccount 办法中曾经没有了业务代码,更多地是协调调用执行流程。对于这种只用来实现执行流程,不在蕴含业务逻辑的服务对象,将它们称为应用服务 (Application Service)。
举个家政服务公司与 AccountService 类似的例子:
比方你想请一位保洁阿姨给家里做一做清洁工作,首先是你打电话给家政服务公司说你要给家里做一做清洁工作,而后家政公司安顿一位保洁阿姨去你家帮忙实现清洁工作,在这个过程中家政公司次要做了接待、协调、安顿、最初可能蕴含一些保洁阿姨的绩效等一系列工作。下面的 AccountService 也一样是在做这样的一件事件。所以在对象模型中,AccountService 只须要做像家政公司这样协调工作,具体地工作由保洁阿姨来实现,这里的保洁阿姨就相当于 Account 对象。
从两处比照来看,采纳数据模型建模配合业务逻辑服务的形式更像是过程化编程,只是在应用面向对象语言来编写过程化代码。而采纳对象模型配合应用服务的形式才是合乎面向对象编程。

组合与聚合

在少数的业务开发中,广泛提到的是关联关系(一对一、一对多、多对多)和继承泛化,很少去关注组合与聚合,然而组合与聚合在面向对象中是相当重要的。
组合与聚合是在探讨整体与局部的关系,这种整体与局部的关系是一种比关联关系更强的关系。比方:汽车与轮胎,汽车是一个整体,轮胎是汽车的一部分。如果汽车没有轮胎,那么就无奈形成汽车的完整性,所以在探讨整体与局部的关系时,要特地留神 整体对局部的依赖性而不是局部对整体的依赖
首先通过一个人进食过程的用例来思考整体与局部的依赖关系,而后在例子中阐明组合与聚合区别和分割。
这个进食过程须要多个人体器官合作配合。首先是通过一种形式将食物送进口腔,由牙齿的咀嚼和舌头的搅拌,再由喉咙吞咽,从食道进入胃中,在通过胃里进行初步消化,将饮食变成食糜,而后传入小肠后,在脾的运化作用下,精微物质被排汇。
留神 :这个从嘴到胃的执行过程并不是一个 Input/Output 形式,而是一个 Stream 形式,前面还有连贯。从这个角度来思考嘴只是 Stream 的入口,然而这个用例次要是想阐明整体与局部的分割,所以把这种连贯的每一个局部批改成 Input/Output 调用形式。
为这次进食过程来建模吧!首先确定要害的对象模型有:人(Person)、嘴(Mouth)、食道(Esophagus)、胃(Stomach)、肠道(Intestine)。代码如下:

// 嘴
public class Mouth {public Object chew(Object food) {return food;}}
// 食道
public class Esophagus {public Object transfer(Object paste) {return paste;}}
// 胃
public class Stomach {public Object fill(Object paste) {return paste;}}
// 肠道
public class Intestine {public void absorb(Object chyme) {// absorbing...}}
public class Person {
 private final Mouth mouth; private final Esophagus esophagus; private final Stomach stomach; private final Intestine intestine;
 public Person() { this.mouth = new Mouth(); this.esophagus = new Esophagus(); this.stomach = new Stomach(); this.intestine = new Intestine();}
 public void eat(Object food) { // 进食。var paste = this.mouth.chew(food); // 咀嚼造成浆糊。paste = this.esophagus.transfer(paste); // 通过食道传送食物。var chyme = this.stomach.fill(paste); // 填充到胃里造成食糜。this.intestine.absorb(chyme); // 在肠道里排汇养分。// 便秘中...
 }}
public class PersonTests {public static void main(String[] args) {new Person().eat("chips"); } }

在整个进食流程中,是由人 (Person) 做的吃 (eat) 这个动作开始,而后由人体外部的多个参加的局部对象协调实现的,这就是整体与局部的关系。Person 是个整体,Mouth, Esophagus, Stomach, Intestine 是整体内的局部。而后在思考一个事件,这些局部对象是不是附丽在整体对象上,比方:嘴是不是独立于人体不能存活,随同着人的存在而存在,沦亡而沦亡。这种局部对象的创立、存在和沦亡都是和整体对象一起的就称为组合 。而聚合就不像组合的整体与局部的关系那么强,比方:汽车与轮胎是一个整体与局部的关系,汽车没有轮胎必定跑不了。然而汽车能够更换轮胎,这种能够更换的关系就没有组合关系那么强。除了更换还有短少的,比方:螃蟹有八条腿,总的来说螃蟹没有腿必定是无奈行走的,然而短少一个两个还是能行走的,可能行走有一些艰难。 这样的能够在初始化之后可能更换的或者不须要强制残缺的整体与局部的关系称之为聚合
随着工夫的向前和空间的扩充,组合和聚合还是会存在转换的状况,比方将来人能够换嘴、进食流程不须要嘴的参加,再比如说一次性轿车,出厂后就不能培修更换等等。所以在探讨组合与聚合的关系时,要在肯定的限界下来探讨。

总结

  • 对象建模,通过对象模型与数据模型的比照来阐明须要一种对象模型的思维。
  • 对象建模的利用,通过账户贷款的业务来简要阐明如何应用对象模型。
  • 组合与聚合,通过重点阐明组合与聚合,让其在对象模型的根底上,探讨整体与局部的关系。

正文完
 0