Advertisement
saasbook

demeter_example.rb

Mar 6th, 2012
1,061
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 2.04 KB | None | 0 0
  1. # This example is from Dan Manges's blog, dcmanges.com
  2.  
  3. class Wallet ; attr_accessor :cash ; end
  4. class Customer
  5.   # assume Customer's initialize() method sets the wallet attribute
  6.   #  to be a wallet instance full of money
  7.   attr_accessor :wallet
  8. end
  9. class Paperboy
  10.   def collect_money(customer, amount)
  11.     # VIOLATION OF DEMETER: Paperboy may know about Customer, but
  12.     #  we're exposing things about implementation of Wallet too.  Also,
  13.     #  we're handling the problem of "not enough cash" in Paperboy,
  14.     #  not in Wallet (where it arguably belongs)
  15.     if customer.wallet.cash < amount
  16.       raise InsufficientFundsError
  17.     else
  18.       customer.wallet.cash -= due_amount
  19.       @collected_amount += due_amount
  20.     end
  21.   end
  22. end
  23.  
  24. # Imagine testing the above code:
  25. describe "collecting money" do
  26.   it "should raise error if customer doesn't have enough money" do
  27.     # "Mock trainwreck" is a warning of a Demeter violation
  28.     wallet = mock('wallet', :cash => 5.00)
  29.     customer = mock('customer', :wallet => wallet)
  30.     lambda { @paperboy.collect(customer, 10.00) }.should raise_error(...)
  31.   end
  32. end
  33.  
  34. # This is better: we *delegate* the cash attribute via Customer.
  35. # Now Paperboy only "talks to" Customer.
  36.  
  37. class Customer
  38.   def cash
  39.     self.wallet.cash
  40.   end
  41. end
  42.  
  43. class Paperboy
  44.   def collect_money(amount)
  45.     if customer.cash >= amount
  46.       customer.cash -= due_amount
  47.       @collected_amount += due_amount
  48.     else
  49.       raise InsufficientFundsError
  50.     end
  51.   end
  52. end
  53.  
  54. # This is the best, because the *behavior* is delegated.  The implementation
  55. # of the behavior can now be changed without affecting Paperboy.
  56.  
  57. class Wallet
  58.   attr_reader :cash # no longer attr_accessor!
  59.   def withdraw(amount)
  60.      raise InsufficientFundsError if amount > cash
  61.      cash -= amount
  62.      amount
  63.   end
  64. end
  65. class Customer
  66.   # behavior delegation
  67.   def pay(amount)
  68.     wallet.withdraw(amount)
  69.   end
  70. end
  71. class Paperboy
  72.   def collect_money(customer, due_amount)
  73.     @collected_amount += customer.pay(due_amount)
  74.   end
  75. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement