现有库 类 应该如何扩展?
How should existing library classes be extended?
normalize: sum
| strategy normalizingSum |
strategy := sum collect: [ :each | each max: 0.0 ].
normalizingSum := strategy sum.
^strategy collect: [ :each |
normalizingSum strictlyPositive
ifTrue: [ each / normalizingSum ]
ifFalse: [ 1.0 / Action subclasses size ]
]
我想让上面的方法成为一个实例方法,而不是必须显式地将 sum 传递给它。问题不是我无法制作方法或无法将其放入 Array
class。只是来自函数式语言,我发现自己完全偏离了这里对我的期望。
据我所知,作为一种约定,大多数 Pharo 方法直接在实例上工作,而在函数式语言中,人们会做的是定义一个类似于 C#/Java 中的静态方法的函数。
一种选择是将该方法放入 Pharo 中的元class,但 Pharo 的语法不太适合这种编程风格。例如,它没有管道运算符,这解释了我观察到的所有库代码中对实例方法的严重倾斜。
通过直接将方法放入其中来扩展标准库 class 对我来说感觉有点不对劲。当需要将更改推送到 Github 时,事情究竟会怎样?直接放到 Array
class 感觉最终会变成版本控制的噩梦。
另一种选择是继承自Array
。这可能对我现在做的问题没问题,但以后我想做一个不同的问题,不想分享实现。
我应该把它放在特征中并将特征添加到 Array
吗?
你绝对应该让它成为 Array
的实例,甚至更好的 Collection
因为你的代码适用于任何可以迭代的东西(并且有数字)。
原因是使用起来更清晰:
#(3 5 49 3 1) normalized
而不是:
SomeMisteriousThirdParty normalize: #(3 5 49 3 1)
此外,如果您有一些特殊的 collections 或其他 classes,他们可以定义自己的 normalized
版本来正确处理事情。
Pharo 的理念是没有人可以为您的项目创造完美的环境。因此,很容易更改已有的库以满足您的需要。
这样做的方法是使用扩展方法,其中 您的 包使用方法扩展了一些 other class。此功能在 Pharo(和一般的 Smalltalk)中存在了十多年,正是出于这个原因,当您需要扩展另一个 class 但将更改与您的代码一起版本化时。
虽然我们讨论规范化的话题,但值得一提的是,至少有两个大项目最有可能需要规范化来完成他们正在做的事情。一种是用于数据可视化的 Roassal,一种是用于各种计算的 Polymath。看看他们的工作,您可能会受益。
这里的问题来自这样一个事实,即我们只考虑您的 collection 应该回答的一条消息:#normalized
。在您的代码中,collection sum
是需要 规范化 的 object。因此,很容易说 sum normalized
。但是,我不建议将 #normalized
添加到 Array
(或 Collection
),因为您特定的 规范化 的逻辑不是 Array
:它取决于 Action
,这看起来是一个在您的项目上下文中有意义的概念。
这并不意味着您不应该用您的东西扩展 Array
。这仅意味着如果您的扩展不是固有的,那么这样的决定将需要更多考虑。
在这种情况下,我的建议是分析您项目中的 collection 之类的 sum
是否具有它们固有的任何其他行为。在那种情况下,我会考虑用 class 来表示它们并添加到此 class 消息中,例如 #normalized
,以及与这些 object 相关的任何其他消息。
举个例子,假设您的 class 名为 StrategyCollection
。你可以在里面定义
strategy
^sum collect: [:each | each max: 0.0].
其中 sum
现在是您 class 的 ivar。然后你可以定义
normalized
strategy := self strategy.
normalizingSum := strategy sum.
^strategy collect: [:each |
normalizingSum strictlyPositive
ifTrue: [each / normalizingSum]
ifFalse: [1.0 / Action subclasses size]]
顺便说一下,这可能是 re-written 作为
normalized
| strategy normalizingSum |
strategy := self strategy.
normalizingSum := strategy sum.
^normalizingSum strictlyPositive
ifTrue: [strategy collect: [:each | each / normalizingSum]]
ifFalse: [strategy class new: sumstrategy size withAll: 1.0 / Action subclasses size]
如果除了 #max: 0
之外还有其他策略,您可以轻松调整上面的代码,通过使用 perform:
或使用 [= 的特殊子 class 使其更通用25=] 实现了自己的 #strategy
.
版本
我还建议为表达式 Action subclasses size
添加一个方法。类似于
actionCount
^Action subclasses size
然后在#normalized
中使用它。表达式 Action subclasses size
很脆弱,可能会出现在其他方法中。例如,如果明天你决定将 Action
的一些子 class 分组在另一个抽象子 class 下,那么 Action
的子class 的数量将不适应这样的重构。更一般地说,您的 object 的行为不应该取决于您组织代码的方式,因为那属于抽象的 meta-level。
normalize: sum
| strategy normalizingSum |
strategy := sum collect: [ :each | each max: 0.0 ].
normalizingSum := strategy sum.
^strategy collect: [ :each |
normalizingSum strictlyPositive
ifTrue: [ each / normalizingSum ]
ifFalse: [ 1.0 / Action subclasses size ]
]
我想让上面的方法成为一个实例方法,而不是必须显式地将 sum 传递给它。问题不是我无法制作方法或无法将其放入 Array
class。只是来自函数式语言,我发现自己完全偏离了这里对我的期望。
据我所知,作为一种约定,大多数 Pharo 方法直接在实例上工作,而在函数式语言中,人们会做的是定义一个类似于 C#/Java 中的静态方法的函数。
一种选择是将该方法放入 Pharo 中的元class,但 Pharo 的语法不太适合这种编程风格。例如,它没有管道运算符,这解释了我观察到的所有库代码中对实例方法的严重倾斜。
通过直接将方法放入其中来扩展标准库 class 对我来说感觉有点不对劲。当需要将更改推送到 Github 时,事情究竟会怎样?直接放到 Array
class 感觉最终会变成版本控制的噩梦。
另一种选择是继承自Array
。这可能对我现在做的问题没问题,但以后我想做一个不同的问题,不想分享实现。
我应该把它放在特征中并将特征添加到 Array
吗?
你绝对应该让它成为 Array
的实例,甚至更好的 Collection
因为你的代码适用于任何可以迭代的东西(并且有数字)。
原因是使用起来更清晰:
#(3 5 49 3 1) normalized
而不是:
SomeMisteriousThirdParty normalize: #(3 5 49 3 1)
此外,如果您有一些特殊的 collections 或其他 classes,他们可以定义自己的 normalized
版本来正确处理事情。
Pharo 的理念是没有人可以为您的项目创造完美的环境。因此,很容易更改已有的库以满足您的需要。
这样做的方法是使用扩展方法,其中 您的 包使用方法扩展了一些 other class。此功能在 Pharo(和一般的 Smalltalk)中存在了十多年,正是出于这个原因,当您需要扩展另一个 class 但将更改与您的代码一起版本化时。
虽然我们讨论规范化的话题,但值得一提的是,至少有两个大项目最有可能需要规范化来完成他们正在做的事情。一种是用于数据可视化的 Roassal,一种是用于各种计算的 Polymath。看看他们的工作,您可能会受益。
这里的问题来自这样一个事实,即我们只考虑您的 collection 应该回答的一条消息:#normalized
。在您的代码中,collection sum
是需要 规范化 的 object。因此,很容易说 sum normalized
。但是,我不建议将 #normalized
添加到 Array
(或 Collection
),因为您特定的 规范化 的逻辑不是 Array
:它取决于 Action
,这看起来是一个在您的项目上下文中有意义的概念。
这并不意味着您不应该用您的东西扩展 Array
。这仅意味着如果您的扩展不是固有的,那么这样的决定将需要更多考虑。
在这种情况下,我的建议是分析您项目中的 collection 之类的 sum
是否具有它们固有的任何其他行为。在那种情况下,我会考虑用 class 来表示它们并添加到此 class 消息中,例如 #normalized
,以及与这些 object 相关的任何其他消息。
举个例子,假设您的 class 名为 StrategyCollection
。你可以在里面定义
strategy
^sum collect: [:each | each max: 0.0].
其中 sum
现在是您 class 的 ivar。然后你可以定义
normalized
strategy := self strategy.
normalizingSum := strategy sum.
^strategy collect: [:each |
normalizingSum strictlyPositive
ifTrue: [each / normalizingSum]
ifFalse: [1.0 / Action subclasses size]]
顺便说一下,这可能是 re-written 作为
normalized
| strategy normalizingSum |
strategy := self strategy.
normalizingSum := strategy sum.
^normalizingSum strictlyPositive
ifTrue: [strategy collect: [:each | each / normalizingSum]]
ifFalse: [strategy class new: sumstrategy size withAll: 1.0 / Action subclasses size]
如果除了 #max: 0
之外还有其他策略,您可以轻松调整上面的代码,通过使用 perform:
或使用 [= 的特殊子 class 使其更通用25=] 实现了自己的 #strategy
.
我还建议为表达式 Action subclasses size
添加一个方法。类似于
actionCount
^Action subclasses size
然后在#normalized
中使用它。表达式 Action subclasses size
很脆弱,可能会出现在其他方法中。例如,如果明天你决定将 Action
的一些子 class 分组在另一个抽象子 class 下,那么 Action
的子class 的数量将不适应这样的重构。更一般地说,您的 object 的行为不应该取决于您组织代码的方式,因为那属于抽象的 meta-level。