Ruby 请教一下,ruby 中能否复制某个 class 的方法到当前 class 中

doraemon0711 · 2025年01月22日 · 最后由 qiumaoyuan 回复于 2025年01月24日 · 337 次阅读

举个例子 以下是个父类

class Base
  attr_accessor :flag
  def exec; raise NotImplementedError; end
  def print_flag; print("#{self.class} - #{flag}"); end
end

这时有个子类BizA继承了Base,并实现了exec

class BizA < Base
  def exec; self.flag = true; self; end
end

现在如果执行BizA.new.exec.print_flag,会输出BizA - true 上面是前提

我的问题是,如果有领个子类BIzB,能否在BizB继承Base的情况下,拥有和 BizA 相同实现的exec方法

class BizB < Base
  # 不去实现exec,而是复制BizA的exec到当前class
end

然后在执行BizB.new.exec.print_flag时,输出BizB - true

我知道的几种方式在这里列一下,但不是我想要的效果

  1. BizB 继承 BizA - 这种方式确实能正确输出想要的结果,但在我看来 BizB 和 BizA 实际上是同级的,只不过 BizB 委托 BizA 中的方法去进行处理,是在没有别的办法的话我会采取该方案
  2. 使用 DelegateClass 委托 - 这样做不符合需求,因为在执行BizB.new.exec.print_flag时,必须要提供一个 BizA 的实例,而 exec 内部获取 self 时获取到的也是 BizA 的实例,会出现问题
  3. 将 exec 抽到 module 中使用 include 的方式 - 这样做有些偏离初衷了,并且改动加到,我当前的场景是在实现Base的基类,给用户继承使用,Base中还有很多其他的方法和属性,所以不考虑

抽到 module 中不影响 BizABizB 继承 Base 吧?其它代码继续放在 Base 里就好了。

如果 module 只是 BizABizB 共有的,那就只给它俩用这个 module 就完了。

你想做的事情应该是把 exec 方法所在的类加入 BizB 的方法查找链里,ruby 有继承、include、extend、prepend 来改变一个类的方法查找链,在不使用以上方式的情况下应该是没办法做到的将 BizA 加到 BizB 的方法查找链中的

qiumaoyuan 回复

抽走确实不影响继承,但现在已经有很多 Biz 了,相当于要把所有 Biz 内部的 exec 实现,以及 exec 中调用的其他方法也放到新 module 中,改动会比较大,或许可以写个 dsl 来实现?(类似 ruby 内 private 的实现,以下内容为动态生成的 module 中的内容)

打算换个思路实现了,实际上我是想让 Base 有不同的处理模式,不同模式在 Base 中处理的分支不一样,但不同模式的 exec 实现内容是一样的。给 Base 添加模式属性进行判断,就是需要改动的 Base 代码比较多

doraemon0711 回复

我觉得改动大不应该成为问题。

一个事情要不要去做,我认为可以通过一个方法判断:这个事情如果不用花时间就能改好,那么我们改还是不改?如果答案是“改”,那实际上花时间改它是对的。要是我的话,甚至我可能会用业余时间去改它。但我发现大家很多时候是在这上面纠结。

qiumaoyuan 回复

但在一个没有 spec 的大型系统中,“大”还是挺成问题的,尤其是改的代码还不是自己实现的情况下

doraemon0711 回复

“大”还是挺成问题的,尤其是改的代码还不是自己实现的情况下

可以一小步一小步的来,而且你举的例子,似乎只是方法的移动,看起来并没什么风险。

另一方面,如果遇到一个问题,不是选择正面解决,而是选择绕过去,就欠下了一次技术债,未来还是要还的。

我是在聊我写代码时的意识,不知道有没有扯远。

打算换个思路实现了,实际上我是想让 Base 有不同的处理模式,不同模式在 Base 中处理的分支不一样,但不同模式的 exec 实现内容是一样的。给 Base 添加模式属性进行判断,就是需要改动的 Base 代码比较多

然后你说的给 Base 里面加条件来判断,我感觉路有点走反了。本来应该是选择用 OOP 的多态特性来替换掉重复的类型条件判断的。因为多态本身在执行时就进行了类型判断,这样能省掉许许多多重复的条件判断代码。否则容易在一个类里积累起来很多带相同条件判断的方法。

但你的意思似乎是反过来,把子类里的代码都移入父类,然后在父类里加上类型标志来做判断。

qiumaoyuan 回复

实际情况比较复杂,不过遇到问题正面解决我是绝对赞成的

最终更换的方式并不是把子类的代码转移到父类,而是从 [由类控制模式] 改为 [由实例控制模式] 因为顶楼的例子并没有写的很详细,可能产生了误解。无论哪种方式,在 Base 中都会有个模式属性,只不过一开始我偏向于添加类变量,即

class Base
  class << self
    attr_accessor :mode
  end
end

class BizA < Base; end
class BizB < Base; self.mode = :fast; end

Base中会用当前 class 的 mode 属性做一些分支处理,而 exec 方法的实现纯粹是各自 Biz 自己的业务逻辑,但是 BizB 和 BizA 从 Biz 这一层来看,exec 是完全相同的,只是 mode 不同

如一开始所说,因为 BizA 和 BizB 并没有上下级关系,我不想直接用继承 其实可以把 mode 类变量变为实例变量(现在也是这么改的),这样就不需要额外定义 BizB 了,只需要在运行时修改 BizA 的 mode 就可以了。但是为一开始不这么做,因为Base中很多地方使用了self.class.name作为外部持久化的值,如果不额外定义 BizB,这些地方就会出问题,于是钻入了牛角尖,当时的想法是 BizB 能复制 BizA 的 exec 方法即可解决了,搜到 stackoverflow 上也有人问类似的问题 https://stackoverflow.com/questions/9294205

doraemon0711 回复

确实网络上不好讨论,项目里的代码枝枝蔓蔓的,要放论坛上,不知道切多少出来合适。上下文不完整的话容易误解。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号