Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

精读《架构设计 之 DCI》 #20

Closed
zhaoyangsoft opened this issue Jun 25, 2017 · 5 comments
Closed

精读《架构设计 之 DCI》 #20

zhaoyangsoft opened this issue Jun 25, 2017 · 5 comments

Comments

@zhaoyangsoft
Copy link
Contributor

随着前端ES6 ES7 的一路前行, 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式; 我们可以使用函数式Functional编程设计,可以使用面向对象OOP的设计,可以使用面向接口的思想,也可以使用AOP, 可以使用注解,代理、反射,各种设计模式; 在大前端辉煌发展、在数据时代的当下 我们来一起阅读一篇设计相关的老文:

《The DCI Architecture》

@ascoders ascoders mentioned this issue Jun 25, 2017
65 tasks
@ascoders
Copy link
Owner

ascoders commented Jun 30, 2017

DCI 即 数据(data) 场景(context) 交互(interactive)。

DCI 之所以被提出,是因为传统 mvc 代码,在越来越丰富的交互需求中变得越来越难读。有人会觉得,复杂的需求 mvc 也可以 cover 住,诚然如此,但很少有人能只读一遍源码就能理解程序处理了哪些事情,这是因为人类思维与 mvc 的传统程序设计思想存在鸿沟,我们需要脑补内容很多,才会觉得难度。

现在仍有大量程序使用面向对象的思想表达交互行为,当我们把所有对象之间的关联记录在脑海中时,可能对象之间交互行为会比较清楚,但任无法轻松理解,因为对象的封装会导致内聚性不断增加,交互逻辑会在不同对象之间跳转,对象之间的嵌套关系在复杂系统中无疑是一个理解负担。

DCI 尝试从人类思维角度出发,举一个例子:为什么在看电影时会轻轻松松的理解故事主线呢?回想一下我们看电影的过程,看到一个画面时,我们会思考三件事:

  1. 画面里有什么人或物?
  2. 人或物发生了什么行为、交互?
  3. 现在在哪?厨房?太空舱?或者原始森林?

很快把这三件事弄清楚,我们就能快速理解当前场景的逻辑,并且轻松理解该场景继续发生的状况,即便是盗梦空间这种烧脑的电影,当我们搞清楚这三个问题后,就算街道发生了180度扭曲,也不会存在理解障碍,反而可以吃着爆米花享受,直到切换到下一个场景为止。

当我们把街道扭曲 180 度的能力放在街道对象上时,理解就变的复杂了:这个函数什么时候被调用?为什么不好好承载车辆而自己发生扭曲?这就像电影开始时,把电影里播放的所有关于街道的状态都走马灯过一遍:我们看到街道通过了车辆、又卷曲、又发生了爆炸,实在觉得莫名其妙。

理解代码也是如此,当交互行为复杂时,把交互和场景分别抽象出来,以场景为切入点交互数据。

举个例子,传统的 mvc 可能会这么组织代码:

UserModel:

class My {
  private name = "ascoders" // 名字
  private skills = ["javascript", "nodejs", "切图"] // 技能
  private hp = 100 // 生命值??
  private account = new Account() // 账户相关
}

UserController:

class Controller {
  private my = new My()
  private account = new Account()
  private accountController = new AccountController()

  public cook() {
    // 做饭
  }

  public coding() {
    // 写代码
  }

  public fireball() {
    // 搓火球术。。?
  }

  public underAttack() {
    // 受到攻击??
  }

  public pay() {
    // 支付,用到了 account 与 accountController
  }
}

这只是我自己的行为,当我这个对象,与文章对象、付款行为发生联动时,就发生了各种各样的跳转。到目前为止我还不是非常排斥这种做法,毕竟这样是非常主流的,前端数据管理中,不论是 redux,还是 mobx,都类似 MVC。

不论如何,尝试一下 DCI 的思路吧,看看是否会像看电影一样轻松的理解代码:

以上面向对象思想主要表达了 4 个场景,家庭、工作、梦境、购物:

  1. home.scene.scala
  2. work.scene.scala
  3. dream.scene.scala
  4. buy.scene.scala

以程序员工作为例,在工作场景下,写代码可以填充我们的钱包,那么我们看到一个程序员的钱包:

codingWallet.scala:

case class CodingWallet(name: String, var balance: Int) {
  def coding(line: Int) { balance += line * 1 }
}

写一行代码可以赚 1 块钱,它不需要知道在哪个场景被使用,程序员的钱包只要关注把代码变成钱。

交互是基于场景的,所以交互属于场景,写代码赚钱的交互,放在工作场景中:

work.scene.scala

object MoneyTransferApp extends App {

  @context
  class MoneyTransfer(wallet: CodingWallet, time: int) {
    // 在这个场景中,工作 1 小时,可以写 100 行代码
    // 开始工作!
    wallet.working

    role wallet {                     
      def working() {                 
        wallet.coding(time)       
      }
    }
  }

  // 钱包默认有 3000 元
  val wallet = CodingWallet("wallet", 3000)

  // 初始化工作场景,工作了 1 小时
  new MoneyTransfer(wallet, 1)

  // 此时钱包一共拥有 3100 元
  println(wallet.balance)
}

总结一下,就是把数据与交互分开,额外增加了场景,交互属于场景,获取数据进行交互。原文的这张图描述了 DCI 与 MVC 之间的关系:

image

@ascoders
Copy link
Owner

抛砖引玉,关于 DCI 项目实践,需要大家来补充

@TingGe
Copy link

TingGe commented Jun 30, 2017

作为架构白,看到 DCI 时一头雾水。于是用心补了下相关知识,从中发现了梳理现代前端越来越多的概念、模式和实践这些知识的蛛丝马迹。

现代前端受益于低门槛和开放,伴随 OO 和各种 MV* 盛行,也出现了越来越多的概念、模式和实践。而 DCI 作为 MVC 的补充,试图通过引入函数式编程的一些概念,来平衡 OO 、数据结构和算法模型。值得我们津津乐道的如 Mixins、Multiple dispatch、 依赖注入(DI)、Multi-paradigm design、面向切面编程(AOP)都是不错的。如果对这些感兴趣,深挖下 AngularJS 在这方面的实践会有不少收获。
当然,也有另辟途径的,如 Flux 则采用了 DDD/CQRS 架构。

软件架构设计,是一个很大的话题,也是值得每位工程师长期实践和思考的内容。个人的几点体会:

  1. 一个架构,往往强调职责分离,通过分层和依赖原则,来解决程序内、程序间的相互通讯问题;
  2. 知道最好的几种可能的架构,可以轻松地创建一个适合的优化方案;
  3. 最后,必须要记住,程序必须遵循的架构。

分享些架构相关的文章:

@zhaoyangsoft
Copy link
Contributor Author

zhaoyangsoft commented Jun 30, 2017

DCI 数据Data 场景Context 交互Interactions

我们在开发的过程中多多少少都会使用到一些设计方法和原则
DCI 重点是关注 数据的不同场景的交互行为, 是面向对象系统 状态和行为的一种范式设计;

它能够将过程逻辑与对象逻辑分开,是一种典型的行为模式设计;
很好的点是 它根据AOP的基本原理,DCI 提出基于AOP 深层次的元编程(可以理解成面向接口编程), 去促使系统的内聚效果和降低耦合度;

举个例子:
在一个BI系统中, 在业务的发展中, 这个系统使用到了多套的 底层图表库,比如: Echarts, G2,Recharts, FusionChart; 等等;

那么问题来了,

  1. 如何去同时支持 这些底层库, 并且达到很容易切换的一个效果?
  2. 如何去面向未来的考虑 将来接入更多类型的图表?
  3. 如何去考虑扩展业务 对图表的日益增强的业务功能(如: 行列转换、智能格式化 等等)

带着这些问题, 我们再来看下 DCI 给我们的启示, 我们来试试看相应的解法:

  1. 图表的模型数据就是 数据Data , 我们可以把[日益增强的业务功能] 认为是各个场景交互Interactions;

  2. 接入更多类型的图表咋么搞?
    不同类型的图表其实是图表数据模型的转换,我们也可以把这些转换的行为过程作为一个个的切片(Aspect),每个切片都是独立的, 松耦合的 ;
    image

  3. 接入多套底层库怎么搞? 每个图形库的 build方法,render 方法 , resize 方法,repaint 方法 都不一样 ,怎么搞 ? 我们可以使用 DCI 提到的元编程- 我们在这里理解为面向接口编程, 我们分装一层 统一的接口;利用面向接口的父类引用指向子类对象 我们就可以很方便的 接入更多的 implement 接入更多的图形库(当然,一个系统统一一套是最好的);

扔个砖,大家继续来补充 ~.

@BuptStEve
Copy link

@ascoders 纠正个错别字...

当我们把所有对象之间的关联记录在脑海中时,可能对象之间交互行为会比较清楚,但【任 -> 仍】无法轻松理解

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants