【问题标题】:C# Implement Observable PatternC# 实现可观察模式
【发布时间】:2019-09-17 09:54:52
【问题描述】:

我正在尝试使用 C# 实现可观察模式。在我的示例代码中,我有两种士兵弓箭手两个类: ArcherSwordsman 他们实现了 Soldier 接口。 Soldier 接口有四种方法:

  • Attack() - 命令士兵攻击敌人
  • Died() - 此方法在该示例中无关紧要
  • Kill() - 命令我们的士兵杀死敌人
  • BattleCry() - 在敌人被杀后庆祝所有我的士兵

还有一个属性 bool IsEnemyKilled - 当 Kill() 方法被调用时,IsEnemyKilled 变为 true。

这就是我想要做的: 我知道要实现观察者模式,我需要提供者和观察者。 当一名士兵例如弓箭手 - 杀死敌人archer.Kill();IsEnemyKilled 成为真的(这是我的提供者)和我所有的其他士兵(我的观察员),例如必须通知剑客和其他弓箭手 IsEnemyKilled 为真,并且他们必须调用 BattleCry()。

我很困惑该怎么做。如果有人提出想法,我将不胜感激。这是我的示例代码。

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();
    }
}

【问题讨论】:

  • 我很困惑如何做到这一点。如果有人提出想法,我将不胜感激”并不是一个真正的问题。您似乎正在寻找一些辅导/指导,但是 StackOverflow 不是合适的地方(它是一个 QuestionAnswer 站点)。互联网上应该有很多关于如何在 C# 中以某种方式实现观察者模式的信息(包括您可以查看的代码示例......)

标签: c# design-patterns observer-pattern observers


【解决方案1】:

您需要将主题(某个士兵)附加到观察者(某个其他士兵)。
为了实现这一点,我首先在士兵界面中添加了三个新成员:

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

这里的事件是通知主体并触发它是使用属性的setter实现的。我将属性更改如下:

private bool isEnemyKilled;

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

AttachDetach的实现如下:

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

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

由于我看到当您必须为两个士兵实施此操作时出现很多重复,请考虑将 Soldierinterface 更改为 abstract class

完成所有这些后,您需要将(所有)士兵连接在一起(当然根据您想要的游戏逻辑)。
跟踪所有士兵的一种方法是在(您现在的抽象类)Soldier 中添加一个static List<Soldier>,每个士兵在创建后添加自己。但是你可以在那里做任何你想做的事情。

这些只是一些想法,并不是成熟的观察者模式。既然你问我想提出一些想法。希望它能让你走上正确的轨道。

另一个提示:如果您只需要属性IsEnemyKilled 来通知其他人,您可以简单地将其省略并直接调用事件EnemyKilled,而不是将IsEnemyKilled 设置为true

【讨论】:

    【解决方案2】:

    我建议将订阅者(观察者)添加到您的 povider(弓箭手)。 这样一来,您的弓箭手就会有士兵订阅弓箭手的攻击。

    我们来分解一下。

    你的弓箭手应该有他的观察者:

    public class Archer : Soldier
        {
            bool IsEnemyKilled;
            private List<Soldirer> soldiers = new List<Soldier>();
    
            public void Attack()
    ...
    

    现在让我们通知胜利:

        public void Attack()
        {
            IsEnemyKilled = false;
            Console.WriteLine("Archer attack!");
    
            soldiers.foreach(soldier => soldier.BattleCry());
        }
    

    最后,让我们订阅我们的士兵:

    public class Archer : Soldier {
    ...
        public void subscribe(Soldier) {
                soldiers.add(soldier);
        }
    }
    
    static void Main(string[] args)
            {
    
                var archer = new Archer();
                var swordsman = new Swordsman();
            ...
    

    您可以(并且应该)向您的 Soldier 类添加一个 alert 方法,这样您就可以调用它而不是 BattleCry 方法。

    我发现以下文章就是一个很好的例子: https://exceptionnotfound.net/the-daily-design-pattern-observer/

    【讨论】:

    • 也不错。但是,一旦 IsEnemyKilled 可以通过多种方式进行更改,这将不再起作用。
    • @Joelius 是的,你是对的,但在更复杂的情况下,我相信正确的解决方案是添加一个用于编排不同情况的类专用方法,也许是handleDeath()。或者事件可能添加一个Battle 类来分离关注点
    猜你喜欢
    • 2013-10-28
    • 2015-01-30
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多