home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #2 / Amiga Plus CD - 2004 - No. 02.iso / AmigaPlus / Tools / Development / AmigaTalk / general / ValueModel.st < prev   
Encoding:
Text File  |  2004-01-31  |  8.3 KB  |  200 lines

  1. " ---------------------------------------------------------------------
  2.  I am a simple Model that provides direct access to some kind of value.  
  3.  I notify dependents when the value changes.
  4.  The collection accessing protocol is here as a convenience, to 
  5.  avoid some of the need for special collection models.
  6.  
  7.  Subclasses must implement the following messages:
  8.     accessing
  9.         value
  10.         setValue:
  11.  
  12.  
  13.  Object Reference:
  14.  
  15.  ValueModel is an abstract class that provides model-like abilities for 
  16.  an enfolded object -- that is, it notifies dependent objects whenever 
  17.  its held object is changed. 
  18.  Value models are most commonly used to hold data models on which widgets 
  19.  such as input fields depend.  To understand why a value model is needed, 
  20.  take the case of an input field that displays a number.  The input 
  21.  field needs to be notified whenever the application changes the number, 
  22.  so it registers itself as a dependent of the number.  If that dependency 
  23.  were established on the raw number, however, the dependency would be 
  24.  obsolete as soon as the application substituted a different number, 
  25.  defeating its purpose.  Instead, a value model is used to hold the 
  26.  number, and the input field registers itself as a dependent of the 
  27.  value model.  The application can insert a new number and the value 
  28.  model then notifies the input field of the change so the field can 
  29.  get the new number and display it.  Since the value model remains in 
  30.  place while its value is changed, the dependency that was established 
  31.  by the input field remains alive as long as the application is running. 
  32.  The most commonly used subclass of ValueModel is ValueHolder, which 
  33.  would be used in the simple case described above.  Because value 
  34.  holders are widely used, every object inherits from the Object class 
  35.  the ability to enfold itself in a value holder -- sending #asValue to 
  36.  any object returns a ValueHolder on the object.  A consequence of 
  37.  inserting a value holder as an adaptor or mediator between a data object 
  38.  and its dependents is that you must send #value to the value model, 
  39.  thus extracting the enfolded object, before you can send a message 
  40.  to that object.  For example, suppose the numeric input field mentioned 
  41.  above uses an instance variable named salesCommission to hold its 
  42.  ValueHolder.  If the application wanted to retrieve the actual number 
  43.  for use in a computation, it would use the expression 'self 
  44.  salesCommission value' instead of simply 'self salesCommission.'  The 
  45.  fact that a value model always gets its enfolded object in response to 
  46.  #value, and sets that value in response to #value:, simplifies 
  47.  communications for widgets.  Another commonly used subclass of ValueModel 
  48.  is AspectAdaptor, which is used to enfold an embedded value.  For 
  49.  example, suppose we have an AccountNumber class that has a string part 
  50.  and a number part, for a composite account number such as 'TEL-4792'. 
  51.  We might want to use a separate input field for each part of this 
  52.  account number.  One AspectAdaptor could be used to enfold the string part, 
  53.  and another the numeric part.  The most flexible subclass of ValueModel 
  54.  is PluggableAdaptor, because it can be configured to transform the value 
  55.  on its way to and from the dependent.  While the TypeConverter subclass 
  56.  provides commonly used transformations, such as number-to-string, 
  57.  PluggableAdaptor can be configured with blocks to perform highly 
  58.  specialized transformations.  Other subclasses of ValueModel are more 
  59.  specialized.  A BufferedValueHolder is used to hold a working copy of 
  60.  the value as well as a confirmed copy.  A BlockValue enables a computation 
  61.  inside a block to have dependents.  An IndexedAdaptor enfolds an element 
  62.  in a collection.  A PrintConverter transforms its enfolded value to and 
  63.  from its printed form.  A RangeAdaptor enfolds an interval of numbers, 
  64.  and is chiefly used by slider widgets.  SlotAdaptor and 
  65.  DependencyTransformer are mainly used by system machinery.  A ValueModel 
  66.  provides a convenient way for an application to arrange to receive a 
  67.  particular message whenever the value is changed.  Making such an 
  68.  arrangement is known as expressing an interest in the value, and 
  69.  involves sending #onChangeSend:to:.  Retracting an interest is
  70.  achieved via #retractInterestsFor:.  When creating a subclass, equip 
  71.  it with the following methods: 
  72.     Instance protocol 
  73.         value 
  74.         setValue: 
  75. ---------------------------------------------------------------------------
  76. "
  77.  
  78. Class ValueModel :Model
  79. [
  80.    new
  81.      ^ super new initialize
  82. |
  83.    initialize
  84.      "Initialize the instance.  Subclasses may extend this."
  85.  
  86.      ^ self
  87. |
  88.    dependents
  89.      ^ super dependents
  90. |
  91.    release
  92.      self releaseParts.
  93.  
  94.      super release
  95. |
  96.    releaseParts
  97.      " Break the dependency links from any parts of myself to myself.
  98.      * Subclasses holding composite values will implement this in 
  99.      * a non-trivial way. 
  100.      "
  101.      ^ nil
  102. |
  103.    setValue: newValue
  104.      " Set the currently stored value, without notifying dependents. "
  105.  
  106.      self subclassResponsibility: 'setValue:'
  107. |
  108.    value
  109.      " Answer the currently stored value. "
  110.  
  111.      ^ self subclassResponsibility: 'value'
  112. |
  113.    value: newValue
  114.      " Set the currently stored value, and notify dependents."
  115.  
  116.      self setValue: newValue.
  117.      self changed: #value
  118. |
  119.    valueUsingSubject: aSubject
  120.  
  121.      ^ aSubject value
  122. |
  123.    asValue
  124.      " Since the receiver is already a ValueModel, merely return self."
  125.  
  126.      ^ self
  127. |
  128.    onChangeSend: aSymbol to: anObject 
  129.      " Arrange for anObject to receive a message named aSymbol when 
  130.      * I signal that my attribute #value has changed.
  131.      "
  132.  
  133.      self expressInterestIn: #value for: anObject sendBack: aSymbol
  134.  
  135. |
  136.    retractInterestsFor: anObject 
  137.      " Undo a send of onChangeSend:to:."
  138.  
  139.      self retractInterestIn: #value for: anObject
  140. |
  141.    compute: aBlock 
  142.      " Answer a BlockValue that computes aBlock with the receiver's value 
  143.      * as the argument. aBlock will become a dependent of the receiver, 
  144.      * and will be sent the message value: when the receiver is sent the 
  145.      * message value:.
  146.      "
  147.  
  148.      ^ BlockValue block: aBlock arguments: (Array with: self)
  149. |
  150.    receive: aSelector 
  151.      " Answer a BlockValue that responds to the message value by sending 
  152.      * aSelector as a message to the receiver. This BlockValue will become a 
  153.      * dependent of the receiver, and will be sent the message value: when 
  154.      * the receiver is sent the message value:.
  155.      "
  156.  
  157.      ^ BlockValue block: [:rcvr | rcvr perform: aSelector] arguments: (Array with: self)
  158.  
  159. |
  160.    receive: aSelector with: value1 
  161.      " Answer a BlockValue that responds to the message value by sending 
  162.      * aSelector as a message to the receiver. This BlockValue will become a 
  163.      * dependent of the receiver, and will be sent the message value: when 
  164.      * the receiver is sent the message value:. The message aSelector has 
  165.      * one argument, value1. It is assumed that value1 itself responds to 
  166.      * the message value (i.e., may be a kind of ValueModel)."
  167.  
  168.      ^ BlockValue block: [:rcvr :arg1 | rcvr perform: aSelector with: arg1]
  169.               arguments: (Array with: self with: value1)
  170.  
  171. |
  172.    with: value2 compute: aBlock 
  173.      " Answer a BlockValue that computes aBlock with the receiver and 
  174.      * value2 as the first and second arguments, respectively. This 
  175.      * BlockValue will become a dependent of the receiver, and will be sent 
  176.      * the message value: when the receiver is sent the message value:. It 
  177.      * is assumed that value2 itself responds to the message value (i.e., 
  178.      * may be a kind of ValueModel).
  179.      "
  180.      ^ BlockValue block: aBlock arguments: (Array with: self with: value2)
  181.  
  182. |
  183.    with: value2 with: value3 compute: aBlock 
  184.      " Answer a BlockValue that computes aBlock with the receiver, value2, 
  185.      * and value3 as the first, second and third arguments, respectively. 
  186.      * This BlockValue will become a dependent of the receiver, and will be 
  187.      * sent the message value: when the receiver is sent the message 
  188.      * value:. It is assumed that the objects value2 and value3 respond to 
  189.      * the message value (i.e., may be a kind of ValueModel).
  190.      "
  191.  
  192.      ^ BlockValue block: aBlock arguments: (Array with: self with: value2 with: value3)
  193. |
  194.    isBuffering
  195.      " ValueModels by default do not buffer values, only special
  196.      * subclasses who should reimplement this message for themselves.
  197.      "
  198.      ^ false
  199. ]
  200.