Slide 1

Slide 1 text

Intro to Object Oriented Programming* *in Ruby Burlington Ruby - September 2012 - Peter Brown

Slide 2

Slide 2 text

Introduction OOP Overview Designing Objects Testing Refactoring OOP Tips class Presentation def language 'Ruby' end def topic 'OOP' end def totally_awesome? true end end

Slide 3

Slide 3 text

Procedural Programming Model a sequence of events Data and functions (procedures) are separate Perfect for procedural languages class DataProcessor def process data = get_data_from_file new_data = convert_to_format(data) write_data_to_file(new_data) end def get_data_from_file end def convert_to_format(data) end def write_data_to_file(new_data) end end processor = DataProcessor.new processor.process

Slide 4

Slide 4 text

What can be improved? Procedural programming alone can be: difficult to relate to real world objects tough to read and modify hard to test

Slide 5

Slide 5 text

Objects to the rescue! OOP is a shift in thinking Dividing code into small classes with a single responsibility: React to change Reuse code Self documenting

Slide 6

Slide 6 text

What is Object Oriented Programming? Design Refactoring Testing

Slide 7

Slide 7 text

Object Oriented Design Modeling Domain Objects Messages and Interfaces

Slide 8

Slide 8 text

What are Objects? Instance of a class Properties / State Instance variables (@var) Methods: Command vs Query

Slide 9

Slide 9 text

Everything in Ruby is an Object String Integer Float Symbol Method Array Hash File Regexp Exception Boolean

Slide 10

Slide 10 text

Object Example pete = Person.new pete.age = 30 pete.age # => 30 class Person def age=(value) @age = value end def age @age end end Class vs Object

Slide 11

Slide 11 text

Interface (API) Public methods exposed to consumers Consumers = developers or code Hide implementation (Encapsulation)

Slide 12

Slide 12 text

Public Methods [:!, :!=, :! ~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :initialize_clone, :initialize_dup, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :kind_of?, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :respond_to?, :respond_to_missing?, :send, :singleton_class, :singleton_methods, :taint, :tainted?, :tap, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?] class Person; end Person.new.methods # =>

Slide 13

Slide 13 text

Interfaces: Good vs Bad Predictable Familiar Consistent Dependable Hide implementation Unpredictable Unfamiliar Inconsistent Subject to change Expose implementation

Slide 14

Slide 14 text

Interfaces: A Comparison

Slide 15

Slide 15 text

Interfaces: A Comparison jack = Person.new('Jack', 35, 'brown') jill = Person.new('Jill', 31, 'red')

Slide 16

Slide 16 text

Interfaces: A Comparison jack = Person.new('Jack', 35, 'brown') jill = Person.new('Jill', 31, 'red') jack = Person.new(name: 'Jack', age: 35, hair: 'brown') jill = Person.new(age: 31, name: 'Jill', hair: 'red') bill = Person.new(age: 40, name: 'Bill')

Slide 17

Slide 17 text

Predictable Interfaces jack.name # => ‘Jack’ jack.hair # => ‘brown’ jack.male? # => true jack.siblings.size # => 2 jack.reset! # => Dangerous!

Slide 18

Slide 18 text

Designing Objects Classical Inheritance Sharing via Modules Composition

Slide 19

Slide 19 text

Classical Inheritance Object A is an Object B All objects have a superclass Methods automatically delegated to superclass class Beverage end class Beer < Beverage end

Slide 20

Slide 20 text

Classical Inheritance class Alarm def check notify if active? end def notify puts "#{self.class} is active!" end def active? raise NotImplementedError end end

Slide 21

Slide 21 text

Classical Inheritance class FireAlarm < Alarm def active? fire_detected? end private def fire_detected? true end end alarm = FireAlarm.new alarm.check # => FireAlarm is active!

Slide 22

Slide 22 text

Classical Inheritance class Person < Object end

Slide 23

Slide 23 text

Sharing via Modules Object A behaves like Object B Can be reused by other classes Classes can include multiple modules module Drinkable def drink puts "mmmm" end end class Beer include Drinkable end

Slide 24

Slide 24 text

Sharing via Modules class Alarm include Notifiable def check notify if active? end def active? raise NotImplementedError end end module Notifiable def notify puts "#{self.class} is active!" end end

Slide 25

Slide 25 text

Composition Object A has an Object B Create objects that are composed of other objects class Roof end class House def roof Roof.new end end

Slide 26

Slide 26 text

Composition 20.meters.to_feet # => 65.6168

Slide 27

Slide 27 text

Composition 20.meters.to_feet # => 65.6168 class Meter def initialize(value) @value = value end def to_feet @value * 3.28084 end end

Slide 28

Slide 28 text

Composition 20.meters.to_feet # => 65.6168 class Meter def initialize(value) @value = value end def to_feet @value * 3.28084 end end class Numeric def meters Meter.new(self) end end

Slide 29

Slide 29 text

Composition class Numeric def feet Foot.new(self) end def meters Meter.new(self) end end 20.feet.to_feet # => 20 20.meters.to_feet # => 65.6168

Slide 30

Slide 30 text

Composition house = Building.new(height: 6.meters) house.height.to_feet # => 19.68504 skyscraper = Building.new(height: 500.feet) skyscraper.height.to_feet # => 500

Slide 31

Slide 31 text

Composition class Building attr_reader :height def initialize(options={}) @height = options[:height] end end house = Building.new(height: 6.meters) house.height.to_feet # => 19.68504 skyscraper = Building.new(height: 500.feet) skyscraper.height.to_feet # => 500

Slide 32

Slide 32 text

Unit Testing Interfaces & BDD go hand in hand Makes refactoring less painful Testing Public vs Private methods

Slide 33

Slide 33 text

Unit Testing: RSpec describe Person do subject { Person.new(name: 'Jack') } context '#siblings' do it { should have(:no).siblings } specify 'adding siblings behaves like an array' do subject.siblings << Person.new(name: 'Jill') subject.siblings << Person.new(name: 'Bill') subject.should have(2).siblings end end end

Slide 34

Slide 34 text

Refactoring Improving internal design of existing code without changing external behavior

Slide 35

Slide 35 text

Code Smells Abusing Inheritance Defensive coding Class names that end in ‘er’ and `or` Complex tests Arrays representing objects Class uses methods from another class excessively Too many parameters Large classes Long methods

Slide 36

Slide 36 text

OOP Tips Avoid unexpected state change Think in terms of what an object does, not what it is (BDD) Don’t have to start with objects, add them as needed Code to an interface Be consistent Favor composition over inheritance Avoid tight coupling

Slide 37

Slide 37 text

ClassyEnum Gem Combines classes, enums and ActiveRecord Composition and Polymorphism https://github.com/beerlington/classy_enum

Slide 38

Slide 38 text

Learning More http://rubyrogues.com/ object-oriented- programming-in-rails-with- jim-weirich/ http://www.confreaks.com/ videos/763- rubymidwest2011-confident- code

Slide 39

Slide 39 text

Thanks! @beerlington [email protected]