ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Programming Ruby (10) 모듈
    Ruby 2016. 10. 25. 23:13
    [출처] Programming Ruby 
    (본 게시물은 저작권의 문제 발생시 출판사의 요청에 의해 삭제될 수 있습니다.)




    모듈 


    모듈은 메서드와 클래스, 상수를 함께 하나로 묶는 수단이다. 모듈은 다음과 같은 두 가지 장점이 있다.

    1) 모듈은 이름 공간을 제공해서 이름이 충돌하는 것을 막아준다.

    2) 모듈은 믹스인 기능을 구현하는데 이용한다.




    이름공간 


    서로 다른 파일에 정의된 변수의 중복을 막기 위해 모듈 구조를 사용한다. 모듈은 이름 공간을 정의하는데 이것은 다른 메서드나 상수에 의해 방해 받을 염려 없이 메서드와 상수를 정의할 수 있는 일종의 샌드박스이다. 


    trig.rb


    module trig

         PI = 3.141592654

         def Trig.sin(x)

              # ...

         end


         def Trig.cose(x)

              # ...

         end

    end


    그리고 좋고 나쁜 행동에 대한 메서드를 다른 모듈에 넣자.


    module Moral

         VERY_BAD = 0

         BAD = 1

         def Moral.sin(badness)

              # ...

         end

    end




    모듈 상수의 이름은 클래스 상숯럼 첫 문자를 대문자로 한다. 모듈 메서드는 클래스 메서드처럼 정의한다. 다른 프로그램에서 모듈을 사용하고자 한다면 require로 불러와 적절한 이름으로 참조하면 된다. 두 모듈에 정의된 상수나 메서드이름을 모호하지 않게 참조하기 위해 사용하고자 하는 메서드가 저의된 모듈이름을 먼저 적고 ::(범위 한정 연산자)나 마침표(.)뒤에 이름을 적는다.


    require_relative 'trig'

    require_relative 'moral'

    y = Trig.sin(Trig::PI/4)

    wrongdoing = Moral.sin(Moral::VERY_BAD)


    클레스 메서드처럼 모듈의 이름과 점을 메서드 이름 앞에 붙여서 모듈 메서드를 호출한다. 그리고 상수는 모듈의 이름과 두 개의 콜론을 이용하여 접근한다. 




    믹스인


    모듈에는 또 다른 훌륭한 사용법이 있다. 바로 믹스인이라는 기능인데, 이 기능을 이용하면 다중 상속을 할 필요가 사라진다. 모듈은 인스턴스를 가질 수 없다. 클래스가 아니기 때문이다. 하지만 클래스 선언에 모듈을 포함할 수 있다. 모듈을 포함하면 이 모듈의 모든 인스턴스 메서드는 클래스의 메서드처럼 동작한다. 

    즉 메서드가 클래스에 녹아서 석여 버린(mixed in)  것이다. 믹스인된 모듈은 실제로는 일종의 부모 클래스처럼 동작한다. 


    module Debug

         def who_am_i?

              "#{self.class.name} (id: #{self.object_id}) : #{self.name}"

         end

    end


    class Phonograph

         include Debug

         attr_reader :name

         def initialize(name)

              @name = name

         end

    end


    class EightTrack

         include Debug

         attr_reader :name

         def initialize(name)

              @name = name 

         end

         # ...

    end


    ph = Phonograph.new("West End Blues")

    et = EightTrack.new("Surrelistic Pillow")


    ph.who_am_i? # => "Phonograph (id: 70296083061000) : West End Blues"

    et.who_am_i? # => "EightTrack (id: 70296083060960) : Surrealistic Pillow"



    위의 예제는 Debug 모듈을 포함함으로써 Phonograph와  EightTrack  둘 다 who_am_i? 인스턴스 메서드를 이용할 수 있게 되었다. 


    include 문을 사용하기전 짚고 넘어갈 부분이 두가지 있다.

    먼저 이것은 파일과 관련해 아무런 일도 하지 않는다는 점이다. 루비의 include는  c언어와 달리 단지 해당 모듈에 대한 참조를 만들뿐이다. 모듈이 분리된 파일에 있는 경우 include를 사용하기 전 해당 파일을 require 해야 한다. 

    두 번째로 루비의 include는 클래스에 모듈의 인스턴스 메서드를 복사하는 것이 아니다. 그 대신 include는 클래스에 포함될 모듈에 대한 참조를 만든다. 여러 클래스가 하나의 모듈을 포함한다면 이 클래스들은 모두 같은 모듈을 참조하게 된다. 모듈의 메서드 정의를 수정한다면 이 모듈을 포함하는 모든 클래스는 새로이 정의된 방식으로 동작할 것이다.


    믹스인의 진정한 힘은 믹스인된느 코드가 자신을 이용하는 클래스와 상호 작용할 때 드러난다. Comparable 모듈을 클래스에서 인클루드 하고 <=>를 정의한다. 이를 통해 p1 > p2 와 같이 비교가 가능해지고, Person 객체들을 순서대로 정렬하는 것도 가능해진다.




    반복자와 Enumerable 모듈 


    새로 작성할 클래스에 다양한 기능을 지원하게 할 수 있다. each라는 반복자를 작성하고 Enumerable 모듈을 믹스인하면 클래스가 map, include?, find_all 과 같은 메서드를 지원하게 된다. 또한 그 집합의 원소들이 <=> 메서드를 통해 그것들 간의 의미 있는 순서를 정할 수 있다면, min, max, sort 같은 메서드도 얻게 될 것이다. 




    모듈 작성하기 


    Enumerable은 표준 믹스인으로 인클루드하는 클래스의 each 메서드를 사용해 다양한 메서드를 구현한다. Enumerable에 정의된 inject 메서드는 컬렉션 맨 앞의 두 개의 요소에 대해 함수나 계산을 실행하고 그 결과를 가지고 세 번째 요소에 대해 다시 같은 연산을 실행하고 이러한 과정을 컬렉션의 모든 요소에 대해 실행한다. 


    [1, 2, 3, 4, 5].inject(:+) #=> 15

    ('a'..'m').inject(:+) #=> "abcdefghijklm"


    Enumerable을 믹스인해서 inject 메서드를 가지는 클래스를 만들어보자.


    class VowelFinder

         include Enumerable


         def initialize(string)

              @string = string

         end


         def each

              @string.scan(/[aeiou]/) do |vowel|

                   yield vowel

              end

         end

    end



    vf = VowwelFinder.new("the quick brown fox jumped")

    vf.inject(:+)          #=> euiooue


    each 메서드를 정의해서 inject를 사용했다. 



    숫자에 적용했을 때는 산술적인 합을 반환하고 문자열에 적용햇을 대는 문자열을 이어주는 모듈을 캡슐화 할수 있다.


    module Summable

         def sum

              inject(:+)

         end

    end


    class Array

         include Summable

    end



    class Range

         include Summable

    end


    require_relative "vowel_finder"

    class VowelFinder

         include Summable

    end


    [1, 2, 3, 4, 5].sum # => 15

    ('a'..'m').sum # => "abcdefghijklm"




    믹스인의 인스턴스 변수 


    믹스인 할 때 인스턴스 변수는 어떻게 처리되는가. 아래의 코드를 살펴보자.


    module Observable

         def observers

              @observer_list | | = [  ]

         end

         

         def add_observer(obj)

              observers << obj

         end


         def notify_observers

              observers.each { |o| o.update }

         end

    end



    위의 코드는 문제가 발생할 수 있다. 믹스인 인스턴스 변수는 호스트 클래스의 변수나 다른믹스인 변수와 충돌할 수 있다. 이러한 경우 프로그램은 알아채기 힘든 방식으로 버그를 일으킨다. 



    메서드 이름에서 모호함 없애기 


    믹스인에 대해 묻는 질문중 메서드를 어떻게 찾는가를 궁금해 한다. 정확히는 같은 메서드가 클래스, 부모 클래스, 클래스의 믹스인에도 있는 경우 어떻게 동작하느냐이다. 답은 바로, 루비는 가장 먼저 객체의 클래스 그 자체를 찾아본 후 클래스에 포함된 믹스인을 찾아보고, 그 후에 상위 클래스와 상위 클래스의 믹스인을 살핀다. 여러 개의 모듈이 믹스인 되어 있다면, 마지막에 포함된  것부터 찾는다. 













    끄읕


Designed by Tistory.