形状
不应该知道它如何被绘制。设计的项目越大,这个决定就越关键。
对我来说,这归结于循环依赖,在除了最边缘的情况下,只会带来麻烦。
模型-视图-控制器的基本原则是,你所做的事情(动词或“视图”)与被操作或分析的东西(名词或“控制器”)明确分离:表现层与逻辑层分离,“模型”扮演中间人角色。
这也是单一职责原则:“每个类都应该有一个单一职责,且该职责应完全封装在该类中。”
原因在于:循环依赖意味着任何更改都会影响所有内容。
单一职责原则的另一个摘录(缩短版):“一个类或模块应该有一个且仅有一个变化的原因。单一职责原则认为,问题的实质和表面方面是两个不同的责任,因此应该在不同的类或模块中处理。将因为不同原因在不同时间而发生变化的两件事耦合在一起是一种糟糕的设计。”(重点是我的)
最后,关注点分离的概念:“目标是设计系统,使得函数可以独立于其他函数进行优化,从而一个函数的失败不会导致其他函数失败,并且通常使得理解、设计和管理复杂的相互依赖的系统更加容易。”(重点是我的)
这不仅仅是一个编程设计问题。
考虑网站的开发,"内容"团队必须将他们的文字、格式、颜色和图片非常谨慎地放置在一些脚本周围(由"开发"团队创建),只有这样才能保证一切正常。内容团队希望完全看不到脚本--他们不想学习如何编程,只是为了改变一些单词或微调一张图片。而开发团队也不想担心每一个由不懂编码的人所做出的微小视觉变化都有可能破坏他们的工作。
当我在自己的项目上工作时,我每天都会思考这个概念。当两个源文件相互引用时,任何一个文件的更改都需要同时重新编译两个文件。对于大型项目来说,这可能意味着微小的更改需要重新编译数百或数千个类。在我目前参与的三个主要项目中,大约有一千个不同的源代码文件,其中只有
一个这种循环依赖关系。
无论是商业团队、源代码文件还是设计编程对象,循环依赖都是我建议避免的,除非绝对必要。
因此,至少我不会将绘图函数放在
Shape
中。虽然非常依赖于正在设计的项目的类型和大小,但渲染可以通过一个
RenderingUtils
类完成,该类仅包含执行大部分工作的公共静态函数。
如果项目规模较大,我会进一步创建
Renderable
接口作为模型层。
Shape
实现
Renderable
,因此不知道或不关心如何绘制。而进行绘制的任何内容都不需要了解
Shape
。
这使您能够完全更改如何进行渲染,而不影响(或不必重新编译!)
Shape
,并且还允许您呈现与
Shape
截然不同的东西,而无需更改绘图代码。
Document
应该有一个打印函数。文档只是某个东西而已,它没有任何操作。打印机是一种接受数字文档并以不同形式输出(纸张,PDF)的机器。为什么要改变这个类比呢?Printer
接口可以抽象出所有差异(点阵,激光,操作系统版本等)。 - aliteralmind