根据 Wikipedia Data,上下文和交互 (DCI) 是计算机软件中用于对通信对象系统进行编程的范例。在这里,我不清楚 DCI 试图解决的问题。你能用简单的例子解释一下吗?您的示例中的数据、上下文和交互是什么?
3 回答
对我来说,理解它的一个简单方法是使用经典的银行应用程序示例。在本例中,我将使用 Rails。
假设我们的应用程序有一项功能,用户可以将资金从一个帐户转移到另一个帐户。
我们可能有一个如下所示的控制器:
# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
def transfer
@source_account = Account.find(params[:id])
@destination_account = Account.find(params[:destination_id])
@amount = params[:amount]
if @source_account.transfer_to(@destination_account, @amount)
flash[:success] = "Successfully transferred #{@amount} to #{@destination_account}"
redirect_to @source_account
else
flash[:error] = "There was a problem transferring money to #{@destination_account}"
render :transfer
end
end
end
在这里,我们正在调用我们transfer_to
的一个Account
对象。该方法在Account
模型中定义。
# app/models/account.rb
class Account < ActiveRecord::Base
def transfer_to(destination_account, amount)
destination_account.balance += amount
self.balance -= amount
save
end
end
这是一个传统的 MVC 解决方案——当transfer
调用控制器上的方法时,我们实例化几个模型对象并调用模型上定义的行为。就像罗伯特说的,业务逻辑是分开的,我们必须查看几个不同的地方来理解代码。
这种方法的缺点是我们最终会在模型内部定义很多行为,而这些行为并不总是需要并且缺乏上下文。如果您以前从事过大型项目,那么模型文件不久就会增长到数百甚至数千行代码,因为所有行为都在其中定义。
DCI 可以通过仅在它们需要使用该行为的特定上下文中赋予我们的数据模型行为来帮助解决这个问题。让我们将此应用于我们的银行应用程序示例。
在我们的示例中,上下文是转账。数据将是Account
对象。行为是转移资金的能力。交互是将资金从一个帐户转移到另一个帐户的实际操作。它可能看起来像这样:
# app/contexts/transferring_money.rb
class TransferringMoney # this is our Context
def initialize(source_account, destination_account) # these objects are our Data
@source_account = source_account
@destination_account = destination_account
assign_roles(source_account)
end
def transfer(amount) # here is the Interaction
@source_account.transfer_to(@destination_account, amount)
end
private
def assign_roles(source_account)
source_account.extend Transferrer
end
module Transferrer
def transfer_to(destination_account, amount)
destination_account.balance += amount
self.balance -= amount
save
end
end
end
从示例中可以看出,当我们调用source_account.extend Transferrer
. 该transfer
方法是发生交互的地方。这可以防止我们将逻辑拆分为单独的文件,并且它们都包含在一个 Context 类中。
我们会像这样从控制器调用它:
# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
def transfer
@source_account = Account.find(params[:id])
@destination_account = Account.find(params[:destination_id])
@amount = params[:amount]
if TransferringMoney.new(@source_account, @destination_account).transfer(@amount)
flash[:success] = "Successfully transferred #{@amount} to #{@destination_account}"
redirect_to @source_account
else
flash[:error] = "There was a problem transferring money to #{@destination_account}"
render :transfer
end
end
end
对于这样一个简单的例子,这看起来可能比它的价值更麻烦,但是当应用程序变得非常大并且我们向模型添加越来越多的行为时,DCI 变得更加有用,因为我们只在某些上下文中向模型添加行为具体互动。这样,模型行为是上下文相关的,我们的控制器和模型要小得多。
DCI 架构的关键方面是:
- 将系统是什么(数据)与它的作用(功能)区分开来。数据和函数有不同的变化率,所以它们应该分开,而不是像现在这样,放在一起的类中。
- 创建从用户的心理模型到代码的直接映射。计算机应该以用户的身份思考,而不是相反,代码应该反映这一点。
- 使系统行为成为一流的实体。
- 出色的代码可读性,在运行时没有意外。
我强调了用户的心理模型,因为这就是它的真正意义所在。系统架构应该基于用户的思维过程,而不是工程师。
当然,与项目相关的每个人都应该讨论和制作心智模型,但这种情况很少见。通常工程师会根据模式、分解、继承、多态进行编码,而对用户有意义的部分代码在结构层后面被混淆了。
这就是 DCI 试图解决的问题。多年来,它遇到了一些阻力,在我看来,因为工程师喜欢他们的结构和课程,所以他们主要关注这一点。
示例代码示例太长,无法在此处发布,但无论如何心态更重要。它是关于对象动态地协同工作,以解决特定问题。我在这里做了一个更大的教程,有一些代码:https ://github.com/ciscoheat/haxedci-example
此外,我强烈推荐DCI 的作者之一 James Coplien的视频A 一瞥 Trygve以获得进一步的解释。
如果您阅读原作者的这篇论文,特别是“我们哪里出错了?”一章,作者会给出一些他们认为需要一种新方法的理由。
简而言之:作者抱怨说,正确的面向对象方法会导致“拆分”业务逻辑。这是真的,因为这是我们分解问题的主要原因,所以我们不必一次性解决整个逻辑。
作者认为(在与上述相同的章节中),以前的程序方法更好(给出 FORTRAN 代码的示例),因为人们可以按顺序阅读代码并决定它是否执行它应该做的事情。
他们还争辩说(在下一章:回到用户的头脑中),开发人员更容易先考虑“数据”,然后再考虑过程(例如交互)。
与将“数据和逻辑”捆绑在一起的面向对象相比,作者基本上主张至少对过程编程进行部分回归,清楚地分离数据和逻辑。
我个人的看法是,将这种方法称为面向对象有点误导,因为这是对它的强烈批评,并且有明显偏离它的意图。但是,不要相信我的话,阅读文章。