多态性
只要使用getType()
或类似的东西,就不会使用多态。
我了解您需要知道自己的类型。但是,您要在知道确实应该将其推入课堂的同时进行任何工作。然后,您只需告诉它何时进行即可。
程序代码获取信息然后做出决定。面向对象的代码告诉对象要做事。
—亚历克·夏普
这个原则叫做告诉,不要问。遵循它可以帮助您避免散布诸如打字之类的细节并创建对其起作用的逻辑。这样做可以使一堂课变得面目全非。最好将这种行为保留在类中,以便在类更改时可以更改。
封装形式
您可以告诉我,将不再需要其他形状,但我不相信您,也不应该。
下面封装的一个很好的效果是,它很容易地添加新的类型,因为它们的细节不散开成,他们在出现的代码if
和switch
逻辑。一种新类型的代码应全部放在一个地方。
类型无知碰撞检测系统
让我向您展示如何设计一种性能出色且可在任何2D形状下工作的碰撞检测系统,而无需关心它的类型。
说您应该画那个。看起来很简单。都是圈子 创建一个了解碰撞的圆形类很诱人。问题在于,这使我们陷入了思考的困境,而当我们需要1000个圆时,它就会崩溃。
我们不应该考虑圈子。我们应该考虑像素。
如果我告诉您,用来绘制这些家伙的代码相同,那么您可以用来检测他们何时触摸甚至是用户单击的代码。
在这里,我为每个圆圈绘制了一种独特的颜色(如果您的眼睛足以看到黑色轮廓,则可以忽略它)。这意味着该隐藏图像中的每个像素都映射回其绘制的位置。哈希图很好地解决了这个问题。您实际上可以通过这种方式进行多态。
您无需显示给用户此图像。使用与绘制第一个相同的代码创建它。只是具有不同的颜色。
当用户单击一个圆圈时,我确切知道哪个圆圈,因为该颜色只有一个圆圈。
当我在另一个圆圈上绘制一个圆圈时,可以将它们倾倒在一组中,从而快速读取要覆盖的每个像素。当我完成设定点时,它碰到了每个圆,现在我只需要呼叫每个圆一次就可以通知碰撞了。
新型:矩形
这一切都是用圆圈完成的,但是我问你:使用矩形是否会有所不同?
没有圈子知识泄漏到检测系统中。它不在乎半径,圆周或中心点。它关心像素和颜色。
这种碰撞系统中唯一需要向下压成单个形状的部分是唯一的颜色。除此之外,形状还可以考虑绘制形状。无论如何,这就是他们擅长的。
现在,当您编写碰撞逻辑时,您不必关心您拥有什么子类型。您告诉它发生碰撞,并且告诉您在假装绘制的形状下找到的内容。无需知道类型。这意味着您可以添加任意多个子类型,而不必更新其他类中的代码。
实施选择
确实,它不必是唯一的颜色。它可以是实际的对象引用,并可以保存一定程度的间接引用。但是,在此答案中得出的结论看起来并不那么好。
这只是一个实现示例。当然还有其他。这意味着要显示的是,您越让这些形状子类型坚持其单一职责,整个系统就越有效。可能会有更快,内存占用较少的解决方案,但是如果它们迫使我在周围传播子类型的知识,即使性能有所提高,我也不愿使用它们。除非我明确需要它们,否则我不会使用它们。
双重派遣
到现在为止,我完全忽略了双重派遣。我这样做是因为我可以。只要冲突逻辑不在乎哪两种类型发生冲突,您就不需要它。如果您不需要它,请不要使用它。如果您认为自己可能需要它,请尽可能推迟处理它。这种态度称为YAGNI。
如果您确定确实需要不同种类的碰撞,请问自己是否n个形状子类型确实需要n 种2种碰撞。到目前为止,我已经非常努力地使添加其他形状子类型变得容易。我不想用双重分发实现来破坏它,它迫使圈子知道正方形的存在。
反正有几种碰撞?稍作推测(一种危险的事情)就发明了弹性碰撞(弹力),非弹性(粘性),高能(爆炸)和破坏性(损坏)。可能会有更多,但如果小于n 2,就不要过度设计碰撞。
这意味着当我的鱼雷击中受到伤害的东西时,不必知道它击中了太空飞船。只需说:“哈哈!您受到了5点伤害。”
造成损害的事物向接受损害消息的事物发出损害信息。通过这种方式,您可以添加新形状而无需告知其他形状。您最终只会在新的碰撞类型中散布。
太空船可以将鱼雷发回鱼雷,“哈哈!你受到了100点伤害。” 以及“您现在被困在我的船体上”。然后鱼雷可以发回“好吧,我已经做好了,所以忘记我了”。
谁都不知道确切的含义。他们只是知道如何通过碰撞界面互相交谈。
现在可以肯定,双调度可以让您比这更紧密地控制事物,但是您真的想要吗?
如果您愿意的话,请至少考虑通过抽象接受形状而不是实际形状实现的抽象碰撞来进行双重调度。此外,可以将碰撞行为作为依赖项注入并委托给该依赖项。
性能
性能始终至关重要。但这并不意味着它总是一个问题。测试性能。不要只是猜测。无论如何,以性能为名牺牲其他所有内容通常都不会导致性能良好的代码。