How can two objects cooperate when one wishes to conceal its representation ?
��������� 객����� 그����� ������(Representation)��� ���기기��� ������ ��� ������게 ��� 객��������� ������(Cooperate)��� ��� �������� ?
��������� 객����� 그����� ������(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.