Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

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.

Kerri Miller

March 29, 2016
Tweet

More Decks by Kerri Miller

Other Decks in Programming

Transcript

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

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

    Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle S.O.L.I.D.
  3. @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
  4. @kerrizor *.rb • Object: A collection of related behaviors •

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

    behaviors (methods) and state (properties or variables) that are related. @kerrizor *.rb
  6. 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
  7. @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
  8. @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
  9. @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
  10. @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
  11. @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
  12. @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
  13. 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
  14. @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
  15. INTERFACE SEGREGATION PRINCIPLE The dependency of one class to another

    one should depend on the smallest possible interface. @kerrizor *.rb
  16. @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
  17. @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
  18. 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
  19. @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
  20. @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
  21. @_jnf & @kerrizor MountainWest RubyConf 2016 July 22nd & 23rd,

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