06. Класове, именовани пространства и наследяване

06. Класове, именовани пространства и наследяване

06. Класове, именовани пространства и наследяване

28 октомври 2013

Днес

#occupySU

Програмистите са следващите господари на света.

Обратно към Ruby...

Въпрос 1

Какво прави alias?

class Something
  def name() 'baba' end
  alias relative name
  def name() 'dyado' end
end

p Something.new.relative

Прави копие на метода

Въпрос 2

Каква е разликата между alias и alias_method?

alias_method е метод, докато alias е синтаксис

class Something
  1.upto(5).each do |index|
    alias_method "to_s_#{index}", :to_s
  end
end

Въпрос 3

Кой (и как) може да вика private методи?

class Something
  private

  def foo
  end
end
  • Могат да се викат от други методи на обекта
  • Могат да се викат от методи в наследници
  • Не могат да се викат с явен target (self.foo)

Въпрос 4

Какви са конвенциите за имена на методи, променливи, константи и имена на класове?

  • UpperCamelCase - константи (включва имена на класове)
  • normal_snake_case - променливи, методи
  • SCREAMING_SNAKE_CASE - константи, които не са имена на класове или модули

Въпрос 5

На какво могат да завършват методите в Ruby?

  • На ?, ако са предикати
  • На !, ако имат две версии
  • На =, ако са setter-и
  • Технически погледнато, и на някои "оператори", напр. %

Наследяване

Наследяването в Ruby става така:

class Person
  def name() 'The Doctor' end
end

class PolitePerson < Person
  def introduction
    "Hi, I am #{name}"
  end
end

PolitePerson.new.introduction # "Hi, I am The Doctor"

Наследяване

ограничения

private методи

Имате достъп до private методите:

class Person
  private
  def name() 'The Doctor' end
end

class PolitePerson < Person
  def introduction() "Hi, I am #{name}" end
end

PolitePerson.new.introduction # "Hi, I am The Doctor"

Наследяване

#is_a? и #instance_of?

class Base; end
class SpaceStation < Base; end

base    = Base.new
station = SpaceStation.new

base.is_a? Base            # true
station.is_a? SpaceStation # true
station.is_a? Base         # true

base.instance_of? Base            # true
station.instance_of? SpaceStation # true
station.instance_of? Base         # false

Наследяване

#is_a? и #instance_of? (2)

#is_a? има синоним #kind_of?

class Base; end
class SpaceStation < Base; end

base    = Base.new
station = SpaceStation.new

base.kind_of? Base            # true
station.kind_of? SpaceStation # true
station.kind_of? Base         # true

Наследяване

#is_a? и #instance_of? (3)

super

Може да предефинирате метод и да извикате версията на родителя със super.

class Person
  def introduction_to(other)
    "Hello #{other}."
  end
end

class PolitePerson < Person
  def introduction_to(other)
    super("Mr. #{other}") + " How do you do?"
  end
end

queen = PolitePerson.new
queen.introduction_to('Smith') # "Hello Mr. Smith. How do you do?"

super (2)

Ако извикате super без скоби родителският метод получава същите аргументи.

class Person
  def introduction_to(other)
    "Hello #{other}."
  end
end

class PolitePerson < Person
  def introduction_to(other)
    super + " How do you do?"
  end
end

queen = PolitePerson.new
queen.introduction_to('Smith') # "Hello Smith. How do you do?"

super (3)

super и super() са различни:

class Person
  def introduction_to(other)
    "Hello #{other}."
  end
end

class PolitePerson < Person
  def introduction_to(other)
    super() + " How do you do?"
  end
end

queen = PolitePerson.new
queen.introduction_to('Smith') # error: ArgumentError

Ancestor chain

Ancestor chain (2)

module Foo; end
module Bar; end
module Qux; end

class Base
  include Foo
end

class Derived < Base
  include Bar
  include Qux
end

Derived.ancestors # [Derived, Qux, Bar, Base, Foo, Object, Kernel, BasicObject]

Ancestor chain (3)

модули, миксирани в други модули

module Foo; end
module Bar; end

module Qux
  include Foo
  include Bar
end

class Thing
  include Qux
end

Thing.ancestors # [Thing, Qux, Bar, Foo, Object, Kernel, BasicObject]

Ancestor chain (4)

Има само една версия на метода:

module Talking
  def greeting() "Hello, #{name}" end
end

class Base
  include Talking
  def name()        'Base'   end
  def say_hi_base() greeting end
end

class Derived < Base
  include Talking
  def name()           'Derived' end
  def say_hi_derived() greeting  end
end

derived = Derived.new
derived.say_hi_base    # "Hello, Derived"
derived.say_hi_derived # "Hello, Derived"

methodfinder

$ gem install methodfinder

Интересен gem:

Основните класове в Ruby

"Всичко наследява от Object"

Основните класове в Ruby

protected

Само обекти от същия клас могат да викат protected методи

class Vector
  def initialize(x, y) @x, @y = x, y          end
  def inspect()        "Vector.new(#@x, #@y)" end

  def +(other)
    Vector.new(*coords.zip(other.coords).map { |a, b| a + b })
  end

  protected
  def coords() [@x, @y] end
end

vector = Vector.new(1, 2) + Vector.new(3, 4)
vector        # Vector.new(4, 6)
vector.coords # error: NoMethodError

private и protected

още известни като Private, Public и General Specific

Клас-макросите private и protected

Понеже private и protected са методи:

class Person
  def name() end
  def age()  end

  private :name, :age
end

Или дори:

class String
  private :upcase!, :downcase!
end

"Foo".upcase! # error: NoMethodError

Клас-макросите private и protected (2)

Помните ли, че дефиницията на метод връща името на метод като символ?

def foo() end # :foo

Значи може да направите така:

class Person
  private def name
    "My name is a secret."
  end

  private def age
    "I'm feeling feminine, so my age is a secret, too."
  end
end

Именувани пространства

Класовете и модулите могат да служат като именувани пространства.

module Useless
  class Thing
  end
end

class Grandfather
  class StraightRazor
  end
end

Useless::Thing.new             # #<Useless::Thing:0x41aa9d44>
Grandfather::StraightRazor.new # #<Grandfather::StraightRazor:0x41aa9b14>

Именувани пространства (2)

Ако се намирате в модул, няма нужда да ползвате пълния път до константите:

module Useless
  class Thing
  end

  Thing.new         # #<Useless::Thing:0x41f65220>
end

Useless::Thing.new  # #<Useless::Thing:0x41f64ff0>
Thing.new           # error: NameError

Търсене на променливи в Ruby

bacon = 2

def foo
  chunky = 10

  1.times do
    chunky       # 10
    chunky = 44
  end

  chunky         # 44
  bacon          # error: NameError
end

foo()

Правила за търсене на константи

Малък пример

PLACE = 'root'
module Outer
  PLACE = 'intermediate'
  module Inner
    PLACE = 'deep'
  end
end

PLACE               # "root"
Outer::Inner::PLACE # "deep"
module Outer
  module Inner
    PLACE           # "deep"
    ::PLACE         # "root"
  end
  PLACE             # "intermediate"
  Inner::PLACE      # "deep"
end

Класови методи

Може да дефинирате класови методи така:

class Something
  def Something.answer
    42
  end
end

Something.answer   # 42

Класови методи (2)

Не може да ги викате неквалифицирано от инстанцията:

class Something
  def Something.answer
    42
  end

  def do_stuff
    answer             # error: NameError
    Something.answer   # 42
  end
end

thing = Something.new
thing.answer           # error: NoMethodError
Something.answer       # 42

thing.do_stuff

Класови методи (3)

Достъпни са в наследниците:

class Base
  def Base.answer() 42 end
end

class Derived < Base
  def Derived.say_answer
    answer         # 42
    Base.answer    # 42
  end
end

Derived.answer     # 42
Base.answer        # 42

Derived.say_answer

Класови методи (4)

Има и други начини за дефиниция на класови методи:

Класови методи (5)

Въпроси