但是draw()方法不是非常依赖于用户界面是什么吗?
从务实的角度来看,Rectangle
如果这是用户端的要求,那么系统中的某些代码需要知道如何绘制类似a的东西。这将归结为做真正底层的事情,例如光栅化像素或在控制台中显示某些内容。
从耦合的角度来看,我的问题是,谁/什么应该依赖这种类型的信息,以及在什么程度上详细(例如抽象程度如何)?
抽象绘图/渲染功能
因为如果高层绘图代码仅依赖于非常抽象的事物,那么该抽象也许能够(通过替换具体的实现)在您要定位的所有平台上起作用。作为一个人为的示例,某些非常抽象的IDrawer
界面可能能够在控制台API和GUI API中实现,以完成诸如绘制图形的操作(控制台实现可能会将控制台视为具有ASCII艺术的80xN“图像”)。当然,这是一个人为的例子,因为通常这不是您想要执行的,而是将控制台输出视为图像/帧缓冲区。通常,大多数用户端需求都要求在控制台中进行更多基于文本的交互。
另一个考虑因素是设计一个稳定的抽象有多容易?因为如果您所针对的只是现代GUI API,可能会很容易抽象出基本的图形绘制功能,例如绘制线条,矩形,路径,文本,此类东西(仅是有限基元集的简单2D栅格化) ,具有一个抽象接口,可以通过各种子类型轻松地为所有接口实现这些接口,而成本却很少。如果您可以有效地设计这样的抽象并将其实现在所有目标平台上,那么对于形状或GUI控件或任何知道如何使用这样的图形自己绘制图形的人来说,它的危害要小得多,甚至根本没有危害。抽象。
但是要说,您正在尝试抽象出Playstation Portable,iPhone,XBox One和功能强大的游戏PC之间的细节,而您的需求是在每台设备上利用最先进的实时3D渲染/阴影技术。 。在那种情况下,当基础硬件功能和API发生如此巨大的变化时,尝试提出一个抽象接口来提取渲染细节,几乎可以肯定会导致大量的设计和重新设计时间,并且由于意外发生而频繁发生设计更改的可能性很高发现,以及同样是最低公分母解决方案,它无法利用基础硬件的全部唯一性和功能。
使依赖关系流向稳定的“轻松”设计
在我的领域中,我处于最后一种情况。我们针对具有根本不同的基础功能和API的许多不同的硬件,并试图提出一种渲染/绘图抽象来统治它们都是无可救药的(我们可能会举世闻名,只要有效地做到这一点就可以成为游戏)行业中的更换者)。因此,就我而言,我想要的最后一件事就像是类比Shape
或,Model
或者Particle Emitter
知道如何绘制自身,即使它以最高级别和最抽象的方式表示该绘制也是如此...
...因为这些抽象太难于正确设计,并且当一个设计难以正确而又取决于一切时,这就是最昂贵的中央设计变更的秘诀,该变更会波动并破坏所有依赖于它的事物。因此,您想要的最后一件事是使系统中的依赖关系流向抽象设计,以至于很难获得正确的设计(如果不进行侵入性更改,则很难稳定)。
困难取决于容易,不容易取决于困难
因此,我们要做的是使依赖关系流向易于设计的事物。设计一个仅专注于存储多边形和材料之类的抽象“模型”并使其设计正确的设计要比设计一个可以有效地(通过可替换的混凝土子类型)实现以服务于绘图的抽象“渲染器”要容易得多。从PC统一请求与PSP不同的硬件。
因此,我们将依赖关系从难以设计的事物中分离出来。我们没有让抽象模型知道如何将它们绘制到它们都依赖的抽象渲染器设计上(如果该设计发生更改,就会中断其实现),而是让一个抽象渲染器知道如何绘制场景中的每个抽象对象(模型,粒子发射器等),然后我们可以为PC实施OpenGL渲染器子类型RendererGl
,对于PSP 则实现另一种RendererPsp
,对于手机等则实现这种情况。从渲染器到场景中的各种类型的实体(模型,粒子,纹理等),而不是相反。
- 我所用的“稳定性/不稳定性”与鲍伯叔叔的传入/传出度量标准略有不同,据我所知,度量标准更能衡量变更的难度。我更多地谈论“需要更改的可能性”,尽管他的稳定性指标在那里很有用。当“变化的可能性”与“变化的容易性”成正比时(例如:最有可能需要变化的事物具有最高的不稳定性,并且与鲍勃叔叔的度量有较高的耦合度),那么任何此类可能的变化都是廉价且无干扰的,仅需替换实现,而无需涉及任何中央设计。
如果您发现自己试图在代码库的中央层抽象出一些东西,并且设计起来太困难了,而不是顽固地殴打墙壁,每个月/每年不断对其进行侵入性更改,则需要更新8,000个源文件,因为打破所有依赖它的东西,我的第一建议是考虑反转依赖关系。看看您是否可以以某种方式编写代码,使得难以设计的事物取决于易于设计的其他事物,而没有取决于难以设计的事物而具有易于设计的事物。请注意,我在说的是设计(特别是接口设计),而不是实现:有时候事情很容易设计且难以实现,有时候事情很难设计但容易实现。依赖关系流向设计,因此,重点应仅在于确定某项设计难于确定依赖关系流向的方向。
单一责任原则
对我而言,SRP通常在这里并不那么有趣(尽管取决于上下文)。我的意思是说,在设计目的明确且可维护的Shape
东西时会采取钢丝绳平衡的动作,但是例如,如果对象不知道如何绘制自己的对象,则可能必须公开更详细的信息,并且可能没有太多有意义的事情要进行。在特定的使用上下文中处理形状,而不是对其进行构造和绘制。在所有情况下都需要权衡取舍,并且与SRP无关,它可以使我意识到在某些情况下如何使自己成为这样的维护梦night。
它与耦合以及依赖关系在系统中流动的方向有关。如果您试图将所有依赖的抽象渲染接口(因为他们使用它来绘制自己)移植到新的目标API /硬件,并且意识到您必须进行大量更改才能使其在那有效地工作,那么请这是一项非常昂贵的更改,需要替换系统中所有知道如何绘制自己的东西的实现。这是我遇到的最实际的维护问题,这些事情都知道如何绘制自身,如果这会转化为大量依赖流向抽象,而这些依赖又很难正确设计。
开发者骄傲
我提到这一点是因为,根据我的经验,这通常是使依赖关系流向更易于设计的事物的最大障碍。开发人员在这里有点野心很容易地说,“我要设计跨平台渲染抽象来统治所有规则,我要解决其他开发人员花了几个月的时间来移植的过程,然后我得到了正确,它将在我们支持的每个平台上像魔术一样工作,并在每个平台上都使用最先进的渲染技术;我已经在脑海中预想了它。”在这种情况下,他们抵制了避免这样做的实际解决方案,而只是改变了依赖关系的方向,并将可能造成巨大成本和反复出现的中心设计更改转化为对实现的简单廉价更改和本地重复执行。开发人员需要某种“白旗”的本能,以在某些东西难以设计到如此抽象的水平时重新考虑并重新考虑他们的整个策略,否则他们会陷入很多痛苦和痛苦。我建议将这种雄心壮志和战斗精神转移到最容易实现的设计的最新实现中,而不是将这种征服世界的雄心提升到界面设计级别。