Oop 继承和责任

Oop 继承和责任,oop,inheritance,single-responsibility-principle,Oop,Inheritance,Single Responsibility Principle,当我读到关于继承的文章时,我总是对某个例子感到困惑 通常有一个与下面的例子类似的例子 class Shape { public: Shape() {} virtual ~Shape () {} virtual void Draw() = 0; }; class Cube : public Shape { public: Cube(){} ~Cube(){} virtual void Draw(); }; Shape* newCube = new C

当我读到关于继承的文章时,我总是对某个例子感到困惑

通常有一个与下面的例子类似的例子

class Shape
{
public:
    Shape() {}
    virtual ~Shape  () {}
    virtual void Draw() = 0;
};

class Cube : public Shape
{
public:
   Cube(){}
   ~Cube(){}
   virtual void Draw();
};

Shape* newCube = new Cube();
newCube->Draw(); 
我的问题是,
形状
为什么要自己画?渲染器类不应该负责知道如何绘制形状,而是向渲染器提供形状吗?如果我们想记录尺寸的变化呢?等在
Shape
中,我们会为这些不同的任务提供一种方法吗


看到无数这样的例子,我有时会怀疑自己是否有能力将责任分配给班级。关于类只有一个职责,有什么我不理解的吗?

OOP促进了发送消息,与“请求”一些外部数据然后处理的过程代码相反

如果将
draw
方法放置在渲染器中,则会破坏类
Shape
封装,因为它肯定需要访问其内部(如坐标(x,y)等)

通过让
Shape
绘制“自身”,您可以保持封装,从而提高内部更改的灵活性

解决方案实际上取决于复杂性。
通过从形状中提取绘制方法,形状将需要公开其数据。
通过保留它,您可以保持封装


因此,如果您的绘图很复杂,则更愿意将其视为渲染器或图形所承担的另一个整体责任,从而与您的建议相对应。

a
形状
应该不知道它是如何绘制的。设计的项目越大,这个决定就越关键

对我来说,归根结底,除了最尖锐的情况外,什么都不会引起,只会引起头痛

的一个基本原则是,你所做的事情(动词,或“视图”)与被操纵或分析的事情(名词,或“控制器”)明确分开:表示和逻辑分开。“模特”是中间人

它也是:“……每个类都应该有一个单独的责任,而这个责任应该完全由类封装”

其背后的原因是:循环依赖意味着对任何事物的任何更改都会影响一切

另一条(为简洁起见而编辑)引自单一责任原则:“一个类或模块应该有一个(而且只有一个)更改的原因。单一责任原则指出,问题的实质性方面和表面性方面实际上是两个独立的责任,因此应分为不同的类别或模块将两件因不同原因在不同时间发生变化的事情结合在一起将是一个糟糕的设计(我的重点)

最后,这一概念是:“目标是设计系统,使功能可以独立于其他功能进行优化,这样一个功能的故障不会导致其他功能的故障,并且总体上使理解、设计和管理复杂的相互依赖的系统变得更容易。”


这不仅仅是一个编程设计问题

考虑一下网站的开发,“内容”团队必须在一些脚本(由“开发”团队创建)周围非常温和地放置他们的文字、格式、颜色和图片内容团队不希望看到任何脚本——他们不想学习如何编程,只是为了能够更改一些单词或调整图像。开发团队也不想担心,不知道如何编写代码的人所做的每一次细微的视觉更改都有可能破坏它们的功能r的东西

我每天在处理自己的项目时都会想到这个概念。当两个源文件相互导入时,其中一个文件的更改都需要同时重新编译。对于更大的项目,这可能意味着一个微不足道的更改需要重新编译数百或数千个类。在三个主要项目中,我直接涉及到,其中有大约一千个不同的源代码文件,有这种循环依赖

无论是业务团队、源代码文件还是设计编程对象,除非绝对必要,否则我建议避免循环依赖


因此,至少,我不会将draw函数放在
形状中
。尽管非常依赖于正在设计的项目的类型和大小,但渲染可以通过
RenderingUtils
类完成,该类只包含完成大部分工作的公共静态函数

如果这个项目是一个中等规模的项目,我会更进一步,创建一个
可渲染的
界面作为模型层。一个
形状
实现了
可渲染的
,因此不知道或不关心它是如何绘制的。而无论画什么,都不需要知道
形状


这使您可以灵活地完全更改渲染方式,而不影响(或必须重新编译!)
形状,还允许您渲染与
形状
大不相同的内容,而无需更改绘图代码。

选择
绘图()
基类的方法取决于上下文——要解决的具体问题。为了让问题更清楚一点,我在采访OO技能时经常使用另一个例子

想象一个文档类和打印机类。打印功能应该放在哪里?有两个明显的选择:

document.print(Printer &p);

哪一个是正确的?答案是:这取决于你想要多态行为的位置——在文档中还是在打印机中。如果我们假设所有打印机都有相同的功能(一个神话)
printer.print(Document &d);