【问题标题】:How to override an existing selector in a class in another package如何覆盖另一个包中的类中的现有选择器
【发布时间】:2016-06-12 02:34:16
【问题描述】:

我正在使用 Pharo 5.0。在某些情况下,我在现有的 Pharo 类(例如、DBXTalk/Garage 或 Regex-Core 中的某些东西)中发现了限制或可能存在错误。我希望能够修改存在于我自己项目之外的类中的一个或一组选择器,并将其作为我的包的一部分。

我找到了一些关于如何在外部类中创建 new 选择器并将其移动到我的包中的说明(例如,如tutorial 所示) .这很酷。但在某些情况下,我实际上想修改外部类中的 existing 选择器,并在我使用该选择器时让该选择器的副本覆盖外部类中的选择器。我不想修改现有的第 3 方或 Pharo 预提供的软件包。

在 GNU Smalltalk 中,我可以将其作为扩展类的正常部分。例如:

Kernel.MatchingRegexResults extend [
    at: anIndex [
        "My updated version of the 'official' Kernel.MatchingRegexResults#at: selector"
        "This is part of my package so overrides the 'official' version"
        ...
    ]

    foo [
        "My new foo selector"
    ]
]

如何在 Pharo 5.0 中做到这一点?我已经做了很多搜索,但找不到这样做的方法。 Pharo by ExampleDeep Into Pharo 书籍中没有出现“扩展”或“覆盖”这些词。

【问题讨论】:

  • 您是否尝试过 Move to package... 命令?它可以从选择器列表的右键菜单中获得。
  • @LeandroCaniglia 是的,我做到了。事实上,我在我的问题中提到了它。如果我想为现有的外部类创建一个新的选择器并将新的选择器移动到我的包中,那么这个移动效果很好。但它不适用于修改/覆盖现有选择器,因为它将将该选择器的实现移动到我的包中并将其从外部包中删除。我不想从外部包中删除覆盖的选择器,而只是希望覆盖它。
  • Override 意味着覆盖...您将重新编写该方法,它将被移动到您的包中,但它将针对所有系统进行更改。 Pharo 没有模块系统,所以你不能在同一个类中拥有同名的不同方法......

标签: smalltalk pharo


【解决方案1】:

请注意,除了 Milan Vavra 编写的内容之外,名为 *YourPackage-(something) 的协议中的每个方法都将属于包 YourPackage,而不管该类属于哪个包。至少在 Squeak 中,有将这样的方法覆盖在 *YourPackage-override 协议中的约定。 Pharo 可能有类似的命名约定。移动到包功能将方法移动到这样的“加星标”协议。

但是,不鼓励使用此类覆盖,因为您不能让两个包同时为同一方法提供实现。 Monticello 将尝试保留原始方法和覆盖方法(请参阅PackageInfo>>isOverrideCategory: 的发件人),但您的覆盖方法仍然有可能被其原始包的更新覆盖,或者您将错过对原始方法的更新,可能打破东西。

“正确的方法”是重构原始包中的原始方法,使其行为更易于定制。

【讨论】:

  • 是的,我明白了。不幸的是,在某些情况下,我需要但未编写的包有错误或缺少基本功能。在这些情况下,除了修改包之外,没有什么小变通办法。当我这样做时,我希望能够轻松跟踪它。一个例子是有一个Capture string in regex replacement(增强)。另一个是在 DBXTalk/Garage 中,当数据库中的整数属性为 NULL 时会出错(应该返回 nil)。
  • 当然,在短期内通常没有办法绕过它(如果您不想发送其他消息)。但我也想为其他来到这里的读者指出这一点。 :-)
  • 加星标的协议和包的组成部分在 PBE 涵盖蒙蒂塞洛的章节中进行了解释。 pharo.gforge.inria.fr/PBE1/PBE1ch7.html#x32-1060003
【解决方案2】:

GNU Smalltalk 语法中的代码

Kernel.MatchingRegexResults extend [
    at: anIndex [
        "method body"
    ]
    foo [
        "My new foo selector"
    ]
]

看起来像这样

!MatchingRegexResults methodsFor: 'protocol'!
at: anIndex 
    "method body"
!
foo
    "My new foo selector"
! !

在可以从文件浏览器中归档的 Pharo 更改集中。

但请注意,在这两种情况下,GNU Smalltalk 和 Pharo Smalltalk,您实际上是在替换该类中方法的原始版本。

在 GNU Smalltalk 中,您可能不习惯保存图像,因此您可能认为扩展语法不会修改原始类中的原始方法。

其实它就是这么做的。

使用 GNU Smalltalk,您通常每次运行 gst 时都从相同的未修改旧映像开始。 这是因为 GNU Smalltalk 中图像的默认位置对于普通用户是不可写的。所以 gst 每次都读取相同的只读图像,您使用类定义和扩展在内存中对其进行修改,修改后的图像仅在程序运行时暂时存在,并在程序退出时被丢弃。

【讨论】:

  • 我想从变更管理的角度来看,最好的办法可能是将修改后的选择器移动到我的包中,正如@LeandroCaniglia 在他的评论中所说,即使这意味着修改现有的包以移动它。至少这样很容易跟踪。如果我想分享这个包并且其他人尝试使用它,那么我包中的修改后的那个会取代他们标准包中的那个吗?还是仅仅取决于上次在他们的环境中更新了哪个包?
  • 一个类中的方法只能有一个版本。最近加载的方法版本成为当前版本。加星标的协议仅用于将扩展方法分组到系统浏览器和蒙蒂塞洛浏览器的不同包中。无论包如何,该方法都属于同一类。
  • 是的,但是如果它被定义在多个包中,那么哪一个“获胜”?它是基于包裹加载的顺序吗?当我在包 Y 中重新定义选择器定义的包 X 时,我知道 Y 版本会替换它。但是,如果我随后加载了更新版本的包 X,如果选择器被更新版本替换,Y 版本是否会?或者它只是被跳过而我的 Y 包版本仍然存在?
  • 如果您在更改集中归档,则其中定义的所有方法都会替换图像中的内容。如果您使用 Monticello .mcz 文件,您可以单击“更改”以查看加载它时会发生什么。简而言之 - 是的,您加载的内容会替换已经存在的内容。这意味着如果你加载一个扩展,之前的版本会在之前所在的包中被“删除”。
  • 好的,感谢您的解释。因此,当需要对我自己以外的包进行更改时,将需要一些仔细的手动管理来维护这些特定的更改。例如,如果出现新版本的 Regex,我必须记住在升级之前我有一个特殊版本的选择器以保留我的版本。
猜你喜欢
  • 2013-04-01
  • 2022-11-07
  • 1970-01-01
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多