매개변수가 있는 Ruby 클래스에 모듈 포함

문제:



행동과 약속이 다른 2 개의 수업이 있습니다. 그러나 일부 메서드에는 동일한 코드가 있습니다.

class User
  def greeting
    puts "Hello!"
  end
end

class AdminUser
  def greeting
    puts "Hello!"
  end
end

user = User.new
user.greeting # => "Hello!"

admin = AdminUser.new
admin.greeting # => "Hello!"


Ruby는 단일 상속 언어이며 클래스UserAdminUser에 이미 다른 기본 클래스가 있는 경우 두 번째 클래스에서 확장할 수 없습니다. 어쨌든, 이러한 클래스에 기본 클래스가 없는 경우 greeting 메서드를 상속하기 위해 두 클래스 모두에 대한 기본 클래스를 만드는 것은 나쁜 방법입니다.

최악의 솔루션:




class Base
  def greeting
    puts "Hello!"
  end
end

class User < Base
end

class AdminUser < Base
end


기본 솔루션:



Ruby의 모듈은 단일 상속 제한을 우회하는 방법입니다.

module Greetable
  def greeting
    puts "Hello!"
  end
end

class User
  include Greetable
end

class AdminUser
  include Greetable
end


작업을 복잡하게 만드십시오.



우리의 앱은 매일 성장하고 있습니다. 수업 UserAdminUser는 행동과 약속이 다릅니다. 각 클래스에는 사람 이름이 있지만 User에서는 full_name이고 AdminUser에서는 nick입니다.

class User
  include Greetable

  attr_reader :full_name

  def initialize(full_name)
    @full_name = full_name
  end
end

class AdminUser
  include Greetable

  attr_reader :nick

  def initialize(nick)
    @nick = nick
  end
end


그리고 greeting 메서드에 닉네임이나 전체 이름을 추가하기로 결정합니다. 이 아키텍처에서는 불가능하지만 name와 같은 세 번째 메서드를 만들고 각 클래스에서 재정의할 수 있습니다.

module Greetable
  def greeting
    puts "Hello, #{name}!"
  end

  def name
    raise NotImplementedError, "not implemented method name"
  end
end

class User
  include Greetable

  attr_reader :full_name

  def initialize(full_name)
    @full_name = full_name
  end

  def name
    full_name
  end
end

class AdminUser
  include Greetable

  attr_reader :nick

  def initialize(nick)
    @nick = nick
  end

  def name
    nick
  end
end


클래스가 2개 이상인 경우 각 클래스에 숨겨진 메서드를 구현해야 하고 포함할 때마다 메서드name를 기억해야 하기 때문에 좋은 변형처럼 보이지 않습니다. 또한 일부 동작을 포함하기 위해 기본 클래스에서 다른 일부를 구현해야 하는 경우 이는 큰 고통이며 개발자의 삶을 쉽게 만들지 않습니다.

해결책:



메시지 넣기에 매개변수를 사용하는 메서드[]가 있는 새 모듈을 반환하는 하나의 매개변수Greetable가 있는 모듈name_attribute에 메서드greeting를 만듭니다.

module Greetable
  def self.[](name_attribute)
    Module.new do
      define_method :greeting do
        puts "Hello, #{public_send(name_attribute)}!"
      end
    end
  end
end

class User
  include Greetable[:full_name]

  attr_reader :full_name

  def initialize(full_name)
    @full_name = full_name
  end
end

class AdminUser
  include Greetable[:nick]

  attr_reader :nick

  def initialize(nick)
    @nick = nick
  end
end

User.new("Bob").greeting # => "Hello, Bob!"
AdminUser.new("root").greeting # => "Hello, root!"


메모:


define_method 값을 얻기 위해 클로저를 사용해야 하므로 일반적인 def method_name 대신 모듈에서 name_attribute를 사용하는 것이 중요합니다. def method_name를 사용하는 경우 클로저는 독립적인 범위를 가지므로 def 코드 내부에서 작동하지 않습니다.

즐거운 코딩 되세요 여러분!

좋은 웹페이지 즐겨찾기