모듈과 믹스인은 의심할 여지 없이 Ruby를 매력적으로 만드는 훌륭한 리소스입니다. 다른 곳에서 쉽게 사용할 수 있는 코드를 응용 프로그램에 공유할 수 있는 기능을 제공합니다. 또한 기능과 우려 사항을 그룹화하여 코드를 구성하는 데 도움이 되므로 코드의 가독성과 유지 관리 용이성이 향상됩니다.
이 기사에서는 모듈과 믹스인의 개념을 살펴보겠습니다. 모듈을 만들고 다른 클래스에 혼합하는 방법을 배우고 Ruby on Rails 애플리케이션에서 모듈을 사용할 때의 이점에 대해 논의합니다.
여행을 즐기시기 바랍니다!
모듈이란 무엇입니까
모듈은 두 가지 큰 이점을 제공하기 때문에 Ruby의 가장 빛나는 리소스 중 하나입니다. 이름 충돌을 방지하기 위해 네임스페이스를 생성할 수 있고 애플리케이션 전체에서 코드를 공유하기 위해 믹스인으로 사용할 수 있습니다.
구조적 측면에서 모듈은 모든 Ruby 클래스와 매우 유사합니다. 사실 Ruby의 경우 Class
Module
입니다. , 아래 irb
에서 볼 수 있듯이 콘솔:
> Class.is_a?(Module)
=> true
클래스와 유사하게 모듈을 사용하여 메서드와 상수를 그룹화하고 코드를 공유합니다. 그러나 모듈과 일반 Ruby 클래스 사이에는 몇 가지 차이점이 있습니다.
module
로 정의를 시작합니다.class
대신 키워드;- 모듈을 인스턴스화할 수 없으므로 모듈에서 객체를 생성할 수 없습니다.
- 모듈에서 상속할 수 없으므로 대신 믹스인으로 사용합니다.
- 모듈은 독립 실행형 코드이므로 모듈의 상속 계층이 없습니다.
모듈은 동일한 책임을 지고 함께 유지해야 하는 서비스, 관심사, 상수 및 기타 코드를 보유할 수 있는 좋은 장소입니다.
모듈은 다음과 같아야 합니다.
# lib/modules/invoice_creator.rb
module InvoiceCreator
TAX_FEE = 0.5
def self.generate
puts "Don't worry! I'll generate the invoice for you at #{TAX_FEE}%"
end
def invoice_total
puts "I'll return the invoice total"
1000
end
end
이 예에서 우리는 모듈이 두 종류의 메소드를 제공할 수 있음을 관찰할 수 있습니다:모듈 메소드와 인스턴스 메소드.
self.generate
는 모듈 메서드입니다. 즉, 다른 개체에 모듈을 포함(또는 확장)하지 않고도 사용할 수 있습니다. 이것은 예를 들어 서비스 객체를 생성할 때 매우 일반적입니다. 다음과 같이 모듈 메서드를 호출할 수 있습니다.
2.5.3 :006 > InvoiceCreator.generate
Don't worry! I'll generate the invoice for you
=> nil
invoice_total
는 인스턴스 메서드이며 이를 사용하려면 다음과 같이 모듈을 클래스에 포함해야 합니다.
# app/models/invoice.rb
class Invoice < ApplicationRecord
include InvoiceCreator # This includes our module in the Invoice class
def calculate_tax
total = invoice_total # included method from our module
tax = total * InvoiceCreator::TAX_FEE
puts "This is the invoice tax: #{tax}"
end
end
InvoiceCreator
의 모든 인스턴스 메소드 Invoice
에서 사용할 수 있게 됩니다. 인스턴스, 그래서 우리는 calculate_tax
를 호출할 수 있습니다 아주 쉽게 방법:
2.5.3 :008 > Invoice.new.calculate_tax
"I'll return the invoice total"
"This is the invoice tax: 500.0"
=> nil
2.5.3 :009 >
또한 calculate_tax
내부에서 모듈 내부에 정의된 상수를 사용하고 있음을 알 수 있습니다. 앞서 언급했듯이 모듈은 훌륭한 고정 키퍼입니다!
이제 두 종류의 InvoiceCreator
가 필요한 시나리오를 상상해 보세요. 공급자와 고객을 위해 완전히 다른 송장을 생성합니다. 우리는 결국 이름 분류를 갖게 되며 이를 피하기 위해 모듈의 다른 큰 이점인 네임스페이스를 사용합니다. 다음 섹션에서 살펴보겠습니다.
어디에나 있는 네임스페이스
네임스페이스는 주어진 기능에 대한 로컬 컨텍스트를 생성하고자 할 때 코드를 구성하는 방법으로 정의할 수 있습니다. 이는 방금 설명한 시나리오에 필요한 것입니다. 고객의 송장 작성자를 위한 다른 컨텍스트와 공급업체의 다른 컨텍스트 송장 작성자.
우리의 코드로 돌아가자. 두 개의 서로 다른 모듈을 만들어야 합니다.
# lib/modules/customer/invoice_creator.rb
module Customer
module InvoiceCreator
def self.generate
puts "Don't worry! I'll generate the customer invoice for you"
end
end
end
# lib/modules/supplier/invoice_creator.rb
module Supplier
module InvoiceCreator
def self.generate
puts "Don't worry! I'll generate the supplier invoice for you"
end
end
end
그런 다음 Customer::InvoiceCreator
를 사용할 수 있습니다. 또는 Supplier::InvoiceCreator
필요한 곳:
2.5.3 :014 > Customer::InvoiceCreator.generate
Don't worry! I'll generate the customer invoice for you
=> nil
2.5.3 :015 > Supplier::InvoiceCreator.generate
Don't worry! I'll generate the supplier invoice for you
=> nil
2.5.3 :016 >
그렇게 하면 관심 분리 원칙을 존중하면서 각 특정 코드가 자체 모듈 내에 래핑됩니다. 그 외에도 모든 곳에 네임스페이스를 지정하는 것은 코드를 잘 정리할 수 있는 좋은 방법입니다.
이제 Ruby 모듈의 다른 이점인 mixin을 활용하는 방법을 살펴보겠습니다.
Mixins 사용의 마법
이미 알고 계시겠지만 Ruby의 한 가지 특징은 단일 상속 메커니즘을 구현한다는 것입니다. 즉, 클래스는 다른 하나의 클래스에서만 상속할 수 있습니다. 종종 더 많은 클래스에서 상속해야 할 수도 있습니다. Ruby에서는 상속 패턴보다 구성을 사용하여 이러한 요구 사항을 충족할 수 있습니다.
이것은 믹스인을 사용하여 수행할 수 있습니다. 다른 Ruby 클래스의 코드를 혼합할 때 상속을 사용하지 않고 이 클래스에 더 많은 동작을 추가하고 있으며 이는 놀라운 일입니다. 따라서 Ruby에서 mixin은 필요한 클래스에 포함하는 모듈입니다. 그렇게 함으로써 우리는 코드를 깨끗하게 유지하고 책임을 분리함으로써 얻을 수 있습니다.
예를 들어 InvoiceCreator
가 모듈은 InvoiceCalculator
와 같은 여러 다른 모듈에서 제공하는 기능을 사용해야 하는 서비스입니다. , InvoiceRenderer
및 InvoiceSender
, 인보이스 생성 프로세스를 수행하는 데 필요합니다.
코드에 모듈 체인을 믹스인으로 포함하여 이를 달성할 수 있으므로 아래 예와 같이 메서드를 직접 사용할 수 있습니다.
# lib/modules/invoice_calculator.rb
module InvoiceCalculator
def calculate_items_total
puts "imagine some math here"
end
end
# lib/modules/invoice_renderer.rb
module InvoiceRenderer
def generate_invoice_pdf
puts "imagine that we are using some PDF generation magic here"
end
end
# lib/modules/invoice_sender.rb
module InvoiceSender
def send_invoice
puts "imagine your favorite mail service being used here"
end
end
InvoiceCreator
를 만들 수 없기 때문에 다른 세 모듈 모두에서 상속하므로 대신 포함합니다. 이렇게 하면 InvoiceCreator
다른 Invoice 모듈의 모든 메서드를 포함하며 모든 클래스/모듈에서 이러한 메서드를 호출할 수 있습니다. InvoiceCreator
모듈이 포함됩니다. 하지만 조심하세요! 모듈에 같은 이름의 메서드가 있는 경우 서로 덮어씁니다.
# lib/modules/customer/invoice_creator.rb
module Customer
module InvoiceCreator
include InvoiceCalculator
include InvoiceRenderer
include InvoiceSender
def generate_invoice
calculate_items_total # from InvoiceCalculator
generate_invoice_pdf # from InvoiceRenderer
send_invoice # from InvoiceSender
end
end
end
이제 다음을 수행하여 포함하는 모든 곳에서 서비스 메서드를 호출할 수 있습니다.
# app/models/invoice.rb
class Invoice < ApplicationRecord
include Customer::InvoiceCreator
def send_invoice_to_customer
puts "Don't worry! I'll generate the customer invoice for you"
generate_invoice
end
end
InvoiceCreator
를 통해 포함된 인보이스 모듈에서 메서드를 호출하는 결과입니다. 모듈:
2.5.3 :051 > Invoice.new.generate_invoice
Don't worry! I'll generate the supplier invoice for you
imagine some math here
imagine that we are using some PDF generation magic here
imagine your favorite mail service being used here
=> nil
이것이 상속 원칙보다 합성을 사용하는 방법입니다. 이 원칙은 가능한 한 컴포지션을 사용하는 것을 선호해야 한다는 것입니다. 작곡에서 우리는 다른 사람들에게 특정 기능을 제공하는 역할을 하는 클래스를 만들며, 이것이 바로 우리가 하는 일입니다.
이게 다야!
Ruby 개발자로서 우리는 코드를 계획, 작성, 유지 관리 및 리팩토링할 때 해당 기능과 구문 설탕을 사용하는 것을 좋아합니다. 이 기사를 통해 모듈이 우리 코드의 가독성을 개선하고 한 가지 책임으로만 유지하며 코드베이스를 깨끗하고 유지 관리하기 쉽게 유지하는 데 도움이 되는 훌륭한 리소스인 이유를 이해할 수 있기를 바랍니다. 또한 여러 소스에서 상속해야 하는 경우 믹스인 마법을 사용하여 코드에 모듈을 포함할 수 있습니다.
여기에서 Ruby와 Rails의 가장 큰 리소스에 대해 계속 이야기할 예정이니 계속 지켜봐 주세요!
추신 Ruby Magic 게시물이 언론에 공개되는 즉시 읽고 싶다면 Ruby Magic 뉴스레터를 구독하고 게시물을 놓치지 마세요!