Data, context and interaction

This is an old revision of this page, as edited by 87.52.76.100 (talk) at 16:51, 14 June 2010 (History: Save before dinner.). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

DCI

Data, Context and Interaction (DCI) is a paradigm used in computer programming. Its goals are:

  • To improve the readability of object-oriented code by giving system behavior first-class status;
  • To cleanly separate code for rapidly changing system behavior (what the system does) from that for slowly changing ___domain knowledge (what the system is), instead of combining both in one class interface;
  • To support an object style of thinking that is close to peoples' mental models, rather than the class style of thinking that overshadowed object thinking early in the history of object-oriented programming languages.

The pattern separates the ___domain model (Data) from Use cases (Context) and Roles that objects play (Interaction). DCI is complementary to Model–view–controller (MVC). MVC as a pattern language is still used to separate the data and its processing from presentation.

DCI was invented by Trygve Reenskaug, also the inventor of MVC. The current formulation of DCI is mostly the work of Reenskaug and James O. Coplien.

Description

Data
The Data are "what the system is." The Data part of the DCI architecture is its (relatively) static data model with relations. The Data design is usually coded up as conventional classes that represent the basic ___domain structure of the system. These classes are barely smart data, and they explicitly lack the functionality that is peculiar to support of any particular use case. These classes commonly abstract the physical storage of the data. These data implement an information structure that comes from the mental model of end users, ___domain experts, programmers, and other people in the system. They may correspond closely to the Model objects of MVC.
An example of a Data object could be a bank account. Its interface would have basic operations for increasing and decreasing the balance and for inquiring about the current balance. The interface would likely not offer operations that involve transactions, or which in any way involve other objects or any user interaction. So, for example, while a bank account may offer a primitive for increasing the balance, it would have no method called deposit. Such operations belong instead in the Interaction part of DCI.
Data objects are the sort that relate to the classes that might come from ___domain-driven design, and such classes might use subtyping relationships to organize ___domain data. Though it reduces to classes in the end, DCI reflects a computational model dominated by object thinking rather than class thinking. Therefore, when thinking "Data" in DCI, it means thinking more about the instances at run time than about the classes from which they were instantiated.
Context
A Context is an object that is a collection of code that includes the roles for a given algorithm, scenario, or use case, as well as the code to map these roles into objects at run time and to enact the use case. A Context is often implemented as a Context class that is instantiated at the beginning of the enactment of an algorithm, scenario, or use case. In summary, a Context is use cases and algorithms in which data models are used through specific roles.
Each Context represents one or more use cases. A Context object is instantiated for each enactment of a use case for which it is responsible. Its main job is to identify the objects that will participate in the use case and to associate them with the roles whose responsibilities characterize the use case. Each role comprises methods, and each method is some small part of the logic of an algorithm implementing a use case. The role-to-object bindings that take place in a Context can be contrasted with the polymorphism of vernacular object-oriented programming. The overall business functionality is the sum of complex networks of methods decentralized in multiple roles. For a given use case enactment, each of these roles runs in the context of an object selected for it by the Context.
Each Context is a scope that includes identifiers that correspond to roles. Any role executing within that Context can refer to the other roles in that Context through these identifiers. These identifiers have come to be called methodless roles. At use case enactment time, each and every one of these identifiers becomes bound to an object playing the corresponding role for this Context.
An example of a context could be a wire transfer between two accounts, where data models (the banking accounts) are used through source account and destination account roles.
Interaction
The Interaction is "what the system does." The Interaction is implemented as roles which are played by objects at run time. These objects combine the data and methods of a data (___domain) object with methods from one or more roles. In good DCI style, a role addresses another object only in terms of its (methodless) role. There is a special role called self which binds to the object playing the current role. Code within a role method may invoke a method on self and thereby invoke a method of the Data part of the current object. One curious facet of DCI is that these bindings are guaranteed to be in place only at run time (using a variety of approaches and conventions; C++ templates can be used to guarantee that the bindings will succeed). This means that Interactions—the role methods—are generic. In fact, some DCI implementations use generics or templates for roles.
A role is a programming construct that corresponds to the end user's mental model of some entity in the system. A role is a collection of responsibilities. Whereas vernacular object-oriented programming speaks of objects or classes as the locus of responsibilities, DCI ascribes them to roles. An object participating in a use case has responsibilities: those that it takes on as a result of playing a particular role. Most modern programming languages have a way to express roles, and to express the injection of role methods into objects, and implementation techniques vary depending the language. The injection can be fully dynamic at run-time in languages like Ruby and Python; it is more static in languages like Scala and C++. The Qi4j programming environment offers a way to express role method injection into objects in Java.
A money transfer use case, for example, might contain roles such as the source account, destination account, and amount to be transferred.

Execution Model

DCI can be thought of as an event-driven paradigm, where some event (as a human gesture in an MVC architecture) triggers a use case. The use case can be short-lived or long-lived. The events are called triggers, and they are handled in the environment in which DCI has been embedded. This environment may be the controller of a conventional MVC architecture or any other system-level code.

The trigger causes the environment to instantiate a Context object. The type of object is chosen according to the kind of use case that will ensue, based on information about the trigger or the system state or both. For example, a cash machine might have separate Context classes for money transfer, withdrawal, deposit, balance inquiry, and so forth. Once the environment instantiates the Context object, it invokes its trigger method to start the use case enactment.

As described above, each Context provides a design scope for the roles that participate in the use case enactment. It is the job of the Context to assign objects to play these roles.

  1. The Context first finds the objects that are to take place in this use case enactment. These objects may be anywhere in the environment, or in a database; DCI does not restrict these objects. Within a Context there is at most one instance playing any given role at any given time.
  2. Second, the Context ensures that each of its roles has an object that can play that role, and arranges for the object to play that role. In strongly dynamic languages (Ruby, Python) the Context injects the role methods into the object. In most dynamic languages, any extant object can be asked to play any role at any time (though some object-role combinations may of course make no sense; nonsense combinations of objects and roles would lead to MESSAGE NOT UNDERSTOOD at run time if the role method were invoked.) In more statically typed languages (Scala, C++) there has been some prior arrangement for the object to support the role methods. For example, Scala creates an anonymous class that combines the rudimentary logic of a ___domain class with the use case logic of the trait used to implement a role; roles are effectively assigned to ___domain objects when they are instantiated.
  3. Third, the Context invokes a role method on the the first object to take part in the use case.
  4. From that point forward, roles invoke each others' methods to carry out the use case. A role method may invoke a method on self which in fact is handled by the object currently playing the role. This is how roles invoke the rudimentary data operations of the objects currently playing the roles.

Implementing DCI

DCI depends on a design process that separates use cases from the data model. The data model is often based on an informal ___domain analysis. The roles that characterize the end-user's model of system functionality come from the use cases. One approach to overall DCI design is Lean Architecture[1] described in a book of the same name [2].

Implementation techniques differ across programming languages. What is common to most approaches is that the code for system behavior is implemented in constructs that represent roles, which are variously represented as generics, templates, classes, or traits. Code for the basic ___domain logic is implemented separately, usually in line with conventional object oriented practice and most commonly using classes. Role code is injected into those ___domain objects that will play the role during a use case enactment. For roles, method injection is usually needed. Traits[3] are one common programming language technique to support method injection. Some languages, such as Scala, have native support for traits, while other languages (e.g., Ruby and Python) allow run time injection of methods. In Java, pre-compiler tricks based on annotations are needed to support DCI.

Several example implementations exists: Smalltalk/Squeak, C++[4], C#[5], Ruby[6], JavaScript[7], Python, Qi4J (Java)[8] , Scala and PHP[9].

History

DCI arose largely as an outgrowth of Trygve Reenskaug's work on role-based computation. [10]. Trygve had long recognized that roles played a central part in the way we think about objects, and that the class-based progression of programming language technology took away much of

Many key advances in the past twenty years of object-orientation exhibit components of DCI. While no one of them fully provides the DCI computational model, the overlap suggests that the problems addressed by DCI are longstanding and fundamental.

  • Mix-ins were a way of encapsulating code for specific what-the-system-does functionality in closed form; however, they lacked mechanisms for linking together multiple mix-ins into a unit at the level of a use case. Mix-ins are very close to the concept of Role in DCI.
  • Multiple dispatch was an early attempt to more fully separate an algorithm from the objects that were involved in its execution, but it lacked DCI's separation of common recurring algorithms from the code fragments that could individually be localized to individual objects. DCI conceptually leads to broader re-use of a single algorithm in closed form across many sets of objects of widely heterogeneous types. DCI's Context object acts like an explicit, intelligence dispatcher that is analogous to the dispatching mechanisms of languages with multiple dispatch.
  • True object-oriented programming languages like self[11] tried to break down the dichotomy between the classful programming world and the objectful exeuction world. While this helped programmers focus on run-time objects, it sacrificed code-level knowledge about the relationships between them, as can be found in DCI Contexts or in the static relationships between role methods.
  • Dependency injection[12] is a longstanding approach to change the functionality of an object at run time by allowing it to "outsource" some of its execution to an external object that can be re-bound at will. Most implementations of dependency injection lead to the self schizophrenia problem, which implementations of DCI address properly.
  • Multi-paradigm design
  • Aspect-oriented programming (AOP) is perhaps the closest historic relative to DCI. However, most use of Aspects is closely tied to the programmer perspective rather than to the end user mental model of use cases. Further, without strong tool support, Aspects usually make code less readable from the perspective of understanding what actually goes on at a given pointcut. Think of DCI as a way of taking a large advice and allowing parts of it to be injected into a number of regularized pointcuts. Whereas AOP...

Other recent similar systems...

References

  1. ^ Lean Software Architecture web page, http://www.leansoftwarearchitecture.com
  2. ^ James Coplien and Gertrud Bjørnvig, Lean Architecture for Agile Software Development. Wiley: 2010.
  3. ^ Nathaniel Schärli et al. Traits: Composable units of behavior. http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf
  4. ^ C++ source code on Object-Composition Google group, http://object-composition.googlegroups.com/web/20090118C__DCI.tar?gda=YZxxZkQAAADrRvU1tICZInrYQGkdqcjVB9LJpDFQtNZStXvSrZaE044UiKBbjmHuWOApdsY8dkxV6u9SiETdg0Q2ffAyHU-dzc4BZkLnSFWX59nr5BxGqA 17.10.2009
  5. ^ C# source code on Object-Composition Google group,http://object-composition.googlegroups.com/web/20090504_C%23_DCI.zip?gda=KuAYkEcAAADrRvU1tICZInrYQGkdqcjVB9LJpDFQtNZStXvSrZaE07Ryyh4ndddBwXohD2r2F8gbzHe87USdioT9uNiA7PHaeV4duv6pDMGhhhZdjQlNAw 17.10.2009
  6. ^ Ruby source code on Object-Composition Google group,http://groups.google.com/group/object-composition/browse_thread/thread/561f638b43f1b960# 17.10.2009
  7. ^ JavaScript source code on Object-Composition Google group,http://groups.google.com/group/object-composition/browse_thread/thread/8ec4cf18e127cc3e# 17.10.2009
  8. ^ Qi4j source code on Object-Composition Google group,http://groups.google.com/group/object-composition/browse_thread/thread/fe317e615b9008fe# 17.10.2009
  9. ^ PHP source code on Google,,http://code.google.com/p/php-coredci
  10. ^ Trygve Reenskaug. Working with Objects: The OOram Software Engineering Method. Prentice-Hall, 1995.
  11. ^ Self, the power of simplicity. http://research.sun.com/self/papers/self-power.html
  12. ^ Jakob Jenkov, Dependency injection. http://tutorials.jenkov.com/dependency-injection/index.html