Menu
Coddy logo textTech

תבנית המצב

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C# של Coddy. שיעור 60 מתוך 70.

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

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

public interface IDocumentState
{
    void Publish(Document doc);
}

public class DraftState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Moving to moderation");
        doc.SetState(new ModerationState());
    }
}

public class ModerationState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Publishing document");
        doc.SetState(new PublishedState());
    }
}

public class PublishedState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Already published");
    }
}

ההקשר מחזיק את המצב הנוכחי ומאציל לו פעולות. אובייקטי מצב יכולים להפעיל מעברים באמצעות קריאה ל-SetState() בהקשר:

public class Document
{
    private IDocumentState _state = new DraftState();
    
    public void SetState(IDocumentState state) => _state = state;
    
    public void Publish() => _state.Publish(this);
}

var doc = new Document();
doc.Publish();  // מעבר לבדיקה
doc.Publish();  // פרסום המסמך
doc.Publish();  // כבר פורסם

תבנית State מבטלת הצהרות if-else או switch מסועפות שבודקות את המצב הנוכחי. כל מחלקת מצב מטפלת רק בהתנהגות שלה, וכך קל יותר לתחזק את הקוד ולהרחיב אותו באמצעות מצבים חדשים.

challenge icon

אתגר

קל

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

תארגנו את הקוד שלכם בשלושה קבצים:

  • TrafficLightState.cs: הגדירו ממשק ITrafficLightState במרחב השמות Traffic עם מתודה Change(TrafficLight light). לאחר מכן צרו שלוש מחלקות מצב קונקרטיות:
    • RedState - כשקוראים ל־Change, היא מדפיסה Red - Stop! Changing to Green... ומעבירה את הרמזור ל־GreenState
    • GreenState - מדפיסה Green - Go! Changing to Yellow... ומעבירה למצב YellowState
    • YellowState - מדפיסה Yellow - Caution! Changing to Red... ומעבירה למצב RedState
  • TrafficLight.cs: צרו מחלקה TrafficLight באותו מרחב שמות. זו מחלקת ההקשר שלכם, שמחזיקה את המצב הנוכחי. כברירת מחדל, היא צריכה להתחיל במצב RedState ולהכיל:
    • מתודה SetState(ITrafficLightState state) שאובייקטי מצב משתמשים בה כדי להפעיל מעברים
    • מתודה Change() שמעבירה את הקריאה למתודת Change של המצב הנוכחי
  • Program.cs: חברו את כל החלקים באמצעות יצירת אובייקט TrafficLight ומעבר בין מצבים בהתאם לקלט.

יתקבל קלט אחד:

  • מספר הפעמים לשינוי מצב הרמזור (מספר שלם)

צרו אובייקט TrafficLight (שמתחיל באור אדום) וקראו ל־Change() מספר הפעמים שצוין.

לדוגמה, אם הקלט הוא 3, הפלט יהיה:

Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...

אם הקלט הוא 5, הרמזור יחזור לתחילת המחזור:

Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...

שימו לב שמחלקת TrafficLight אינה מכילה לוגיקה לגבי משמעותו של כל צבע או מהו המצב הבא - כל הידע הזה נמצא במחלקות המצב הנפרדות. כל מצב אחראי להתנהגות שלו ולהפעלת המעבר למצב הבא!

נסו בעצמכם

using System;
using Traffic;

class Program
{
    public static void Main(String[] args)
    {
        // קרא את מספר הפעמים שיש לשנות את מצב הרמזור
        int n = Convert.ToInt32(Console.ReadLine());

        // TODO: צור מופע של TrafficLight (מתחיל ב-RedState כברירת מחדל)

        // TODO: קרא למתודה Change()‏ n פעמים באמצעות לולאה
    }
}
quiz iconבחנו את עצמכם

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

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

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