Menu
Coddy logo textTech

תבנית Factory

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Ruby של Coddy. שיעור 43 מתוך 57.

תבנית factory מרכזת את יצירת האובייקטים במקום אחד. מי שקוראים לה מבקשים את מה שהם צריכים, לעיתים קרובות לפי שם, ומקבלים אובייקט מוכן בלי לדעת איזו מחלקה נוצרה:

class Square
  def initialize(side)
    @side = side
  end

  def area
    @side * @side
  end
end

class Circle
  def initialize(r)
    @r = r
  end

  def area
    (3.14159 * @r * @r).round(2)
  end
end

class ShapeFactory
  def self.create(kind, size)
    case kind
    when "square" then Square.new(size)
    when "circle" then Circle.new(size)
    end
  end
end

puts ShapeFactory.create("square", 3).area
puts ShapeFactory.create("hexagon", 3).inspect

פלט:

9
nil

מיפוי גיבוב משמות למחלקות הופך את המפעל לקצר יותר וקל יותר להרחבה. מחלקות הן אובייקטים, ולכן אפשר לאחסן אותן במיפוי גיבוב ולשלוח להן new:

class Dog
  def speak
    "Woof"
  end
end

class Cat
  def speak
    "Meow"
  end
end

class AnimalFactory
  KINDS = { "dog" => Dog, "cat" => Cat }.freeze

  def self.create(kind)
    klass = KINDS.fetch(kind) { raise ArgumentError, "unknown animal: #{kind}" }
    klass.new
  end
end

puts AnimalFactory.create("cat").speak
begin
  AnimalFactory.create("cow")
rescue ArgumentError => e
  puts e.message
end

פלט:

Meow
unknown animal: cow

מפעל יכול גם לאפשר לקוד אחר לרשום סוגים חדשים בזמן ריצה, כך שהוספת סוג אינה מחייבת לערוך את המפעל:

class Plugins
  @kinds = {}

  class << self
    def register(name, klass)
      @kinds[name] = klass
    end

    def build(name)
      @kinds.fetch(name).new
    end

    def names
      @kinds.keys.sort
    end
  end
end

class Zip
  def run
    "zipping"
  end
end

Plugins.register("zip", Zip)
puts Plugins.build("zip").run
p Plugins.names

פלט:

zipping
["zip"]

הקוראים תלויים רק במה שהאובייקטים יכולים לעשות, כאן area או speak, ולעולם לא במחלקות הקונקרטיות. צורה חדשה פירושה מחלקה חדשה אחת ורשומה חדשה אחת במפעל, וללא שינוי בשום מקום אחר:

shape = ShapeFactory.create(kind, size)   # Square, Circle, ...
puts shape.area                           # הקוד הקורא לעולם אינו מציין שם של מחלקה
challenge icon

אתגר

קל

השלימו את הצורות ואת ShapeFactory.create(kind, size). Square, Circle ו-Triangle (שווה־צלעות) יורשות ממחלקת הבסיס Shape, שהמתודה to_s שלה מחזירה square 3: area 9.00, עם שם המחלקה באותיות קטנות, הגודל והשטח בשתי ספרות אחרי הנקודה העשרונית. השתמשו ב-3.14159 עבור פאי וב-Math.sqrt(3) / 4 * size**2 עבור המשולש. המפעל מחפש את סוג הצורה בקבוע מסוג גיבוב קפוא בשם KINDS ומחזיר nil עבור סוג צורה לא מוכר.

הקוד שסופק קורא שורות square 3, מבקש מהמפעל ליצור צורה עבור כל שורה ומדפיס אותה, או unknown shape: hexagon.

הקוד שלכם ייכתב בקבצים shape.rb, square.rb, circle.rb, triangle.rb ו-shape_factory.rb. הקובץ main.rb מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

require_relative 'shape'
require_relative 'square'
require_relative 'circle'
require_relative 'triangle'
require_relative 'shape_factory'

# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  kind, size = line.split
  shape = ShapeFactory.create(kind, size.to_f)
  puts shape || "unknown shape: #{kind}"
end
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Ruby אונליין