扩展@kara 的答案,以下代码实现了独立的Stomach 和Brain 对象,并使用Being 将它们连接起来。
Being 知道什么Stomach:
Being 知道什么Brain
- 有一个
OnRaiseIsHungryEvent(即“饥饿”信号——谁在乎它来自哪里)
- 它有一个
IsHungryEvent
请记住,在实际实现中,可能会有其他对象在监听这些事件。例如也许你有一个会切换到“饥饿”的情绪系统和一个基于目标的人工智能会切换到觅食模式。两个系统都不需要知道对方,但都可以响应来自Brain 的信号。在这个简单的实现中,Being 响应Stomach 信号,同时通知和响应Brain。
重要的一点不是引发和响应事件的特定方法(在这种情况下是默认的 .Net 机制),而是两个对象都不知道另一个对象的内部结构(参见不同的实现) HumanStomach 和 ZombieStomach),而是以更合适的级别连接连接(在这种情况下为 Being)。还要注意对接口的依赖,它允许我们做一些事情,比如创建混合生物(即将ZombieBrain 与HumanStomach 配对)。
代码是使用 .Net Core CLI 作为控制台应用程序编写/测试的,但它应该与大多数 .Net > 3.5 版本兼容。
using System;
using System.Linq;
using System.Threading;
namespace so_example
{
public class Program
{
static void Main(string[] args)
{
var person1 = new Being("Human 1", new HumanBrain(), new HumanStomach());
var zombie1 = new Being("Zombie 1", new ZombieBrain(), new ZombieStomach());
var hybrid1 = new Being("Hybrid 1", new ZombieBrain(), new HumanStomach());
var hybrid2 = new Being("Hybrid 2", new HumanBrain(), new ZombieStomach());
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
public class HungryEventArgs : EventArgs
{
public string Message { get; set; }
}
public interface IStomach
{
event EventHandler<HungryEventArgs> NeedsFoodEvent;
}
public class Stomach : IStomach
{
public event EventHandler<HungryEventArgs> NeedsFoodEvent;
protected virtual void OnRaiseNeedsFoodEvent(HungryEventArgs e)
{
EventHandler<HungryEventArgs> handler = NeedsFoodEvent;
if (handler != null)
{
handler(this, e);
}
}
}
public class HumanStomach : Stomach
{
private Timer _hungerTimer;
public HumanStomach()
{
_hungerTimer = new Timer(o =>
{
// only trigger if breakfast, lunch or dinner (24h notation)
if (new [] { 8, 13, 19 }.Any(t => t == DateTime.Now.Hour))
{
OnRaiseNeedsFoodEvent(new HungryEventArgs { Message = "I'm empty!" });
}
else
{
Console.WriteLine("It's not mealtime");
}
}, null, 1000, 1000);
}
}
public class ZombieStomach : Stomach
{
private Timer _hungerTimer;
public ZombieStomach()
{
_hungerTimer = new Timer(o =>
{
OnRaiseNeedsFoodEvent(new HungryEventArgs { Message = "Need brains in stomach!" });
}, null, 1000, 1000);
}
}
public interface IBrain
{
event EventHandler<HungryEventArgs> IsHungryEvent;
void OnRaiseIsHungryEvent();
}
public class Brain : IBrain
{
public event EventHandler<HungryEventArgs> IsHungryEvent;
protected string _hungryMessage;
public void OnRaiseIsHungryEvent()
{
EventHandler<HungryEventArgs> handler = IsHungryEvent;
if (handler != null)
{
var e = new HungryEventArgs
{
Message = _hungryMessage
};
handler(this, e);
}
}
}
public class HumanBrain : Brain
{
public HumanBrain()
{
_hungryMessage = "Need food!";
}
}
public class ZombieBrain : Brain
{
public ZombieBrain()
{
_hungryMessage = "Braaaaaains!";
}
}
public class Being
{
protected readonly IBrain _brain;
protected readonly IStomach _stomach;
private readonly string _name;
public Being(string name, IBrain brain, IStomach stomach)
{
_stomach = stomach;
_brain = brain;
_name = name;
_stomach.NeedsFoodEvent += (s, e) =>
{
Console.WriteLine($"{_name}: {e.Message}");
_brain.OnRaiseIsHungryEvent();
};
_brain.IsHungryEvent += (s, e) =>
{
Console.WriteLine($"{_name}: {e.Message}");
};
}
}
}
一些注意事项
为了提供一些输出,我在 2 个IStomach 实现中伪造了一些东西。 HumanStomach 在构造函数中创建一个计时器回调,它每 1 秒触发一次,并检查当前时间是否是用餐时间。如果是,则引发NeedsFoodEvent。 ZombieStomach 也每 1 秒使用一次回调,但它每次都会触发 NeedsFoodEvent。在真正的 Unity 实现中,您可能会根据来自 Unity 的某些事件触发偶数 - 玩家在预设时间后采取的动作等。