在阅读了大量博客,论坛条目和几个Apple文档之后,我仍然不知道在Objective-C中进行广泛的子类化是否是明智之举。
以下列案例为例:
说我正在开发一款益智游戏 有很多元素。所有这些 元素共享一定数量的 同样的行为。然后,在我的内心 元素的集合,不同 元素组共享相等 行为,区分群体 团体等...
所以,确定继承之后 从什么,我决定继承 遗忘为什么我不应该? 考虑到轻松调整一般 行为需要这个模型,我 我认为我已经完成了OOP 意思是。
但是, - 这是我的问题的来源 - Apple提到使用委托,数据源方法和非正式协议支持子类化。这真让人难以理解为什么?
似乎有两个阵营。那些支持子类化的人,那些不支持的子类。这显然取决于个人品味。 我想知道大规模子类化的优点和缺点是什么,而不是大规模子类化?
总结一下,我的问题很简单:我是对的吗?为什么或为什么不呢?
答案 0 :(得分:12)
委托是一种使用组合技术来替换您可能子类化的编码的某些方面的方法。因此,它归结为手头任务的古老问题,需要一个知道如何做很多事情的大事,或者你是否有一个松散的专门对象网络(一种非常类型的UNIX责任模型)。 / p>
使用委托和协议的组合(定义委托应该能够做什么)提供了很大的行为灵活性和编码的简易性 - 回到Liskov替换原则,当你子类化时要小心你不做任何事情,整个班级的用户会发现意外。但是,如果你只是创建一个委托对象,那么你只需要负责,只有你实现的委托方法做了一个协议所要求的,超出你不关心的范围。
使用子类还有很多很好的理由,如果你真正拥有多个类之间的共享行为和变量,那么它可能对子类很有意义。但是,如果您可以利用委托理念,您通常会使您的课程更容易扩展或以设计师可能没有预料到的方式使用。
我倾向于更多地使用正式协议而不是非正式协议,因为不仅正式协议确保您将方法视为委托期望的类,而且因为协议定义是一个自然的地方记录您对实现这些方法的委托的期望。
答案 1 :(得分:10)
就个人而言,我遵循这条规则:如果它尊重Liskov substitution principle,我可以创建一个子类。
答案 2 :(得分:7)
子类化有它的好处,但它也有一些缺点。作为一般规则,我尝试避免实现继承,而是使用接口继承和委托。
我这样做的原因之一是,当你继承实现时,如果你覆盖方法但不遵守它们(有时没有文档的合同),你可能会遇到问题。另外,我发现具有实现继承的步行类层次结构很困难,因为可以在任何级别覆盖或实现方法。最后,在子类化时,您只能扩展接口,不能缩小它。这导致泄漏抽象。一个很好的例子是java.util.Stack,它扩展了java.util.Vector。我不应该将堆栈视为Vector。这样做只允许消费者在界面上运行。
其他人提到Liskov替代原则。我认为使用它肯定会清除java.util.Stack问题,但它也会导致非常深的类层次结构,以确保类只获得它们应该具有的方法。
相反,对于接口继承,基本上没有类层次结构,因为接口很少需要相互扩展。这些类只是实现了它们所需的接口,因此可以由消费者以正确的方式处理。此外,由于没有实现继承,这些类的使用者不会因为以前使用父类的经验而推断他们的行为。
最后,你走哪条路并不重要。两者都完全可以接受。这更多的是你更熟悉的东西以及你正在使用的框架鼓励什么。正如那句老话:“在罗马做罗马人的时候。”
答案 3 :(得分:6)
在Objective-C中使用继承没有任何问题。 Apple使用它相当多。例如,在Cocoa-touch中,UIButton的继承树是UIControl:UIView:UIResponder:NSObject。
我认为马丁在提到利斯科夫替代原则时提到了一个重要的观点。另外,正确使用继承要求子类的实现者对超类有深入的了解。如果你曾经在复杂的框架中努力扩展一个非平凡的类,你就会知道总有一个学习曲线。此外,超级类的实现细节经常“泄漏”到子类,这是@ $& amp;对于框架构建者。
Apple在许多情况下选择使用委托来解决这些问题;像UIApplication这样的非平凡类通过委托对象公开了常见的扩展点,因此大多数开发人员都有更简单的学习曲线和更松散耦合的方式来添加特定于应用程序的行为 - 很少需要直接扩展UIApplication。
在您的情况下,对于您的应用程序特定代码,请使用您熟悉的技术并最适合您的设计。如果使用得当,继承是一个很好的工具。
我经常看到应用程序员从框架设计中吸取教训并尝试将它们应用到他们的应用程序代码中(这在Java,C ++和Python世界以及Objective-C中很常见)。虽然考虑和理解框架设计者所做的选择是很好的,但这些课程并不总是适用于应用程序代码。
答案 4 :(得分:4)
一般情况下,如果存在完成您要执行的操作的委托等,则应该避免继承API类。在你自己的代码中,子类化通常更好,但它确实取决于你的目标,例如。如果您提供API,则应提供基于委托的API,而不是假定子类化。
在处理API时,子类化有更多潜在的错误 - 例如。如果类层次结构中的任何类获得与您的添加具有相同名称的新方法,则可以创建中断内容。而且,如果你提供一个有用/辅助类型的函数,将来有可能会将类似的东西添加到你正在子类化的实际类中,并且可能更有效等等,但你的覆盖将隐藏它。 / p>
答案 5 :(得分:4)
请阅读Apple文档"Adding behavior to a Cocoa program"!在“继承Cocoa类”部分下,请参阅第2段。 Apple明确提到Subclassing是向框架添加特定于应用程序的行为的主要方式(请注意, FRAMEWORK )。 MVC模式并不完全禁止使用子类或子类型。至少我没有看到苹果或其他人的这个建议(如果我错过了,请随时向我指出有关此信息的正确信息来源)。如果你只在你的应用程序中继承api类,请继续,没有人阻止你,但要注意它不会破坏class / api的整体行为。子类化是扩展框架api功能的好方法。我们在Apple IOS框架API中也看到了很多子类。 作为一名开发人员,必须注意实施是有充分记录的,并且不会被其他开发人员意外重复。如果您的应用程序是一组计划作为可重用组件分发的API类,那么它就是另一个球类游戏。
恕我直言,首先要仔细阅读相关文档,实施并测试,而不是询问最佳实践。做出自己的判断。你最了解自己的目标。 其他人(比如我和其他许多人)很容易从网上的不同来源阅读内容并抛出条款。做你自己的判断,到目前为止它对我有用。答案 6 :(得分:3)
我真的认为这取决于你想要做什么。如果您在示例中描述的益智游戏确实有一组共享公共属性的独特元素,并且没有提供的类 - 例如,“NSPuzzlePiece” - 符合您的需求,那么我没有看到问题进行广泛的子类化。
根据我的经验,代表,数据源方法和非正式协议在Apple提供的类已经做了类似于您希望的操作时更有用。
例如,假设您正在构建一个使用表的应用程序。有(我在这里讲的是iPhone SDK,因为这是我有经验的地方)一个类UITableView,它可以创建一个与用户交互的表格,并且为定义数据源的效率更高效。 UITableView的实例比完全继承UITableView并重新定义或扩展其方法以定制其行为。
类似的概念适用于代理和协议。如果您可以将您的想法融入Apple的课程中,那么通常更容易(并且将更加顺利地)这样做并使用数据源,代理和协议,而不是创建自己的子类。它可以帮助您避免额外的工作和浪费时间,并且通常不易出错。 Apple的课程负责使功能高效和调试的业务;你可以越多地使用它们,从长远来看你的程序错误就越少。
答案 7 :(得分:2)
我对ADC强调'反对'子类化的印象更多地与操作系统如何演变的遗留问题有关...当时c ++是大多数mac的主要接口,当时(Mac Classic aka os9)工具箱,子类化是事实上的标准,以便程序员修改普通操作系统功能的行为(这确实有时是颈部的痛苦,并且意味着必须非常小心,任何和所有修改都表现出可预测性和没有破坏任何标准行为)。
这就是说,我对ADC强调子类化的印象是 not 提出了设计一个没有继承的应用程序类层次结构的案例,但是INSTEAD 指出新的做事方式(即OSX)在大多数情况下,更合适的方法是在不需要子类的情况下自定义标准行为。
因此,无论如何,尽可能强大地设计您的拼图程序的架构,并在您认为合适的情况下利用继承!
期待看到你的酷炫新拼图应用程序!
| K<
答案 8 :(得分:0)
OOP设计的一个公理是赞成合成而不是实现。