Day15. Inheritance & Super - Ruby 继承 part2

Day2 提到过,Ruby为单一继承的语言。若我们要实现多重继承的话,我们在 Day14 提到可以使用mixin。今天要介绍的是 Ruby程序语言内,Class 级别的继承

# 後台退货单controller
module Admin
  class ReturnOrdersController < ApplicationController
    # Day14 提到的Datatable module
    include Datatable
  end
end


class Admin::ApplicationController < ApplicationController
  # admin 後台动作
end

Rails的专案中,我们一般会在後台管理介面的内容放在admin,并给予NameSpaceAdmin

从上方的关系我们看到,Admin::ReturnOrdersController 继承了 Admin::ApplicationController,而 Admin::ApplicationController 则继承了ApplicationController。我们用以下表格说明彼此的权责分工

Class in Controller 说明
Admin::ReturnOrdersController 後台退货单 Controller
Admin::ApplicationController 後台管理介面相关的 Controller 的动作
ApplicationController 所有 Controller 的动作

Admin::ReturnOrdersControllerancestors 一共有下面这些。

Admin::ReturnOrdersController.ancestors

#=> [Admin::ReturnOrdersController, Datatable, Admin::ApplicationController, #<Module:0x00007fe5cf522800>, ApplicationController, ..., Object, ..., Kernel, BasicObject]

我们分别可以看到 Admin::ReturnOrdersController, Datatable, Admin::ApplicationController, ApplicationController 在继承链的位置。

Inheritance

我们用 ancestors 的继承链,可以看到继承关系如下!

class A 
end

class B < A
end

class C < B
end

C.ancestors 
#=> [C, B, A, Object, Kernel, BasicObject]

我们习惯会称 B为A的子类别SubClass、C为B的子类别。如果看到外国文章提到SubClass,记得就是指这种关系。

#inherited

在使用继承的方法时,可以使用#inherited hook,我们可以在#inherited 新增实体方法跟类别方法。

class A 
  def self.inherited(subclass)
    puts "Singleton class just got subclassed by #{subclass}"
    
    #=> add instance method
    subclass.class_eval do
    end
    
    #=> add class method
    subclass.instance_eval do
    end
  end
end

class B < A
end
#=> Singleton class just got subclassed by B

super

当我们使用了super的关键字,就可以取得继承链上一层的内容。换句话说,若我们在继承链写super,则可以避免覆写方法。

下列例子中,StrongWarrior 继承了战士的属性。

module Sword
  mattr_accessor :material, default: :silver
  
  def sword_name
    [material.capitalize, 'Sword'].join(' ')
  end
end

class Warrior
  include Sword
  attr_accessor :height, :weight, :tag
  
  def initialize(height = nil, weight = nil)
    @weight = weight
    @height = height
  end
  
  def warrior_info
    {
      height: height,
      weight: weight,
      tag: tag
    }
  end
end

super 不带参数,代表概括继承。首先,我们使用没有参数的super

class StrongWarrior < Warrior
  def initialize(height, weight)
    super
    @tag = 'Strong'
  end
end

warrior = StrongWarrior.new(172, 72)
warrior.warrior_info   #=> {:height=>172, :weight=>72, :tag=>"Strong"}

super带参数的用法也可以

class WeekWarrior < Warrior
  def initialize(height, weight)
    super(height)
    @tag = 'Week'
  end
end

warrior = WeekWarrior.new(172, 72)
warrior.warrior_info  #=> {:height=>172, :weight=>nil, :tag=>"Week"}

带入空括号,代表不带参数进去。

class NilWarrior < Warrior
  def initialize(height, weight)
    super()
    @tag = 'Week'
  end
end

warrior = NilWarrior.new(172, 72)
warrior.warrior_info  #=> {:height=>nil, :weight=>nil, :tag=>"Week"}

⭐️ 由此可以知道,super, super()是完全不一样的概念

super可以在SubClass的各种方法内使用。

class SuperWarrior < Warrior  def initialize(height, weight)    super    @tag = 'Week'  end    def warrior_info    { **super, a: 1, b: 2 }   endendwarrior = SuperWarrior.new(172, 72)warrior.warrior_info  #=> {:height=>172, :weight=>72, :tag=>"Week", :a=>1, :b=>2}

SuperWarrior#warrior_info 的概念是将原本的Warrior#warrior_info额外填写其他资料。

此外,Super可以搭配yield来使用喔!

class Warrior
  attr_accessor :height, :weight, :tag
  
  def initialize(height = nil, weight = nil)
    @weight = weight
    @height = height
  end
  
  def foo
    block_given? ? yield : 'foo'
  end
end

class YieldWarrior < Warrior
  def initialize(height, weight)
    super
    @tag = 'Yield'
  end
  
  def foo
    [super {"I am a good boy"}, "am I?"].join(', ')
  end
end

warrior = YieldWarrior.new(172, 72)
warrior.foo  #=> "I am a good boy, am I?"

继承的基本概念是这样,接着我们讲Bonus的主题 ➡️ 如何取得该类别的各种方法

methods tips and tricks

Day10-15 中,已经介绍了几个方法

ancestors
instance_methods
singleton_methods

我们可以参考以下的方式筛选查看方法

User.instance_methods
#=> [:name_in_email, :email_upcase, ..., ..., ...] 

#======= false ➡️ 不包含 ancestors 的方法 =======#
User.methods(false)
User.instance_methods(false)

#======= 用减集合来排除多余的方法 =======#
User.methods - ActiveRecord::Base.methods

#======= grep =======#
string = "This is my IT challenge for 15th day."
string.methods.grep(/.!/).sort

#======= 取得 ancestors =======#
A.singleton_methods - A.singleton_methods(false)

结论

这两天分别讲到module, class级别的继承,大致上已经介绍了一个段落。

明天会开始介绍常用的设计流程

参考资料


<<:  [Day 1] - 前言

>>:  Day3

DAY 02 CSS 预处理器

预处理器是什麽? 透过不同的编译方式,最後都会产生成 CSS 的样式,在变成 CSS 前,这些预处理...

How to split a (HUGE) pcap file into a set of smaller ones ? keyword: tcpdump

tcpdump Introduction wiki: tcpdump 是一个执行在命令列下的嗅探工具...

[DAY11] Data Access Layer 设计概念

前言 这篇将介绍 boxenn 与 DAL 层的依赖关系和介面。 简易 Class Diagram ...

後记

这是铁人赛的最後一篇文章,我想在这个结尾分享为什麽我会写这个主题,这是因为几个月前的某一天我正在与跨...

第 7 集:你有 Flex Style 吗?

此篇会简单介绍 FlexBox,以及 flex container 的排列方向设置、对齐设置技巧。 ...