Принцип единой ответственности против принципа открытого закрытия - PullRequest
0 голосов
/ 07 июня 2018

Я пишу программу, чтобы показать набор вопросов пользователю, собрать его ответы и распечатать их.

У меня есть разные типы вопросов в зависимости от того, какой ответ ему требуется: целое число, логическое значениеили текст.

Я начал писать этот код:

abstract class Question
{
    string text;
}

class IntegerQuestion : Question
{
    int response;
}

class TextQuestion : Question
{
    string response;
}

class BooleanQuestion : Question
{
    bool response;
}

Что ж, теперь мы должны напечатать вопросы и ответы.

Мой первый подход заключался в определении новогоабстрактная функция Print в классе Question, чтобы заставить подклассы определять метод 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 голосов
/ 07 июня 2018

Не являются подклассами вопросов, нарушающими SRP, потому что они знают, как печатать их в виде обычного текста и HTML

Вы совершенно правы.

Во-первых, в соответствии с вашим соглашением об именах и дизайном, если я понимаю вашу демонстрацию, почему ответы расширяются Question?Наследование - это отношение «есть» между объектом.

Должны ли мы сказать, что ответ - это вопрос?Похоже, что в вашем бизнесе есть две разные концепции:

  • Вопрос, на котором стоит вопрос
  • Ответ, на котором содержится ответ пользователя на вопрос

IВозможно, вы сделаете что-то вроде: (простите за синтаксис, это какой-то псевдокод)

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