Menu
CoddyTech

תבנית Factory

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

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

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 עבור המשולש. המפעל מחפש את סוג הצורה בקבוע hash קפוא 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 אונליין