U E D R , A S I H C RSS

Smalltalk Best Practice Patterns/Dispatched Interpretation

How can two objects cooperate when one wishes to conceal its representation ?
하나의 객체가 그것의 표현(Representation)을 숨기기를 바랄 때 어떻게 두 객체들은 협력(Cooperate)할 수 있는가 ?

Encoding is inevitable in programming. At some point you say, "Here is some information. How am I going to represent it?" This decision to encode information happens a hundred times a day.

인코딩은 프로그래밍에 있어서 필수적이다. 몇몇 관점에서 당신은 말한다, "약간의 정보가 있다. 어떻게 그것을 표현할 수 있는가?" 정보를 인코딩하는 결정은 하루에도 아주 많이(a hundred times a day) 발생한다.

Back in the days when data was separated from computation, and seldom the twain should meet, encoding decisions were critical. Any encoding decision you made was propagated to many different parts of the computation. If you got the encoding wrong, the cost of change was enormous. The longer it took to find the mistake, the more ridiculous the bill.

과거로 돌아가서 데이타가 연산으로부터 불리되었을 때, 그리고 종종 그 둘이 만나야 했을 때, 인코딩 결정은 중대한 것이었다. 너의 어떠한 인코딩 결정은 연산의 많은 다른 부분들을 점차적으로 증가시켜나아갔다. 만약 잘못된 인코딩을 한다면, 변화의 비용은 막대하다. The longer it took to find the mistake, the more ridiculous the bill.

Objects change all this. How you distribute responsibility among objects is the critical decision, encoding is a distant second. For the most part, in well factored programs, only a single object is interested in a piece of information. That object directly references the information and privately performs all the needed encoding and decoding.

객체들 사이에서 어떻게 책임을 분배하는가는 중요한 결정이다, 인코딩은 그 다음이다. 잘 요소화된 프로그램들의 대부분에서, 오직 하나의 객체만이 한 조각의 정보에 관심을 가진다. 객체는 정보를 직접적으로 참조하고 사적으로 필요한 모든 인코딩과 디코딩을 수행한다.

Sometimes, however, information in one object must influence the behavior of another. When the uses of the information are simple, or the possible choices based on the information limited, it is sufficient to send a message to the encoded object. Thus, the fact that boolean values are represented as instances of one of two classes, True and False, is hidden behind the message #ifTrue:ifFalse:.

때때로, 그러나, 한 객체의 정보는 다른 객체의 행위에 영향을 미칠 것이다. 정보의 사용이 단순할 경우, 또는 제한된 정보에 기반해서 선택이 가능할 경우에는 인코딩된 객체에게 메시지를 보내는 것이 충분히 가능하다. 이와 같이, 부울린 값들은 두 클래스의 하나의 인스턴스로 표현되어진다는 사실은, True 그리고 False, 메시지 #ifTrue: ifFalse:.뒤로 숨겨진다

~cpp 
True>>ifTrue:trueBlock ifFalse:falseBlock
     ^trueBlock value
False>>ifTrue:trueBlock ifFalse:falseBlock
      ^falseBlock value

We could encode boolean values some other way, and as long as we provided the same protocol, no client would be the wiser.

Sets interact with their elements like this. Regardless of how an object is represented, as long it can respond to #=and #hash, it can be put in a Set.

Sometimes, encoding decisions can be hidden behind intermediate objects. And ASCII String encoded as eight-bit bytes hides that fact by conversing with the outside world in terms of Characters:

~cpp 
String>>at: anInteger
      ^Character asciiValue: (self basicAt: anInteger)


When there are many different types of information to be encoded, and the behavior of clients changes based on the information, these simple strategies won't work. The problem is that you don't want each of a hundred clients to explicitly record in a case statement what all the types of information are.

For example, consider a graphical Shape represented by a sequence of line, curve, stroke, and fill commands. Regardless of how the Shape is represented internally, it can provide a message #commandAt: anInteger that returns a Symbol representing the command and #argumentsAt: anInteger that returns an array of arguments. We could use these messages to write a PostScriptShapePrinter that would convert a Shape to PostScript:

~cpp 
PostScriptShapePrinter>>display: aShape
    1 to: aShape size do:
           [:each || command arguments |
           command := aShape commantAt: each.
           arguments := aShape argumentsAt: each.
           command = #line if True:
               [self
                   printPoint: (arguments at: 1);
                   space;
                   printPoint: (arguments at: 2);
                   space;
                   nextPutAll:'line'].
                command = #curve...
                ...]

Every client that wanted to make decisions based on what commands where in a Shape would have to have the same case statement, violating the "once and only once" rule. We need a solution where the case statement is hidden inside of the encoded objects.

* Have the client send a message to the encoded object. PAss a parameter to which the encoded object will send decoded messages.

The simplest example of this is Collection>>do:. By passing a one argument Block(or any other object that responds to #value:), you are assured that the code will work, no matter whether the Collection is encoded as a linear list, an array, a hash table, or a balanced tree.

This is a simplified case of Dispatched Interpretation because there is only a single message coming back. For the most part, there will be several messages. For example, we can use this pattern with the Shape example. Rather than have a case statement for every command, we have a method in PostScriptShapePrinter for every command, For example:

~cpp 
PostScriptShapePrinter>>lineFrom: fromPoint to: toPoint 
     self
           printPoint: fromPoint;
           space;
           printPoint: toPoint;
           space;
           nextPutAll: 'line'

Rather than Shapes providing #commandAt: and #argumentsAt:, they provide #sendCommantAt: anInteger to: anObject, where #lineFrom:to: is one of the messages that could be sent back. Then the original display code could read:

~cpp 
PostScriptShapePrinter>>display:aShape
     1 to: aShape size do:
            [:each |
            aShape
                sendCommandAt: each
                to: self]

This could be further simplified by giving Shapes the responsibility to iterate over themselves:

~cpp 
Shape>>sendCommandsTo: anObject
     1 to: self size do:
           [:each |
           self
                 sendCommandAt: each
                 to: anObject]

PostScriptShapePrinter>>display: aShape
     aShape sendCommandsTo: self

The name "dispatched interpretation" comes from the distribution of responsibility. The encoded object "dispatches" a message to the client. The client "interprets" the message. Thus, the Shape dispatches message like #lineFrom:to: and #curveFrom:mid:to:. It's up to the clients to interpret the messages, with the PostScriptShapePrinter creating PostScript and the ShapeDisplayer displaying on the screen.

You will have to design a Mediating Protocol of messgaes to be sent back. Computations where both objects have decoding to do need Double Dispatch.
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2009-05-27 07:09:19
Processing time 0.0867 sec