D-22. 继承(继承链问题)、模组(extend、include、prepend差异) && Add to Array-Form of Integer

继承(Inheritance)

class A < B<就是继承,A继承B
Ruby是单向继承,抱歉我用比较粗俗的方式说明,只可以有一个爸爸,但可以有很多小孩。
另外模组不是养父,是爸爸学会了,儿子就会了,模组也不是越多功能越好,能做到几个相同性质组一个模组最好。还有昨天说明过了,类别方法都会被继承。

继承看code就能明白。设定一个类别。

class Protype_monster
  attr_accessor :name, :power
  def initialize(name = "黑龙", power = rand(800..900))
    @name = name
    @power = power
  end

  def final_fight
    final_boss
  end

  protected
  def cant_sleep
    puts "有睡眠抵抗力"
  end

  private
  def final_boss
    puts "我通常在最後一张地图出现"
  end
end

2.7.3 :022 > millaboreus = Protype_monster.new
 => #<Protype_monster:0x00007f88f941b020 @name="黑龙", @power=889>
 
2.7.3 :023 > p millaboreus
#<Protype_monster:0x00007f88f941b020 @name="黑龙", @power=889>
 => #<Protype_monster:0x00007f88f941b020 @name="黑龙", @power=889>
 
2.7.3 :024 > millaboreus.final_fight
我通常在最後一张地图出现
 => nil
 
millaboreus.cant_sleep   #NoMethodError:protected方法不能外篰直接使用。
millaboreus.fly          #NoMethodError
millaboreus.environment  #NoMethodError

再来是一个子层。

class Elder_dragon < Protype_monster
  def fly
    puts "滞空#{rand(30..50)}秒"
  end

  def environment
    rand(1..10) > 5 ? (puts "改变环境") : (puts "环境未改变")
  end

  def resistance
    self.cant_sleep
  end
end

ksardaora = Elder_dragon.new("钢龙")

2.7.3 :042 > p ksardaora
#<Elder_dragon:0x00007f88f9412060 @name="钢龙", @power=850>
 => #<Elder_dragon:0x00007f88f9412060 @name="钢龙", @power=850>
 
2.7.3 :043 > puts ksardaora.name
钢龙
2.7.3 :044 > puts "力量#{ksardaora.power}"
力量850
##子层没设定的,但父层有,所以继承沿用,attr_accessor系列,initialize的架构。

2.7.3 :045 > ksardaora.fly
滞空38秒
2.7.3 :046 > ksardaora.environment
环境未改变
#子层的方法,父层无法使用。

ksardaora.final_boss  #=>NoMethodError ,private的实体方法不继承。
2.7.3 :048 > ksardaora.resistance  ##protected内的可继承,所以使用方式正确就可用。
有睡眠抵抗力  

可以被很多子类别继承。

class New_elder_dragon < Elder_dragon
end

class Fly_dragon < Elder_dragon
end

2.7.3 :057 > tigrex = Fly_dragon.new("轰龙", 600)
 => #<Fly_dragon:0x00007f88f93f2e90 @name="轰龙", @power=600>
 
2.7.3 :058 > tigrex.fly
滞空46秒

2.7.3 :059 > tigrex.environment
环境未改变

最後的类别,爸爸跟爷爷会的自己都会,当然这样不算好设计,最後的怪物变什麽都会。

顺便补充了protected
重点在於,单向继承,利用子类别里没有的方法,Ruby会往父层找的功能(父没有,会继续往模组以及父的上层找),子类别可以加入其他更多功能,也不会觉得过於庞大。如有同名方法,看是父或子类别呼叫,以用自己的优先。


模组(Module)

Class最大差异在於,模组模组间无法继承,以及无法New物件,Class中如果引入模组,其子class会继承到父层引入的模组,但无法模组继承模组,另外Class的superclass是Module

Mixins:由於Ruby不支援多重继承,可以以模组来扩充功能,让子类别越来越完整。

无论Ruby或在Rails上,Module通常会集中放置。

module Elder
  def environment
    rand(1..10) > 5 ? (puts "改变环境") : (puts "环境未改变")
  end
end

module Flight_mode
  def stay_in_the_air
    puts "滞空#{rand(30..50)}秒"
  end
end

module Scared
  def scared
    puts "被闪光弹吓到会站着不动"
  end
end

module Brute_wyvern
  def run_wild
    puts "朝向玩家冲撞"
  end

  def head_hammer
    puts "使用头部攻击"
  end
end

我把一些skill先模组化。

接着重新设计class

class Biology
  include Scared  ##引入模组
  attr_accessor :name, :power
  def initialize(name = "食草龙", power = 10)
    @name = name
    @power = power
  end

  def behavior
    eat()
  end

  private
  def eat
    puts "吃草!"
  end
end

2.7.3 :045 > herbivore = Biology.new
 => #<Biology:0x00007fc9c13318e0 @name="食草龙", @power=10>
 
2.7.3 :046 > herbivore.behavior
吃草!
 => nil
 
2.7.3 :047 > herbivore.scared
被闪光弹吓到会站着不动
 => nil

初始的class设计成只有基本功能。

接着第二个类别。

class Velkhana < Biology

  include Elder
  include Flight_mode  #模组引入
  
  def initialize(name = "冰咒龙", power = 650)
    @name = name
    @power = power
  end

  def behavior
    eat_snow()
  end

  private
  def eat_snow
    puts "吃雪!"
  end
end

2.7.3 :066 > velkhana = Velkhana.new
 => #<Velkhana:0x00007fc9c1238c18 @name="冰咒龙", @power=650>
 
2.7.3 :067 > velkhana.behavior 
吃雪!
 => nil
 
2.7.3 :068 > velkhana.environment
环境未改变
 => nil
2.7.3 :069 > velkhana.stay_in_the_air
滞空43秒
 => nil
2.7.3 :070 > velkhana.scared
被闪光弹吓到会站着不动   ## 引入模组会被继承,所以它也会被吓到

velkhana.run_wild  #NoMethodError 模组没引入不能用
velkhana.head_hammer #NoMethodError 模组没引入不能用

Velkhana.stay_in_the_air  #NoMethodError (undefined method `stay_in_the_air' for Velkhana:Class), include方式导入模组,都是实体方法,类别都不能用。

第三个类别

class Brute_wyverns < Biology
  include Brute_wyvern
  def initialize(name = "惶怒恐暴龙", power = 700)
    @name = name
    @power = power
  end

  def behavior
    boom()
  end

  private
  def boom
    puts "黑色的大黄瓜,超烦人!"
  end
end

2.7.3 :089 > savagedeviljho = Brute_wyverns.new
 => #<Brute_wyverns:0x00007fc9c132af18 @name="惶怒恐暴龙", @power=700>

2.7.3 :090 > savagedeviljho.scared
被闪光弹吓到会站着不动
 => nil
2.7.3 :091 > savagedeviljho.run_wild
朝向玩家冲撞
 => nil
2.7.3 :092 > savagedeviljho.head_hammer
使用头部攻击
 => nil

savagedeviljho.environment  #没引入模组,不会这些
savagedeviljho.stay_in_the_air  #没引入模组,不会这些

最後来一个什麽都会的魔王。

class Final_boss < Biology
  extend Elder, Flight_mode, Brute_wyvern
end


2.7.3 :097 > Final_boss.run_wild
朝向玩家冲撞
 => nil
2.7.3 :098 > Final_boss.head_hammer
使用头部攻击
 => nil
2.7.3 :099 > Final_boss.environment
改变环境
 => nil
2.7.3 :100 > Final_boss.stay_in_the_air
滞空35秒

顺便以这份code显示了,子与父层如有同名方法,可以自行更改做扩充或改变。

def behavior
  boom()
end

code中也展示了extendinclude的差异。
extend引入的模组方法成为类别方法。
include引入的模组方法成为实体方法。
还有一个prepend下面会说道。


模组同名方法处理

这个不是说引用原则,而是设计上怎麽处理,在设计上难免得同名。
引用才发现同名,请乖乖回头修改...

module One
  def One.say_hi  #Modlue_name.method_name
   puts "你好喔!"
  end
end

module Two
  def Two.say_hi  #Modlue_name.method_name
   puts "我今天很好喔!"
  end
end

class Hello
  extend One
  extend Two
  def hello
    One.say_hi
    Two.say_hi
  end
end

2.7.3 :060 > Hello.new.hello
你好喔!
我今天很好喔!
 => nil

prepend

有点反向思考,但没办法,初期常被问的问题之一。
刚刚我尽量模组写在上面,这个要反过来写。

class Hello
  prepend One
  def say_hi
    "你好喔"
  end
end

module One
  def say_hi
    "先呼叫模组的say_hi,再跑到super,也就是父层执行  " + super
  end
end

2.7.3 :125 > Hello.new.say_hi
 => "先呼叫模组的say_hi,再跑到super,也就是父层执行  你好喔"

如果没super

class Hello
  prepend One
  def say_hi
    "你好喔"
  end
end

module One
  def say_hi
    "先呼叫模组的say_hi,没了"
  end
end

2.7.3 :166 > Hello.new.say_hi
 => "先呼叫模组的say_hi,没了"

如果是改用extend``include,就没效果了。

class Hello
  extend One
  include One
  def say_hi
    "你好喔"
  end
end

module One
  def say_hi
    "先呼叫模组的say_hi,再跑到super,也就是父层执行  " + super
  end
end

2.7.3 :029 > Hello.new.say_hi
 => "你好喔"
2.7.3 :030 > Hello.say_hi
NoMethodError (super: no superclass method `say_hi' for Hello:Class)

简单点想extend``include``prepend都是能挂载模组,extend引入的模组方法成为类别方法。
include引入的模组方法成为实体方法。prepend能让class使用super方法,使模组运算完的资料再到自己的方法里处理。


既然说到了super...

还记得superclass吗?

2.7.3 :031 > Array.superclass
 => Object

一开始说明继承的程序码,已经显示super是不用写入,Ruby也会自动使用的,那就看看指定使用的方式。

class Top
  def self.one  #已经开始懒得想方法名了,直接用self代表连new都懒了
    "hello, "
  end
end

class Child < Top
  def self.one
    super + "Player!"
  end
end

2.7.3 :048 > Child.one
 => "hello, Player!"

对,不是模组prepend才能用supersuper本身就是一种方法。

class Super_top
  def self.one
    "OH! "
  end
end

class Top < Super_top
  def self.one
    super + "hello, "
  end
end

class Child < Top
  def self.one
    super + "Player!"
  end
end
2.7.3 :018 > Child.one
 => "OH! hello, Player!"

娱乐性质示范~~


再来,带参数使用。

class Top
  def math(int)
    int * 10
  end
end

class Child < Top
  def math(int)
    super * 10
  end
end
2.7.3 :012 > Child.new.math(10)
 => 1000

参数是由子传给父层方法处理。

class Top
  def math(int)
    int = 5
  end
end

class Child < Top
  def math(int)
    int * super
  end
end
2.7.3 :012 > Child.new.math(20)
 => 100

接着是如果不想把参数传给上层

class Top
  def math
    40
  end
end

class Child < Top
  def math(int)
    int * super()
  end
end
2.7.3 :012 > Child.new.math(20)
 => 800

最後是传给block

class Top
  def say_hello
    puts "123"
    yield
  end
end

class Child < Top
  def say_hello
    puts "456"
    super
  end
end

2.7.3 :015 > Child.new.say_hello {puts "789"}
789
123
456

呼...

记得require
自己制作的模组不像Math,虽然Math也是模组,但已经成为Ruby的标准函式库了。
可以直接以Module_name.Moudle_method_name这样呼叫

2.7.3 :002 > Math.sqrt(16)
 => 4.0

而其实还是可以引入,直接让irb的空白物件挂载。

:001 > sqrt(16)  #这样不行
NoMethodError (undefined method 'sqrt' for main:Object)

:002 > include Math  #这样後就可以了
 => Object
:003 > sqrt(16)
 => 4.0

第八天的leetcode989. Add to Array-Form of Integer
题目连结:https://leetcode.com/problems/add-to-array-form-of-integer/
题目重点:很简单,手续比较多而已。

# @param {Integer[]} num
# @param {Integer} k
# @return {Integer[]}
def add_to_array_form(num, k)

end

puts add_to_array_form([1,2,0,0], 34) #=>[1, 2, 3, 4]
puts add_to_array_form([2,7,4], 181) #=>[4, 5, 5]
puts add_to_array_form([2,1,5], 806) #=>[1, 0, 2, 1]
puts add_to_array_form([9,9,9,9,9,9,9,9,9,9], 1) #=>[1,0,0,0,0,0,0,0,0,0,0]

第一个例子其实就把答案说完了。

[1, 2, 0, 0] #=> 1200
 :085 > [1, 2, 0, 0].join.to_i
 => 1200

1200 + 34 # Array.join.to_i + k
1234 #=> [1, 2, 3, 4]
:088 > 1234.to_s
 => "1234"
:090 > "1234".split""
 => ["1", "2", "3", "4"]
:091 > ["1", "2", "3", "4"].map{|num| num.to_i}
 => [1, 2, 3, 4]

整理完

def add_to_array_form(num, k)
  (num.join.to_i + k).to_s.split("").map(&:to_i)
end

今天提到的
1.类别继承
2.Module
3.super方法
4.leetcode989. Add to Array-Form of Integer

明天会解释&``monkey patch
Ruby部分就介绍完噜!

头一个礼拜发现篇幅过大,重点容易丢失,之後就都是一个Rails问题跟一题Leetcode了。


<<:  @Day8 | C# WixToolset + WPF 帅到不行的安装包 [自订动作]

>>:  Day 03:专案01 - 超简单个人履历02 | HTML基本元素

谈思考

我先说一个故事。我认识的一位建筑师长辈,前些日子新居落成,邀请我们去他新家喝咖啡聊天。这位长辈说,他...

DAY1- 密码学在干嘛?

我快要猜到你的密码了。等一下,为什麽是我的生日。 我是谁? 徐老师 开玩笑的。我是陈彦龙 ,主修经济...

[30天 Vue学好学满 DAY25] axios API

vue.js2.0後版本推荐使用axios来完成ajax请求 为Promise-based HTTP...

Day 1 - 前言,写作动机分享与准备事项

去年参加 Software Development 类别的铁人赛,主题为PHP 大师之路 - 开源的...

[Day 8] 2D世界中的数学 (一)

今日目标 基本的数学函式库(向量与阵列) 要多少才够 从另一个角度看,我认为游戏中的从小小的让角色移...