Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

루비(Ruby) 전략 디자인 패턴 완벽 가이드: 유연한 코드 설계의 핵심

오늘은 전략 디자인 패턴(Strategy Design Pattern)에 대해 알아보겠습니다.

이 디자인 패턴은 메서드 내부의 핵심 알고리즘을 손쉽게 교체할 수 있도록 도와줍니다. 알고리즘을 클래스 안에 직접 하드코딩하는 대신, 해당 알고리즘을 구현한 클래스를 인자로 전달하는 방식입니다.

여기서 말하는 '알고리즘'은 거창한 컴퓨터 과학 알고리즘을 의미하는 것이 아닙니다. 결과를 얻기 위해 일련의 단계를 수행하는 모든 코드를 뜻합니다.

전략 패턴은 언제 사용해야 할까?

그렇다면 이 패턴은 정확히 어떤 문제를 해결해줄까요?

바로 개방-폐쇄 원칙(Open/Closed Principle)의 문제입니다.

이 원칙은 다음과 같이 말합니다:

“소프트웨어 엔티티(클래스, 모듈, 메서드)는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다.”

즉, 클래스에 새로운 기능을 추가할 때 클래스 자체를 변경할 필요가 없어야 한다는 의미입니다. 이렇게 하면 코드가 더 유연하고 동시에 더 견고해집니다.

그런데 이것을 실제로 어떻게 달성할 수 있을까요?

전략 패턴과 같은 디자인 패턴을 활용하면 됩니다 :)

예를 들어 ReportGenerator 클래스가 있고, 동일한 데이터로 여러 형식의 보고서를 생성하고 싶다고 가정해봅시다.

보고서 타입마다 메서드를 하나씩 작성할 수도 있습니다. 하지만 그럴 경우 새로운 타입을 추가할 때마다 클래스를 변경(새 메서드 추가)해야 하므로 개방-폐쇄 원칙을 위반하게 됩니다.

심지어 더 나쁜 방법도 있습니다!

각종 if문으로 가득 찬 하나의 거대한 메서드를 만드는 것이죠... 이런 코드는 대개 좋지 않은 결과로 끝나곤 합니다.

그런데 만약 클래스가 보고서를 포맷팅하는 방법을 몰라도 된다면 어떨까요?

알고리즘이 클래스 외부에서 제공된다면 말입니다.

그렇다면 클래스 자체를 전혀 건드리지 않고도 포맷팅 알고리즘을 언제든지 자유롭게 바꿀 수 있습니다.

이것이 바로 문제의 해결책이며, 전략 패턴의 핵심 철학입니다.

전략 패턴 예제

전략(Strategy)은 단일 메서드를 가진 클래스 형태로 구현됩니다.

다음은 ReportGenerator 클래스를 위한 몇 가지 포맷팅 전략입니다:

require 'json'

module ReportFormatters
  class JSON
    def self.format(data)
      data.to_json
    end
  end

  class PlainText
    def self.format(data)
      data.to_s
    end
  end

  class HTML
    def self.format(data)
      html = ""

      html << "
    " data.each { |product, amount| html << "
  • #{product}: #{amount}
  • " } html << "
" html end end end

주목할 점은 모든 전략 클래스가 format 메서드를 구현하고 있다는 것입니다. 이 메서드는 리포트 생성기 클래스가 호출하게 됩니다.

실제 코드는 다음과 같습니다:

class ReportGenerator
  def self.generate(data, formatter)
    formatter.format(data)
  end
end

data = { onions: 31, potatoes: 24, eggs: 10 }

p ReportGenerator.generate(data, ReportFormatters::HTML)
p ReportGenerator.generate(data, ReportFormatters::JSON)
p ReportGenerator.generate(data, ReportFormatters::PlainText)

이제 출력 형식을 변경하고 싶다면(HTML에서 PlainText로), 다른 전략을 인자로 전달하기만 하면 됩니다. 해당 전략이 format 메서드를 구현하고 있기만 하면 정상적으로 동작합니다.

이 패턴을 다른 관점에서 이해해볼 수도 있습니다. 마치 특정 비즈니스 전략에 특화된 컨설턴트를 고용하는 것과 같습니다. 컨설턴트는 직접 들어와 전략을 실행하고, 그 전략이 특정한 결과를 만들어냅니다.

반면 데코레이터 패턴(decorator pattern) 같은 패턴은 크리스마스 트리에 장식을 하나씩 더해 더욱 화려하게 만드는 것에 비유할 수 있습니다.

전략은 한 번에 하나만 사용할 수 있지만, 장식은 원하는 만큼 무제한으로 달 수 있는 셈이죠.

정리

지금까지 전략 디자인 패턴에 대해 살펴보았습니다. 클래스 자체를 수정하지 않으면서 클래스가 하는 일의 일부를 변경해야 할 때 이 패턴을 활용해 보세요. 개방-폐쇄 원칙을 준수하는 유연하고 확장 가능한 코드를 작성하는 데 큰 도움이 될 것입니다.

읽어주셔서 감사합니다!

이 글이 흥미로웠다면 주변에 공유해 주세요. 더 많은 분들이 유용하게 활용할 수 있을 것입니다 :)