Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Learnings from POODR - part 1

Learnings from POODR - part 1

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.

Tejas Bubane

December 17, 2021
Tweet

More Decks by Tejas Bubane

Other Decks in Programming

Transcript

  1. Object-Oriented Software • Software built with objects communicating by sending

    messages to achieve desired behaviour. • Mindset shift - procedural => Object- oriented. • Each app = unique programming language tailored specifically for your domain
  2. Object-Oriented Design • Mindset shift : procedural => Object-oriented •

    Assembly line => art studio • Biggest problem is Change • More about achieving changeability than perfection
  3. Single Responsibility class Gear def initialize(chainring, cog) @chainring = chainring

    @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
  4. Solutions class Gear attr_reader :chainring, :cog, :rim, :tire def initialize(chainring:,

    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.
  5. Identify classes class Gear attr_reader :chainring, :cog, :wheel def initialize(chainring,

    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.
  6. Designing Interfaces • Interface = glue that holds together objects

    • 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
  7. Sequence Diagrams • Quick and easy way to prototype, erase,

    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
  8. Sequence Diagrams Customer should not be iterating on each trip

    and finding suitable bicycle. They just want a “planned” trip.
  9. Law of Demeter customer.bicycle.wheel.rotate • Try not to have chained

    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