c# - 用C#学习单一职责原则

标签 c# solid-principles single-responsibility-principle

我正在尝试学习单一职责原则 (SRP),但这非常困难,因为我很难弄清楚我应该在什么时候从一个类(class)中删除什么以及应该删除/组织它的位置。

我在谷歌上搜索了一些 Material 和代码示例,但我发现的大多数 Material 并没有让它更容易理解,而是让它变得难以理解。

For example if I have a list of Users and from that List I have a class Called Control that does lots of things like Send a greeting and goodbye message when a user comes in/out, verify weather the user should be able to enter or not and kick him, receive user commands and messages, etc.

从示例中您不需要太多了解我已经对一个类做了太多的工作,但我还不清楚之后如何拆分和重组它。

如果我理解 SRP,我会有一个加入 channel 的类,一个用于问候和再见的类,一个用于用户验证的类,一个用于读取命令的类,对吧?

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

我有验证类,所以我确信我会在那里进行各种用户验证,包括天气是否应该踢用户。

所以 kick 函数将在 channel join 类中,并在验证失败时调用?

例如:

public void UserJoin(User user)
{
    if (verify.CanJoin(user))
    {
        messages.Greeting(user);
    }
    else
    {
        this.kick(user);
    }
}

如果你们能帮助我提供易于理解的在线免费 C# Material ,或者向我展示如何拆分引用的示例,并在可能的情况下提供一些示例代码、建议等,我将不胜感激。

最佳答案

让我们从 Single Responsibility Principle 的作用开始(SRP) 实际上意味着:

一个类应该只有一个改变的理由。

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

绝对必须阅读的是源代码本身(来自 "Agile Software Development, Principles, Patterns, and Practices" 的 pdf 章节):The Single Responsibility Principle

话虽如此,您应该设计您的类,以便它们在理想情况下只做一件事并且做好一件事。

首先想想你有什么“实体”,在你的例子中我可以看到 UserChannel 以及它们之间进行通信的媒介(“消息”) . 这些实体之间有一定的关系:

  • 一个用户有多个他加入的 channel
  • 一个 channel 有多个用户

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

  • 用户可以请求加入 channel 。
  • 用户可以向他加入的 channel 发送消息
  • 用户可以离开 channel
  • channel 可以拒绝或允许用户的加入请求
  • channel 可以踢用户
  • channel 可以向 channel 内的所有用户广播一条消息
  • channel 可以向 channel 内的各个用户发送问候消息 channel

SRP 是一个重要的概念,但很难独立存在——对您的设计同样重要的是 Dependency Inversion Principle (蘸)。要将其纳入设计,请记住您对 UserMessageChannel 实体的特定实现应依赖于抽象 或接口(interface)而不是特定的具体实现。出于这个原因,我们从设计接口(interface)而不是具体类开始:

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

此列表没有告诉我们执行这些功能的原因。我们最好将“为什么”(用户管理和控制)的责任放在一个单独的实体中——这样 UserChannel 实体不必更改“为什么”改变。我们可以在这里利用策略模式和 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;
        //..
    }
    //..
}

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

最终,人们应该努力实现一种灵活、松散耦合的设计,但总是需要权衡取舍,灰色区域也取决于您期望应用程序更改的位置。

在我看来,采用极端的 SRP 会导致非常灵活但也零散和复杂的代码,这些代码可能不像更简单但耦合更紧密的代码那样容易理解。

事实上,如果两项职责总是预计会同时发生变化,您可以说不应该将它们分成不同的类别,因为引用 Martin 的话,这会导致“不必要的复杂性” .永不改变的职责也是如此——行为是不变的,不需要拆分。

这里的主要思想是,你应该在你看到责任/行为在未来可能独立改变的地方做出判断,哪些行为是相互依赖的,并且总是会同时改变(“绑定(bind)在hip”)以及哪些行为从一开始就永远不会改变。

关于c# - 用C#学习单一职责原则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7542051/

相关文章:

c# - 覆盖一个增变器列表来改变它的子类型

c# - 非常冗长的存储过程状态

c# - 强烈命名第三方程序集后出现构建错误。

iOS Offline 第一个应用单一责任问题

c# - 获取序列化异常 : '<>f__AnonymousType2` is not marked as serializable

oop - 违反了哪些 SOLID 原则?

open-closed-principle - 当业务逻辑发生变化时,如何遵守开闭原则?

java - 开闭和接口(interface)隔离

java - 单一职责原则的重构方法

oop - 单一职责原则 - 一个很难看到的例子?