این سایت برای ie9 طراحی نشده است

لطفا دستگاه خود را بچرخانید.

الگوی طراحی برنامه نویسی State

۲۰ خرداد ۱۳۹۶ حسین صداقت
بدون دیدگاه

به نام خدا

همانطور که در مطلب قبل اشاره کردیم در این مطلب قصد بررسی الگوی طراحی  برنامه نویسی State را داریم.این Desgin Pattern این قابلیت رو به ما می دهد که ما بتوانیم بر اساس تغییر حالت داخلی یک آبجکت عملکرد های متفاوتی برای متد های آن آبجکت داشته باشیم.. نمودار دیاگرام این الگو به صورت زیر می باشد:

الگوی طراحی یا Design Pattern State

مثال : فرض کنید ما یک دستگاه ATM یا خودپرداز پول داریم و بانک بر اساس ۳ وضعیت زیر سناریو های مختلفی را طراحی کرده است:

۱-زمانی که کارت به دستگاه وارد نشده است.

۲-اعتبار سنجی کارت وارد شده انجام شده است

۳-برداشت وجه از خودپرداز

در ادامه با هم به بررسی نحوه پیاده سازی این سناریو می پردازیم.

ابتدای امر ما یک کلاس Abstract به اسم ATMState را به صورت زیر پیاده سازی می کنیم:

public abstract class ATMState
{
    private ATM atm;

    public ATM Atm
    {
        get { return atm; }
        set { atm = value; }
    }
    private int dummyCashPresent = 1000;

    public int DummyCashPresent
    {
        get { return dummyCashPresent; }
        set { dummyCashPresent = value; }
    }

    public abstract string GetNextScreen();
}

 

این کلاس دارای یک Property از جنس کلاس ATM می باشد( که در ادامه کلاس ATM را نیز بررسی خواهیم کرد).همچنین دارای یک فیلد با نام dummyCashPresent  می باشد که نشان دهنده موجودی ابتدایی می باشد و مقدار اولیه آن ۱۰۰۰ ست شده است.همچنین ساختار متد GetNextScreen نیز در این کلاس تعریف شده است.

حالا می خواهیم که با استفاده از کلاس ATMState به پیاده سازی سناریو های مختلفی که بانک برای این دستگاه در نظر  گرفته است بپردازیم.اولین وضعیتی که قصد پیاده سازی آن را داریم وضعیت NO_CARD می باشد.پس یک کلاس به اسم NoCardState ایجاد کرده که از کلاس ATMState ارث برده و سپس آن را به صورت زیر پیاده سازی می کنیم:

class NoCardState : ATMState
{
    // This constructor will create new state taking values from old state
    public NoCardState(ATMState state)     
        :this(state.DummyCashPresent, state.Atm)
    {
        
    }

    // this constructor will be used by the other one
    public NoCardState(int amountRemaining, ATM atmBeingUsed)
    {
        this.Atm = atmBeingUsed;
        this.DummyCashPresent = amountRemaining;
    }

    public override string GetNextScreen()
    {            
        Console.WriteLine("Please Enter your Pin");
        string userInput = Console.ReadLine();

        // lets check with the dummy pin
        if (userInput.Trim() == "1234")
        {
            UpdateState();
            return "Enter the Amount to Withdraw";
        }

        // Show only message and no change in state
        return "Invalid PIN";
    }

    private void UpdateState()
    {
        Atm.currentState = new CardValidatedState(this);
    }
}

 

این کلاس دارای دو Constructor می باشد.Constructor اول که ورودی آن یک شی از جنس ATMState می باشد در هنگام تغییر وضعیت مورد استفاده قرار می گیرد.Constructor دوم نیز برای ست کردن کلاس ATM فعلی و موجودی فعلی دستگاه مورد استفاده است.همچنین این کلاس متد GetNextScreen را همانطور که  در بالا مشاهده می کنید Override و بازنویسی کرده است.آخرین متد این کلاس هم متد UpdateState است.این متد در متد قبلی یعنی GetNextScreen مورد استفاده قرار گرفته و وظیفه آن تغییر State با استفاده از وضعیت کلاس فعلی می باشد.

ساختار پیاده سازی State های دیگر هم دقیقا شبیه حالت NoCardState می باشد با این تفاوت که منطق اجرایی هر حالت با استفاده از Override کردن متد GetNextScreen  متفاوت شده است و همچنین در صورت نیاز به تغیر وضعیت می توانند باعث از متد UpdateState این کار را انجام دهند.پیاده سازی State های دیگر به صورت می باشد :

CardValidateState :

class CardValidatedState : ATMState
{
    // This constructor will create new state taking values from old state
    public CardValidatedState(ATMState state)    
        :this(state.DummyCashPresent, state.Atm)
    {
        
    }

    // this constructor will be used by the other one
    public CardValidatedState(int amountRemaining, ATM atmBeingUsed)
    {
        this.Atm = atmBeingUsed;
        this.DummyCashPresent = amountRemaining;
    }

    public override string GetNextScreen()
    {
        string userInput = Console.ReadLine();

        int requestAmount;
        bool result = Int32.TryParse(userInput, out requestAmount);

        if (result == true)
        {
            if (this.DummyCashPresent < requestAmount)
            {
                // Show only message and no change in state
                return "Amount not present";
            }

            this.DummyCashPresent -= requestAmount;
            UpdateState();

            return string.Format(@"Amount of {0} has been withdrawn. Press Enter to proceed", requestAmount);
        }

        // Show only message and no change in state
        return "Invalid Amount";
    }

    private void UpdateState()
    {        
        Atm.currentState = new NoCashState(this);        
    }
}

CASH_WITHDRAWN :

class CashWithdrawnState : ATMState
{
    // This constructor will create new state taking values from old state
    public CashWithdrawnState(ATMState state)      
        :this(state.DummyCashPresent, state.Atm)
    {
        
    }

    // this constructor will be used by the other one
    public CashWithdrawnState(int amountRemaining, ATM atmBeingUsed)
    {
        this.Atm = atmBeingUsed;
        this.DummyCashPresent = amountRemaining;
    }

    public override string GetNextScreen()
    {
        UpdateState();
        return string.Format("Thanks you for using us, Amount left in ATM: {0}", this.DummyCashPresent.ToString());
    }

    private void UpdateState()
    {
        Atm.currentState = new NoCardState(this);
    }
}

NO_CASH :

class NoCashState : ATMState
{
    // This constructor will create new state taking values from old state
    public NoCashState(ATMState state)      
        :this(state.DummyCashPresent, state.Atm)
    {
        
    }

    // this constructor will be used by the other one
    public NoCashState(int amountRemaining, ATM atmBeingUsed)
    {
        this.Atm = atmBeingUsed;
        this.DummyCashPresent = amountRemaining;
    }

    public override string GetNextScreen()
    {            
        Console.WriteLine("ATM is EMPTY");
        Console.ReadLine();
        return string.Empty;
    }

    private void UpdateState()
    {
        // nothing here as someone will have to fill in cash and then
        // restart the atm, once restarted it will be in no card state
    }
}

 

 

در نهایت کلاس ATM را پیاده سازی می کنیم که این کلاس با استفاده از متد StartTheAtm وظیفه اجرای کد های نوشته شده و استارت دستگاه ATM را بر عهده دارد.همچنین در  Constructor این کلاسState اولیه ، NoState با مقدار اولیه ۱۰۰۰ ست می شود.

در مطلب بعدی با هم الگوی طراحی برنامه نویسی Strategy را بررسی خواهیم کرد.