Eastward HO!

09477c358c5897d44121a248326e16d7?s=47 Jim Gay
November 19, 2014

Eastward HO!

09477c358c5897d44121a248326e16d7?s=128

Jim Gay

November 19, 2014
Tweet

Transcript

  1. EASTWARD HO! A clear path with OO Jim Gay /

    @saturnflyer
  2. None
  3. EAST ORIENTED

  4. EAST ORIENTED James Ladd

  5. None
  6. COMMANDS QUERIES

  7. EAST WEST

  8. RULE 1 ALWAYS RETURN SELF

  9. RULE 2 OBJECTS MAY QUERY
 THEMSELVES

  10. RULE 3 FACTORIES ARE EXEMPT

  11. FANCY WORDS

  12. FANCY WORDS POLYMORPHISM

  13. FANCY WORDS ENCAPSULATION

  14. FANCY WORDS COUPLING

  15. FANCY WORDS IDEMPOTENT

  16. IF

  17. IF ELSIF ELSIF ELSIF ELSIF

  18. CASE WHEN WHEN WHEN WHEN

  19. IF

  20. IF IF IF IF IF IF IF IF IF IF

    IF IF IF
  21. class Person! attr_reader :address! end!

  22. class Person! attr_reader :address! ! def display_address! ! end! end!

  23. class Person! attr_reader :address! ! def display_address! ! end! end!

    "123 Main St. Arlington" ! "123 Main St. VA 22222" "123 Main St. Arlington, VA 22222” ! "Arlington, VA"
  24. class Person! attr_reader :address! ! def display_address! ! end! end!

    "123 Main St. Arlington" ! "123 Main St. VA 22222" "123 Main St. Arlington, VA 22222” ! "Arlington, VA"
  25. class Person! def display_address! "".tap do |string|! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end! ! string << "\n" unless string.empty?! ! if !address.city.nil? address.city.empty? || address.city.nil?! string << address.city! end! ! if address.province || address.postal_code! string << ", "! end! ! if address.province || address.postal_code! if address.province! string << address.province! end! ! string << " "! ! "123 Main St. Arlington" ! "123 Main St. VA 22222" "123 Main St. Arlington, VA 22222” ! "Arlington, VA"
  26. RULE 1 ALWAYS RETURN SELF

  27. class Person! def display_address! ! ! ! ! ! !

    ! # … all the code …! ! ! ! ! ! ! ! ! ! ! self! end! end! "123 Main St. Arlington" ! "123 Main St. VA 22222" "123 Main St. Arlington, VA 22222” ! "Arlington, VA" RULE 1
  28. class Person! def display_address! ! ! ! ! ! !

    ! # … all the code …! ! ! ! ! ! ! ! ! ! ! self! end! end! "123 Main St. Arlington" ! "123 Main St. VA 22222" "123 Main St. Arlington, VA 22222” ! "Arlington, VA"
  29. Tell,  Don’t Ask

  30. Tell,  Don’t Ask Andy Hunt and 
 Dave Thomas

  31. Tell,  Don’t Ask ensure a correct 
 division of

    responsibility that places the right functionality in the 
 right class 
 without causing 
 excess coupling 
 to other classes. Andy Hunt and 
 Dave Thomas
  32. Tell,  Don’t Ask ensure a correct 
 division of

    responsibility that places the right functionality in the 
 right class 
 without causing 
 excess coupling 
 to other classes. Andy Hunt and 
 Dave Thomas
  33. Command,  Don’t Query

  34. street street?

  35. street street?

  36. Command,  Don’t Query ! ! ! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end
  37. Command,  Don’t Query ! ! ! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end
  38. Command,  Don’t Query ! ! ! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end
  39. Command,  Don’t Query ! ! ! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end
  40. class Person! def display_address! "".tap do |string|! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end! ! string << "\n" unless string.empty?! ! if !address.city.nil? address.city.empty? || address.city.nil?! string << address.city! end! ! if address.province || address.postal_code! string << ", "! end! ! if address.province || address.postal_code! if address.province! string << address.province! end! ! string << " "! !
  41. The Pretty Good Idea of Demeter

  42. The Pretty Good Idea of Demeter ! ! ! if

    !address.street.nil? || !address.street.empty?! string << address.street! end
  43. The Pretty Good Idea of Demeter ! ! ! if

    !address.street.nil? || !address.street.empty?! string << address.street! end
  44. The Pretty Good Idea of Demeter ! ! ! if

    !address.street.nil? || !address.street.empty?! string << address.street! end
  45. The Pretty Good Idea of Demeter ! ! ! if

    !address.street.nil? || !address.street.empty?! string << address.street! end
  46. The Pretty Good Idea of Demeter ! ! ! if

    !address.street.nil? || !address.street.empty?! string << address.street! end
  47. my_television.front_panel.switches.power.on();

  48. my_television.front_panel.switches.power.on();

  49. my_television.front_panel.switches.power.on();

  50. my_television.front_panel.switches.power.on();

  51. my_television.front_panel.switches.power.on();

  52. my_television.front_panel.switches.power.on();

  53. my_television.front_panel.switches.power.on();

  54. my_television.front_panel.switches.power.on();

  55. my_television.front_panel.switches.power.on();

  56. my_television.front_panel.switches.power.on();

  57. my_television.front_panel.switches.power.on();

  58. class User def watch_tv my_television.front_panel.switches.power.on end end

  59. my_television.power_up();

  60. class Person! def display_address! "".tap do |string|! if !address.street.nil? ||

    !address.street.empty?! string << address.street! end! ! string << "\n" unless string.empty?! ! if !address.city.nil? address.city.empty? || address.city.nil?! string << address.city! end! ! if address.province || address.postal_code! string << ", "! end! ! if address.province || address.postal_code! if address.province! string << address.province! end! ! string << " "! !
  61. class Person! def display_address! address.display! ! self! end! end!

  62. class Person! def display_address! address.display! ! ! self! end! end!

    ! class Address! def display! "".tap do |string|! if !street.nil? || !street.empty?! string << street! end! ! string << "\n" unless string.empty?! ! if !city.nil? city.empty? || city.nil?! string << city! end! ! if province || postal_code! string << ", "! end!
  63. RULE 2 OBJECTS MAY QUERY
 THEMSELVES

  64. class Person! def display_address! address.display! ! ! self! end! end!

    ! class Address! def display! "".tap do |string|! if !street.nil? || !street.empty?! string << street! end! ! string << "\n" unless string.empty?! ! if !city.nil? city.empty? || city.nil?! string << city! end! ! if province || postal_code! string << ", "! end! RULE 2
  65. class Address! def display! ! ! ! ! ! !

    ! end! end
  66. class Address! def display! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! ! ! ! ! end! end "" "VA 22222" "VA 22222” "VA"
  67. class Address! def display! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! ! end! end "Arlington" "VA 22222" “Arlington, VA 22222” “Arlington, VA"
  68. class Address! def display! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! [street, city_province_postal_code].compact.join("\n")! end! end “123 Main St. Arlington” “123 Main St. VA 22222" “123 Main St. Arlington, VA 22222” “Arlington, VA"
  69. class Address! def display! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! [street, city_province_postal_code].compact.join("\n")! end! end
  70. class Address! def display(line_break="\n")! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! [street, city_province_postal_code].compact.join(line_break)! end! end
  71. class Address! def display(line_break="\n")! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! [street, city_province_postal_code].compact.join(line_break)! end! end! ! class Person! def display_address(line_break)! address.display(line_break)! self! end! end
  72. class Address! def display(line_break="\n")! province_and_postal_code = [province, postal_code].compact.join(' ')! province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [city, province_and_postal_code].compact.join(', ')! city_province_postal_code = nil if city_province_postal_code.empty?! ! [street, city_province_postal_code].compact.join(line_break)! end! end! ! class Person! def display_address(line_break)! address.display(line_break)! self! end! end
  73. class Template! def display_address(address)! province_and_postal_code = [address.province, address.postal_code].compact.join(' ' province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [address.city, province_and_postal_code].compact.join(' city_province_postal_code = nil if city_province_postal_code.empty?! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end
  74. class Template! def display_address(address)! province_and_postal_code = [address.province, address.postal_code].compact.join(' ' province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [address.city, province_and_postal_code].compact.join(' city_province_postal_code = nil if city_province_postal_code.empty?! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end! ! class HtmlTemplate! def display_address(address)! ! ! # data processing… ! ! File.open ‘address.html’ do |file|! file.write [address.street, city_province_postal_code].compact.join("<br />”)! end! ! self! end!
  75. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end

  76. RULE 3 FACTORIES ARE EXEMPT

  77. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end RULE 3

  78. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end! ! class

    Address! def display(template)! template.display_address(self)! self! end! end!
  79. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end! ! class

    Address! def display(template)! template.display_address(self)! self! end! end! ! class Template! def display_address(address)! # data processing…! ! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end
  80. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end! ! class

    Address! def display(template)! template.display_address(self)! self! end! end
  81. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end! ! !

    ! ! class Address! def display(template)! template.display_address(self)! self! end! end
  82. class Person! def display_address(template=Template.new)! address.display(template)! self! end! ! private! attr_reader

    :address! end! ! class Address! def display(template)! template.display_address(self)! self! end! ! private! attr_reader :street, :city, :province, :postal_code! end
  83. class Address! def display(template)! template.display_address(self)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! end
  84. class Address! def display(template)! template.display_address(self)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! end! ! class Template! def display_address(address)! province_and_postal_code = [address.province, address.postal_code].compact.join(' ' province_and_postal_code = nil if province_and_postal_code.empty?! ! city_province_postal_code = [address.city, province_and_postal_code].compact.join(' city_province_postal_code = nil if city_province_postal_code.empty?! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end
  85. class Address! def display(template)! template.display_address(self)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! end! ! ! ! ! ! ! class Template! def display_address(address)! # …code…! end! end
  86. class Address! def display(template)! template.display_address(to_value)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! ! require ‘ostruct’! def to_value! OpenStruct.new(street: street, city: city, province: province, postal_code: postal_ end! end! ! class Template! def display_address(address)! # …code…! end! end
  87. class Address! def display(template)! template.display_address(to_value)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! ! require ‘ostruct’! def to_value! OpenStruct.new(street: street, city: city, province: province, postal_code: postal_ end! end! ! class Template! def display_address(address)! # …code…! end! end
  88. “123 Main St. Arlington” “123 Main St. VA 22222" “123

    Main St. Arlington, VA 22222” “Arlington, VA"
  89. class Address! def display(template)! template.display_address(to_value)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! ! require ‘ostruct’! def to_value! OpenStruct.new(street: street, city: city, province: province, postal_code: postal_ end! end
  90. class Address! def display(template)! template.display_address(to_value)! self! end! ! private! attr_reader

    :street, :city, :province, :postal_code! ! require ‘ostruct’! def to_value! if protect_privacy?! OpenStruct.new(street: nil, city: city, province: province, postal_code: nil)! else! OpenStruct.new(street: street, city: city, province: province, postal_code: posta end! end! end
  91. class Template! def display_address(address)! province_and_postal_code = [address.province, address.postal_code].compact.join(' ' province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [address.city, province_and_postal_code].compact.join(' city_province_postal_code = nil if city_province_postal_code.empty?! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end
  92. class Template! def display_address(address)! province_and_postal_code = [address.province, address.postal_code].compact.join(' ' province_and_postal_code

    = nil if province_and_postal_code.empty?! ! city_province_postal_code = [address.city, province_and_postal_code].compact.join(' city_province_postal_code = nil if city_province_postal_code.empty?! ! STDOUT.puts [address.street, city_province_postal_code].compact.join(“\n")! ! self! end! end QUERIES EVERYWHERE
  93. RULE 4 BREAK
 THE RULES SPARINGLY

  94. class Person! def display_address(template=Template.new)! address.display(template)! self! end! end! ! class

    Address! def display(template)! template.display_address(self)! self! end! end! ! class Template! def display_address(address)! # data processing…! ! ! STDOUT.puts […].compact.join(“\n”)! ! self! end! end
  95. EAST ORIENTED James Ladd

  96. EAST ORIENTED James Ladd The structuring of code to an

    
 East orientation decreases coupling and the amount of code needed to be written, whilst increasing code clarity, cohesion and flexibility.
  97. EAST ORIENTED James Ladd The structuring of code to an

    
 East orientation decreases coupling and the amount of code needed to be written, whilst increasing code clarity, cohesion and flexibility.
  98. COMMANDS QUERIES

  99. LEAK

  100. LEAK KNOWLEDGE

  101. LEAK RESPONSIBILITY

  102. POLYMORPHISM

  103. ENCAPSULATION

  104. LOOSEN COUPLING

  105. IDEMPOTENT

  106. EAST ORIENTED

  107. RULE 1 ALWAYS RETURN SELF

  108. RULE 2 OBJECTS MAY QUERY THEMSELVES

  109. RULE 3 FACTORIES ARE EXEMPT

  110. RULE 4 BREAK
 THE RULES SPARINGLY

  111. TELL JAMES THANKS @jamesladd The East principle 
 is not

    like the 
 “Tell, Don’t Ask” principle because there is no ambiguity, you simply cannot ask, you can only tell.
  112. TELL JAMES THANKS @jamesladd The East principle 
 is not

    like the 
 “Tell, Don’t Ask” principle because there is no ambiguity, you simply cannot ask, you can only tell.
  113. What if we had tools to help guide us?

  114. Let’s try it

  115. class Person! ! ! attr_reader :friend! end! ! class Friend!

    def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  116. class Person! ! ! attr_reader :friend! ! def make_me_a_sandwich! friend.make_me_a_sandwich!

    end! end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end
  117. class Person! extend Forwardable! delegate :make_me_a_sandwich => :friend! attr_reader :friend!

    end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  118. gem install  direction

  119. describe Direction do! let(:friend){ Friend.new }! let(:person){ person = Person.new!

    person.friend = friend! person! }! before do! Table.clear! end! it 'forwards a message to another object' do! assert_equal [], Table.contents! person.make_me_a_sandwich! assert_includes Table.contents, "a sandwich!"! end! end
  120. describe Direction do! let(:friend){ Friend.new }! let(:person){ person = Person.new!

    person.friend = friend! person! }! before do! Table.clear! end! it 'forwards a message to another object' do! assert_equal [], Table.contents! person.friend.make_me_a_sandwich! assert_includes Table.contents, "a sandwich!"! end! ! ! !
  121. class Person! extend Forwardable! delegate :make_me_a_sandwich => :friend! attr_reader :friend!

    end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  122. class Person! extend Forwardable! delegate :make_me_a_sandwich => :friend! ! private!

    attr_reader :friend! end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  123. class Person! extend Direction! command :make_me_a_sandwich => :friend! ! private!

    attr_accessor :friend! end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  124. describe Direction do! let(:friend){ Friend.new }! let(:person){ person = Person.new!

    person.friend = friend! person! }! before do! Table.clear! end! it 'forwards a message to another object' do! assert_equal [], Table.contents! person.make_me_a_sandwich! assert_includes Table.contents, "a sandwich!"! end! ! it 'returns the original receiver' do! assert_equal person, person.make_me_a_sandwich! end!
  125. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich! end! end
  126. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink! end! end
  127. describe Direction do ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car! end! end
  128. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car. ! ! jump! end! end
  129. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car. ! ! jump. ! ! gimme_five! end! end
  130. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car. ! ! jump. ! ! gimme_five. ! ! up_high! end! end
  131. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car. ! ! jump. ! ! gimme_five. ! ! up_high. ! ! down_low! end! end
  132. describe Direction do! ! it 'returns the original receiver' do!

    assert_equal person, person.make_me_a_sandwich. ! ! get_me_a_drink. ! ! wash_the_car. ! ! jump. ! ! gimme_five. ! ! up_high. ! ! down_low. ! ! too_slow! end! end
  133. module Direction! def command(options)! method_defs = []! options.each_pair do |key,

    value|! Array(key).map do |command_name|! method_defs.unshift %{! def #{command_name}(*args, &block)! #{value}.__send__(:#{command_name}, *args, &block)! self! end! }! end! end! self.class_eval method_defs.join(' ')! end! end
  134. module Direction! def command(options)! method_defs = []! options.each_pair do |key,

    value|! Array(key).map do |command_name|! method_defs.unshift %{! def #{command_name}(*args, &block)! #{value}.__send__(:#{command_name}, *args, &block)! self! end! }! end! end! self.class_eval method_defs.join(' ')! end! end
  135. class Person! extend Direction! command [:make_me_a_sandwich, :other] => :friend! !

    private! attr_accessor :friend! end! ! class Friend! def make_me_a_sandwich! Table.place "a sandwich!"! end! end!
  136. CHANGE YOUR CODE

  137. RETURN SELF

  138. RUN YOUR TESTS

  139. DID IT BREAK?

  140. IT’S TELLING YOU SOMETHING

  141. GO EAST

  142. THANK YOU

  143. THANK YOU clean-ruby.com

  144. BUY THE BOOK clean-ruby.com

  145. BUY THE BOOK clean-ruby.com RC2EC 25% off