niedziela, 29 lipca 2012

[Wzorce projektowe] State

Analogia z życia:

Chcąc pobrać pieniądze idziemy do bankomatu. Po włożeniu karty i podaniu PINu możemy zgłosić żądanie pobrania pieniędzy. Jeśli spełnione jest kilka warunków, to maszyna wyda nam kwotę jakiej żądamy. Może się jednak zdarzyć tak, że nie mamy środków na koncie, albo nasze konto zostało zablokowane. Inną możliwością jest brak funduszy w maszynie. Każda z tych sytuacji jest osobnym stanem, który musi być rozpoznany i obsługiwany przez bankomat.





Zastosowanie:

W każdej sytuacji, gdy mamy do czynienia z obiektem, którego akcje zależą od stanu w jakim się znajduje. Stan obiektu jest zmieniany w czasie gdy aplikacja jest uruchomiona. Przejścia pomiędzy stanami niekonieczną muszą być dozwolone w pewnym kontekście, co też należy obsłużyć.

Zasada działania:

Głównym rdzeniem programu jest obiekt zmieniający stany dynamicznie. Stan reprezentuje się np. za pomocą enuma. Obiekt udostępnia metodę do zmiany stanu i sprawdzenia, czy dany stan można w danej chwili osiągnąć. Każdy stan implementuje jeden wspólny interfejs i udostępnia metody reprezentujące akcje.

Przykład implementacyjny:

    public interface ITvActions
    {
        void TurnOn();
        void TurnOff();
        void ChangeChannel(int n);
    }
public abstract class TVState
    {
        public Tv Owner { get; set; }

        protected TVState(Tv @object)
        {
            Owner = @object;
        }
    }
 public class RunningState : TVState, ITvActions
    {
        public RunningState(Tv @object) : base(@object)
        {
        }

        public void TurnOn()
        {
            Console.WriteLine("TV already running");
        }

        public void TurnOff()
        {
            Owner.IsRunning = false;
            Owner.SetState("Off");
            Console.WriteLine("Turned Off");
        }

        public void ChangeChannel(int n)
        {
            Owner.CurrentChannel = n;
            Console.WriteLine("Switched to " + n);
        }
    }
class TurnedOffState : TVState, ITvActions
    {
        public TurnedOffState(Tv @object) : base(@object)
        {
        }

        public void TurnOn()
        {
            Owner.SetState("Running");
            Owner.IsRunning = true;
            Console.WriteLine("Succesfully turned on");
        }

        public void TurnOff()
        {
            Console.WriteLine("Already turned off");
        }

        public void ChangeChannel(int n)
        {
            Console.WriteLine("Unable to change");
        }
    }
public class Tv : ITvActions
    {
        public ITvActions State { get; set; }
        public int CurrentChannel { get; set; }
        public bool IsRunning { get; set; }

        public Tv()
        {
            IsRunning = true;
            CurrentChannel = 1;
            State = new RunningState(this);
        }

        public void TurnOn()
        {
            State.TurnOn();
        }

        public void TurnOff()
        {
            State.TurnOff();
        }

        public void ChangeChannel(int n)
        {
            State.ChangeChannel(n);
        }

        public void SetState(string state)
        {
            switch (state)
            {
                case "Running":
                    State = new RunningState(this);
                    break;
                case "Off":
                    State = new TurnedOffState(this);
                    break;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Tv tv = new Tv();
            tv.TurnOff();
            tv.ChangeChannel(4);
            tv.TurnOn();
            tv.ChangeChannel(1);
            Console.ReadKey();
        }
    }

Brak komentarzy:

Prześlij komentarz