作者adrianshum (Alien)
看板java
标题Re: [问题] 系统设计
时间Thu Jun 13 07:33:28 2019
※ 引述《gasbomb (虚空雷神兽)》之铭言:
: 大家好, 小弟 java 新手
: 个人的第一个作品因为缺乏经验当初做得苦不堪言
: 肥胖商务层处理完资料後
: 一行一行的把资料填进 VO 里面
: 到了 DAO 又要一行一行的把资料填入 PreparedStatement
: 写起来既枯燥又充满重复的程式码
: 後来才了解到这其实是一种 Anemic Domain Model (贫血领域模型)
: 最近练习刻新系统, 取消商务层的设计只保留部分的 service
: 让大部分的逻辑进入物件, 看看可不可以让自己的系统充血一点
: 但是现在遇到了一些设计上的问题
: 假设今天有一家面包店
: ┌───────┐ ┌────┐
: │bread_category│ │bread │
: ╞═══════╡ ╞════╡
: │cid (PK)←──┼┐│bid (PK)│
: │cname │└┼cid (FK)│
: │...... │ │...... │
: └───────┘ └────┘
: bread 是面包(废话)
: bread_category 是面包分类
: 今天的逻辑是 cid 可以删除
: 删除以後该分类下面所有的面包全部移到未分类 cid = 0 底下
: 这件事情在 DB 上面做非常简单, 只要两行指令就搞定了
: UPDATE bread SET cid = 0 WHERE cid = 1;
: DELETE FROM bread_category WHERE cid = 1;
: 因为这是 DB 的操作, 所以在 DAO 里面写一个方法让物件使用也是很合理的
: 但是当我准备这麽作时却有一种不安的感觉浮上心头, 觉得自己好像已经破坏了什麽规
则
: 照理说更改面包分类是事务逻辑, 应该在 model 层处理, DAO 只负责资料存取
: 从相依性的角度来说, DAO 写太多东西进去也会加重日後换 DB 的负担
: 所以我可能会在 model 这样写
: new Bread().getAll().stream()
: .filter(bread -> bread.getCID() == 1)
: .forEach(bread -> {
: bread.setCID(0);
: bread.update();
: });
: 这样打开面包原始码所有功能一目了然, 日後也方便修改
: 但是为了删除一个 cid 叫出所有面包好像哪里怪怪的
: 叫所有面包一起更新也会占用大量的资料库连线 (当初没考虑到大量更新的需求)
: 为了解决上面的问题在 DAO 另外写两个方法
: 一个是用 cid 查询面包
: 一个是大量更新面包......
: 这样看起来好像兼顾逻辑跟效能, 可是 DAO 肥大的问题又回来了啊~~~~(崩溃)
: 而且这样需要写的程式更多
: 那我还不如回去写那两行 SQL 指令
: 虽然我知道这间面包店可能一辈子都不会有效能瓶颈的问题
: 不过上面的问题确实已经困扰我一天了
: google 找到的也都是一些很 general 的, 介绍设计模式的文章
: 不知道大家在遇到这种问题的时候都是如何决策的?
: 如果今天是我的案例, 大家会用哪一种方案解决呢?
原文两位推文在说的还是DB 实作上的取舍,
OP烦恼的是Anemic model vs behaviorial-rich
model . 我相信你应该有看一些Domain Driven
Design (DDD)的入门?单看你的code 有些诡异:
正常设计不会new Bread().getAll() 去取,
Model 本身不负责update自己(可能你误解
Anemic model的问题了?)
存取层该是Repository 而不叫DAO 。
回到你的问题,首先你这个”移动category” 的
行为打算放在哪个domain model?假设你有个叫 BreadShop 的model (没有合用的Model
可以弄个Domain Service, 详见DDD).
东西大概会长这样(pseudo code):
class BreadShop {
void cancelCategory(Category cat, Category moveTo) {
// 假设你Category 有指向它拥有的包
cat.moveBreadsTo(moveTo);
breadCategoryRepo.update(moveTo);
breadCategoryRepo.remove(cat);
//又或者长这样
breads=breadRepo.findByCategory(cat);
for b in breads
bread.putToCaregory(moveTo);
// 诸如此类
}
}
重点是这个动作也是放在Domain 层内,假
设後来你发现真的太慢,也只需要在Repo
内提供bulk update 的功能,但domain 所
Expose 的API 仍然可以维持不变。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 223.19.42.108 (香港)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/java/M.1560382410.A.3BD.html
1F:推 dream1124: 推 06/16 11:50