According to Wikipedia Data, context and interaction (DCI) is a paradigm used in computer software to program systems of communicating objects. Here I am not clear about the problem which DCI tries to solve. Can you explain it with simple example? What is Data, Context and Interactions in your example?
An easy way for me to understand it is with the classic banking application example. In this example, I'll use Rails.
Let's say there's a feature of our app where users can transfer money from one account to another account.
We might have a controller that looks like this:
# 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
In here, we're calling transfer_to
on one of our Account
objects. This method is defined in the Account
model.
# app/models/account.rb
class Account < ActiveRecord::Base
def transfer_to(destination_account, amount)
destination_account.balance += amount
self.balance -= amount
save
end
end
This is a traditional MVC solution - when the transfer
method on the controller is called, we instantiate a couple of model objects and call the behavior defined on the model. Like Robert said, the business logic is split up, and we have to look in a couple different places to understand the code.
The downside of this approach is that we can end up with a lot of behavior defined inside of our models that they don't always need and lacks context. If you've worked on a large project before, it's not long before model files grow to be several hundred or even a couple thousand lines of code because all of the behavior is defined inside of them.
DCI can help solve this problem by giving our data models behavior only within certain contexts that they need to use that behavior. Let's apply this to our bank application example.
In our example, the Context is transferring money. The Data would be the Account
objects. The Behavior is the ability to transfer money. The Interaction is the actual action of transferring money from one account to the other. It could look something like this:
# 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
As you can see from the example, the Data is given its behavior within the Context at runtime, when we call source_account.extend Transferrer
. The transfer
method is where the Interaction takes place. This prevents us from splitting logic into separate files and it's all contained inside one Context class.
We would call it from the controller like this:
# 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
With such a simple example, this might seem like more trouble than it's worth, but when apps grow really large and we add more and more behavior to our models, DCI becomes more useful because we only add behavior to our models within the context of some specific interaction. This way, model behavior is contextual and our controllers and models are a lot smaller.
The key aspects of the DCI architecture are:
- Separating what the system is (data) from what it does (function). Data and function have different rates of change so they should be separated, not as it currently is, put in classes together.
- Create a direct mapping from the user's mental model to code. The computer should think as the user, not the other way around, and the code should reflect that.
- Make system behavior a first class entity.
- Great code readability with no surprises at runtime.
I highlighted user's mental model because that's what it's really about. The system architecture should be based on the users thought processes, not the engineers.
Of course the mental model should be discussed and produced by everyone related to the project, but that's rare. Usually the engineers will code according to patterns, decomposition, inheritance, polymorphism, and the part of the code that makes sense to the user is obfuscated behind layers of structure.
This is what DCI tries to remedy. It has run across some resistance over the years, in my opinion because engineers love their structure and classes, so they focus primarily on that.
An illustrating code example would be too lengthy to post here, but the mindset is more important anyway. It's about objects dynamically working together, to solve specific problems. I have made a larger tutorial here, with some code: https://github.com/ciscoheat/haxedci-example
Also, I highly recommend the video A glimpse of Trygve for further explanation, by one of the DCI authors, James Coplien.
If you read this paper from the original authors, specifically the chapter "Where did we go wrong?", the authors give some reasons why they felt a new approach was needed.
In short: The authors complain, that proper Object-Oriented methodology leads to "splitting up" the business logic. This is true, as this is the primary reason we decompose problems, so that we don't have to tackle the whole logic in one go.
The authors argue (in the same chapter as above), that the previous procedural approach was better (giving the example of FORTRAN code), since one could read the code sequentially and decide whether it does what it is supposed to do.
They also argue (in the next chapter: Back into the Users' Head) that it is easier for developers to think about "data" first, and procedures (e.g. interactions) later.
The authors basically argue for at least a partial regression to procedural programming, clearly separating data and logic, in contrast to object-orientation which bundles "data and logic" together.
My personal opinion is, that it is slightly misleading to call this approach object-oriented, since it is a strong critique of it, with clear intention of deviating from it. But, don't take my word for it, read the Article.
© 2022 - 2024 — McMap. All rights reserved.