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는 여전히 나를 많이 괴롭힐 것입니다.
하지만 광고 항목의 따옴표 대신 관점? 묻는 대신 어떤 종류의 광고 항목을 처리하고 견적에 추가하는지, 방금 말씀하신 자체 추가할 광고 항목 견적에?
방법을 반대로 하세요!
내가 가장 좋아하는 코드 리팩터링 방법 중 하나는 발신자와 수신자를 반대로 해보는 것입니다. 다음은 위의 코드를 사용한 예입니다.
class Quote
...
def add_line_item(line_item)
line_item.add_to_quote(self)
end
end
class Ad < LineItem
...
def add_to_quote(quote)
quote.line_items << self
end
end
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
에 속성을 노출해야 할 수도 있습니다. 공개적으로 노출하고 싶지 않은 개체입니다. -
커플링을 증가시킬 수 있습니다. 둘 다
Quote
및Ad
이제 서로에 대해 알아야 합니다. 그리고 얼마나에 따라 그들은 서로에 대해 알아야 하므로 코드를 더욱 만들 수 있습니다. 복잡합니다. -
Ad
에 대한 단일 책임 원칙을 위반할 수 있습니다. , 이제Ad
Quote
에 자신을 추가하는 방법을 알아야 할 책임이 있습니다. .
일반적으로 이러한 문제를 해결할 수 있습니다. 그러나 리팩토링으로 인해 코드가 나쁘게 되는 것을 원하지 않기 때문에 이를 알고 있어야 합니다.
내가 가장 좋아하는 것 중 하나인 이유
이러한 문제에도 불구하고 이것은 내가 가장 좋아하는 리팩토링 중 하나입니다. 이 패턴을 사용한 후에 작성하는 코드가 더 명확하고 자신감이 생기는 경향이 있습니다.
하지만 그렇지 않더라도 이 패턴을 사용하면 사물 간의 관계에 대해 다른 방식으로 생각하게 됩니다. "이 기능은 끔찍합니다. 이 끔찍한 코드를 작성하여 처리해야 한다는 것이 믿기지 않습니다."라는 틀에 박히면 뇌가 이러한 문제를 해결할 수 있는 새로운 방법을 보게 됩니다. 코드를 다르게 구성할 수 있는 방법에 대해 생각하게 합니다. 정말 유용합니다.
자신의 코드로 사용해 보기
내가 좋아하는 많은 패턴과 마찬가지로 역전 방식을 처음 접했습니다. Smalltalk 모범 사례 패턴에서, 그리고 그 이후로 귀중한 도구가 되었습니다.
다음에 약간 다른 동작을 가진 유사한 개체를 처리하는 데 어려움을 겪을 때 시도해 보십시오! 새 코드가 더 마음에 들면 그대로 두십시오. 하지만 그렇게 하지 않더라도 더 나은 코드로 이끄는 길로 마음을 빼앗길 것입니다.