Behavioral completeness is cool, but...

by Norbert Ehreke

The notion of bahavioral completeness as introduced with the Naked Objects approach comes across as a two-sided coin, at least to me. After taking note of Eitan Suez' JMatter framework, I started cross-reading the book Naked Objects by Richard Pawson and Robert Mathews that can be found online. The core concept is a strict interpretation of object oriented methodology in a way such that every action that can be taken with a certain object must be defined within the boundaries of that object definition. The object must be behaviorally complete. This philosophy is in sync with the idea, that a software should display the internal model as closely to the user in its GUI as possible, which I agree with.

12 Comments

annonymous
2006-09-24 04:33:28
First of all, I don't speak English (sorry for the errors).


For persistent business objects, it is nice to only complete code with schema information/documentation using persistence annotations like EJB3/JPA, instead of writting "save" methods. JPA frameworks can "auto-discover" the target store at runtime (ie: using a persistence unit XML with a a "jndi" datasource alias to database connection pool managed from an application server), instead of hard write the logic in the code.


Other cases of unwanted code completion in the business objects, could be replaced with custom "templates" (in WEB frameworks) or "plugins" (in RCP for desktop applications), to add new button/menu actions that are implemented by other classes (instead of include all the code in the business object).


"Trails" is a good example for a WEB framework of this kind.
But anybody knows a similar framework for desktop applications?


Thanks.

Przemyslaw Pokrywka
2006-09-24 08:00:58
I'm very demanding API user. Given that a library allows me to do everything I need (meets functional req.) with acceptable efficiency (meets non-functional req.), convinience is everything for me.
Therefore I value the behavioral completness very high.
Whenever the library's internal design is more "pure" or less "pure" is the author's problem, not mine. I'm also a library developer and I recognize, that striving for behavioral completness (while preserving modularity) is very challenging in many cases, but I'm convinced, that client is always right and you should strive to make his life easier firstly. This often requires top design skills and language fluency, but is worth to do.


Common superclass and special StringE0 classes are among things, I would strive to avoid in my designs. Fortunately, they can be almost always replaced by clever use of dynamic proxy classes introduced in JDK1.3 (that is, by some hand-made AOP - AspectJ or other robust AOP system may be needed in more advanced cases). Languages with more powerful constructs (like Ruby) can deal with that in more elegant way and requiring less effort from library authors.


Neverthless, making the objects behaviorally complete is worth the effort when the customers use the improved API. In my opinion, aiming at making API "cool" is always a good thing. It makes life easier for customers and makes your skills sharper (when you try to keep the internal design modular at the same time - a thing you certainly should do).

Marcus Breese
2006-09-24 09:36:58
I don't like the idea of an object saving itself. The reason for this is two-fold. First, there is the logistical problem of increasing the size/complexity of what is (usually) a simple object. When you increase the size of an object, you limit what you can ultimately do with it with regards to scale. Moving large objects around isn't as scaleable as moving smaller ones around. Also, if/when you need to change something, it is more difficult to change many model objects than just one context object.


Secondly, there is a philosophical argument that the "Customer" object doesn't need to know anything about "saving" or persisting itself. What does "persist" mean to a "Customer"? A customer may know how to buy something, may know how to send an email, but what does a "Customer" care about saving to a database?

Przemyslaw Pokrywka
2006-09-24 13:52:36
My opinion about raised logistic/scalability issues: JDK1.3 dynamic proxies or more generally AOP techniques are the answer for modularizing concerns such as persistence. Changes in persistence layer affect only one aspect - a mixin-type aspect with save() method.


About philosophical issue: "persist" does not relate to any "Customer" in the real world. Following this path, we use some frameworkish "Persister" object able to "persist" "Customer". What does "persisting a Customer" mean in the real world? We return to beginning. The point is, that objects in an application never live without a context. "Customer" wouldn't be useful without a database, that lets it survive server reboots. True philosophers take it into account.


Saving "Customer" to database can be designed either by "Customer" itself or by some external "Persiter". The first way is however more convinient to client code in most cases. Ruby on Rails developers chose to add save method to every domain object and their framework rapidly gains popularity.

Terry Laurenzo
2006-09-24 17:26:29
Perhaps the terminology that is being forced on your perspective from implementing using a static language is catching us up here.


Just a thought, but I would expect a Customer *object* that *came from* or is *destined to* a datastore via an O/R mapping tool to have a meaningful save() method. Without it, the *object* is incomplete behaviorally. Depending on the implementation language, this is different than saying that a Customer *class* should have a save() method.


Sorry for the emphasis, but it would perhaps be useful to remember that OO techniques did not originate with the static typing system of Java but instead take a meandering path that many would say comes directly from Smalltalk. In Smalltalk for instance, the entire set of methods (or messages) that an object can respond to is not bound by how you coded the class at compile time (especially since it does not strictly have a "compile time").


The dynamic languages have learned this (generally being more directly derived from Smalltalk) and the Java camp has learned it somewhat via AOP. The problem with Java is not how to introduce arbitrary mehtods onto an object so that it is behaviorally complete (although this is harder than in other environments, it can generally be accomplished through runtime bytecode engineering). The problem is how to use these methods from other Java code without having them defined at compile time.


And now I just have one rant: It's 2006. Why are we still puzzled by how to best get an object in and out of a relational database. I've got bigger problems to solve and if my implementation environment forces me to consider this problem until the end of time, then my implementation environment has failed me.

Marc Loy
2006-09-24 19:05:30
I like the idea of behaviour completeness, but I don't actually think that the Customer object should have the 'Save' method behaviour.
How can you find a customer know how to save himself.


Persistent in my view is another resources that need separate object to behave itself.


Thanks.

Tom
2006-09-25 08:56:17
I'm not sure if this would work for you, but GORM handles this nicely without the need for a single base class. (Of couse you need to be using Groovy.) http://grails.codehaus.org/GORM


I think this sort of problem can't be surmounted without using AOP/ multiple inheritance, or a higher level dynamic language that allows for this type of 'behavior injection.'

anjan bacchu
2006-09-25 15:56:38
nakedobjects book : it is not readily apparent how to access the book in the wiki ?


thank you,


BR,
~A

Norbert
2006-09-25 22:45:59
Hmm. That's odd. The book used to be found online at http://www.nakedobjects.org/content.html. It has been a while since I was looking at this. Apparently they changed the online content into the Wiki pages. They also announced Naked Objects 3.0, which should eventually support full POJO support -- which would invalidate this little discussion...


Terry -- Your remarks on the history of type systems and the resulting consequences are enlightening. Especially your last sentence! I certainly agree with you, but at the moment we're stuck with the constraints Java forces upon us. And that means we're back at sqare one when it comes to decide how to design our APIs, right?


Prezemyslaw -- I agree with your demands as an API user and developer.


Marcus -- Yeah, size and complexity are further arguments to consider when designing a system like that. Of course, it's a balancing act, right? Usefulness vs. scalability, no? Plus, in an enterprise context, persistence is quite omnipresent and so it'll be necessary to somehow persist Customer objects.


Tom -- Gotta take a look at GORM. Codehaus usually has pretty cool stuff. I used XStream recently and it worked like a charm.


Thanks all.

Richard Pawson
2006-09-27 06:10:43
(First, in response to Anjan's point, I have added a direct link to the online version of the book from the Wiki's sidebar).


I see no real conflict here. In arguing for 'behavioural completeness' of domain objects, I was primarily concerned with the business (or domain) behaviours - business behaviour relating to a Customer should be implemented on Customer and not in external controllers.


Technical behaviours are in a different category. After all, the whole point of the Naked Objects framework is that the framework provides a huge set of behaviours in relation to presenting those objects to the user, controlling visibility and access rights and so on. In version 2.0 of the framework this required that the domain objects inherit from a common superclass - version 3.0 has eliminated that need.


I absolutely agree that your Customer object should not need to know anything about how the object is saved - since it should be portable across different persistence platforms. We achieve this through the use of the 'Repository' pattern, as articulated by Eric Evans and advocated widely elsewhere. Thus the Customer's Save method will delegate execution to a CustomerRepository (defined by an Interface). We usually prototype using a naive in-memory implementation of this interface, and then deploy with an object-store-specific implementation such as a HibernateCustomerRepository or SQLCustomerRepository. The actual repositories to be used are injected at run-time, based on a configuration file (it may also be done using Spring).


One advantage of initiating this call to the repository from a Save method on customer is that there may well be pure business behaviours associated with Save. In particular the rules concerning when you may or may not save an object may be business issues. These should be kept in the Customer object and not left down in the Repository implementation. On the other hand, any rules that are coming from the database implementation rather than the business requirement, should be implemented in the Repository implementation rather than the Customer.


It seems to me that various of the postings in this discussion are saying essentially the same thing.

chuck simpson
2006-09-28 09:23:20
The Customer object is saved in an instance variable of some other object, for example CustomerManager. Objects are responsible for loading and persisting their internal state so in this case CustomerManager would be responsible for persisting the Customer object. If you make Customer responsible for persisting itself you are tightly coupling the Customer object to its persistence strategy.


I recommend you read the article "The Five Habits of Highly Profitable Software Developers" by Robert J. Miller. You can find it at http://today.java.net/pub/au/400. He has a great description of state and behavior and the differences between them.

Roland Kofler
2007-10-21 10:32:05
In Domain Driven Design by E. Evans you find the notion of a "Repository" wich should look to the domain model as a in memory collection but can encapsulate the logic of persisting Aggregates. Thus when you need a Customer you Query the Repository and when you save it you simply add it to the collection. As previous commentators emphasised the Customer truly has no responsibility derived from the Business Domain that would a justify a save().