2015-10-13 43 views
2

我已经建立了两个类如下图所示红宝石继承和重写类方法

class Parent 

    def self.inherited(child) 
    child.custom_class_method 
    end 

    def self.custom_class_method 
    raise "You haven't implemented me yet!" 
    end 
end 

class Child < Parent 

    def self.custom_class_method 
    "hello world" 
    end 
end 

看来,当的传承Child < Parent评估,它调用self.inherited,这又引发了self.custom_class_methodParent的版本而不是Child的。这是因为,而不是让问题预期"hello world"我得到一个错误募说"You haven't implemented me yet!"

Child的,直到后self.custom_class_method没有得到评估Parentself.inherited完成评估?如果是的话,那么可能是围绕这个做了一个工作?我是不是应该在父类上放一个raise检查?

+0

这似乎很奇怪。获得父类的'custom_class_method'的唯一方法应该是调用'super'。否则,只调用'Child.custom_class_method'应该会导致您的“hello world”输出。你能否提供更深入的日志记录? –

+0

嗯,我同意它应该!但似乎我可以通过将代码块复制粘贴到“irb”控制台来复制此内容。由于这个原因,评估“Child”类时会出错。 – aMat

回答

3

我认为这应该澄清:

class Parent 
    def self.inherited(child) 
    puts "Inherited" 
    end 
end 

class Child < Parent 
    puts "Starting to define methods" 
    def self.stuff; end 
end 

输出清楚地表明,.inherited被称为打开新类的那一刻,而不是当你关闭它。因此,正如你猜测的那样,Child.custom_class_method在你试图调用它的时候不存在 - 所有的.inherited都是空白的。

(至于怎么去解决它......我不能没有更深入到说你正在尝试做的,很遗憾。)

+0

啊聪明,我没有想到这样调试它!那么在这里定义抽象方法的最佳方式是什么?我只是避免提出错误(这是我不坦率的说实话)。 – aMat

+0

基本上我想要做的是创建一个能够在'self.inherited'块中执行代码的父类,它将从子类到子类稍有不同。然而,为了确保我们不会出错,我想强制每个继承孩子都会定义'custom_method'(因此为什么我会提升Parent类)。 – aMat

+0

不要问“我如何获得继承的子代码”,因为你不能。这似乎是一个XY问题。 *为什么*你想要继承孩子代码? '.inherited'是为了通知新生儿的父母,而不是在儿童身上运行密码。 *孩子*可以在孩子身上运行代码(参见我的'puts')。 – Amadan

2

模板模式/延迟初始化可能会帮助您解决问题。代码假设子类之间有什么不同是数据库连接信息,也许只是一个表名或可能完全不同的数据库。父类拥有创建和维护连接的所有代码,只让儿童承担提供不同的责任。

class Parent 
    def connection 
    @connection ||= make_connection 
    end 

    def make_connection 
    puts "code for making connection to database #{database_connection_info}" 
    return :the_connection 
    end 

    def database_connection_info 
    raise "subclass responsibility" 
    end 
end 

class Child1 < Parent 
    def database_connection_info 
    {host: '1'} 
    end 
end 

class Child2 < Parent 
    def database_connection_info 
    {host: '2'} 
    end 
end 

child1 = Child1.new 
child1.connection # => makes the connection with host: 1 
child1.connection # => uses existing connection 

Child2.new.connection # => makes connection with host: 2 
+0

真棒的东西,谢谢!作为一个设计问题,你认为这比将数据抽象成一个DatabaseManager模型更好/更糟糕,该模型维护这个连接逻辑并在每个类中调用DatabaseManager.make_connection而不是继承它? – aMat

+0

优秀的问题。代表团与继承是一个古老的问题。 Rails通过继承进行持久化(技术上可以包含模块,但通常通过继承来完成)。许多人认为持久性应该不是班级的责任,而是走向另一个方向,比如数据映射模式(https://en.wikipedia.org/wiki/Data_mapper_pattern)。即使你属于持久性责任阵营,你仍然可能想在课堂外抽象出数据库行为 - 可能是连接池。这里有很好的库,比如续集宝石。 –

+0

我没有结合'DatabaseManager'的想法,所以这是另一个很好的建议。正如我所说,我的答案一定是模糊的,因为你比我更了解你的项目方式。我试图做的主要观点是,你不能在'继承'上做到这一点。你可以使用外部管理器,或者像这里一样使用继承方法,或者甚至在祖先上使用'initialize';但“继承”有另一个目的。 – Amadan