Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

관점의 간단한 변화로 얽힌 스파게티 코드 풀기

if의 거대한 혼란 진술은 당신의 얼굴을 계속 응시합니다. 해야 계속해서 방해가 되는 비즈니스 로직을 제외하고는 단순화할 수 있습니다.

예를 들어 Quote를 구축하는 판매 플랫폼이 있다고 가정해 보겠습니다. 많은 LineItem이 있는 s 에스. 단, 광고인 경우 중복된 광고 항목이 포함된 견적을 가질 수 있습니다. 하지만 웹사이트가 여러 개인 경우 , 가격을 함께 합산하여 단일 항목으로 표시해야 합니다. 아 그리고 또한 웹사이트를 구매하고 견적에 이미 5개의 광고가 있는 경우 웹사이트에서 20% 할인을 제공해야 합니다.

여기서 창문 너머로 노트북을 던지는 소리가 들립니다.

당신은 할 수 있습니다 많은 if 작성 이러한 규칙을 처리하기 위한 명령문:

class Quote
  attr_accessor :line_items
 
  ... 
 
  def add_line_item(line_item)
    if line_item.kind_of?(Ad)
      self.line_items << line_item
    elsif line_item.kind_of?(Website)
      if @line_items.select {|item| item.kind_of?(Ad) }.length >= 5
        # TODO: Put the fractions of a cent into a bank account
        # I have set up
        line_item.price *= 0.8
      end
      existing_website = self.line_items.detect { |item| item.kind_of?(Website) }
      if existing_website
        existing_website.price += line_item.price
      else
        self.line_items << line_item
      end
    end
  end
end

하지만 우리는 그것이 끔찍하다는 데 동의할 수 있다고 생각합니다. 어떻게 그런 문제를 풀 수 있나요?

방법을 여러 개의 작은 방법으로 분해할 수 있지만, 이는 마치 모든 장난감을 옷장에 밀어넣어서 엄마가 당신이 방을 청소했다고 생각하게 하는 것과 같습니다. 그리고 그 kind_of? s는 여전히 나를 많이 괴롭힐 것입니다.

하지만 광고 항목의 따옴표 대신 관점? 묻는 대신 어떤 종류의 광고 항목을 처리하고 견적에 추가하는지, 방금 말씀하신 자체 추가할 광고 항목 견적에?

방법을 반대로 하세요!

내가 가장 좋아하는 코드 리팩터링 방법 중 하나는 발신자와 수신자를 반대로 해보는 것입니다. 다음은 위의 코드를 사용한 예입니다.

앱/모델/quote.rb
class Quote
  ...
  def add_line_item(line_item)
    line_item.add_to_quote(self)
  end
end
앱/모델/line_item.rb
class Ad < LineItem
  ...
  def add_to_quote(quote)
    quote.line_items << self
  end 
end
앱/모델/웹사이트.rb
class Website < LineItem
  def add_to_quote(quote)
    if quote.line_items.select {|item| item.kind_of?(Ad) }.length >= 5
      # TODO: Put the fractions of a cent into a bank account
      # I have set up
      self.price *= 0.8
    end
    existing_website = quote.line_items.detect { |item| item.kind_of?(Website) }
    if existing_website
      existing_website.price += self.price
    else
	  quote.line_items << self
    end
  end
end

완벽하지 않습니다. website.rb 여전히 많은이 필요합니다 리팩토링 도움말, 방법을 반대로 하여 line_items 캡슐화를 중단한 방법에 만족하지 않습니다. .

그러나 첫 번째 복잡성 계층을 제거했습니다. 이제 LineItem에 코드를 넣을 수 있습니다. 또는 Quote , 가장 의미 있는 위치에 따라 다릅니다. LineItem 개체는 상속 및 믹스인을 사용하여 각 LineItem 간의 유사점과 차이점을 처리할 수 있습니다. 아강. 또한 이제 새 LineItem을 추가하는 것이 정말 쉽습니다. add_line_item을 부풀리지 않는 하위 클래스 방법.

코드가 조금 더 깔끔하고 훨씬 유연해졌습니다. 일반적으로 승리라고 부르겠습니다.

이 패턴을 사용하고 싶지 않은 위치

역전 방식만큼 유용 즉, 이 패턴을 사용하지 않으려는 몇 가지 이유가 있습니다.

  • 캡슐화를 깨뜨릴 수 있습니다. Quote에 속성을 노출해야 할 수도 있습니다. 공개적으로 노출하고 싶지 않은 개체입니다.

  • 커플링을 증가시킬 수 있습니다. 둘 다 QuoteAd 이제 서로에 대해 알아야 합니다. 그리고 얼마나에 따라 그들은 서로에 대해 알아야 하므로 코드를 더욱 만들 수 있습니다. 복잡합니다.

  • Ad에 대한 단일 책임 원칙을 위반할 수 있습니다. , 이제 Ad Quote에 자신을 추가하는 방법을 알아야 할 책임이 있습니다. .

일반적으로 이러한 문제를 해결할 수 있습니다. 그러나 리팩토링으로 인해 코드가 나쁘게 되는 것을 원하지 않기 때문에 이를 알고 있어야 합니다.

내가 가장 좋아하는 것 중 하나인 이유

이러한 문제에도 불구하고 이것은 내가 가장 좋아하는 리팩토링 중 하나입니다. 이 패턴을 사용한 후에 작성하는 코드가 더 명확하고 자신감이 생기는 경향이 있습니다.

하지만 그렇지 않더라도 이 패턴을 사용하면 사물 간의 관계에 대해 다른 방식으로 생각하게 됩니다. "이 기능은 끔찍합니다. 이 끔찍한 코드를 작성하여 처리해야 한다는 것이 믿기지 않습니다."라는 틀에 박히면 뇌가 이러한 문제를 해결할 수 있는 새로운 방법을 보게 됩니다. 코드를 다르게 구성할 수 있는 방법에 대해 생각하게 합니다. 정말 유용합니다.

자신의 코드로 사용해 보기

내가 좋아하는 많은 패턴과 마찬가지로 역전 방식을 처음 접했습니다. Smalltalk 모범 사례 패턴에서, 그리고 그 이후로 귀중한 도구가 되었습니다.

다음에 약간 다른 동작을 가진 유사한 개체를 처리하는 데 어려움을 겪을 때 시도해 보십시오! 새 코드가 더 마음에 들면 그대로 두십시오. 하지만 그렇게 하지 않더라도 더 나은 코드로 이끄는 길로 마음을 빼앗길 것입니다.