Search code examples
c#design-patternsobserver-patternobservers

C# Implement Observable Pattern


I`m trying to implement observable pattern using C#. In my sample code I have two kind of soldiers archer two classes: Archer and Swordsman they implement Soldier interface. Soldier interface has has four methods:

  • Attack() - command the soldier to attack the enemy
  • Died() - this method doesn`t matter in that example
  • Kill() - command our soldier to kill the enemy
  • BattleCry() - celebration of ALL my soldiers after emeny is killed

and one property bool IsEnemyKilled - when Kill() method is called IsEnemyKilled becomes true.

And here is what I want to do: I know that to implement Observer Pattern I need Provider and Observers. When one of soldiers e.g. archer - kills an enemy archer.Kill();. IsEnemyKilled become true (this is my provider) and all my other soldiers (my observers) e.g. swordsman and another archer must be notified that IsEnemyKilled is true and they must call BattleCry().

I`m confused how to do it. I will appreciate if anyone suggest in idea. Here is my sample code.

namespace ImplementObservable
{
    class Program
    {
        static void Main(string[] args)
        {

            var archer = new Archer();
            var swordsman = new Swordsman();
            archer.Attack();
            archer.Kill();
            Console.ReadKey();
        }
    }

    public class Archer : Soldier
    {
        bool IsEnemyKilled;
        // watch somehow prop "IsEnemyKilled" and if it changes to true call BattleCry() for all units

        public void Attack()
        {
            IsEnemyKilled = false;
            Console.WriteLine("Archer attack!");
        }

        public void Died()
        {
            IsEnemyKilled = false;
            Console.WriteLine("Archer died :(");
        }

        public void Kill()
        {
            IsEnemyKilled = true;
            Console.WriteLine("Archer killed enemy! Hurray!!");
        }

        public void BattleCry()
        {
            Console.WriteLine("Archer: Go for victory !");
        }
    }

    public class Swordsman : Soldier
    {
        bool IsEnemyKilled;
        // watch somehow prop "IsEnemyKilled" and if it changes to true call BattleCry() for all units

        public void Attack()
        {
            IsEnemyKilled = false;
            Console.WriteLine("Swordsman attack!");
        }

        public void Died()
        {
            IsEnemyKilled = false;
            Console.WriteLine("Swordsman died :(");
        }
        public void Kill()
        {
            IsEnemyKilled = true;
            Console.WriteLine("Swordsman killed enemy! Hurray!!");
        }

        public void BattleCry()
        {
            Console.WriteLine("Swordsman: Go for victory !");
        }
    }

    public interface Soldier
    {
        void Kill();

        void Attack();

        void Died();

        void BattleCry();
    }
}

Solution

  • You need to attach the Subject (some soldier) to the observer (some other soldier).
    To achieve this I first added three new members to the Soldier-Interface:

    event Action EnemyKilled;
    void Attach(Soldier observer);
    void Detach(Soldier observer);
    

    The event here is to notify the subject and firing it is achieved using the setter of the property. I changed the property as follows:

    private bool isEnemyKilled;
    
    private bool IsEnemyKilled {
        get => isEnemyKilled;
        set {
            isEnemyKilled = value;
            if(isEnemyKilled) EnemyKilled?.Invoke();
        }
    }
    

    The implementation of Attach and Detach are as follows:

    public void Attach(Soldier observer)
    {
        observer.EnemyKilled += BattleCry;
    }
    
    public void Detach(Soldier observer)
    {
        observer.EnemyKilled -= BattleCry;
    }
    

    Since I see a lot of repetition when you have to implement this for both Soldiers, consider changing Soldier from an interface to an abstract class.

    When you've done all that, you will need to attach (all) the Soldiers together (of course according to your desired game-logic).
    One way of keeping track of all Soldiers would be a static List<Soldier> in (your now abstract class) Soldier where each Soldier adds himself, once created. But you can do whatever you want there.

    These are just some ideas and are not a full blown observer-pattern. Since you asked for ideas I wanted to throw in some. Hope it brings you on the right track.

    Another tip: if you only need the property IsEnemyKilled to notify the others, you can simply leave it out and invoke the event EnemyKilled directly instead of setting IsEnemyKilled to true.