전략 패턴의 장점


15

if / thecase에서 코드를 작성할 수 있다면 전략 패턴을 사용하는 것이 왜 유리합니까?

예를 들어 : TaxPayer 클래스가 있고 해당 메소드 중 하나가 다른 알고리즘을 사용하여 세금을 계산합니다. 그렇다면 전략 패턴을 사용하는 대신 if / thecase를 사용하여 해당 방법에 어떤 알고리즘을 사용해야하는지 파악할 수없는 이유는 무엇입니까? 또한 TaxPayer 클래스의 각 알고리즘에 대해 별도의 메소드를 구현할 수없는 이유는 무엇입니까?

또한 알고리즘이 런타임에 변경된다는 것은 무엇을 의미합니까?


2
숙제입니까? 그렇다면 선불로하는 것이 좋습니다.
Fuhrmanator

2
@Fuhrmanator 아니 그것은 아니야
Armon Safai

답변:


20

우선 if/else블록의 큰 덩어리는 쉽게 테스트 할 수 없습니다 . 각각의 새로운 "분기"는 또 다른 실행 경로를 추가하여 순환 복잡성 을 증가시킵니다 . 코드를 철저히 테스트하려면 모든 실행 경로를 다루어야하며 각 조건마다 최소 하나 이상의 테스트를 작성해야합니다 (작고 집중된 테스트를 작성한다고 가정). 반면에 전략을 구현하는 클래스는 일반적으로 테스트하기 쉬운 하나의 공개 메소드 만 노출합니다.

따라서 중첩을 사용 if/else하면 코드의 한 부분에 대한 많은 테스트가 끝나고 전략을 사용하면 더 간단한 여러 전략에 대한 테스트가 거의 없습니다. 후자는 실행 경로를 놓치기가 더 어렵 기 때문에 더 나은 적용 범위를 쉽게 가질 수 있습니다.

확장성에 관해서는 사용자가 자신의 행동을 주입 할 수 있어야하는 프레임 워크를 작성한다고 가정하십시오. 예를 들어, 일종의 세금 계산 프레임 워크를 작성하고 다른 국가의 세금 시스템을 지원하려고합니다. 이들 모두를 구현하는 대신 프레임 워크 사용자에게 특정 세금을 계산하는 방법에 대한 구현을 제공 할 수있는 기회를 제공하려고합니다.

전략 패턴은 다음과 같습니다.

  • 당신은 예를 들어, 인터페이스를 정의 TaxCalculation하고 프레임 워크는 계산 세금이 유형의 인스턴스를 받아
  • 프레임 워크 사용자는이 인터페이스를 구현하고이를 프레임 워크에 전달하는 클래스를 작성하여 계산의 일부를 수행 할 수있는 방법을 제공합니다.

if/else프레임 워크의 코드를 변경해야하므로 더 이상 프레임 워크가 아니기 때문에 와 동일하게 작업 할 수 없습니다 . 프레임 워크는 종종 컴파일 된 형태로 배포되기 때문에 이것이 유일한 옵션 일 수 있습니다.

그래도 정규 코드를 작성하더라도 의도가 명확 해지기 때문에 전략이 유리합니다. "이 논리는 플러그 가능하고 조건부로되어 있습니다"라고되어 있습니다. 즉, 사용자 작업, 구성 또는 플랫폼에 따라 다양한 구현이있을 수 있습니다.

전략 패턴을 사용하면 가독성 이 향상 될 수 있습니다. 특정 전략을 구현하는 클래스는 일반적으로 설명이 포함 된 이름을 가져야합니다. 예를 들어 USAIncomeTaxCalculator, if/else블록에는 "이름이 없습니다", 방금 언급 한 주석이있을 수 있습니다. 또한 내 개인적인 취향에 따라 3 개 이상의 if/else블록을 연속으로 갖는 것은 읽을 수 없으며 중첩 된 블록으로 인해 꽤 나쁩니다.

오픈 / 청산 원칙은 내가 위의 예에서 설명 된 바와 같이, 전략은 당신이 "수정을 위해 폐쇄"(그 부분을 다시 작성하지 않고 코드 ( "확장 오픈")의 일부의 논리를 확장 할 수 있습니다, 때문에, 매우 관련이 ).


1
if/else블록은 또한 코드 가독성을 낮 춥니 다. 전략 패턴에 대해서는 개방 / 폐쇄 원칙이 IMO를 언급 할 가치가 있습니다.
Maciej Chałapuk

1
테스트 가능성이 큰 이유입니다. (대부분) 코드의 모든 분기를 테스트해야합니다. 더 많은 if이야 당신이 더 가능한 경로는 당신의 코드를 통해 존재, 그 방법에 대한 쓰기에 당신이 더 많은 테스트와 더 많은 방법이 실패합니다. 내가 요기 베라 (Yogi Berra)의 말을 인용 할 수 있다면 : "길에서 포크로 오면 가져 가십시오." 이것은 단위 테스트에 훌륭하게 적용됩니다. 또한, 많은 if진술은 이러한 조건에 대해 논리를 반복하여 테스트로드를 더 높이고 버그가 발생할 위험을 증가시킬 가능성이 있음을 의미합니다.
Greg Burghardt

답변 감사합니다. 그렇다면 왜 같은 클래스의 다른 알고리즘에 대해 별도의 메소드를 사용할 수 없습니까?
Armon Safai

당신은 할 수 있지만 여전히 if/else호출 할 블록 이 필요 합니다 (또는 내부에서 무언가를 해야하는지 여부를 결정하기 위해), 더 읽기 쉬운 코드를 제외 하고는별로 도움이되지 않습니다. 또한 가상 프레임 워크 사용자에게는 확장 성이 없습니다.
scriptin

1
왜 테스트가 더 쉬운 지 더 명확하게 알 수 있습니까? 사례 설명 (또는 if / then)을 다형성 방법 (전략 기반)으로 리팩토링하는 예제는 테스트하기가 매우 쉽습니다. refactoring.com/catalog/replaceConditionalWithPolymorphism.html 테스트 할 모든 조건을 알고 있으면 각각에 대한 테스트를 작성합니다. 전략이 있다면 각 인스턴스마다 인스턴스를 생성하고 실행해야합니다. 전략 접근법은 어떻게 테스트하기가 더 쉬워 집니까? 전략을 리팩토링 할 때 복잡한 중첩 된 if에 대해서는 이야기하지 않습니다.
Fuhrmanator

5

if / thecase에서 코드를 작성할 수 있다면 전략 패턴을 사용하는 것이 왜 유리합니까?

때로는 if / then을 사용해야합니다. 읽기 쉬운 간단한 코드입니다.

간단한 if / then 코드의 두 가지 주요 문제점은 공개 폐쇄 원칙을 위반할 수 있다는 것 입니다. 들어가서 조건을 추가하거나 변경해야하는 경우이 코드를 수정하는 것입니다. 더 많은 조건이 필요할 것으로 예상되는 경우, 새로운 전략을 추가하는 것이 더 간단하고 더 깨끗하며 덜 쉬울 것입니다.

다른 문제는 커플 링입니다. if / then을 사용하면 모든 구현이 해당 구현에 연결되어 향후 변경하기가 더 어려워집니다. 전략을 사용함으로써 유일한 전략은 전략의 인터페이스에 연결됩니다.


if / then 코드에서 코드를 수정하는 데 무엇이 문제입니까? 알고리즘 중 하나의 기능을 변경하기로 결정한 경우 전략 패턴에서 코드를 수정하지 않아도 되겠습니까?
Armon Safai

@armonsafai-전략을 수정하는 경우 전략을 테스트하기 만하면됩니다. 모든 알고리즘 을 수정하면 모든 알고리즘을 테스트해야합니다. 더 나쁜 것은 새로운 전략을 추가하면 전략을 테스트하기 만하면됩니다. 새로운 조건을 추가하면 모든 조건을 테스트해야합니다.
Telastyn

4

전략은 http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html에 설명 된대로 if/then조건이 유형을 기반으로 할 때 유용합니다 .

타입 검사 조건은 일반적으로 순환 사이클 복잡도가 높지 않으므로 전략이 반드시 필요한 것을 개선한다고 말할 수는 없습니다.

전략의 주요 이유는 패턴을 소개 한 GoF 책 p.316에 설명되어 있습니다.

전략 패턴을 사용할 때

...

  • 클래스는 많은 동작을 정의하며 동작에서 여러 조건문으로 나타납니다. 많은 조건부 대신 관련 조건부 분기를 자체 전략 클래스로 이동하십시오.

다른 답변에서 언급했듯이 전략 패턴을 적절하게 적용하면 나머지 코드를 수정하지 않고도 새로운 확장 (콘크리트 전략)을 추가 할 수 있습니다. 이것은 소위 공개 폐쇄 원칙 또는 보호 변형 원칙 입니다. 물론 새로운 구체적인 전략을 코딩해야하며 클라이언트 코드는 전략을 플러그인으로 인식해야합니다 (사소한 것은 아닙니다).

if/then조건문, 그것은 조건부 논리를 포함하는 클래스의 코드를 변경할 필요가있다. 다른 답변에서 언급했듯이 때로는 다시 컴파일하지 않고 새로운 기능 (플러그인)을 추가하기 위해 복잡성을 추가하지 않으려는 경우에도 괜찮습니다.


3

[...] if / then 경우에 코드를 작성할 수 있다면?

이것이 전략적 패턴의 가장 큰 이점입니다. 조건이 없습니다.

클래스 / 메소드 / 함수가 가능한 한 간단하고 짧기를 원합니다. 짧은 코드는 테스트하기 쉽고 읽기 쉽습니다.

조건 ( if/ elseif/ else)하게 긴 수업 / 방법 / 기능, 하나 개의 의사 결정들을 평가에 일반적으로 코드 때문에 true결정들을 평가에 일부 다른 false.


전략 패턴의 또 다른 큰 장점은 전체 프로젝트에서 재사용 할 수 있다는 것입니다.

전략 설계 패턴을 사용하는 경우 IoC 컨테이너가있을 가능성이 높습니다.이 컨테이너에서 원하는 인터페이스 구현 방법을 얻을 수 있습니다 getById(int id).id 열거 형 멤버가 될 수 수 있습니다.

즉, 구현 작성은 코드의 한 위치에만 있습니다.

더 많은 구현을 추가하려면 getById메소드에 새 구현을 추가 하고이 변경 사항은 호출하는 코드의 모든 곳에 반영됩니다.

if/ elseif/를 사용하면 else불가능합니다. 새 구현을 추가하면 새 elseif블록 을 추가 하고 구현이 사용 된 모든 곳에서이를 수행해야합니다. 그렇지 않으면 구현을 구조에 추가하는 것을 잊었 기 때문에 잘못된 코드로 끝날 수 있습니다.


또한 알고리즘이 런타임에 변경된다는 것은 무엇을 의미합니까?

내 예제에서는 id사용자 입력을 기반으로 채워지는 변수 일 수 있습니다. 사용자가 버튼 A id = 2를 클릭하면 버튼 B를 클릭하면을 클릭합니다 id = 8.

다른 id값으로 인해 IoC 컨테이너에서 다른 인터페이스 구현이 얻어지고 코드는 다른 작업을 수행합니다.


답변 감사합니다. 그렇다면 왜 같은 클래스의 다른 알고리즘에 대해 별도의 메소드를 사용할 수 없습니까?
Armon Safai

@ArmonSafai 별도의 방법으로 실제로 문제를 해결할 수 있습니까? 나는 그렇게 생각하지 않습니다. 문제를 한 곳에서 다른 곳으로 옮길 때 조건의 결과에 따라 어떤 방법을 호출할지 결정합니다. 다시 if/ elseif/ else상태입니다. 다른 곳에서와 동일합니다.
Andy

그렇다면 if / then 사례가 주된 권리입니까? 전략 패턴에도 if / the case를 사용해야합니까?
Armon Safai

1
@ArmonSafai 아니오, 당신은하지 않을 것입니다. 메소드 에서 id변수를 전환 getById하면 특정 구현이 리턴됩니다. 인터페이스 구현이 필요할 때마다 IoC 컨테이너에 인터페이스를 제공하도록 요청합니다.
Andy

1
@ArmonSafai 인터페이스 getSortByEnumType(SortEnum type)구현을 리턴 Sort하는 메소드를 가질 수도 있고 getSortType, SortEnum변수를 리턴 하고 콜렉션을 매개 변수로 가져 오는 메소드를 가질 수 있으며, 메소드 에는 매개 변수 getSortByEnumType의 스위치가 포함되어 type올바른 정렬 알고리즘을 리턴합니다. 새로운 정렬 알고리즘을 추가해야하는 경우 열거 형과 한 가지 방법 만 편집하면됩니다. 그리고 당신은 설정되었습니다.
Andy

2

if / thecase에서 코드를 작성할 수 있다면 전략 패턴을 사용하는 것이 왜 유리합니까?

전략 패턴을 사용하면 알고리즘 (세부 사항)과 비즈니스 로직 (고수준 정책)을 분리 할 수 ​​있습니다. 이 두 가지는 혼합 될 때 읽기가 혼란 스러울뿐만 아니라 변경해야 할 이유가 매우 다릅니다.

여기에는 주요 팀워크 확장 성 요소가 있습니다. 많은 사람들이이 회계 패키지를 작업하고있는 대규모 프로그래밍 팀을 상상해보십시오. 세금 알고리즘이 모두 TaxPayer 클래스 또는 모듈에있는 경우 병합 충돌이 발생할 수 있습니다. 병합 충돌은 시간이 많이 걸리고 오류가 발생하기 쉽습니다. 이 시간은 팀의 생산성을 떨어 뜨리고 잘못된 병합으로 인한 오류는 고객과의 신뢰성을 손상시킵니다.

또한 알고리즘이 런타임에 변경된다는 것은 무엇을 의미합니까?

런타임에 변경되는 알고리즘은 구성 또는 컨텍스트에 따라 동작이 결정되는 알고리즘입니다. 경우는 / 다음 은 기존 적극적으로 사용되는 클래스를 다시로드 포함으로 장소에 접근이 효과적으로를 사용하지 않습니다. 전략 패턴을 사용하면 각 알고리즘을 구현하는 전략 개체를 사용할 수 있습니다. 결과적으로 이러한 알고리즘 (버그 수정 또는 개선)에 대한 변경 사항은 런타임에 만들어지고 다시로드 될 수 있습니다. 이 접근 방식은 지속적인 가용성과 다운 타임없는 릴리스를 가능하게합니다.


1

if/else그 자체로 는 아무런 문제가 없습니다 . 대부분의 경우 if/else논리를 표현하는 가장 단순하고 읽기 쉬운 방법입니다. 따라서 당신이 묘사하는 접근법은 많은 경우에 완벽하게 유효합니다. (또한 완벽하게 테스트 할 수 있으므로 문제가되지 않습니다.)

그러나 전략 패턴이 전체 코드의 유지 관리 성을 향상시킬 수있는 특별한 경우가 있습니다. 예를 들면 다음과 같습니다.

  • 특정 세금 계산 알고리즘이 서로 독립적으로 그리고 핵심 논리에 따라 변경 될 수 있습니다. 이 경우 변경 사항이 현지화되므로 개별 클래스로 분리하는 것이 좋습니다.
  • 핵심 로직을 변경하지 않고 향후 새로운 알고리즘을 추가 할 수 있습니다.
  • 두 알고리즘의 차이의 원인이 코드의 다른 부분에도 영향을 미치는 경우. 납세자의 소득 계층에 따라 두 알고리즘 중 하나를 선택한다고 가정합니다. 이 소득 괄호로 인해 코드에서 다른 지점을 다른 지점으로 선택하게되면 코드에 여러 if / el-branches가 흩어져있는 대신 소득 괄호에 해당하는 전략을 한 번 인스턴스화하고 필요할 때 호출하는 것이 더 깨끗합니다.

전략 패턴을 이해하려면 핵심 논리와 세금 계산 알고리즘 간의 인터페이스 가 개별 구성 요소 보다 안정적 이어야합니다. 요구 사항 변경으로 인해 인터페이스가 변경 될 가능성이있는 경우 전략 패턴이 실제로 책임이 될 수 있습니다.

"세금 계산 알고리즘"이이를 호출하는 핵심 로직에서 완전히 분리 될 수 있는지 여부가 결정됩니다. 전략 패턴은에 비해 약간의 오버 헤드가 if/else있으므로 투자 가치가있는 경우 사례별로 결정해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.