Advertisement
Guest User

Untitled

a guest
Apr 27th, 2017
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.86 KB | None | 0 0
  1. >Object-Oriented Design is about the messages that get sent between objects and not the objects themselves.
  2.  
  3. Message passing is usually referred to the form of communication between parallel processes, but in a broader sense, it is how object interfaces communicate in dynamically typed languages. As in Ruby, thinking of 'method call' in terms of 'message passing' adds perspective to how you can design delegations.
  4.  
  5. ---
  6.  
  7. Russ Olsen, in Design Patterns in Ruby reiterates thinking about object interfaces and the combination of composition and delegation as a powerful technique to design flexible applications. He gives us a perspective that method invocation between object interfaces can be thought as 'message passing'.
  8.  
  9. For example, take a look at the code below.
  10.  
  11. `object.do_something(arg)`
  12.  
  13. - In statically typed languages, methods are generally considered to be more 'baked' into the objects, in the sense that running the code above would translate to the object calling or invoking the #do_something method found within itself.
  14.  
  15. - In dynamically typed languages, it is possible for the object to not contain a method that was invoked (statically typed languages are compiled). We can translate the code above to 'sending or passing the #do_something message to the object'.
  16.  
  17. Ruby will initially look for the method first in the class, then in its superclass, and so on, until it either finds the method or runs out of superclass (if it doesn't find one, a fallback method is called: method_missing). The idea of 'message passing' is thoroughly integrated in the language. Thus you can not only catch unexpected messages, but also send messages (delegate) across objects with `object.send()`.
  18.  
  19. For example:
  20.  
  21. `:message [payload1, ... payloadN]`
  22.  
  23. is a Ruby message consists of a name, usually represented by a symbol such as :message, and an optional payload. The payload is often called the list of arguments or parameters.
  24.  
  25. ```
  26. 5.send :+, 2
  27. 5.+ 2 # same thing
  28. 5 + 2 # same thing
  29. ```
  30.  
  31. You can use the send method on any object to invoke a message handler. These 'send' methods are definitely easier to understand in the context of 'message passing'.
  32.  
  33. ### Implementations
  34. There are a few implementations.
  35.  
  36. When building applications thinking about object interfaces, you end up with a lot of boilerplate code, only of dumb methods that only exist for delegation. These can become a little more elegant with the method_missing fallback mentioned above.
  37.  
  38. Example use of method_missing:
  39.  
  40. ```test.rb
  41. class TestMethodMissing
  42. def hello
  43. puts 'hello'
  44. end
  45.  
  46. def method_missing(name, *args)
  47. puts "unknown method called: #{name}"
  48. puts "arguments: #{args.join(' ')}"
  49. end
  50. end
  51. ```
  52.  
  53. ```
  54. test = TestMethodMissing.new
  55. test.hello
  56. hello
  57.  
  58. test.goodbye('cruel', 'world')
  59. unknown method called: goodbye
  60. arguments: cruel world
  61.  
  62. test.send(:hello)
  63. hello
  64.  
  65. test.send(:goodbye)
  66. unknown method called: goodbye
  67. arguments: cruel world
  68. ```
  69.  
  70. Imagine a bank account application with the need of proxy to control access to the subject. It uses protective/virtual proxy around the real_account.
  71.  
  72. ```account.rb
  73. class BankAccount
  74. attr_reader :balance
  75.  
  76. def initialize(starting_balance = 0)
  77. @balance = starting_balance
  78. end
  79.  
  80. def deposit(amount)
  81. @balance += amount
  82. end
  83.  
  84. def withdraw(amount)
  85. @balance -= amount
  86. end
  87. end
  88.  
  89. class AccountProtectionProxy
  90. def initialize(real_account, owner_name)
  91. @subject = real_account
  92. @owner_name = owner_name
  93. end
  94.  
  95. def deposit(amount)
  96. check_access
  97. @subject.deposit(amount)
  98. end
  99.  
  100. def withdraw(amount)
  101. check_access
  102. @subject.withdraw(amount)
  103. end
  104.  
  105. def balance
  106. check_access
  107. @subject.balance
  108. end
  109.  
  110. # ad infinitum ...
  111.  
  112. def check_access
  113. if Etc.getlogin != @owner_name
  114. raise "Illegal access: #{Etc.getlogin} cannot access account."
  115. end
  116. end
  117. end
  118. ```
  119.  
  120. The code above can be refactored to this:
  121.  
  122. ```proxy.rb
  123. class AccountProtectionProxy
  124. def initialize(real_account)
  125. @subject = real_account
  126. @owner_name = owner_name
  127. end
  128.  
  129. def method_missing(name, *args)
  130. check_access
  131. @subject.send(name, *args)
  132. end
  133.  
  134. def check_access
  135. login_name = Etc.getlogin
  136. if login_name != @owner_name
  137. raise "Illegal access: #{login_name} cannot access account."
  138. end
  139. end
  140. end
  141. ```
  142.  
  143. There is also the `Forwardable` module to generate similar dull delegation methods.
  144.  
  145. ```forwardable.rb
  146. require 'forwardable'
  147.  
  148. class Delegator
  149. extend forwardable
  150.  
  151. def_delegators :@base, :method_b, :method_c
  152.  
  153. def initialize(base)
  154. @base = base
  155. end
  156.  
  157. def method_a
  158. @base.method_a
  159. end
  160. end
  161. ```
  162.  
  163. ### Concerns
  164.  
  165. Remember when using the method_missing technique, that every object actually starts out with a minimal set of methods - those that it inherits from Object class. For example, be careful of `to_s` as every object inherits a method called `to_s` from Object.
  166.  
  167. Delegation in general, and using method_missing may worsen performance.
  168.  
  169. Overuse leads to obscure code.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement