单一责任原则与公开关闭原则

时间:2018-06-07 07:30:56

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

我正在撰写一个程序,向用户展示一系列问题,收集他的回复并打印出来。

我有不同类型的问题,具体取决于他们需要的响应类型:整数,布尔值或文本。

我开始编写这段代码:

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 { ... }

¿不是问题子类违反SRP,因为他们知道如何以纯文本和HTML格式打印它们吗?

1 个答案:

答案 0 :(得分:1)

  

Aren没有质疑违反SRP的子类,因为他们知道如何以纯文本和html打印它们

你是对的。

首先,根据您的命名惯例和设计,如果我理解您的演示,为什么答案会延伸Question?继承是一个&#34;是一个&#34;对象之间的关系。

我们应该说答案是个问题吗?您的业​​务看起来有两个不同的概念:

  • 提出问题的问题
  • 回答用户对问题的回答

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

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。

制作答案界面是好的,因为打印机不必知道答案是整数,布尔或字符串还是随便。如果你有其他&#34;类型&#34;问题,你应该定义一个界面IQuestion:

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

然后IQuestionPrinter应该考虑它:

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