• Aucun résultat trouvé

How Dependency Works

Dans le document The Art and Science of Smalltalk (Page 95-99)

The dependency mechanism is implemented by a set of methods defined in the class Object. This means that every object in the system can take part in dependency relationships. Some methods are re-implemented to do the same thing in different ways lower down in the hierarchy for efficiency or other reasons. Don't let this confuse you if you browse theclasses to see how dependency works (as you should).

The mechanism might work differently internally, but you use it in exactly the same way.

Every object has a collection of other objects which are its dependents. To get hold of this collection just send the message dependents to the object. Sometimes, the dependents are held in an

instance variable inherited from above. In this case you'll be able to see it via an inspector. Other times (actually, whenever the default mechanism inherited from Object is being used), the dependents are held elsewhere (in a class variable in fact), and you won't be able to see them directly in an inspector. However, the message dependents will always give you the dependents, or nil if there are none. You can evaluate self dependents in an inspector to see the object's dependents.

To make an object become dependent on another object use addDependent: . The following expression will make nyObject a dependent ofyourObject:

yourObject addDependent: myObject.

Look very carefully at which way around this expression is. An object holds a collection of dependents. That is, an object holds a list of objects which are dependent upon it. It does not hold a list of objects on which it depends. An object does not know on which other objects it depends. It only knows which objects depend on it. This seems more confusing than it is, but you will find that it's important to remember which way around a dependency relationship is working.

To make an object no longer be a dependent of another object use removeDependenfc :. The following expression will make myObjecfc no longer a dependent ofyourObject:

yourObj eot removeDependent: myObj eot.

You will find the three methods described above in the dependents access protocol of Obj eot, along with some other more complex, but less useful methods. Now that we know how to set up dependencies let's look at how to make them work.

Remember that the whole point of dependency is to let one object (the dependent) know when another object changes. There are two sets of methods which accomplish this feat. There is a set we shall call 'changed' methods, and a set we shall call 'update' methods. The diagram over the page shows the relationship between these methods.

Here is the crux of the dependency mechanism. Whenever an object is sent a 'changed' message, it will automatically send all its dependents an 'update' message. Of course like everything in Smalltalk this automatic behaviour is not hidden. It's there for you to see in the changing protocol of Object .For most practical purposes though, you can consider it as magic.1 When an object receives a 'changed' message all its dependents wilt magically receive an 'update' message.

What is not magic however is the sending of the 'changed*

The Dependency Mechanism

'Changed' messages to one object cause 'update' messages to be sent to its dependents.

message. If you change an object and you want its dependents to know, a 'changed' message must be sent to the changed object. Sometimes this happens in code you inherit, and sometimes you do it yourself.

Either way, if you want to communicate a change, you must make sure a 'changed' message is sent. Likewise, if you want dependent objects to do something when the object on which they are dependent changes, you must implement an 'update' method.

There is a default implementation of the 'update' methods in Object. These methods do essentially nothing and are there to make sure you don't get an error if you send a 'changed' message to an object which has a dependent which doesn't implement an 'update' method. It just inherits the default definition and safely does nothing.

So, to make dependency work, you must send 'changed' messages, and implement 'update' methods to catch the resulting 'update*

messages. Again rather confusing. You send a message of one type to one object, and another object receives a different message. Let's look in more detail at these two types of message.

'Changed' Messages

There are three different 'changed' messages, which take zero, one or two parameters. Remember, these messages are not telling an object to change, they are telling it that it has changed, and informing it that it should tell its dependents. Which one of the three messages you choose depends only on how much information you wish to communicate about the change. For example:

anObj changed: an&Bpect with: aParm. (two parameters) anObj changed: anAspect. (one parameter)

anObj changed. (no parameters)

These messages are implemented in the changing protocol of Object (along with some more complicated ones which we won't consider but which you are free to explore). You never have to reimplement or over-ride them. You just send them and rely on the behaviour already defined for you.

The most powerful of these methods is changed: with: which takes two parameters. The first parameter is conventionally known as the aspect. It allows you to specify which part or aspect ofanObject has changed. Very often (but not always), this will be the name of the instance variable which has changed. The second parameter allows you to communicate how that aspect of the object aspect has changed. Very often (but again, not always), this will be the new value of the instance variable.

The other two methods (changed: and changed) are convenience methods. They are exactly equivalent to sending changed:wifch: using nil for either aParameter, or anAspect and aParameter. If you look at their definitions in Object you will see that this is the case. Using them can just make your code slightly more readable.

'Update' Messages

Just like the 'changed' message, there are also three different 'update' messages, which take one, two, or three parameters. Remember, you will never directly send these messages. However, they will be received by your objects if other objects on which they depend are sent 'changed' messages. Exactly which one is received depends in a sense on which ones you've implemented. This may seem peculiar, but it comes about because the inherited versions of these methods actually subsume each other. This means that unless they're over-ridden, each method will simply call the next simplest. Here are the three update methods:

dependent update: anAspect with: aParm from: anObj.

dependent update: anAspect with: aParameter.

dependent update: anAspect.

The first message is the most powerful. If you've implemented it, it is the message your object will receive whenever another object on which it is dependent receives any of the 'changed' messages. The values of anAspecfc and aParameter will be those used in the 'changed' message, or nil if one of the simpler 'changed' messages was used.

The value ofanObject is the object which was sent the 'changed'

The Dependency Mechanism

message, which is included so that you'll know where the 'update' message came from.

If you don't implement the update; with: from: method, your class will inherit the default implementation from Object. This simply calls update: with: in case you've implemented that in your class.

Here you get access to the same parameters, except that you don't see where the update came from (anObject).

If you haven't implemented an update:with: method, the default implementation (which is inherited from Object) will call update:. Here you only get access to anAspect. If you don't implement an update: method the default implementation simply does nothing but return. Notice that there is no update method. Don't try implementing one in the hope that it will get called with no parameters. It won't!

You can see that update:wifch: and update: are convenience versions of update :with: from:. They may seem rather pointless, but the usual Smalltalk style is to use the version which takes only as many parameters as you actually need. This makes your code slightly more readable.

The diagram on the next page summarises what we have discussed, and shows how simpler 'changed' methods call more complex ones, while more complex 'update' methods call simpler ones.

Ultimately, unless an object understands at least one of these 'update' messages, nothing will happen.

Dans le document The Art and Science of Smalltalk (Page 95-99)