• Aucun résultat trouvé

The AspectAdaptor Class

Dans le document The Art and Science of Smalltalk (Page 121-125)

AspectAdaptor is similar to ValueHolder, but provides an interface to more complex models. For example, you might have created a class with several instance variables holding strings, numbers and so on. You may also have created 'get' and 'set' methods for those instance variables. The names of these methods should be the same as the instance variable names, so you might have methods called insideLeg and insideLeg: to get and set the value of the insideLeg variable (as in the diagram below). Now you want to display and edit the value of this variable in a TextEditorView.

However, the view sends the messages value and value: when it wants to get and set the value of the object it is displaying. So, you need a way of converting these messages into the ones your class understands. This is what AspectAdaptor does.

In this context, the insideLeg variable is referred to as an 'aspect* of your model. The task of AspectAdaptor is to interface the general-purpose view object to just one aspect of the model. To do this, it needs to know which messages to send. The As pecfcAdaptor refers to these messages as the getSelector and the putSelector (selector is a term frequently used to refer to a message name). When you make an AspectAdaptor you can either set these two messages at the same time using the forAspect: message (in which case the getSelector will be set to the symbol you give, and the putSelector to the same symbol with a colon (:) appended), or (more unusually) you can set them separately using

Chapter 10

accessWith:assignWith:. You must also tell theAspectAdapfcor which object it is adapting using the subj ect: message.

The AspectAdaptor will also propagate 'update' messages. To make this happen, make sure you have sent the message subjectSendsUpdafces : true to the adaptor. Then, if the domain model changes the value of insideLeg, and provided it sends itself a changed: # insideLeg message, the AspectAdaptor will forward the resulting update: message to the view.

The PluggableAdaptor Class

Instances of PluggableAdaptor take the whole concept of pluggability a stage further than AspectAdaptor. Instead of merely being able to provide selectors to be used to adapt a model to a view, you get to provide entire blocks of Smalltalk code. The blocks are called the getBlock, the putBlock and the updateBlock. They are executed when the PluggableAdaptor receives the messages value, value: andupdate:with:£rom: respectively. This gives a great deal of flexibility in adapting a model to a view. Here is a simple example in which we wish to adapt a view to a model which holds a value in the variable insideLeg in inches, but we wish to display and edit it in centimetres.

MyPA := PluggableAdaptor on: MyModel.

MyPA getBlock: [nn | m insideLeg * 2.5]

putBlock: [:m :v | m insideLeg: (value / 2.5)1 updateBlock: [:m :a :p | (a = ttinsideLeg) & (p > 34)].

First, we make an instance of PluggableAdaptor and connect it to the model (MyModel). This tells the PluggableAdaptor what object to send as the :m parameter in the above blocks, and instructs it to become a dependent of that object. Then we set the values of the three blocks.

The getBlock takes just a single parameter, :m — the model (which will be MyModel). When executed it sends the message insideLeg to the model, multiplies the result by 2.5 and returns it.

This has the effect of adapting the value message sent by the view to the PluggableAdaptor to a more complex operation performed on the model.

The putBlock takes two parameters, :ni and :v — the value (the object sent as the parameter to the value: message). In this case, the value is divided by 2.5 before being sent to the model using the

Pluggability and Adaptors

Several P luggableAdaptor

objects being used to adapt different aspects of a single model to individual views.

insideLeg: message. This has the effect of adapting the value:

message sent to the P luggableAdaptor to another complex operation performed on the model.

Finally, the u p d a t e B l o c k is executed by the PluggableAdapfcor whenever it receives an update message from the model. This might happen if MyModel has changed the value of its insideLeg variable and sent itself the message changed:

#insideLeg with: insideLeg. This informs all its dependents that insideLeg has changed its value, and sends them the new value.

The P luggableAdaptor must decide whether to forward this update on to its own dependents (typically a view). To make this decision, it runs the updateBlock. If it evaluates to true it forwards the update, if it evaluates to false it does not. If the updateBlock evaluates to anything else an error is generated, so be careful!

This process allows PluggableAdaptors to filter the many updates they might receive from their model, and forward only those that the view is interested in. This is essential because, as the diagram above shows, a given model may have many instances of P luggableAdaptor connected it. The model may generate an update whenever any of its variables changes, and all of the adaptors will receive these messages. If the update messages weren't filtered, all the views would refresh themselves when only one needed to. This can cause unpleasant flickering on the screen.

Instances of P luggableAdaptor really do lead a double life.

They connect a model to a view, appearing to be like a model to the view, and like a view to the model. The code that can be placed in their blocks is essentially unlimited, and this can make them very powerful indeed. Be careful though, because code placed in blocks, and hence

applying to only one instance, is invariably more difficult to debug than code in the model object (see Chapter 15—Debugging Smalltalk Code).

Reuse is one of the key benefits of OOP, and in Smalltalk pluggability is one of the ways in which reuse is achieved. This chapter has concentrated on just three of the classes in the class library which are pluggable. Each of these classes adapts the value, value: , and 'update' messages to something more appropriate. This means that view-like objects (buttons, text-editors, etc.) which typically send value and value: can be connected to model objects which don't directly understand these messages.

Don't worry if you haven't understood the details of what these adaptor classes do. In many cases their functionality is hidden and you needn't worry about it. As long as you have understood the general principles, you will be in a good position to work out exactly what is happening as and when you need to do so.

Many other classes in the class library are also pluggable, and you should feel free to browse them to discover how they work. Be aware though, that pluggability is one of the features that has been around in the class library for a little while, which is why there is some inconsistency in the way it is implemented, and in the way the classes are named.

Finally, if you have really understood the concepts presented in this chapter, you should find that if you need to build classes which are reused in general ways by other programmers, pluggability is one of the ways you'll consider.

Dans le document The Art and Science of Smalltalk (Page 121-125)