Menu
CoddyTech

Mẫu Factory

Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 43 trên 57.

Mẫu factory tập trung việc tạo đối tượng ở một nơi. Bên gọi yêu cầu đối tượng cần dùng, thường bằng tên, rồi nhận về một đối tượng sẵn sàng mà không cần biết lớp nào đã được tạo:

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

Đầu ra:

9
nil

Một hash ánh xạ tên đến lớp giúp factory ngắn gọn hơn và dễ mở rộng hơn. Lớp là các đối tượng, vì vậy bạn có thể lưu chúng trong một hash rồi gửi 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

Đầu ra:

Meow
unknown animal: cow

Một factory cũng có thể cho phép đoạn code khác đăng ký các loại mới khi chương trình đang chạy, vì vậy việc thêm một loại không đồng nghĩa với việc chỉnh sửa factory:

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

Đầu ra:

zipping
["zip"]

Các bên gọi chỉ phụ thuộc vào những gì đối tượng có thể làm, ở đây là area hoặc speak, chứ không phụ thuộc vào các lớp cụ thể. Một hình dạng mới chỉ cần một lớp mới và một mục mới trong factory, không cần thay đổi ở bất kỳ nơi nào khác:

shape = ShapeFactory.create(kind, size)   # Square, Circle, ...
puts shape.area                           # Bên gọi không cần nêu tên lớp (class)
challenge icon

Thử thách

Dễ

Hoàn thiện các hình và ShapeFactory.create(kind, size). Square, Circle và Triangle (tam giác đều) dùng chung lớp cơ sở Shape, trong đó to_s trả về square 3: area 9.00, gồm tên lớp viết thường, kích thước và diện tích với hai chữ số thập phân. Dùng 3.14159 cho pi và Math.sqrt(3) / 4 * size**2 cho tam giác. Factory tra cứu loại hình trong hằng số hash đóng băng KINDS và trả về nil nếu không tìm thấy loại hình.

Code được cung cấp đọc các dòng square 3, yêu cầu factory tạo từng hình rồi in ra, hoặc unknown shape: hexagon.

Code của bạn nằm trong các tệp shape.rb, square.rb, circle.rb, triangle.rb và shape_factory.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

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

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Ruby online