如何扩展现有的库类?

时间:2019-03-03 06:38:45

标签: pharo

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类中。仅仅是来自功能语言的,我发现自己完全对这里的期望不满意。

据我所知,按照惯例,大多数Pharo方法直接在实例上工作,而在功能语言中,要做的就是定义一个类似于C#/ Java中静态方法的函数。

一种选择是将方法放入Pharo中的元类,但是Pharo的语法不太适合这种编程风格。例如,它没有管道运算符来解释我观察到的所有库代码中对实例方法的严重偏爱。

通过直接在其中添加方法来扩展标准库类对我来说有点不对劲。当需要将更改推向Github时,情况将如何发展?直接将其放到Array类中,感觉最终它将变成版本控制的噩梦。

另一种选择是从Array继承。对于我现在正在解决的问题,这可能很好,但是稍后我想做一个不同的问题,并且不想共享实现。

我应该将其放入特征中并将其添加到Array中吗?

2 个答案:

答案 0 :(得分:4)

这里的问题来自以下事实:我们只考虑了一条您的收藏集应该回答的消息:#normalized。在您的代码中,集合sum是需要 normalized 的对象。因此,说sum normalized很诱人。但是,我不建议将#normalized添加到Array(或Collection)中,因为您的特定 normalization 的逻辑不是Array固有的:它取决于 Action ,这看起来对您的项目而言很有意义。

这并不意味着您不应该将自己的内容扩展为Array。这仅意味着,如果您的扩展名不是固有的,则需要更多的考虑。

在这种情况下,我建议您分析项目中类似sum的集合是否具有其固有的其他行为。在这种情况下,我将考虑使用一个类来表示它们,并向该类添加诸如#normalized之类的消息,以及与这些对象相关的任何其他消息。

仅作为示例,假设您的类名为StrategyCollection。您可以在其中定义

strategy
  ^sum collect: [:each | each max: 0.0].

其中sum现在是您班上的一个ivar。然后您可以定义

normalized
  strategy := self strategy.
  normalizingSum := strategy sum.
  ^strategy collect: [:each |
    normalizingSum strictlyPositive
      ifTrue: [each / normalizingSum]
      ifFalse: [1.0 / Action subclasses size]]

,顺便说一下,可以重写为

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:或使用专门实现自己的特殊子类StrategyCollection使它更通用。 #strategy的版本。

我还建议为表达式Action subclasses size添加一个方法。

actionCount
  ^Action subclasses size

,然后在#normalized中使用它。表达式Action subclasses size很脆弱,可能会以其他方式出现。例如,如果明天您决定将Action的某些子类归为另一个抽象子类,则Action的子类的数量将无法适应这样的重构。更一般地,对象的行为不应取决于组织代码的方式,因为这属于抽象的元级别。

答案 1 :(得分:1)

您绝对应该将其设为ArrayCollection的一个实例,因为您的代码适用于任何可迭代的(并且有数字)。

原因是使用起来更清晰:

#(3 5 49 3 1) normalized

而不是:

SomeMisteriousThirdParty normalize: #(3 5 49 3 1)

此外,如果您有一些特殊的集合或其他类,他们可以定义自己的normalized版本来正确处理。

Pharo的理念是,没有人可以为您的项目创造一个完美的环境。因此,很容易更改现有库以满足您的需求。

执行此操作的方法是使用扩展方法,其中您的包使用方法扩展了某些 other 类。正是出于这个原因,当您需要扩展另一个类但将更改与代码一起版本化时,正是在这个原因中,Pharo(通常是Smalltalk)已经有超过10年的功能了。

尽管我们涉及规范化问题,但值得一提的是,至少有两个大型项目很可能需要规范化才能完成其工作。一种是Roassal用于数据可视化,另一种是Polymath用于各种计算。看看它们的作用,您可能会受益。