Ruby 请教 继承 父类后 需要 覆盖 父类的 private method 如何做到 在不加上 private 宣告 ,仍然是 private method

shewer · 2018年11月17日 · 最后由 shewer 回复于 2018年11月27日 · 2161 次阅读

请教各位先进 原由是这样的:

我想要做 模板模式,需要子类去实现 子方法 可是子方法部份不想要 PUBLIC 为避免以后的 建立子类 忘了加上 private 宣告,而把方法变成 public method.

或者各位有其他做法可以解决 这个问题 谢谢

class A
    private
     def  priv_a
             3
     end
end


class B   < A
    def priv_a
          5
    end
end
a=A.new
a.private_methods    # 有 method :a 
b=B.new
b.private_methods   #  没有 metode :a 
b.methods  # 有 method  :a 
# 表示   method :a  被重新定 且换到到 public_method



1 楼 已删除

你可以在父类加一个限制,不符合条件就抛出异常,比如

class A
  def initialize
    must_private_method :text
  end

  def must_private_method(method)
    if self.private_methods.include?(method)
      # 方法是私有的时候输出一句话
      puts "#{self.class.name} #{method} method is private method" 
    else 
      # 方法不是私有的时候抛出异常进行提示
      raise "#{self.class.name} #{method} method is not private method" 
    end
  end

  private 
  def text
    3
  end
end


class B < A
  def text
    5
  end
end

a = A.new
# A text method is private method
b = B.new
# in `must_private_method': B text method is not private method (RuntimeError)

@tuliang : 如果子类 重定义 initialize 且没有 super 时 又会被 pass 掉; 在想 能否在 建立 class 时就能检查

利用 class method : private_instance_methods & public_instance_methods

def initialize 好像就会自动调回 private_method 这个方法 不知道在那里?

可以用继承钩子解决你的问题,Class#inherited

shewer 回复

使用 initialize 是一种方案

4 楼说了另外一种方案

思路都是一样的,即在使用 B 的调用链路上去检查 B 的某个方法是否是私有

@pinewong
刚才试了一下 inherited , 是在 subclass 建立时 执行,class block 内容是在 inherited 之后,这样就无法确认子类 宣告的 方法

class Foo
  def self.inherited(subclass)
    puts "New subclass: #{subclass}"
  end
end

class Bar < Foo
    def a
          3
    end 
    puts "class #{self}"
end

class Baz < Bar
    puts "class #{self}"
    def a
          3
   end
end

#New subclass: Bar
#class Bar
#New subclass: Baz
#class Baz

无论 B 的方法是否 private,都已经覆盖了 A 的那些方法了。不懂 LZ 想问什么

需要子类实现的话,应该是 protected 吧

class A
    protected
     def  priv_a
             3
     end
end

class B   < A
    def priv_a
          5
    end
end

要实现类似 JAVA 之类的 private,可以看下这个

https://stackoverflow.com/questions/1565269/are-there-good-reasons-for-private-to-work-the-way-it-does-in-ruby/1565640#1565640

私有方法不能继承,更不能外部访问

很奇怪的问题,我的实验例子和楼主是一样的,但结果完全和楼主的不一样,我的实验结果完全证明了 9 楼@bysxiang的结论,想知道楼主的 ruby 版本

@bysxiang @sanm1992 啊真的吗? 我是用 2.5.3 2.4 2.3 应该也是可以 以前 试过 ruby method 在 self 找不到 方法会向上找 实际上 并没有覆盖,而是 子类 也有同样名字的方法,的方法 参照到 子类的方法 如果用 undef 把子类的方法拿掉 当找不到方法就会向上去找

比较特别的是 B 类找不到本身的 b 方法 呼叫 A 类 的 b 方法 , A 类 b 方法内的 c 却是 B 类的 c 方法

这样讨论后,自己也有深一点的理解。

class A
    def a 
         4
     end 
    def b
        a + c
    end 
    private 
    def c
        3
    end 
end 

class B < A
    private 
    def c
        50
    end 
end 

a=A.new
a.b  #   a.a +a.b
b=B.new
b.b      使用 a.b  调用到 a.a b.c
'''


shewer 回复

引用:比较特别的是 B 类找不到本身的 b 方法 呼叫 A 类 的 b 方法 , A 类 b 方法内的 c 却是 B 类的 c 方法

因为你的 B 继承了 A 的 b() 方法啊,你在类内部当然是可以调用 B 本身的私有方法的 (即 B#c)

这是面向对象基础知识,找本 java 或.net 的书好好看看吧,别做实验了,对你有害无益

@bysxiang java 的 private 和 RUBY private 不同吧 父类的 private 子类是调用不到的,必须调用继承自父类的 public method,透过调用到 父类的 public method , 才可能调用到 private method

而 ruby 在继承后 子类的 public method 是直接可以调用父类的 private method 的 从 子类的 private_methods 可以看到, 也就是说 子类 可以新增 public method 来调用 父类的 private method

class A
   private 
    def a
        4
    end
end
class B
   def b
       a
   end
end

b=B.new
b.b   #   4

shewer 回复

自己玩吧,你开心就好

我觉得 Ruby 里的 private 没什么必要,能被随意覆盖成非 private 的,用 send 就能打破限制…… 如果真想隐藏一段代码,完全可以用别的方式

顶吕神,接口没有必要强制 private,Ruby 的封装很容易就可以被破坏,如果你希望子类一定要实现某些方法,可以在父类定义接口的时候 raise NotImplementedError

在我的理解当中,private 是用来限制人的,不是限制程序的,让别的程序员不要瞎调用。相当于是个声明或者约定:这个方法在不是必须的情况下请不要在该类的外部调用。

其次,private 的一个优点是,在重构的时候,如果要移除某个方法,或者要重命名某个方法,该方法是 private 的,那么你就能很清楚的知道,我只要在这个类内部做修改就行了,不需要整个项目全局搜索对该方法的调用。

在 Ruby 代码当中,编写所有方法的时候我首先考虑的是 private,尤其是“写入”(修改了该类的成员变量)的方法。只有在发现该类的外部真的需要调用的时候,才会修改为 protected 或者 public。没有必要的情况下绝不暴露接口。

一个方法一旦成为 public,就相当于开放了一个全局的约定,所有人都知道你有这个方法,所有人都可以调用,这时候如果你要修改它,相当于修改了之前公布出来的约定,那么所有使用过这个方法的人有可能都要修改自己的代码来重新调用这个方法。

另一方面,暴露越多的接口,对外部来说这个类越不简洁、越复杂,或者换句话说:越难用。本来一个类就是封装了对该类成员变量操作的所有逻辑,如果把细枝末节的 private 操作都暴露出来,那封装本身就变得没什么意义,本该封装起来的复杂性还是暴露给了外部。外部的调用者很容易受诱惑去调用这些 public 接口,复杂性就这样外泄了,系统逻辑就容易变得混乱。所以写一个方法,private 是首选,真的需要开放的时候再开放。同时这样首先考虑 private 的编写原则也完全避免了楼主所担忧的问题。

还有,父类的 private 方法对子类来说未必就是 private 的,子类本来就是父类的特例化,在一些情况下,外部需要调用子类的某个方法,而该方法在父类当中是 private 的这种情况并不少见。 回头看了一下,这句与主题无关。

结论就是,楼主可能思考的方向错了。

感谢 @qiumaoyuan 的答覆,当初的想法是:父类做抽象接口,子类:实现的 METHOD 用 PRIVATE METHOD,才有这个问题;是否有能够判定子类没有将该方法设定为 private 的警告。

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