单一职责原则 vs 开闭原则
Single responsability principle vs open close principle
我正在编写一个程序来向用户显示一系列问题,收集他的回答并打印出来。
我有不同类型的问题,具体取决于他们需要的回答类型:整数、布尔值或文本。
我开始写这段代码:
abstract class Question
{
string text;
}
class IntegerQuestion : Question
{
int response;
}
class TextQuestion : Question
{
string response;
}
class BooleanQuestion : Question
{
bool response;
}
好了,现在我们要打印问题和答案了。
我的第一个方法是在问题class中定义一个新的抽象打印函数来强制子classes定义打印方法,然后是一个打印机class:
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;
}
}
我想到的另一种方法是忽略抽象方法并像这样创建打印机 class:
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 class,而是首先遵循。
但是,SRP 呢?
如果那我需要在HTML中写问题和回复:
abstract class Question
{
string text;
abstract string Print();
abstract string PrintHTML();
}
class HTMLPrinter { ... }
不质疑 subclass 是否违反了 SRP,因为他们知道如何以纯文本格式打印它们 html?
Aren't question subclasses violating SRP because they know how to print them in plain text and html
你完全正确。
首先,关于您的命名约定和设计,如果我理解您的演示,为什么答案扩展 Question
?继承是对象之间的"Is a"关系。
我们应该说 Answer 是 Question 吗?您的业务中似乎有两种不同的概念:
- 问题中的问题
- 答案包含用户对问题的回答
我可能会做类似的事情:(抱歉语法,它是某种伪代码)
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。
制作 Answer 界面很好,因为 Printer 不必知道 Answer 是 Integer、Boolean 还是 String 或其他。如果你有其他 "types" 的问题,你应该定义一个接口 IQuestion :
interface IQuestion{
IAnswer answer; // or List<IAnswer> answers
string toString();
}
然后 IQuestionPrinter 应该考虑它:
interface IQuestionPrinter{
string print(List<IQuestion> questions);
}
我正在编写一个程序来向用户显示一系列问题,收集他的回答并打印出来。
我有不同类型的问题,具体取决于他们需要的回答类型:整数、布尔值或文本。
我开始写这段代码:
abstract class Question
{
string text;
}
class IntegerQuestion : Question
{
int response;
}
class TextQuestion : Question
{
string response;
}
class BooleanQuestion : Question
{
bool response;
}
好了,现在我们要打印问题和答案了。
我的第一个方法是在问题class中定义一个新的抽象打印函数来强制子classes定义打印方法,然后是一个打印机class:
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;
}
}
我想到的另一种方法是忽略抽象方法并像这样创建打印机 class:
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 class,而是首先遵循。
但是,SRP 呢?
如果那我需要在HTML中写问题和回复:
abstract class Question
{
string text;
abstract string Print();
abstract string PrintHTML();
}
class HTMLPrinter { ... }
不质疑 subclass 是否违反了 SRP,因为他们知道如何以纯文本格式打印它们 html?
Aren't question subclasses violating SRP because they know how to print them in plain text and html
你完全正确。
首先,关于您的命名约定和设计,如果我理解您的演示,为什么答案扩展 Question
?继承是对象之间的"Is a"关系。
我们应该说 Answer 是 Question 吗?您的业务中似乎有两种不同的概念:
- 问题中的问题
- 答案包含用户对问题的回答
我可能会做类似的事情:(抱歉语法,它是某种伪代码)
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。
制作 Answer 界面很好,因为 Printer 不必知道 Answer 是 Integer、Boolean 还是 String 或其他。如果你有其他 "types" 的问题,你应该定义一个接口 IQuestion :
interface IQuestion{
IAnswer answer; // or List<IAnswer> answers
string toString();
}
然后 IQuestionPrinter 应该考虑它:
interface IQuestionPrinter{
string print(List<IQuestion> questions);
}