Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

تعدد الأشكال

جزء من قسم البرمجة كائنية التوجه في رحلة Ruby على Coddy. الدرس 32 من 57.

تعدد الأشكال يعني أن استدعاءً واحدًا لأسلوب يعمل مع كائنات من فئات مختلفة، وأن كل كائن يستجيب بطريقته الخاصة. يستدعي الكود الذي يكرّر على الأشكال area لكل شكل دون أن يسأل عن نوع الشكل:

class Shape
  def to_s
    format("%s %.2f", self.class.name.downcase, area)
  end
end

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

  def area
    3.14159 * @r * @r
  end
end

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

  def area
    @side * @side
  end
end

shapes = [Circle.new(1), Square.new(3)]
shapes.each { |s| puts s }
puts format("total %.2f", shapes.sum(&:area))

الناتج:

circle 3.14
square 9.00
total 12.14

من دون تعدد الأشكال، يتعين على المستدعي التحقق من نوع كل كائن وتكرار هذا التحقق أينما كان الاختلاف مهمًا. أما معه، فيعيش الاختلاف داخل كل فئة:

# بدون تعدد الأشكال
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end

# مع تعدد الأشكال
shape.area

إضافة نوع جديد من الكائنات لا تتطلب بعد ذلك أي تغيير في الشيفرة المستدعية: فالفئة الفرعية الجديدة التي تمتلك area الخاص بها تعمل في كل حلقة موجودة مسبقًا:

class Shape
  def describe
    "area #{area}"
  end
end

class Square < Shape
  def area
    4
  end
end

class Triangle < Shape
  def area
    6
  end
end

[Square.new, Triangle.new].each { |s| puts s.describe }

الناتج:

area 4
area 6

to_s هو المثال اليومي: تستدعيه puts على أي كائن، وتجيب كل فئة بطريقة مختلفة. وتنضم فئاتك إلى ذلك من خلال تعريف to_s الخاص بها:

class Coin
  def to_s
    "a coin"
  end
end

[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }

الناتج:

[42]
[text]
[]
[[1, 2]]
[a coin]
challenge icon

التحدي

سهل

أكمل الأصناف. تُرجع Shape#to_s القيمة circle: 3.14، أي اسم الصنف بأحرف صغيرة والمساحة بمنزلتين عشريتين، ويوفّر كل صنف فرعي area: Circle.new(r) (استخدم 3.14159 للعدد π)، وRectangle.new(w, h) وTriangle.new(base, height).

يقرأ الكود المزوَّد أسطرًا مثل circle 1 أو rectangle 2 3 أو triangle 4 5، ويطبع كل شكل، ثم total: 22.14 وlargest: rectangle: 6.00، مع العمل باستخدام to_s وarea فقط.

ضع الكود الخاص بك في shape.rb وcircle.rb وrectangle.rb وtriangle.rb. يحتوي main.rb على كود الإدخال/الإخراج المزوَّد، ولا يمكن تعديله.

جرّب بنفسك

require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'

# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input = []
while (line = gets)
  input << line.chomp
end
kinds = { "circle" => Circle, "rectangle" => Rectangle, "triangle" => Triangle }
shapes = input.map do |line|
  kind, *numbers = line.split
  kinds[kind].new(*numbers.map(&:to_f))
end
shapes.each { |s| puts s }
puts format("total: %.2f", shapes.sum(&:area))
puts "largest: #{shapes.max_by(&:area)}"
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Ruby عبر الإنترنت