“杂乱多态性” 反模式

发布于 2024-07-16 06:01:42 字数 997 浏览 5 评论 0原文

在相当大的 Ruby 应用程序中,我们会遇到这样的情况:给定的对象由几个东西来标识:例如名称和 ID。 这些值类型中的每一个都有不同的用途,因此并不完全相同(id 和 name 保留在不同的位置)。 因此,我们最终会在应用程序中传递各种值(id、名称和对象)。 这种情况至少在某种程度上看起来是一个问题,因为我们已经遇到了一些错误,这些错误涉及不清楚应该将什么类型传递给给定函数。 事实上,我记得多年来在许多应用程序中看到过类似的问题代码,尽管我再次从未给出过具体的名称。

Ruby 作为一种无类型语言,不允许像 C++ 那样使用经典的基于类型的多态函数。 作为一种解决方法,一位同事经常采用这种类型的代码:

  def initialize starting_value
    if starting_post.kindof? Foo
      @starting_id = get_id_from_foo starting_value
    elsif starting_post.kindof? Bar
      @starting_id = get_id_from_bar starting_value
    else
      raise "illegal type"
    end
  end

这种代码在我们的代码库(不仅仅是初始化程序)周围的扩散导致了我所说的“混乱的多态性”。 它通常有效,但有时会造成非常令人困惑的情况。

对此我有三个问题。

  • 是否有一个正式的名称作为 反模式? “混乱的接口?”、“混乱的多态性?” 或者是其他东西?
  • 人们认为这有多糟糕?
  • 有没有系统化的重构方法 这? 我们面临的挑战 我们创建的许多测试都使用它的普通重构 松散类型,因此我们必须改变两者 测试和实施 同时,因此不会产生普通基于测试的支架效应 重构。 我认为人们实际上可以“加强”这种松散的多态性, 并将代码抽象为函数,而不是立即将其剥离。 但会 这是个好主意吗?

In fairly large Ruby application, we have a situation where a given object is identified by a couple of things: name and id, say. Each of these value types serves a somewhat different purpose and so are not exactly equivalent (id and name persist in different places). So we wind-up with a variety of values being passed around the application (ids, names and objects). This situation seems like a problem to at least some degree since we've already been bitten by bugs which involved not being clear what type should be passed to a given function. I actually recall seeing similar problem code in a number of applications over the years, though I again never gave a specific name to it.

Ruby, as a typeless language, does not allow classical type-based polymorphic functions in the way that C++ does. As a work around, a colleague has often resorted code of this sort:

  def initialize starting_value
    if starting_post.kindof? Foo
      @starting_id = get_id_from_foo starting_value
    elsif starting_post.kindof? Bar
      @starting_id = get_id_from_bar starting_value
    else
      raise "illegal type"
    end
  end

The proliferation of this code around our code-base (not just initializers) results in what I would call "messy-polymorphism". It often works but sometimes creates very puzzling situations.

I have three questions about this.

  • Is there a formal name for this as an
    anti-pattern? "Messy Interface?", "Messy Polymorphism?" or something else?
  • How bad to do people think this is?
  • Is there a systematic way to refactor
    this? The challenge that we have with
    ordinary refactoring that many tests we're created use this
    loose-typing and thus we would have to change both
    the tests and the implementations
    simultaneously and so wouldn't have the scaffold effect of ordinary test-based
    refactoring. I am think that one could actually "strengthen" this loose polymorphism,
    and abstract-out the code into a function rather than immediate rip it out. But would
    this be a good idea?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

毁我热情 2024-07-23 06:01:42

难道你不能定义某种 get_id 方法,以便 id 返回自身,对象返回 id,并且名称执行获取 id 所需的任何操作吗? 然后,您始终可以将您知道属于这三者之一的任何内容规范化。 如果需要的话,也可以使用 get_name 和 get_object 方法。

也就是说,您定义了一个隐式 ThingWhatHasAnID 接口,并将其设为函数参数的鸭子类型。

除非我遗漏了某些东西,否则我会将这种反模式称为“错过了创建抽象的机会”。

Can't you define some sort of get_id method so that an id returns itself, an object returns the id, and the name does whatever it needs to do to get an id? Then you can always canonicalise anything which you know will be one of the three. Likewise with get_name and get_object methods if those are needed.

That is to say, you've defined an implicit ThingWhatHasAnID interface, and made that the duck-type of your function parameter.

Unless I'm missing something, I'd call this anti-pattern "missed opportunity to create an abstraction".

雪化雨蝶 2024-07-23 06:01:42

几乎任何时候你发现自己打开一个对象的类,这都是一个线索,表明该行为应该是对象本身的方法。 消息调度是基于接收者的多态性。 在这种情况下,它应该类似于:

def initialize starting_value
  @starting_id = starting_value.id
end

定义 id 来执行各种 get_id_from_* 方法用来执行的操作。 非法类型的情况已经发生,因为你会得到一个NoMethodError。

至于如何称呼它,我将其称为“面向对象语言的过程编程”。

Almost anytime you find yourself switching on an object's class, it's a clue that the behavior should be a method of the object itself. Message dispatch is polymorphic based on the receiver. In this case, it should be something like:

def initialize starting_value
  @starting_id = starting_value.id
end

Define id to do whatever the various get_id_from_* methods used to do. The illegal type case will already raise because you'll get a NoMethodError.

As for what to call this, I'd call it "procedural programming in an OO language."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文