Smalltalk:自定义构造的正确方法

Smalltalk: right way for custom construction

哪本书最好地描述了制作自定义构造函数的正确方法?

例如,我想要特殊的文件系统(在 RDBMS 存储中模拟)。

Object subclass: #C1_Object
C1_Object subclass: #C1_File
    instanceVariableNames: 'stream name'

用例:

  1. C1_File new: 'blablabla'

  1. C1_File create: 'blablabla'

(1) 看起来是原生的,但是 我看到建议不要覆盖系统分配机制。

下一步:什么更好

C1_File class>>create: aFileName
    ^ self new initialize: aFileName
C1_File>>initialize: aFileName
    name := aFileName.
    stream := C1_FileStream forceNewFileNamed: aFileName.

C1_File class>>create: aFileName
    | instance |
    instance := super new.
    instance name: aFileName.
    instance stream: ( C1_FileStream forceNewFileNamed: aFileName ).
    ^ instance initialize
C1_File>>initialize
    ^ super initialize

谁告诉你不要覆盖系统分配机制?

在任何情况下,在这种情况下都不建议覆盖 #new:,因为按照惯例#new:参数接收的是大小,而不是字符串,因此会造成混淆。

现在,我会使用类似:named:newWithName: 等,但这取决于您(是偏好问题)。

一件事:在 Pharo 中,如果你执行 instance := self new 和稍后的 instance initialize 你将调用初始化两次,因为 #new 的默认实现是 self basicNew initialize,所以您的方法需要这样定义:

C1_File class>>create: aFileName
    | instance |
    instance := self basicNew.
    instance name: aFileName.
    instance stream: ( C1_FileStream forceNewFileNamed: aFileName ).
    ^ instance initialize

但我也不建议这样做(在创建者方法中初始化流感觉不太好)。相反,我会这样做:

C1_File class>>create: aFileName
    ^ self basicNew
        initializeName: aFileName;
        yourself.

C1_File>>initializeName: aFileName
   self name: aFileName.
   self stream: ( C1_FileStream forceNewFileNamed: aFileName ).
   self initialize.

除了我在 Sqeuak 和第三方包中看到的 Smalltalk 代码外,我没有任何参考。像 Read-/WriteStream class>>on: aCollectionText class>>fromString: 这样的自定义构造函数以它们的参数将在创建的实例中用于什么的方式进行通信。另一种风格是直接在构造函数初始化的实例变量之后命名构造函数。类似于 Point class>>x:y:。集合构造函数 with:withAll: 使代码阅读流畅。

我总是会努力命名构造函数,以便发送的 reader 变得清晰,您将得到什么作为答案(在该集合上运行的 Stream,具有这些坐标的 Point,具有给定 name/path? 的打开文件)。我不会覆盖 new:,并且 create 听起来很普通,但在谈论文件时可能很有用(打开以写入或创建,如果它不存在),尽管它不同于 FileStream API,据我所知。

否则,仍然有可能不定义构造函数,而是直接在new:

之后用访问器等初始化对象
MyFileDoesNotExist new
    file: c1File;
    yourself "or signal in this case"

What book best describes right way to make custom constructors ?

Kent Beck 的 Smalltalk Best Practice Patterns(强烈建议随身携带以供参考)包含大约 100 个 Smalltalk 模式,其中还包括

  • 构造方法
  • 构造函数参数方法
  • 快捷构造方法

他们都讨论了对象创建和参数传递的各个方面,但共同的主题是增加理解和清晰度(以及揭示选择器的意图,这是另一种模式)。

当你有

C1_File create: 'blablabla'

不清楚实际会发生什么; C1_File 会创建 blablabla 吗?那是什么意思?正如 Esteban 指出的那样,最好将论点命名为... C1_File named: 'blablabla';现在我知道会发生什么了。

(1) looks native, but I have seen recommends don't override system allocation mechanics.

你必须弄乱 #basicNew 才能弄乱分配机制。如果您查看 #new 的实现,它实际上并没有做太多。

Behavior>>new
    ^ self basicNew initialize

系统中还有很多例子:

  • OrderedCollection with: anItem
  • Color fromString: '#AC13D9'Color r: 0.2 g: 0.5 b: 0.1
  • Point x: 10 y: 17
  • Readers/Writers经常使用on:..STONReader on: aReadStream

请注意,开头提到的这本书不仅展示了如何创建一个基本的构造函数,还讨论了实例创建本身的其他问题和挑战(例如,当你有多个不同的构造函数时不会炸毁你方法协议等)

Class-side vs instance-side - Esteban 回答的更多补充:

将 class 方面的常规行为数量保持在最低限度; class 端主要用于元行为 --- 管理 class 本身,而不是进行实际工作。