草庐IT

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

coder 2023-07-08 原文

我正在尝试学习单一职责原则 (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#学习单一职责原则的更多相关文章

  1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  2. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  5. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  6. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  7. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  8. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

  9. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  10. ruby - 我可以使用 RVM 为所有用户维护单一版本的 Ruby 吗? - 2

    我喜欢RVM。我意识到它的主要用例是让不同的用户在不同版本的Ruby之间切换。但是假设我正在将Rails应用程序部署到服务器,并且我只想运行单个版本的Ruby。特别是,我想要1.9.2,用RVM安装它很容易,但没有它就很痛苦。有没有一种方法可以让我说“我希望这是所有用户的规范Ruby安装”(连同它的所有gem),而不必手动创建一堆符号链接(symboliclink)并在每次更新到更新时更改它们Ruby版本? 最佳答案 以root身份安装RVM并执行sudorvmuse1.9.2--default。任何采购/usr/local/rvm

随机推荐