DEV Community 👩‍💻👨‍💻

Cover image for Behavioral Design Patterns In C#
Bardia Mostafavi
Bardia Mostafavi

Posted on

Behavioral Design Patterns In C#

if you are new , I highly recommend you to read my first and second articles which are about Creational Design Patterns and Structural Design Patterns In C#.

As we explained in earlier post , we have three main categories in Design Patterns :

  1. Creational
  2. Structural
  3. Behavioral

Behavioral Design Patterns mainly focus on the communication between objects and the interactions between them by algorithm and their responsibilities.

Chain of Responsibility

this pattern is use to passing request along multiple handler, so it prevents from coupling request to handle by one handler.

    public interface IHandler
    {
        public IHandler SetNext(IHandler handler);

        public object Handle(object input);
    }

    public class Handler : IHandler
    {
        private IHandler _handler;

        public IHandler SetNext(IHandler handler)
        {
            _handler = handler;

            return handler;
        }

        public virtual object Handle(object input)
        {
            return _handler?.Handle(input);
        }
    }

    public class HandlerA : Handler
    {
        public override object Handle(object input)
        {
            if (input as string == "A")
            {
                Console.WriteLine("HandlerA said : gotcha you ! that's enough go back");
                return true;
            }

            Console.WriteLine("HandlerA said : i can not do anything calling next handler");
            return base.Handle(input);
        }
    }

    public class HandlerB : Handler
    {
        public override object Handle(object input)
        {
            if (input as string == "B")
            {
                Console.WriteLine("HandlerB said : gotcha you ! that's enough go back");
                return true;
            }

            Console.WriteLine("HandlerB said : i can not do anything calling next handler");
            return base.Handle(input);
        }
    }

    public class HandlerC : Handler
    {
        public override object Handle(object input)
        {
            if (input as string == "C")
            {
                Console.WriteLine("HandlerC said : gotcha you ! that's enough go back");
                return true;
            }

            Console.WriteLine("HandlerC said : chain is useless for you , this is end of the road bro.");
            return base.Handle(input);
        }
    }

    public static class ChainOfResponsibilityExample
    {
        public static void Test()
        {
            var handlerA = new HandlerA();
            var handlerB = new HandlerB();
            var handlerC = new HandlerC();

            handlerA.SetNext(handlerB).SetNext(handlerC);

            var resultOne = handlerA.Handle("A");
            var resultTwo = handlerA.Handle("B");
            var resultThree = handlerA.Handle("C");
            var resultFour = handlerA.Handle("D");
        }

        //results:
        //
        // resultOne :
        // HandlerA said : gotcha you ! that's enough go back
        //
        // resultTwo :
        // HandlerA said : i can not do anything calling next handler
        // HandlerB said : gotcha you ! that's enough go back
        //
        // resultThree :
        // HandlerA said : i can not do anything calling next handler
        // HandlerB said : i can not do anything calling next handler
        // HandlerC said : gotcha you ! that's enough go back
        //
        // resultFour :
        // HandlerA said : i can not do anything calling next handler
        // HandlerB said : i can not do anything calling next handler
        // HandlerC said : chain is useless for you , this is end of the road bro.
Enter fullscreen mode Exit fullscreen mode

Command

this pattern is use to transform request to a object so you can delay or queue or parameterize request. and do undoable operations.

    public interface ICommand
    {
        public void Execute();
    }

    public class ExampleCommand : ICommand
    {
        private readonly string _parameter;

        public ExampleCommand(string parameter)
        {
            _parameter = parameter;
        }

        public void Execute()
        {
            Console.WriteLine(_parameter);
        }
    }

    public static class Invoker
    {
        public static void SendAction(ICommand command)
        {
            command.Execute();
        }
    }

    public static class CommandExample
    {
        public static void Test()
        {
            var command = new ExampleCommand("Query filter setup and executed");

            Invoker.SendAction(command);
        }

        //results:
        //Query filter setup and executed
    }
Enter fullscreen mode Exit fullscreen mode

Iterator

its main goal is to traverse elements of collection without exposing them.

    public abstract class IteratorBase
    {
        public abstract bool EndOfDocument();
        public abstract object Current();
        public abstract object Next();
        public abstract object First();
    }

    public class Iterator : IteratorBase
    {
        private readonly List<object> _customList;
        private int current = 0;

        public Iterator(List<object> customList)
        {
            _customList = customList;
        }

        public override bool EndOfDocument()
        {
            if (current >= _customList.Count - 1)
                return true;
            return false;
        }

        public override object Current()
        {
            return _customList[0];
        }

        public override object Next()
        {
            if (current < _customList.Count - 1)
                return _customList[++current];

            return null;
        }

        public override object First()
        {
            return _customList[0];
        }
    }

    public static class IteratorExample
    {
        public static void Test()
        {
            var ourList = new List<object>() {"a", "b", "c", "d"};

            var iterator = new Iterator(ourList);

            Console.WriteLine("lets Iterate on list");

            var item = iterator.First();
            while (item != null)
            {
                Console.WriteLine(item);
                item = iterator.Next();
            }

            if (iterator.EndOfDocument())
                Console.WriteLine("Iterate done");
        }

        //results:
        // lets Iterate on list
        // a
        // b
        // c
        // d
        // Iterate done
    }
Enter fullscreen mode Exit fullscreen mode

Interpreter

it is use for get different type and behavior on same context , and usually use in language scenario in software design.

    internal class Context
    {
        public string Value { get; private set; }

        public Context(string value)
        {
            Value = value;
        }
    }

    internal abstract class Interpreter    
    {    
        public abstract void Interpret(Context context);    
    }

    internal class EnglishInterpreter : Interpreter
    {
        public override void Interpret(Context context)
        {
            switch (context.Value)
            {
                case "1" :
                    Console.WriteLine("One");
                    break;
                case "2" : 
                    Console.WriteLine("Two");
                    break;
            }
        }
    }

    internal class FarsiInterpreter : Interpreter
    {
        public override void Interpret(Context context)
        {
            switch (context.Value)
            {
                case "3" :
                    Console.WriteLine("سه");
                    break;
                case "4" : 
                    Console.WriteLine("چهار");
                    break;
            }
        }
    }

    public static class InterpreterExample
    {
        public static void Test()
        {
            var interpreters = new List<Interpreter>()
            {
                new EnglishInterpreter(),
                new FarsiInterpreter()
            };

            var context = new Context("2");

            interpreters.ForEach(c => c.Interpret(context));
        }

        //results:
        //two
    }
Enter fullscreen mode Exit fullscreen mode

Mediator

the goal of this pattern is to decoupling direct communication between objects and forces them to pass from a mediator object for communicating.

    public interface IMediator
    { 
        public void Send(string message, Caller caller);
    }

    public class Mediator : IMediator
    {
        public CallerA CallerA { get; set; }
        public CallerB CallerB { get; set; }

        public void Send(string message, Caller caller)
        {
            if (caller.GetType() == typeof(CallerA))
            {
                CallerB.ReceiveRequest(message);
            }
            else
            {
                CallerA.ReceiveRequest(message);
            }
        }
    }

    public abstract class Caller
    {
        protected readonly IMediator _mediator;

        public Caller(IMediator mediator)
        {
            _mediator = mediator;
        } 
    }

    public class CallerA : Caller
    {
        public void SendRequest(string msg)
        {
           _mediator.Send(msg,this);
        }

        public void ReceiveRequest(string msg)
        {
            Console.WriteLine("CallerA Received : " + msg);
        }

        public CallerA(IMediator mediator) : base(mediator)
        {

        }
    }

    public class CallerB : Caller
    {
        public void SendRequest(string msg)
        {
            _mediator.Send(msg,this);
        }

        public void ReceiveRequest(string msg)
        {
            Console.WriteLine("CallerB Received : " + msg);
        }

        public CallerB(IMediator mediator) : base(mediator)
        {

        }
    }

    public static class MediatorExample
    {
        public static void Test()
        {
            var mediator = new Mediator();
            var callerA = new CallerA(mediator);
            var callerB = new CallerB(mediator);

            mediator.CallerA = callerA;
            mediator.CallerB = callerB;

            callerA.SendRequest("hello how are you ?");
            callerB.SendRequest("fine tnx");


        }

        //results:
        //CallerB Received : hello how are you ?
        //CallerA Received : fine tnx
    }
Enter fullscreen mode Exit fullscreen mode

Memento

this pattern is use for saving state of object with considering state encapsulation. so you can restores object's first state even after multiple manipulation.

    public class Memento
    {
        private readonly string _state;

        public Memento(string state)
        {
            _state = state;
        }

        public string GetState()
        {
            return _state;
        }
    }

    public class CareTaker
    {
        private Memento _memento;

        public void SaveMemento(Originator originator)
        {
            _memento = originator.CreateMemento();
        }

        public void RestoreMemento(Originator originator)
        {
            originator.RestoreState(_memento);
        }
    }


    public class Originator
    {
        public string State { get; set; }

        public Originator(string state)
        {
            State = state;
        }

        public Memento CreateMemento()
        {
            return new Memento(State);
        }

        public void RestoreState(Memento memento)
        {
            State = memento.GetState();
        }
    }

    public static class MementoExample
    {
        public static void Test()
        {
            var originator = new Originator("First Value");

            var careTaker = new CareTaker();

            careTaker.SaveMemento(originator);

            Console.WriteLine(originator.State);

            originator.State = "Second Value";

            Console.WriteLine(originator.State);

            careTaker.RestoreMemento(originator);

            Console.WriteLine(originator.State);
        }

        //results:
        //First Value
        //Second Value
        //First Value
    }
Enter fullscreen mode Exit fullscreen mode

Observer

it is use to handle one to many communication between objects , so if one object's state changed , its dependent objects will notify and will update.

    public class Updater
    {
        public string NewState { get; }

        private readonly List<ObserverBase> _observers = new List<ObserverBase>();

        public Updater(string newState)
        {
            NewState = newState;
        }

        public void AddObserver(ObserverBase observerBase)
        {
            _observers.Add(observerBase);
        }

        public void BroadCast()
        {
            foreach (var observer in _observers)
            {
                observer.Update();
            }
        }
    }

    public abstract class ObserverBase
    {
        public abstract void Update();
    }

    public class Observer : ObserverBase
    {
        private readonly string _name;
        public string State;
        private readonly Updater _updater;

        public Observer(string name, string state, Updater updater)
        {
            _name = name;
            State = state;
            _updater = updater;
        }

        public override void Update()
        {
            State = _updater.NewState;
            Console.WriteLine($"Observer {_name} State Changed to : " + State);
        }
    }

    public static class ObserverExample
    {
        public static void Test()
        {
            var updater = new Updater("Fire");

            updater.AddObserver(new Observer("1", "dust", updater));
            updater.AddObserver(new Observer("2", "water", updater));
            updater.AddObserver(new Observer("3", "air", updater));

            updater.BroadCast();
        }

        //results:
        //Observer 1 State Changed to : Fire
        //Observer 2 State Changed to : Fire
        //Observer 3 State Changed to : Fire
    }
Enter fullscreen mode Exit fullscreen mode

State

with this pattern object can change its behavior whenever its state changed.

    internal interface IState
    {
        public void Handle(Context context);
    }

    internal class StateA : IState
    {
        public void Handle(Context context)
        {
            context.State = new StateB();
        }
    }

    internal class StateB : IState
    {
        public void Handle(Context context)
        {
            context.State = new StateA();
        }
    }

    internal class Context
    {
        private IState _state;
        public IState State
        {
            get => _state;
            set
            {
                _state = value;
                Console.WriteLine("State: " + _state.GetType().Name);
            }
        }

        public Context(IState state)
        {
            State = state;
        }

        public void Action()
        {
            State.Handle(this);
        }
    }

    public static class StateExample
    {
        public static void Test()
        {
            var context = new Context(new StateA());

            context.Action();
            context.Action();
            context.Action();
            context.Action();
        }

        //results:
        //State: StateA
        //State: StateB
        //State: StateA
        //State: StateB
        //State: StateA
    }
Enter fullscreen mode Exit fullscreen mode

Strategy

this pattern is use to encapsulate family of algorithm and makes them interchangeable . so they can independently change without any tight coupling.

    internal interface IStrategy
    {
        public void AlgorithmAction();
    }

    internal class AlgorithmStrategyA : IStrategy
    {
        public void AlgorithmAction()
        {
            Console.WriteLine("Strategy A is taking place");
        }
    }

    internal class AlgorithmStrategyB : IStrategy
    {
        public void AlgorithmAction()
        {
            Console.WriteLine("Strategy B is taking place");
        }
    }

    internal class Context
    {
        private readonly IStrategy _strategy;

        public Context(IStrategy strategy)
        {
            _strategy = strategy;
        }

        public void GeneralAction()
        {
            _strategy.AlgorithmAction();
        }
    }

    public static class StrategyExample
    {
        public static void Test()
        {
            var context = new Context(new AlgorithmStrategyA());

            context.GeneralAction();

            context = new Context(new AlgorithmStrategyB());

            context.GeneralAction();
        }

        //results:
        //Strategy A is taking place
        //Strategy B is taking place

    }
Enter fullscreen mode Exit fullscreen mode

Template Method

in simple word , this pattern includes number of operations on base class and it allows subclasses to only override some of the them.

    internal abstract class TemplateBase
    {
        public void Operate()
        {
            FirstAction();
            SecondAction();
        }

        private void FirstAction()
        {
            Console.WriteLine("First action from template base and it is necessary");
        }

        protected virtual void SecondAction()
        {
            Console.WriteLine("Second action from template base and it is overrideable");
        }
    }

    internal class  TemplateMethodA : TemplateBase
    {

    }

    internal class  TemplateMethodB : TemplateBase
    {
        protected override void SecondAction()
        {
            Console.WriteLine("Second action from templateMethodC");
        }
    }

    internal class  TemplateMethodC : TemplateBase
    {
        protected override void SecondAction()
        {
            Console.WriteLine("Second action from templateMethodC");
        }
    }

    public static class TemplateMethod
    {
        public static void Test()
        {
            var templateMethodA = new TemplateMethodA();
            var templateMethodB = new TemplateMethodB();
            var templateMethodC = new TemplateMethodC();

            templateMethodA.Operate();
            templateMethodB.Operate();
            templateMethodC.Operate();

        }

        //results:
       //First action from template base and it is necessary
       //Second action from template base and it is overrideable

       //First action from template base and it is necessary
       //Second action from templateMethodB

       //First action from template base and it is necessary 
       //Second action from templateMethodC
    }
Enter fullscreen mode Exit fullscreen mode

Visitor

the main goal is to add new behavior to class hierarchy without any changing in their code.

    internal interface IVisitor
    {
        public void VisitItem(ItemBase item);
    }

    internal class VisitorA : IVisitor
    {
        public void VisitItem(ItemBase item)
        {
            Console.WriteLine("{0} visited by {1}",item.GetType().Name , this.GetType().Name);
        }
    }

    internal class VisitorB : IVisitor
    {
        public void VisitItem(ItemBase item)
        {
            Console.WriteLine("{0} visited by {1}",item.GetType().Name , this.GetType().Name);
        }
    }

    internal abstract class ItemBase
    {
        public abstract void Accept(IVisitor visitor);
    }

    internal class ItemA : ItemBase
    {
        public override void Accept(IVisitor visitor)
        {
            visitor.VisitItem(this);
        }

        public void ExtraOperationA()
        {

        }
    }

    internal class ItemB : ItemBase
    {
        public override void Accept(IVisitor visitor)
        {
            visitor.VisitItem(this);
        }

        public void ExtraOperationB()
        {

        }
    }

    internal class StructureBuilder
    {
        readonly List<ItemBase> _items = new();
        public void AddItem(ItemBase element)
        {
            _items.Add(element);
        }
        public void Accept(IVisitor visitor)
        {
            foreach (var item in _items)
            {
                item.Accept(visitor);
            }
        }
    }

    public static class VisitorExample
    {
        public static void Test()
        {
            var structure = new StructureBuilder();

            structure.AddItem(new ItemA());
            structure.AddItem(new ItemB());


            structure.Accept(new VisitorA());
            structure.Accept(new VisitorB());
        }

        //results:
        //ItemA visited by VisitorA
        //ItemB visited by VisitorA
        //ItemA visited by VisitorB
        //ItemB visited by VisitorB
    }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Good news! You can update to dark mode in your DEV settings.