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

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

举个例子 以下是个父类

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 中不影响 BizA 和 BizB 继承 Base 吧?其它代码继续放在 Base 里就好了。

如果 module 只是 BizA 和 BizB 共有的,那就只给它俩用这个 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 回复

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

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