Sharing my learnings from the book Practical Object Oriented Programming in Ruby. The presentation was divided into 2 parts. This is part 1 which covers first 4 chapters.
messages to achieve desired behaviour. • Mindset shift - procedural => Object- oriented. • Each app = unique programming language tailored specifically for your domain
@cog = cog end def ratio @chainring / @cog.to_f end end puts Gear.new(52, 11).ratio puts Gear.new(30, 27).ratio class Gear def initialize(chainring, cog, rim, tire) @chainring = chainring @cog = cog @rim = rim @tire = tire end def ratio @chainring / @cog.to_f end def gear_inches ratio * (@rim + (@tire * 2)) end end Adds 2 new arguments which breaks existing clients
cog:, rim: nil, tire: nil) @chainring = chainring @cog = cog @rim = rim @tire = tire end def ratio chainring / cog.to_f end def gear_inches ratio * (rim + (tire * 2)) end end class Gear attr_reader :chainring, :cog, :wheel def initialize(chainring:, cog:, rim: nil, tire: nil) @chainring = chainring @cog = cog @wheel = Wheel.new(rim, tire) end def ratio chainring / cog.to_f end def gear_inches ratio * wheel.diameter end Wheel = Struct.new(:rim, :tire) do def diameter rim + (tire * 2) end def circumference diameter * Math::PI end end end Isolate problematic code using Struct Always access instance variables via getters. Makes it easy to add logic to getters without changing everywhere instance variable is used.
cog, wheel = nil) @chainring = chainring @cog = cog @wheel = wheel end def ratio chainring / cog.to_f end def gear_inches ratio * wheel.diameter end end class Wheel attr_reader :rim, :tire def initialize(rim, tire) @rim = rim @tire = tire end def diameter rim + (tire * 2) end def circumference diameter * Math::PI end end wheel = Wheel.new(26, 1.5) Gear.new(52, 11, wheel).gear_inches Gear.new(52, 11).ratio Dependency Injection Can be anything that responds to diameter Cylinder, Disk, etc.
• Expose as little about yourself as possible Know as little about others as possible • Public - used by others, rarely changes, well tested • Private - not used by others, frequent changes, not tested directly
redo • Gives clear idea about classes and their interactions • Reveal new (sometimes abstract) classes which were not foreseen • Ask what you need instead of telling how to do
method calls • Reaching far out objects • Knowing too much Exceptions • Intermediate values are of same type. • No mutation - you don’t own intermediate objects so you shouldn’t be mutating them User.where(status: :active) .where(paid: true) .order(:created_at) Such chaining of methods is fine because it follows both exceptions