【问题标题】:Single responsability principle vs open close principle单一责任原则与开放封闭原则
【发布时间】:2018-11-17 00:30:37
【问题描述】:

我正在编写一个程序来向用户显示一组问题,收集他的回答并打印出来。

根据他们需要的回答类型,我有不同类型的问题:整数、布尔值或文本。

我开始写这段代码:

abstract class Question
{
    string text;
}

class IntegerQuestion : Question
{
    int response;
}

class TextQuestion : Question
{
    string response;
}

class BooleanQuestion : Question
{
    bool response;
}

好吧,现在我们必须打印问题和回答。

我的第一个方法是在 Question 类中定义一个新的抽象 Print 函数来强制子类定义 Print 方法,然后是 Printer 类:

abstract class Question
{
    string text;
    abstract string Print();
}

class Printer
{
    string PrintQuestions(List<Question> questions)
    {
        string result = "";

        foreach(var question in Questions)
            result += question.Print() + "\r\n";

        return result;
    }
}

我想到的另一种方法是原谅抽象方法并像这样创建 Printer 类:

class Printer
{
    string PrintQuestions(List<Question> questions)
    {
        string result = "";

        foreach(var question in Questions)
        {
            if(question is IntegerQuestion)
            {
                var integerQuestion = (IntegerQuestion)question;
                result += integerQuestion.text + integerQuestion.response;
            }
            if(question is TextQuestion)
            {
                ...
            }
            ...
        }

        return result;
    }
}

显然,第二种方法不遵循 OCP for Printer 类,而是首先遵循。

但是,SRP 呢?

如果那时我需要用 HTML 编写问题和回答:

abstract class Question
{
    string text;
    abstract string Print();
    abstract string PrintHTML();
}

class HTMLPrinter { ... }

¿问题子类不是因为知道如何以纯文本和 html 格式打印而违反 SRP 吗?

【问题讨论】:

    标签: solid-principles single-responsibility-principle open-closed-principle


    【解决方案1】:

    问题子类不违反 SRP,因为它们知道如何以纯文本和 html 形式打印它们

    你完全正确。

    首先,关于您的命名约定和设计,如果我理解您的演示,为什么答案会扩展 Question ?继承是对象之间的“是”关系。

    我们应该说答案就是问题吗?您的业​​务中似乎有两个不同的概念:

    • 包含问题的问题
    • Answer 保留用户对问题的回答

    我可能会做类似的事情:(对不起语法,这是某种伪代码)

    interface IAnswer{
        string toString();
    }
    class IntegerAnswer implements IAnswer{
        int answer;
        string toString(){
            return (string)this.answer;
        }
    }
    ....
    class Question{
        string text;
        IAnswer answer; //or List<IAnswer> answers if you can old more than one answer by Question
        string toString(){
            return this.text;
        }
    }
    

    然后,你可以定义一个打印机:

    interface IQuestionPrinter{
        string print(List<Question> questions);
    }
    class Printer implements IQuestionPrinter{
         string print(List<Question> questions){
              string res = '';
              foreach(question in questions){
                  res+=question.toString() + " : " + question.answer.toString();
              }
              return res;
         }
    }
    class HTMLPrinter implements IQuestionPrinter{
        string print(List<Question> questions){
              string res = "<ul>";
              foreach(question in questions){
                  res+="<li>";
                  res+= "<span>" + question.toString() + "</span>";
                  res+="<span>" + question.answer.toString()+"</span>;
                  res+="</li>";
              }
              return res+"</ul>";
         }
    }
    

    或者类似的东西。

    然后您的所有问题和答案都知道它们必须扩展 toString() 方法,我们将打印工作委托给专用的 IQuestionPrinter。

    制作答案界面很好,因为打印机不必知道答案是整数、布尔值、字符串还是其他。如果你有其他“类型”的问题,你应该定义一个接口 IQuestion :

    interface IQuestion{
        IAnswer answer; // or List<IAnswer> answers
        string toString();
    }
    

    然后 IQuestionPrinter 应该考虑它:

    interface IQuestionPrinter{
        string print(List<IQuestion> questions);
    }
    

    【讨论】:

    • 我喜欢这个。我没有考虑将答案和响应分成两个单独的类。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    相关资源
    最近更新 更多