Object Oriented Programming in Ruby: Aleksander Smywiński-Pohl
Object Oriented Programming in Ruby: Aleksander Smywiński-Pohl
Lecture 2
Object Oriented Programming in
Ruby
Aleksander Smywiński-Pohl
10 kwietnia 2013
Agenda
OOP techniques
I encapsulation
I abstraction
I delegation
I inheritance
I polymorphism
I dependency injection
OOP principles
I duplication avoidance
I single responsibility principle
I loose coupling
I high cohesion
I Law of Demeter
I and more . . .
Agenda
Encapsulation
Accessors
def remove_task(index)
raise IllegalArgument if index < 0 || index >= self.size
@list.delete(index)
end
end
def remove_task(index)
check_index(index)
@list.delete(index)
end
protected
def check_index(index)
raise IllegalArgument if index < 0 || index >= self.size
end
end
Rails pathology
# controller
def show
@post = Post.find(params[:id])
end
# view
<%= @post.title %>
https://github.com/voxdolo/decent_exposure
# controller
class Controller
expose(:post)
def create
if post.save
redirect_to(post)
else
render :new
end
end
end
# view
<%= post.title %>
Agenda
Abstraction
Class abstraction
class TodoList
def completed?(index)
@task_status[index]
end
def toggle_task(index)
@task_status[index] = ! @task_status[index]
end
def task_name(index)
@tasks[index]
end
def <<(task_name)
@tasks << task_name
@task_status << false
end
def delete(index)
@tasks.delete(index)
@task_status.delete(index)
end
end
Class abstraction
Method abstraction
# show.html.erb
<%= post.user.first_name + " " + post.user.last_name %>
# index.html.erb
<% posts.each do |post| %>
<%= post.user.first_name + " " + post.user.last_name %>
<% end %>
Method abstraction
# show.html.erb
class User
<%= post.user.full_name %>
def full_name
self.first_name + " " +
# index.html.erb
self.last_name
<% posts.each do |post| %>
end
<%= post.user.full_name %>
end
<% end %>
Module abstraction
def >(other)
(self <=> other) > 0
end
end
class Post
include Comparable
def <=>(other)
self.pub_date <=> other.pub_date
end
end
Service abstraction
Agenda
Manual delegation
class TodoList
def initialize
@items = []
end
def size
@itmes.size
end
def empty?
@items.empty?
end
def first
@items.first
end
end
SimpleDelegator
Forwardable
require 'forwardable'
class TodoList
def_delegators :@items, :size, :empty?, :first, :last
def initialize(items=[])
@items = items
end
end
list = TodoList.new
list.size #=> delegates to @items.size
list = TodoList.new
list.size #=> @items.size
delegate :name, :to => :user, :prefix => true, :allow_nil => true
end
post = Post.new
post.user_name
#=> nil
Agenda
class Measure
# Initialize the measure with +value+ and +scale+.
def initialize(value,scale)
@value = value
@scale = scale
end
# Default string representation of the measure.
def to_s
"%.2f %s" % [@value, @scale]
end
end
temperature = Temperature.new(10,:c)
puts temperature.to_kelvin.to_s #=> 283.15 k
ActiveRecord::Base
post.save
post.destroy
Agenda
Polymorphism
def print_collection(collection)
collection.each do |element|
puts "- #{element}"
end
end
print_collection([1,3,5])
# - 1
# - 3
# - 5
print_collection(1..3)
# - 1
# - 2
# - 3
Agenda
Dependency Injection
Dependency example
class TodoList
def initalize
@items = []
end
def <<(name)
@items << Task.new(name)
end
end
Dependency removal
class TodoList
def initialize(options={})
@items = []
@task_factory = options[:task_factory] || Task
end
def <<(name)
@items << @task_factory.new(name)
end
end
Alternative dependency
require_relative 'spec_helper'
require_relative '../../lib/todo_list'
stub_class 'Task'
describe TodoList do
subject(:list) { TodoList.new(:task_factory => task_factory) }
let(:task_factory) { stub!.new(task_name) { task }.subject }
let(:task_name) { "Buy toilet paper" }
let(:task) { Struct.new(:title,:completed).(task_name,false) }
end
Dependency setter
class TodoList
attr_writer :task_source
def initalize
@items = []
end
def <<(name)
@items << task_source.call(name)
end
private
def task_source
@task_source ||= Task.public_method(:new)
end
end
list = TodoList.new
fake_task_class = Struct.new(:title)
list.task_source = fake_task_class.public_method(:new)
Aleksander Smywiński-Pohl EPI
Lecture 2: OOP in Ruby
OOP Encapsulation Abstraction Delegation Inheritance Polymorphism DI Principles References
Default dependency
class Post
def publish(clock=DateTime)
self.pub_date = clock.now
# ...
end
end
class FixedClock
def initialize(date)
@date = date
end
def now
DateTime.parse(@date)
end
end
class DeleyedClock
def now
DateTime.now + 24.hours
end
end
Agenda
Duplication avoidance
DRY data
DRY data
id VAT rate
name price VAT rate id 1 23
starter 10 1 2 5
vegetarian dish 20 1 3 0
main dish 25 1
DRY code
class TodoList
def toggle_task(index)
raise IllegalArgument if index < 0 || index >= self.size
@list[index].completed = ! @list[index].completed
end
def remove_task(index)
raise IllegalArgument if index < 0 || index >= self.size
@list.delete(index)
end
end
DRY code
class TodoList
def toggle_task(index)
check_index(index)
@list[index].completed = ! @list[index].completed
end
def remove_task(index)
check_index(index)
@list.delete(index)
end
protected
def check_index(index)
raise IllegalArgument if index < 0 || index >= self.size
end
end
class Product
# Creates new product by parsing the XML
# representation of the product found
# under the +url+.
def self.import(url)
end
class ProductParser
# Parses the product definition and
# returns a struct containing the product
# name and price.
def parse(url)
end
end
class Product
# Stores the product in the database.
def save
end
end
class ProductPresenter
# Renders the product as a list item.
def render(context)
end
end
class TodoList
def completed?(index)
@task_status[index]
end
def toggle_task(index)
@task_status[index] = ! @task_status[index]
end
def task_name(index)
@tasks[index]
end
def <<(task_name)
@tasks << task_name
@task_status << false
end
def delete(index)
@tasks.delete(index)
@task_status.delete(index)
end
end
High cohesion
Loose coupling
Law of Demeter
Example
def user_full_name
user.profiles.first.personal_data.full_name
end
end
def format(line)
line.chomp.strip.capitalize
end
It’s ok, since we only have one class – String. line, the method
parameter, is an instance of String.
Agenda
References
References