http://www.aspectprogrammer.org/blogs/adrian/2004/05/person_owns_dog.html

实际上,我一直在找寻这样的一个例子来说明类的应该就是一个独立的个体,不应该其他无关系的类有联系,今天看到这篇文章,一句话:正和我意!

Person owns Dog...

May 28, 2004

There's a famous OO problem involving people and dogs that I first learnt about in Meilir Page-Jones' excellent book "Fundamentals of Object-Oriented Design in UML." It involves a class Person, with an attribute numOfDogsOwned and a simple accessor method getNumDogsOwned(). The argument goes that this may not neccessarily be good design, since Person and Dog are distinct concepts, and dog ownership is not an essential property of being a person.

Page-Jones calls this mixed-role cohesion, and he explains the problem better than I can: "What if you wanted to reuse Person in an application that had no dogs? You could do so, but you'd have extra, useless baggage from the class, and you might get some pesky warnings about missing dogs from your compiler (or linker). And where should we stop with this design philosophy? Why not include these attributes in Person: numOfCarsOwned, numOfBoatsOwned, numOfCatsOwned, numOfFrogsOwned,... ?"

It's an interesting problem, because there's no perfect solution in OO - Page-Jones discusses the pros and cons of four possible solutions including just adding the members directly to Person, using a PersonDogOwnership relationship class, using an abstract mix-in class, and using aggregation. Mixed-role cohesion sounds like the kind of problem that inter-type declarations in AspectJ should be able to help us address, so here's the world's first (AFAIK) aspect-oriented solution to the person-owns-dog problem:

/** * not a dog in sight... */ public class Person { private String lastName; private Address address; ... public String getLastName() { return lastName; } ... }

Some of you will have heard me use my adverb/adjective analogy for aspects before, and that's exactly what we've got here. We want to create a dog-owning person, which we could do by creating a DogOwningPerson class (a bit like creating a new noun), but dog-owning isn't limited to people, maybe an Organisation can own dogs too? What we've got is a concept (a compound adjective, dog-owning) that stands in its own right independent of any one class, and that could be applied to many different classes. I'm thinking interface, and I'm thinking aspect...

/** * not a person in sight... */ public aspect DogOwnership { public interface IDogOwner {}; private int IDogOwner.numDogsOwned; public int IDogOwner.getNumDogsOwned() { return numDogsOwned; } }

This aspect represents in a single module the concept of dog ownership. It defines an IDogOwner interface (it doesn't have to be an inner-interface, but making it so helps to keep the whole thing together), and uses inter-type declarations to define a numDogsOwned attribute and a getNumDogsOwned() method on behalf of all dog owners.

We still haven't quite got to person-owns-dog - I wanted to keep the concept of dog ownership independent of any one kind of dog owner. If we have an application where we need person-owns-dog, we can write the following:

public aspect PersonOwnsDog { declare parents : Person implements DogOwnership.IDogOwner; }

I like this solution because Person and DogOwnership are independently reusable, and represent cohesive abstractions in their own right. The PersonOwnsDog aspect that binds the two together is also very clear and simple.

With these aspects in place, you could call the getNumDogsOwned method on a Person as follows:

Person person = new Person(); person.getNumDogsOwned();

 

this will compile happily and execute without any problems. If ever you build the application without the PersonOwnsDog aspect though, you'll get a compile-time error about missing dogs. If you don't want that to happen, you could code the client this way (and I probably would in this particular circumstance):

... if (person instanceof DogOwnership.IDogOwner) { ((DogOwnership.IDogOwner)person).getNumDogsOwned(); } ...

 

but it's just a matter of personal taste, the compiler doesn't require it.

Posted by adrian at May 28, 2004 12:21 PM [permalink]