From a high level DCI et al is an effort to "fix" what is broken in the OO model from an OO perspective.
The brokenness manifests itself with standard OO Analysis and Design. Parse the domain into classes, assign responsibilities to said classes ,and execute Use Cases via collaboration of runtime object instances of said classes.
The failure is the inability to pigeon-hole a clear unit of capability/responsibility solely to a single class. It all becomes a forced, shoe horning of capabilities into classes, the opposite, a thin scattering across classes losing all cohesiveness. OO says you _have_ to "put it" somewhere.
And your told if you can't figure out where to put it, you're OO chops are just not up to snuff or your OO model is flawed. Throw it out and start over until you can assign each capability/responsibility to its appropriate OO owner. The fact that this is not possible is ignored by OO.
One camp says the problem is your classes are just too domain anemic, that Order class needs to be beefed up for example. But it becomes a blackhole. More capability, entails and entrains more capability. The Order class soon becomes the Ordering class, a massive God Object that IS the program.
At which point the other camp chimes in no, no, no, "do one thing and one thing only" in your classes. Then all your devouring blackhole God objects go Nova, exploding into a scintilla of ever more anaemic single purpose classes., devolving towards simple state wrappers and manipulators, increasingly orphaning the "do stuff" aspects of your program with nowhere to "put" it.
As all the Noun classes rapidly devolve into simple Data Encapsulators a myriad of -e/or classes explode onto the scene to stitch things together. These are the artificial Class constructs sprung straight from the forehead to act as places to attach all the orphaned "do stuff" code.
The Transact-or, the Book-er, the Price-r, the Submit-or, the ... Do'er. But it's all wrong, wrong, wrong, it ain't OO, and you HAVE to be OO. So you take all those zillions of teeny tiny classes do'er and state wrappers that lack any sort of coherence and start merging and combining things back into Nouns classes. Carefully assigning capabilities strictly and solely to each one. Blackholes God classes re-emerge sucking in responsibilities, demanding attachment, until ...
While BigBang followed by Collapse and re-Bang in an endless cycle may be a fine way to run a universe, it is not necessarily so for running the design of a program.
Fixing OO Can't Be Done Via OO
I think the "ideas" to fix OO are and have been out there for quite awhile. The failure is the repeated attempts to apply the fixes in the context of OO in tortured OO terms.
A entirely new and different vocabulary is necessary. Instead of trying to stretch OO concept semantics to "bend" in new concepts why not just properly label the new concepts and retain OO terms and concepts for OO.
For example, in DCI, objects tend to degenerate into "dumb" simplistic state encapsulators barely smart enough to manipulate their enclosed state. The canonical DCI Account classes encapsulate an amount value and token manipulator methods of this value. Yet is is still discussed as an OO class with OO instances. But it's not. Its no longer a robust domain driven Account stuffed with transactive capability for example. Its just a state wrapper and manipulator. Its closer to a simple structure with associated procedures with an implicit self parameter than full-blown OO object.
The point of all the above is to say that fixing the conceptual flaws of OO-ness by constraining solutions to be OO-centric in nature does not appear to be working very well. The solutions appear well known but fail to translate and for the life of me I don't understand why the translation is necessary! OO must be subsumed by a new solution; it cannot be the solution, only an aspect of it.
To be clear I certainly don't know enough to state the solution. What I am saying is it appears some really smart people appear to know the answers but fail to materialize them in translation. The self imposed OO-only constraints completely obstuficate what were originally rather straight forward solutions.
Attempts to demonstrate new multiparadigm higher level design approaches that sit above OO design become a morass with repeated attempts to reifiy the solutions within OO boundaries.
Watch for example the Account with Funds Transfer problem degenerate into exercises in tortured C++ templates or pushing anaemic, kindergarten Java to do things via ever more clever contortions.
What Must Be Done
Stop trying to shoehorn multiparadigm solutions within a legacy context OO paradigm context. Stop defining solution paradigms a top of ancient languages such as C++, Java or SmallTalk. Total waste of time. They cannot be fixed. Enough with exotic, boilerplate, effusive "Patterns" over these substrates. We need a better substrate.
OO must be subsumed or at minimum become a peer paradigm NOT the central tenant. Why continue to discuss a new paradigm such as DCI in OO terms within an OO constrained language, when a priori your objects are not objects, often just dumb data manipulators, and just-in-time role casting is necessary?
The answer is obviously a multi-paradigm / hybrid one. I'd go as far to claim at a minimum one which combines Functional with OO.
One must start with a multi paradigm / hybrid language to properly demonstrate new design and implementation solutions. Lets use Scala for the rest of the discussion. Why? Its multiparadigm (OO, Functional, Actor, Pattern, Algebraic) in design AND clean enough that there is a good chance missing paradigms could be added cleanly as smooth extensions of the language or properly as libraries.
For the hell of, imagine if the current semantics of Scala was _not_ modified one iota, however the following term syntax aliasing was allowed.
To get a gist of the impact of these simple term substitutions let's examine a couple of them.
Module over object encourages people to "think" conceptually different. Its a gathering spot for those procedural, algorithmic things. It is a place where "functional" things can be defined cleanly. These is no dissonance in concieving the below in a "module". But it's a mental hurdle to view it both as a singleton "object", and as the natural container for such constructs.
type T = ...
val c = ...
val f[A,B,C]: (g: (A, B) => C) (x: A, y: B) => g (y, x) val h (x: Int, ..) => ...
def k (..) = ...
It is the proper place in Scala land of course, but it seems forced. Of course the Scala folks know this. So why Object and not Module. Because we keep trying to defined new concepts as tortured OO concepts otherwise the Java folks won't grok it. People don't know what a Module is anymore therefore the most intuitive way to add modules conceptually is as singleton object.
Aside: Imagine, giving a talk to COBOL, VB, PL1, RPG, PLSQL and Java developers, its the folks with only the "modern OO" language experience you have to slow down for to explain what modules, procedures and functions are. Ironic.
Object implies Class instance and hence a sort of domain mapping (for me) and the reification of some domain "thing". But it's simply a cohesive grouping of procedural code, functions and algorithms that "do stuff". Where is the "object" I ask you? Sure one can hand wave and claim scala Object is isomorphic to a Module, but damn it it's a module first and a clever means singleton object construction second.
Class -> Struct. (SML structure, not C) Again the idea here is to move away from the OO relentless domain mapping of objects and things and allow one to think in a guilt free manner of constructions whose primary purpose is to encapsulate and manipulate state without OO burden.
I want to be guilt free to think of these things as simply encapsulated data, with associated functions with an implicit self parameter without a receiving a citation from the OO police for an Anaemic Object violation.
By simply aliasing 5 or 6 key terms in the Scala language, without changing any of the semantics of the language, one gets a "whole" new language in the sense that the "way" a single individual would write a program to solve a problem would radically shift.
Same problem, same programmer, same language. Alias five terms and one would gets a more functional code base with appropriate and prevalent state encapsulation. Same semantics just different labelling, say Structures/Instances instead of Classes/Objects.
Leave the current syntax labelling in place one would get a OO centric code base with modest migration of procedure code and algo out of Classes. It all depends on how much the developer can soar above the OOness of the Scala's bias to use non-OO paradigm constructions with peer OO constructions to solve the problem.
One variation of Scala supports DCI and other solutions with less dissonance, the other implies a straining to achieve DCI, a continued uphill struggle to break free beyond just OO in the solution space. In less capable languages such as C++ or Java the strain becomes an all out war against the language limitations itself.
I think you are confusing Object Oriented Analysis, Design and Modelling with Object Oriented Programming. The term "Object Oriented" means something completely different in OOA/OOD/OOM than in OOP. In OOP, objects encapsulate behaviour, in OOA/OOD/OOM objects encapsulate attributes and relations.
I think that the attempt to oppose OOA/OOD/OOM to OOP is the bad idea in the sense that the final result of all this exercise is the implementation (that is, the result of OOP). If the design is bad, there won't be any good code from it. I heard many times from people who claim to do the analysis and high level design that they do not care about details. But details pop up when you implement some design in code. The less details are not spoken out at the design phase, the better implementation will be. Besides, there is no need to oppose Scala or functional programming to Java, C#, etc. Each language has its own issues and limitations due to the limited set of keywords and the semantics behind that syntax. One language does some things better than another. But these better designed features come because of the analysis of predecessors. It does not seem the case with DCI. It is an attempt to formalize the process and the implementation at the same time and this never worked together. By the way, even in Java you can separate the state and actions or communications without any problems. Simply sometimes (or, maybe, quite often) there is not enough time to do that consistently. Another example for what DCI missed, but this one is from the non-OO world, is the well-known relational databases. Those exist for over two decades and seem to continue to exist without any problem by better implementing the old MVC, not DCI.
Additionally to the previous comment. The example with bank account in the original article is not the right one to illustrate the problem. That should be well known to the authors. The problem with the example is that any banking is backed by the database, nowadays with a relational database which is used to store any relevant data. It is the well known fact that relational databases do not fit into the OO paradigm and as such are under the scrutinity for the last two decades - that is, from the moment they were invented and implemented. So, the database design actually dictates the way how software is decomposed into specific classes, not the imaginery description of how we think we perform transactions on the account. In relational databases they have currently four normal forms, but I doubt you find any database in the production cycle that is normalized further than the second normal form. This is due to the requirements to make transactions in 'real time'. With further database normalization you never get to the point of extracting any data in 'real time'. From my personal experience, once we did the acceptance testing and compared the existing database and the new (normalized without any consideration to the everyday tasks) one. The existing database provided extracts of data efficiently while the new one could not get any data on a relatively small number of rows - multiple joins killed the possibility to get any data in reasonable time. The point I wanted to make here is that the compain and the example should reflect the actual OOD/OOP. The one that is used by the authors is irrelevant to the topic.
DCI's "how to implement" is too complicated , traits is not new thing, just like AOP's static weaving.
Domain Events is another way:
Account owner send event to his account, and the source account do his balance, after that it will send domain events to target account, let target account update its balance.
> > No, no, no. Just no. Let me play with the code.
I think John Zabroski missed the title of the article which states "The DCI Architecture: A New Vision of Object-Oriented Programming".
1) Its an architecture - hence nothing that you can play with only reason about so play with different implementations of where each implementation is different.
I will use word entity for a object which is a domain noun and is mapped to db. Entity class is a class of this object.
I've worked 2 years part time (40-80 hours a month) and less than 1 year fulltime (I finished my school a year ago) on a java project which is now 7 years old and was programmed by the team of about 15 developers. We have 376 entity classes and some of them have thousands of lines. We even made a utility classes to put logic inside but I know this is not the final solution:
BankAccount{
publicvoid transferTo(BankAccount destination){
new BankTransfer().transferTo(destination);
}
}
E.q. BankTransfer is utility class with methods for source and destination but these methods are still in every BankAccount object, even if we are not doing any transaction at a moment.. After a half year I realized that this is not a good way and we should do something like
new BankTransfer(BankAccount source, BankAccount destination).transfer();
but no one is listening to me.
Remarks 1. I totally agree with the motivation behind DCI. Classes tend to accumulate code describing interactions between objects from use-cases. A single object can play different roles in different use cases so it is really a mess.
2. I have seen talk by Jim Coplien: Why DCI is the Right Architecture for Right Now on infoq.com and I agree also with that the patterns are overrated. More important is motivation and forces behind them. But I think is important to know them, to know th emotivation behind and because, you can modify them and it is good way to communicate. E.g. "We use visitor pattern." Instead of "We send an object with behavior.. which does.." In Scala or Haskell (my favorite) one is using Strategy pattern all the time but it has no specific name..
Questions 1. When working in java isn't it easier to create single use case classes with the roles as attributes and the methods needed instead of trying to inject some methods to objects? Can that be a valid implementation of DCI?
2. What about processes? How do they relate to DCI?
"Business processes don't represent things the business object is doing, rather things being done to the business object.”
IMO process is a long running interaction between objects and/or human actors. So entities have process agnostic methods, processes have process specific actions. Process is like a class and process instance is a instance of a process. One explicitelly creates process instances and set objects to roles they play. There are processes which every object has to take.
E.g. Every human beeing is created in a process instance called Birth.
Not every human has to take a process Marriage, but there can be only one instance of this process in a meantime where a man or a woman is in a role fiancee. One can took this role in more marriages but one after another. But one be a wittness in another marriage instance in the same time.
Law suit process is different. One can take the same role in many process instances concurently.
The problem process instances solve is not only the problem of use case specific methods in entity classes but also the problem of tracking long running process instances. One can not see flow in a code if it is interrupted by human interactions (spawns multiple transactions). In a process diagram there are actions performed by humans as well as action executed by a system. (The same problem arrises when there are several systems involved or something needs to be doen asynchonously in a single system.)
Example: User clicks on a button which updates the entity. It is a single transaction so one can see what is done in the code. After that several buttons can be disabled/enabled by the new state of the entity. This logic is usually scattered in the client components (buttons, ..) by overriding isEnabled()/isVisible() methods or setting specific visibility behavior. How I know what can user do as next? And which user can do that?
This problem is solved by the explicitely creating processes instances and not handling them as something what emerges from ojects interactions. How it is solved in DCI? Or does DCI try to solve this as well?
3. Aren't you looking for a new developer?
Thanks! And I'm waiting for a reply by some of the gurus, Trygve, Jim, Bill! But everyone can answer me as well:-)
In the "Other bits" section there is statement about business rules and how DCI does not provide a convenient way to capture them. You can look at the book "Streamlined Object Modeling" by Nicola, et al and see if that might work. Since the patterns are based on the relationships between objects, and not the objects themselves, mixing in the required behavior might be straightforward.
I read the article with great interest and I salute the authors for taking the time to write it. However, from the article:
"Object-oriented programming languages traditionally afford no way to capture collaborations between objects"
to which I respond that a bad workman blames his tools!
It is your choice to create and use objects for whatever you want to, including capturing the collaborations between objects. In fact, this is the heart of OO. When you model an object and have it interact with another you can choose to model that interaction as another object with a simply method invocation to carry out the interaction, or you can create no object and call a series of methods. A lot of this is captured in the OO principle Tell Don't Ask but most people just Ask, ignoring the benefits of Tell all together.
For example:
address.to_string() <- simplistic - why do you need a string? address.printOn(medium) <- OO, capturing the interaction. You can also take this further with an object that represents the process of taking an address and putting it onto medium.
I wrote a lot about this in several posts on my blog about a technique I call East Oriented - http://jamesladdcode.com/?p=12
> I read the article with great interest and I salute the > authors for taking the time to write it. However, from the > article: > > "Object-oriented programming languages traditionally > afford no way to capture collaborations between objects" > > to which I respond that a bad workman blames his tools! > > It is your choice to create and use objects for whatever > you want to, including capturing the collaborations > between objects. In fact, this is the heart of OO. When > you model an object and have it interact with another you > can choose to model that interaction as another object > with a simply method invocation to carry out the > interaction, or you can create no object and call a series > of methods. A lot of this is captured in the OO principle > Tell Don't Ask but most people just Ask, ignoring the > benefits of Tell all together. > > For example: > > address.to_string() <- simplistic - why do you need a > string? > address.printOn(medium) <- OO, capturing the interaction. > You can also take this further with an object that > represents the process of taking an address and putting it > onto medium. > > I wrote a lot about this in several posts on my blog about > a technique I call East Oriented - > http://jamesladdcode.com/?p=12 > > We have failed OO, it has not failed us. > > - James.
Totally agree with this post.
The assumption in the article seems to be that an 'object' has to represent something that can be stored in a database (> object boundaries already mean something else: they are loci of encapsulated domain knowledge, of the data <), which is a too narrow view of what objects are.
If you have a high level action you want the application to perform, why not just use the Command Pattern, which encapsulates an action as an object? To prevent strong coupling between this Command object and the objects on which it operates, you can implement the Commmand object as a Composite Pattern, where a high level Command consists of series of lower level Commands.
A high level Command could represent the transferral of money from one bank account to another. At a lower level, the Command object representing the actual money transfer will tell one Account object to lower its balance, and the other Account object to increase its balance.
If DCI tries to solve the problem of not being able to capture interactions between 'objects', I think this problem has already been solved by the Command Pattern, using OO principles.
Flat View: This topic has 119 replies
on 8 pages
[
«
|
5678
]