我在Pharo Smalltalk中编写类,但我认为这个问题对其他Smalltalk实现有效。
我知道一种强制使用特定属性的实例的方法是为实例创建提供类方法,然后建议使用类创建方法。但是任何用户都知道可以随时使用new或basicNew。
我考虑过使new和basicNew无效引发异常,但这似乎是太过激烈的措施,有时我可能需要创建实例来调试,例如。
是否有其他库或机制来强制执行这些特定属性?
答案 0 :(得分:8)
没有。这很好。
以下是您可能会遵循的一些方法:
通过为对象提供验证来确保您的对象有效。这是一个非常广泛的主题,所以我只想说一个验证框架能够检查你的对象,并以某种方式明确它们未能遵守的任何规则。
根据您正在做的事情,您可能会在对象生成或修改时限制对GUI的验证。但是,更复杂的方法应该允许任何对象决定它是否有效(在给定的上下文中)。
从实例端协议中排除单个setter。只提供多个关键字设置器,如果缺少或不合适,将会失败。这意味着您可以提供Point >> #x:y:
但不是Poit >> #x:
或Point >> #y:
等方法。
如示例所示,您会发现很难完全采用这种做法,因为基本类不遵循这种风格。另请注意,这种做法需要进行某种验证,因为只检查notNil
通常过于天真。
在需要解决这些问题之前,请放松并不做任何事情。
换句话说,在您的软件进化到足以引起您注意其对象的创建和更改方式之前,请提出问题。
我声称事情的方式很好,因为Smalltalk传统上倾向于开放性而非安全性,甚至以“错误”的方式促进甚至鼓励人们进行实验。旨在防止物体被破坏的每个功能迟早都会阻止您修复它们。更重要的是,它们会消耗大量能量,否则这些能量将被用于更高效的目标。
答案 1 :(得分:2)
我同意Leandro的回答。防御性编程很少是Smalltalk的做事方式。没有用于强制执行任何操作的静态类型,也没有私有消息。 Smalltalk是开放和后期的。但另一方面,错误很少是灾难性的(Smalltalk通常不会因错误而崩溃)。
这种后期绑定以及优雅的错误恢复使得系统的演变成为一个非常轻量级的过程。当您使用Smalltalk进行编程时,如果您想到它,除了让实时系统发展之外什么都不做。
由于我们唯一拥有的是发送消息,我们最终可以使用它来协商合同,或只是验证一些先决条件。
通过使用Exception,您建议的策略是可行的。这个想法是,有时最好的早期Exception可能最接近根本原因,而不是更难以调试和纠正的后期异常。例如,请参阅消息#shouldNotImplement
及其发件人:您会看到它有时会用于阻止使用#new
。
另外不要忘记有一条#initialize
消息可用于为实例变量提供合理的默认值(有点像C ++的默认构造函数)。当您想要传递其他信息时,有一些变体:对于通常通过#new:
创建的集合,有一条#initialize:
消息,其大小为参数。您可以随意细化类似的机制,以便在创建时传递其他信息。
但请勿尝试阻止使用#basicNew
。您可以通过破坏依赖于此低级功能的某些服务来创建自己的痛苦,例如在修改类布局时改变现有实例,例如复制,例如存储在外部文件中等等...请参阅#adoptInstance:
。
相反,您必须学会信任您图书馆的用户(包括您自己):用户不会使用basicNew
,除非他们知道他们做了什么(他们迟早会知道)。正如Amos所说,记录课堂上的合同和期望或消息评论或单元测试,以便人们可以更快地学习,并且当你想要表达消息仅供知识使用时,可能使用不那么引人注目的名称,如basicNew。
编辑,如果你禁止basicNew,你将无法创建实例,所以你必须创建一个新的消息来调用基元来创建一个实例,并且最后你只需要混淆/复杂的代码,因为你刚刚取代了问题。除非你做了非常讨厌的事情,比如:
basicNew
thisContext sender method selector == #mySepcialCreationMessageWithParameter: ifTrue: [^super basicNew].
^self error: 'new instances should be created with mySepcialCreationMessageWithParameter:'
正如我上面所说,你可以玩它,但不要真实。
EDIT 2 另一种观点认为,编码是一种社交活动,而您在Smalltalk中编写的代码不仅仅是编写自动机。它是由人类阅读和重用。在这种情况下,防御性编程有点像强制管理。花费时间来创建易于理解和重用的简单和逻辑抽象,而不是在你试图约束时浪费时间,可能在你没有预见到的其他环境中。