Pages

22 June 2006

Responsabilities dilemna

When can you say "too much is too much"?

Let's say you have an object: Customer. Wow, how original! Anyway, please bear with me.

So, your Customer class has some "customer info" responsabilities, but also it may be a "billable customer" or a "prospective customer" (for cross-selling). Hey wait, it can also be a "persistent customer". And so on.

How do you manage all these responsabilities with a classical object-oriented language?

The Horror show

First of all, you could put all the code in the Customer class. Boo-oo! "OO for beginners" tells us its bad, you end up with a 2000 lines class.

or,... you could have only a Customer class containing the customer info and a BillManager that bills a customer, a SellManager that tries to sell him stuff,... No, I'm kidding, let's forget that one too.

or,... you could inherit from a PersistentObject class, derive BillableCustomer from Customer,...

Time to stop the horror show.

Delegation forever

Then you can put everything in the Customer, but it can delegate to other objects. For instance, customer.save() would delegate the real action to a CustomerRepository. This would be the same for other responsabilities.

What about testing? Oh yes, testing,... Ok, you can use your best-of-breed dependency injection framework to inject the proper objects behing finely crafted interfaces.

Right, but what about dependencies anyway !? Your Customer class is still not reusable without a whole bunch of interfaces! What if you don't want your customer to be billable at all?

Ok, you can set the persistence aside by having the Customer clients make calls to an appropriate factory after dealing with the customer. But what can you do with the billing responsability (No, not the BillManager again,...)?

Object inheritance

Another way, suggested by the Streamlined Object Modeling book is to use "Object Inheritance". You use the Actor-Role pattern and define 2 classes:
  • the actor: Customer class
  • the role: Billable customer class
A billable Customer class will have the same interface as the Customer class and will delegate all customer info requests. We could say this is a kind of "external delegation" in contrast to the "internal delegation" mentioned before.

When discussing this pattern with a friend recently, I told him that I felt uneasy with one thing (beside the heavy use of delegation which results in silly code in Java). It was strange for me to have 2 objects representing the same logical entity.

He told me to consider another way to see things: BillableCustomer would be a "decorator" of Customer. If I need a BillableCustomer, I write: new BillableCustomer(customer) and do my job with it. I really like this way of seeing things. There's still one drawback in my mind: if BillableCustomer needs to be persisted, you still have to consider 2 entities for the same logical object in the system.

Dynamic languages to the rescue

In the end, what I would like to do is to mix behaviour into an object. This is exactly what Ruby allow me to do: I have a Customer class and I can extend it Billable responsabilities at runtime:

customer.extend(Billable)

This is the idea of "Duck typing". You cannot rely on the object type alone to know its capacities. If it walks like a duck and quack like a duck, then it is a duck (even if it is really a swan,...)

There is certainly closer to "object-orientation" than any class-interface-inheritance pattern. If we think of it, object are often thought as human when doing object analyzing. And we, as humans, often learn and forget, get abilities and loose abilities. Objects should be able to do the same.

A new persistence challenge

But "when is easy, too easy?".

Yes, when it comes to persistence, how do you do in Ruby with something like that? ActiveRecord was not thought with that kind of use in mind.

;-) I have no doubt that Rails fanatics could come with a fairly elegant solution to that problem.

No comments: