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

نمط الحالة

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

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

class Red
  def next_state
    Green.new
  end

  def to_s
    "red"
  end
end

class Green
  def next_state
    Yellow.new
  end

  def to_s
    "green"
  end
end

class Yellow
  def next_state
    Red.new
  end

  def to_s
    "yellow"
  end
end

class TrafficLight
  def initialize
    @state = Red.new
  end

  def change
    @state = @state.next_state
    self
  end

  def to_s
    "light is #{@state}"
  end
end

light = TrafficLight.new
puts light
puts light.change
puts light.change.change

الناتج:

light is red
light is green
light is red

من دون النمط، تحتاج كل طريقة في السياق إلى case على قيمة الحالة، وتعني إضافة حالة جديدة تعديلها جميعًا. ومع كائنات الحالة، تكون كل حالة فئة واحدة تحتوي على قواعدها الخاصة:

# بدون النمط
def can_go?
  case @status
  when :red then false
  when :green then true
  when :yellow then false
  end
end

# مع النمط
def can_go?
  @state.can_go?
end

يمكن للحالات رفض حدث لا يناسبها. هنا لا يمكن نشر المسودة مباشرةً؛ بل يجب أن تمر بالمراجعة أولًا:

class Draft
  def submit
    Review.new
  end

  def publish
    raise ArgumentError, "cannot publish a draft"
  end

  def to_s
    "draft"
  end
end

class Review
  def publish
    Published.new
  end

  def to_s
    "in review"
  end
end

class Published
  def to_s
    "published"
  end
end

state = Draft.new
begin
  state.publish
rescue ArgumentError => e
  puts e.message
end
state = state.submit
state = state.publish
puts state

الناتج:

cannot publish a draft
published

يمكن لفئة أساسية مشتركة أن تمنح كل حالة الإجابة الافتراضية، مثل رفض كل حدث، بحيث تعرّف كل حالة الأحداث التي تقبلها فقط:

class State
  def submit
    raise ArgumentError, "cannot submit #{self}"
  end

  def approve
    raise ArgumentError, "cannot approve #{self}"
  end
end

class Review < State
  def approve
    Published.new
  end
end
challenge icon

التحدي

سهل

أكمل إشارة المرور. تحتوي الحالات Red وGreen وYellow كلٌّ منها على next_state (تنتقل الحمراء إلى الخضراء، والخضراء إلى الصفراء، والصفراء إلى الحمراء)، وcan_go? (الخضراء فقط) وto_s (اللون). تبدأ TrafficLight باللون الأحمر، وتنقل change إلى الحالة التالية، وتُرجع status القيمة red: stop أو green: go، وذلك بالاستعلام عن الحالة.

يقرأ الكود المُزوَّد الأوامر change أو status ويطبع الحالة بعد كل أمر.

ضع الكود الخاص بك في red.rb وgreen.rb وyellow.rb وtraffic_light.rb. يحتوي main.rb على كود الإدخال/الإخراج المُزوَّد، ولا يمكن تعديله.

جرّب بنفسك

require_relative 'red'
require_relative 'green'
require_relative 'yellow'
require_relative 'traffic_light'

# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input = []
while (line = gets)
  input << line.chomp
end
light = TrafficLight.new
input.each do |cmd|
  light.change if cmd == "change"
  puts light.status
end
quiz iconاختبر نفسك

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

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

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