SOLID和C#事件

时间:2015-09-01 06:22:52

标签: c# solid-principles

我目前正在学习SOLID(面向对象设计)并且有些麻烦:依赖性倒置原则,根据该原则,上层策略层应该能够符合较低层的界面,让我奇迹; event适合哪里?

例如(取自" C#中的敏捷原则,模式和实践" ):

此处,当按钮开启/关闭时,它会在turnOn上调用turnOff / ButtonServer,它会有一些参考。

现在,如果有几个对象依赖于一个按钮,为了使其工作(imo),该按钮必须存储ButtonServer的列表,然后调用每个对象#39; s turnOn / turnOff。 这让我觉得重新发明了event已经做过的事情。

现在,如果该按钮具有新状态,睡眠,我们必须创建一个新界面ButtonServerSleep(或其他名称),我们就是这样做的。 d必须存储每个ButtonServerSleep的新的不同列表,这将取决于按钮*。并且最终会编写相同类型的代码来循环遍历ButtonServer并调用turnOn / turnOff,而不是循环遍历ButtonServerSleep并调用sleepevent要避免的代码类型。
*如果这会打破SRP,请告诉我。

1 个答案:

答案 0 :(得分:1)

按钮和灯的示例很难理解,但我会尝试:

public class Button
{
   IButtonServer _buttonServer;
   private bool _amIOn;

   public Button(IButtonServer buttonServer)
   {
      _buttonServer = buttonServer;
   }
   void Poll()
   {
     _amIOn = !_amIOn;
     if(_amIOn) _buttonServer.TurnOn(); else _buttonServer.TurnOff();
   }
}

interface ITurnOnOffableDevice
{
   void TurnOn();
   void TurnOff();
}

interface IButtonServer : ITurnOnOffableDevice
{
   void RegisterDevice(ITurnOnOffableDevice l);
}

public Lamp : ITurnOffableDevice
{
   public Lamp(IButtonServer buttonServer)
   {
     buttonServer.RegisterDevice(this);
   }
   public void TurnOn()
   {
     Shine();
   }
   public void TurnOff()
   {
     Darken();
   } 
}

public MeatTriturator : ITurnOffableDevice
{
   public MeatTriturator(IButtonServer buttonServer)
   {
     buttonServer.RegisterDevice(this);
   }
   public void TurnOn()
   {
     Triturate();
   }
   public void TurnOff()
   {
     ShutItDown();
   } 
}

现在,在一些不了解灯具或肉类捣碎器的DLL上,你有这个:

public ButtonServer : IButtonServer
{
   private List<ITurnOnOffableDevice> _devices = new List<ITurnOnOffAbleDevice>();

   public void RegisterDevice(ITurnOnOffAbleDevice l)
   {
     _devices.Add(l);
   }
   public void TurnOn()
   {
     foreach(var l in _devices) { l.TurnOn(); }
   }
   public void TurnOff()
   {
     foreach(var l in _devices) { l.TurnOff(); }
   }
}

然后你创造了一切:

public static IButtonServer MyLampAndTrituratorButtonServer;

static void Prepare()
{
   DependencyInject.ForType<IButtonServer>.Create<ButtonServer>();
}

static void Main()
{
   Prepare();

   MyLampAndTrituratorButtonServer = DependencyInject.CreateObject<IButtonServer>();

   // The MyLampAndTrituratorButtonServer could be injected directly in the
   // constructor of these objects, but for clarity I've left the normal
   // object declaration

   // this button will turn on lamps and meat triturators
   var button = new Button(MyLampAndTrituratorButtonServer);

   // these lamp and meat tritutator will be turned on/off by
   // the buttons in that server
   var lamp = new Lamp(MyLampAndTrituratorButtonServer);
   var meatTriturator = new MeatTriturator(MyLampAndTrituratorButtonServer);
}

然后在其他地方,在你的程序深处,你需要创建一个新的按钮点亮那个灯和肉咀嚼器,你只需这样做:

   var button = new Button(MyLampAndTrituratorButtonServer);

对实际灯泡或肉类捣碎器没有任何参考。

(或者更好的是:在构造函数参数上注入IButtonServer依赖项时创建按钮(这取决于你的依赖注入器,所以我不给代码),但是你会既不需要引用MyLampAndTrituratorButtonServer

通过这种方式,您可以将按钮与灯具或肉类捣碎器分离。关闭和打开的操作由按钮服务器完成,您可以这样做 see是注入的,所以它可以是任何东西(任何实现IButtonServer的东西)。

因此,您已将开启和关闭设备的责任委派给一个依赖项。

同样,在“现实世界”中,按钮往往只做一件事,而灯只能关闭和打开,这在未来可能会改变......但这是将责任委托给单点。

但是让我们更进一步......想象一下你的要求已经改变了,打开/关闭灯具和肉类捣碎器(只有那些,而不是所有按钮)的按钮需要有一个安全措施。无论何时打开它们,它们都需要在30秒内自动关闭。

使用“基于事件的方法”,您需要从“按钮”(即ButtonThatTurnsOffAt30secs : Button)派生一个对象,并在每次创建灯或肉咀嚼器的地方进行更改,然后重新编译。

使用隔离的依赖项,您只需重写按钮服务器:

public ButtonServerThatTurnsOffAt30seconds : IButtonServer
{
   private List<ITurnOnOffableDevice> _devices = new List<ITurnOnOffAbleDevice>();

   public void RegisterDevice(ITurnOnOffAbleDevice l)
   {
     _devices.Add(l);
   }
   public void TurnOn()
   {
     foreach(var l in _devices) { l.TurnOn(); new Timer(30, () => { TurnOff(); } }
   }
   public void TurnOff()
   {
     foreach(var l in _devices) { l.TurnOff(); }
   }
}

并改变你的依赖注入:

DependencyInject.ForType<IButtonServer>.Create<ButtonServerThatTurnsOffAt30seconds>();

如果你已经正确地分离了你的项目,你甚至不需要重新编译整个应用程序,只需要一次DLL更改(或者如果你有一个不同的依赖注入器引导程序那么两个)和voilá,现在你所有的灯和肉类捣碎器在30秒后关闭。

同样,这不是最好的例子,它使得一个例子很难,但我希望你可以效仿。

作为免责声明:我试图在依赖性隔离上创造一个值,而不是为了解释SOLID原则或任何模式,因为你似乎对此感到困惑。我自己并没有“盲目地遵循模式”,我只是找到了我对所有这些模式感兴趣的东西,并将其用于一个很好的目的。

如果你确定你的按钮永远不会改变(或者全部你的按钮会同时改变),那么就不需要所有这些了。是否全力以赴,或者是否应用GTD( Getting Things Done )原则取决于您。

应用模式,解耦依赖关系等需要花费时间和精力,并且根据项目,截止日期,预算以及未来变化的可能性,它们值得实施与否。