Factory Methods
Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 15 of 57.
A factory method is a class method that creates and returns an instance. It gives a name to one way of building an object. Inside a class method, new means self.new:
class Temperature
attr_reader :celsius
def initialize(celsius)
@celsius = celsius
end
def self.from_fahrenheit(f)
new((f - 32) * 5 / 9.0)
end
def self.freezing
new(0)
end
end
puts Temperature.from_fahrenheit(212).celsius
puts Temperature.freezing.celsiusOutput:
100.0
0Turning text into an object is a common job for a factory. The parsing lives next to the class, and initialize stays simple:
class Duration
attr_reader :seconds
def initialize(seconds)
@seconds = seconds
end
def self.parse(text)
new(text.split(":").map(&:to_i).reduce(0) { |acc, part| acc * 60 + part })
end
end
puts Duration.parse("1:30").seconds
puts Duration.parse("2:00:05").secondsOutput:
90
7205A factory can refuse bad input. Returning nil lets the caller test the result with if; raising an error stops the program unless the caller rescues it:
class Color
attr_reader :hex
def initialize(hex)
@hex = hex
end
def self.parse(text)
return nil unless text.match?(/\A#[0-9a-f]{6}\z/i)
new(text.downcase)
end
end
["#FF8800", "orange"].each do |text|
color = Color.parse(text)
puts color ? "ok #{color.hex}" : "not a color: #{text}"
endOutput:
ok #ff8800
not a color: orangeprivate_class_method :new makes new callable only inside the class, so the factories become the only way to create an object:
class Money
attr_reader :cents
def initialize(cents)
@cents = cents
end
private_class_method :new
def self.from_cents(cents)
new(cents)
end
end
puts Money.from_cents(250).cents
begin
Money.new(250)
rescue NoMethodError => e
puts e.message
endOutput:
250
private method `new' called for class MoneyChallenge
EasyComplete the class Duration. initialize(seconds) stores a number of seconds. The factory Duration.parse(text) reads m:ss or h:mm:ss, the factory Duration.from_minutes(m) takes whole minutes, plus(other) returns a new Duration with the sum, and to_s returns h:mm:ss with two-digit minutes and seconds.
The supplied code reads durations: lines like 45m go through from_minutes, the rest through parse. It prints each duration and finally total 1:47:10.
Your code goes in duration.rb. main.rb holds the supplied input/output code and cannot be edited.
Try it yourself
require_relative 'duration'
# Supplied input/output code: keep it as it is
input = []
while (line = gets)
input << line.chomp
end
durations = input.map do |line|
line.end_with?("m") ? Duration.from_minutes(line.to_i) : Duration.parse(line)
end
durations.each { |d| puts d }
puts "total #{durations.reduce(Duration.new(0)) { |sum, d| sum.plus(d) }}"
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesInstance VariablesMethods and selfto_s and inspectRecap - Library Card7Polymorphism and Duck Typing
PolymorphismDuck TypingAbstract Methodscase and ClassesRecap - Payment Methods2Accessors and Self
Readers and WritersCustom Settersself in MethodsIdentity and EqualityRecap - Contact List8Special Methods
Equality and hashOperator MethodsIndexing and AppendingCustom CollectionsRecap - Fractions11Project: Library Management
Books and MembersBorrowing Books3Class Methods and Variables
Class MethodsClass Instance VariablesConstantsFactory MethodsRecap - Coupon CodesPractice on your own: Online Ruby compiler