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

SOLID 101: A Review For Rubyists

SOLID 101: A Review For Rubyists

SOLID is an acronym that tries to capture the first 5 principles of object-oriented programming and design, as enumerated by Michael Feathers and Robert Martin. Most Rubyists are probably familiar with one or two, but do you know what the rest are? Let's review them, see them in action, and learn how they can help us create maintainable, extensible, and comprehensible software.

F4d5752d6f4f839083cf810ad2c3a911?s=128

Kerri Miller

March 29, 2016
Tweet

Transcript

  1. @kerrizor *.rb SOLID 101: A REVIEW FOR RUBYISTS

  2. @kerrizor *.rb

  3. T.J. Higgins, JSD @kerrizor *.rb

  4. @kerrizor *.rb

  5. @kerrizor *.rb

  6. @kerrizor *.rb • Idempotent • CAP Theorem • Service •

    Correctness • SOLID BIG WORDS PEOPLE USE
  7. @kerrizor *.rb • Single Responsibility Principle • Open-Closed Principle •

    Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle S.O.L.I.D.
  8. @kerrizor *.rb

  9. @kerrizor *.rb

  10. @kerrizor *.rb • Object: A representation of a thing (a

    noun) • Method: An action of a thing (its verbs) • Properties: Description of a thing (its adjectives) “CLASSICAL” OBJECTS
  11. @kerrizor *.rb • Object: A collection of related behaviors •

    Method: A behavior • Properties: Details of behavior “MODERN” OBJECTS
  12. AN OBJECT SAYS WHAT? An object is a collection of

    behaviors (methods) and state (properties or variables) that are related. @kerrizor *.rb
  13. @kerrizor *.rb

  14. SINGLE RESPONSIBILITY PRINCIPLE A class should have one and only

    one reason to change, meaning that a class should have only one job. @kerrizor *.rb
  15. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Player attr_accessor :name, :kicking_average def initialize(options = {}) @name = options[:name] @kicking_average = options[:kicking_average] end def stats { kicking_average: @kicking_average } end end
  16. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Team attr_accessor :name, :players def initialize(name = nil, players = []) @name = name @players = players end def team_kicking_average players.collect{ |p| p.stats[:kicking_average] }.sum / players.size end def output_average "<h2>Team kicking average: #{team_kicking_average}</h2>" end end
  17. OPEN-CLOSED PRINCIPLE Objects or entities should be open for extension,

    but closed for modification. @kerrizor *.rb
  18. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Player attr_accessor :name, :position def initialize(options = {}) @name = options[:name] @position = options[:position] end def kicking_order if @position == "First Base" 1 elsif @position == "Second Base" 2 ... end end
  19. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Player attr_accessor :name, :position def initialize(options = {}) @name = options[:name] @position = options[:position] end def kicking_order if @position == "First Base" 1 elsif @position == "Second Base" 2 ... elsif @position == "Scorekeeper" 10 end end end
  20. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Player attr_accessor :name, :kicking_average def initialize(options = {}) @name = options[:name] @kicking_average = options[:kicking_average] end def position nil end def kicking_order nil end end
  21. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class FirstBaseperson < Player attr_accessor :name, :kicking_average def initialize(options = {}) @name = options[:name] @kicking_average = options[:kicking_average] end def position "First Base" end def kicking_order 1 end end
  22. LISKOV SUBSTITUTION PRINCIPLE Let q(x) be a property provable about

    objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T. @kerrizor *.rb
  23. @_jnf & @kerrizor MountainWest RubyConf 2016 LISKOV SUBSTITUTION PRINCIPLE @kerrizor

    *.rb
  24. LISKOV SUBSTITUTION PRINCIPLE Subtypes (children) must be substitutable for their

    base types (parents). @kerrizor *.rb
  25. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class ThirdBaseperson < Player attr_accessor :name, :kicking_average def initialize(options = {}) @name = options[:name] @kicking_average = options[:kicking_average] end def position { position: "Third Base" } end def kicking_order 1 end end
  26. INTERFACE SEGREGATION PRINCIPLE The dependency of one class to another

    one should depend on the smallest possible interface. @kerrizor *.rb
  27. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Team def calculate_salaries salaries = [] players.each do |player| if player.is_a? ThirdBase salary = player.salary[:salary] elsif player.respond_to? :favorite_pitch salary = "One Candy Bar per recess" elsif player.name == "Kerri" salary = Sandwich.new(type: "Tuna Fish w/Potato Chips") else salary = player.salary end end salaries.reject(:unavailable_at_cafeteria?) end end
  28. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Team def calculate_salaries players.collect(&:salary) end end class Pitch def salary "One Candy Bar per recess" end end class BenchSitter def salary Sandwich.new(type: "Tuna Fish w/Potato Chips") if name == "Kerri" end end
  29. DEPENDENCY INVERSION PRINCIPLE Entities must depend on abstractions not on

    concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions. @kerrizor *.rb
  30. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Team def print_roster RosterPrinter.new(players.collect(&:name)).print end end class RosterPrinter def initialize(roster = []) @roster = roster end def print roster.each do |name| puts "Name: #{name}" end end end
  31. @kerrizor Ancient City Ruby 2015 @kerrizor Ancient City Ruby 2015

    @kerrizor *.rb class Team def print_roster(printer = RosterPrinter) printer.new(roster).print end end class HtmlRosterPrinter def initialize(roster=[]) @roster = roster end def print roster.each do |name| "<h1>Player Name: #{name}</h1>" end end end
  32. @kerrizor *.rb

  33. @_jnf & @kerrizor MountainWest RubyConf 2016 July 22nd & 23rd,

    2016 @kerrizor *.rb http://cfp.osfeels.com
  34. @kerrizor *.rb