用C#学习单一责任原则

Gua*_*apo 55 c# single-responsibility-principle solid-principles

我正在努力学习单一责任原则(SRP),但这是非常困难的,因为我很难弄清楚我应该从一个班级中删除的时间和内容,以及我应该放置/组织它的地方.

我正在谷歌搜索一些材料和代码示例,但我找到的大多数材料,而不是让它更容易理解,使它很难理解.

例如,如果我有一个用户列表,并且从该列表中我有一个叫做控制的类,可以执行很多事情,例如当用户进/出时发送问候和再见消息,验证用户应该能够进入的天气并踢他,接收用户命令和消息等.

从示例中你不需要太多了解我已经在一个类中做了太多但是我不清楚如何在之后拆分和重新组织它.

如果我理解SRP,我会有一个加入频道的课程,问候和再见,一个用户验证课程,一个阅读命令的课程,对吧?

但是我在哪里以及如何使用踢球?

我有验证课程,所以我相信我会在那里进行各种用户验证,包括天气或者不应该踢用户.

因此,kick函数将位于通道连接类中,并在验证失败时被调用?

例如:

public void UserJoin(User user)
{
    if (verify.CanJoin(user))
    {
        messages.Greeting(user);
    }
    else
    {
        this.kick(user);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你们可以借助易于理解的C#在线和免费资料,或者通过向我展示如何分割引用的示例以及可能的一些示例代码,建议等,我将不胜感激.

Bro*_*ass 60

让我们从单一责任原则(SRP)的实际含义开始:

一堂课应该只有一个改变的理由.

这实际上意味着每个对象(类)应该有一个责任,如果一个类有多个职责,这些职责成为耦合,并且不能独立执行,在一个即变化会影响甚至攻破对方在特定的实现.

必须阅读的是源本身(来自"敏捷软件开发,原则,模式和实践"的 pdf章节):单一责任原则

话虽如此,你应该设计你的课程,这样他们理想情况下只做一件事,做好一件事.

首先想想你所拥有的"实体",在我看到的例子中User,Channel以及它们之间通信的媒介("消息").这些实体之间有某种关系:

  • 用户拥有他加入的多个频道
  • 频道有许多用户

这也自然导致以下功能列表:

  • 用户可以请求加入频道.
  • 用户可以向他加入的频道发送消息
  • 用户可以离开频道
  • 频道可以拒绝或允许用户的加入请求
  • 频道可以踢用户
  • 频道可以向频道中的所有用户广播消息
  • 频道可以向频道中的各个用户发送问候消息

SRP是一个重要的概念,但很难独立 - 对您的设计同样重要的是依赖倒置原则(DIP).为并入到设计记住,你的特定实现User,MessageChannel实体应取决于抽象或接口,而不是一个特定的具体实施.出于这个原因,我们首先设计接口而不是具体类:

public interface ICredentials {}

public interface IMessage
{
    //properties
    string Text {get;set;}
    DateTime TimeStamp { get; set; }
    IChannel Channel { get; set; }
}

public interface IChannel
{
    //properties
    ReadOnlyCollection<IUser> Users {get;}
    ReadOnlyCollection<IMessage> MessageHistory { get; }

    //abilities
    bool Add(IUser user);
    void Remove(IUser user);
    void BroadcastMessage(IMessage message);
    void UnicastMessage(IMessage message);
}

public interface IUser
{
    string Name {get;}
    ICredentials Credentials { get; }
    bool Add(IChannel channel);
    void Remove(IChannel channel);
    void ReceiveMessage(IMessage message);
    void SendMessage(IMessage message);
}
Run Code Online (Sandbox Code Playgroud)

这个列表没有告诉我们的是这些功能的执行原因什么.我们最好将"为什么"(用户管理和控制)的责任放在一个单独的实体中 - 这样,如果"为什么"改变User,Channel实体就不必改变.我们可以在这里利用策略模式和DI,并且可以具有IChannel依赖于IUserControl给我们"为什么" 的实体的任何具体实现.

public interface IUserControl
{
    bool ShouldUserBeKicked(IUser user, IChannel channel);
    bool MayUserJoin(IUser user, IChannel channel);
}

public class Channel : IChannel
{
    private IUserControl _userControl;
    public Channel(IUserControl userControl) 
    {
        _userControl = userControl;
    }

    public bool Add(IUser user)
    {
        if (!_userControl.MayUserJoin(user, this))
            return false;
        //..
    }
    //..
}
Run Code Online (Sandbox Code Playgroud)

你看,在上面的设计中,SRP甚至不是完美的,即IChannel仍然依赖于抽象IUserIMessage.

最后,人们应该努力实现灵活,松散耦合的设计,但总是需要进行权衡,灰色区域也取决于您希望应用程序发生变化的位置.

在我看来,SRP走向了极端,导致了非常灵活但也是碎片化和复杂的代码,这些代码可能不像更简单但更紧密耦合的代码那样容易理解.

事实上,如果两个职责总是在同一时间发生变化,你可以说不应该将它们分成不同的类别,因为这会引起马丁的"不必要的复杂气味".对于永不改变的责任也是如此 - 行为是不变的,没有必要拆分它.

这里的主要想法是,你应该做一个主观判断,你看到的责任/行为可能在未来,它的行为是相互依存相互独立改变,总是在同一时间("在臀部捆绑")改变什么行为永远不会改变.


And*_*ray 21

我很容易学习这个原理.它呈现给我三个小的,一口大小的部分:

  • 做一件事
  • 只做那件事
  • 做那件事

符合这些标准的准则符合单一责任原则.

在上面的代码中,

public void UserJoin(User user)
{
  if (verify.CanJoin(user))
  {
    messages.Greeting(user);
  }
  else
  {
    this.kick(user);
  }
}
Run Code Online (Sandbox Code Playgroud)

UserJoin不符合SRP; 它正在做两件事,即如果用户可以加入则问候用户,或者如果他们不能加入则拒绝他们.重新组织方法可能更好:

public void UserJoin(User user)
{
  user.CanJoin
    ? GreetUser(user)
    : RejectUser(user);
}

public void Greetuser(User user)
{
  messages.Greeting(user);
}

public void RejectUser(User user)
{
  messages.Reject(user);
  this.kick(user);
}
Run Code Online (Sandbox Code Playgroud)

从功能上讲,这与最初发布的代码没有什么不同.但是,这段代码更易于维护; 如果出现新的业务规则,由于最近的网络安全攻击,您想记录被拒绝用户的IP地址,该怎么办?您只需修改RejectUser方法即可.如果您想在用户登录时显示其他消息怎么办?只需更新方法GreetUser.

根据我的经验,SRP可以实现可维护的代码.可维护的代码往往会在很长的路要走,以实现SOLID的其他部分.

  • 抱歉花了一年时间回复.如果你只看理论,你的答案是100%正确的; 但是,在_writing_代码的实际操作中,它只有一半,如果那样的话.可以证明工作良好的模块化小方法往往优于容纳更多失败机会的复杂方法.通过构建用于单一目的的类和方法,您更有可能拥有无错误,健壮和_testable_代码.虽然以这种方式呈现给我的人可能是"错误的",但我自己多年来的经历不同意SRP只适用于课程. (4认同)