c# - 哪个优先 : Don't Repeat Yourself or the Single Responsibility Principle?

标签 c# oop dry single-responsibility-principle

在升级一些旧代码时,我发现这两个 OO 原则似乎相互冲突。

考虑以下伪代码(这是我遇到的简化版本):

int numberOfNewRecords;
int numberOfOldRecords;
int numberOfUndefinedRecords;

void ColourAndCount()
{
    foreach(Row row in dataGridView1.Rows)
    {
        switch(row.Cells["Status"])
        {
            case: "Old"
                row.BackColor = Color.Red;
                numberOfOldRecords++;
                break;
            case: "New"
                row.BackColor = Color.Blue;
                numberOfNewRecords++;
                break;
            default:
                row.BackColor = Color.White;
                numberOfUndefinedRecords++;
                break;
        }
    }
}

这段代码做了两件事:它根据记录的状态计算记录的数量,它还根据它们的状态为每一行着色。这很困惑,但由于这两个操作(到目前为止)总是同时被调用,所以它没有造成任何问题,并且使维护要求(如额外的状态)易于添加。

尽管如此,单一职责原则告诉我应该将其拆分为两个单独的方法:

(编辑)小注:我刚刚意识到我可能在这里误用了术语“单一职责原则”,据我所知,它指的是类。 “一个方法一个操作”设计模式的术语是什么?

int numberOfNewRecords;
int numberOfOldRecords;
int numberOfUndefinedRecords;

void Count()
{
    foreach(Row row in dataGridView1.Rows)
    {
        switch(row.Cells["Status"])
        {
            case: "Old"
                numberOfOldRecords++;
                break;
            case: "New"
                numberOfNewRecords++;
                break;
            default:
                numberOfUndefinedRecords++;
                break;
        }
    }
}

void Colour()
{
    foreach(Row row in dataGridView1.Rows)
    {
        switch(row.Cells["Status"])
        {
            case: "Old"
                row.BackColor = Color.Red;
                break;
            case: "New"
                row.BackColor = Color.Blue;
                break;
            default:
                row.BackColor = Color.White;
                break;
        }
    }
}

但是这违反了不要重复自己:loop 和 switch 语句在两种方法中都是重复的,并且由于此代码最有可能的升级路径是添加其他 Status,因此它使 future 的升级更加困难而不是更少。

我无法找出最优雅的方式来重构它,所以我觉得最好问问社区,以防我遗漏了一些明显的东西。你会如何处理这种情况?

(编辑)

我想出了一个可能的解决方案,但在我看来它像是过度设计一个简单问题的例子(而且它并没有真正解决最初的单一职责问题)。

struct Status
{
    public string Name,
    public int Count,
    public Color Colour,
}

Dictionary<string, Status> StatiiDictionary = new Dictionary<string, int>();
void Initialise()
{
    StatiiDictionary.Add(new Status("New", 0, Color.Red));
    StatiiDictionary.Add(new Status("Old", 0, Color.Blue));
    StatiiDictionary.Add(new Status("Undefined", 0, Color.White));
}

void ColourAndCountAllRows()
{
    foreach(Row row in dataGridView1.Rows)
    {
        CountRow(row, StatiiDictionary);
        ColourRow(row, StatiiDictionary);
    }
}

void CountRow(Row row, Dictionary<string, Status> StatiiDictionary)
{
    StatiiDictionary[row.Cells["Status"]].Count++; 
}

void ColourRow(Row row, Dictionary<string, Status> StatiiDictionary)
{
    row.BackColour = StatiiDictionary[row.Cells["Status"]].Colour;
}

最佳答案

出于实用的原因,需要不时违反许多编程规则。在你的情况下,我同意 DRY 和 SRP 似乎在竞争,所以我会建议两个标准来决定获胜者:

  1. 代码遵守每一个意味着什么。
  2. 应用程序遵守每一个意味着什么。

在这种情况下,两次枚举网格行的低效率对我来说似乎是最重要的因素,而 DRY 将胜出在这种特殊情况下。在其他一些情况下,情况可能恰恰相反。

添加注释来解释您所做的决定以及原因是值得的,这样以后查看代码的任何人都会清楚。这正是应该使用注释的那种事情,即为什么代码正在做它正在做的事情,而不仅仅是它正在做的事情。

关于c# - 哪个优先 : Don't Repeat Yourself or the Single Responsibility Principle?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23097222/

相关文章:

c# - 泛型类中的数组和索引器

class - 如何访问/读取 Octave 中的类实例属性?

ruby-on-rails - 如何优雅地对 'nested' 哈希值进行 symbolize_keys

ruby-on-rails - 如何干燥这段代码?

c# - 将超链接添加到 SSIS 中的电子邮件正文

c# - 如何实现 VaryByCustom 缓存?

c# - 将 onmouseover 事件分配给我的 htmldocument 不起作用

php - 命名空间类、函数和如何使用

c# - 如何在 C# 中的接口(interface)上进行多次分派(dispatch)?

javascript - 如何优化我的 Jquery 代码?