2008-10-18 2 views
5

몇 가지 (~ 20 +) 구성 옵션을 허용하려는 클래스가 있습니다. 각 옵션은 기능을 켜거나 끕니다. 그렇지 않으면 작업이 변경됩니다. 이 작업을 용이하게하기 위해 기본값을 사용하여 별도의 옵션 클래스를 코딩했습니다. 그러나 메소드가 작동해야하는 방식을 결정하기 위해 코드를 가드 조건에 버려야했습니다. 나는 거의 끝났지 만, 지금은 코드가 냄새가 나는 것처럼 보입니다."옵션"을 클래스에 추가하기위한 패턴이 있습니까?

이와 같은 클래스를 구현하는 데 선호되는 방법/패턴이 있습니까?

편집 : 특히, 나는 파싱 클래스 작업을하고 있습니다. 각 옵션은 기본 구문 분석 알고리즘의 상호 배타적 인 부분을 구성합니다. 예를 들어 나는 아래처럼 내 클래스의 여러 분야가 있습니다

if (this.Option.UseIdAttribute) 
     attributeIDs = new Hashtable(); 
else 
     attributeIDs = null; 


    public Element GetElementById(string id) 
    { 
     if (string.IsNullOrEmpty (id)) 
      throw new ArgumentNullException("id"); 

     if (attributeIDs == null) 
      throw new Exception(ExceptionUseIdAttributeFalse); 

     return attributeIDs[id.ToLower()] as Element; 
    } 
+0

코드를 게시하지 않는 한 말을하기가 매우 어렵습니다 ... – johnstok

답변

5

어떻게 Decorator pattern에 대해? 클래스에 동작을 동적으로 추가하기 위해 설계되었습니다.

+0

나는 이것에 대해 생각했지만, 나는 그것이 맞는지 확인하기 위해 다시 코드를 검토합니다. 처음에는 약간 과잉이라고 생각했고 클래스가 부 풀리는 것을 피하려고했습니다. – Nescio

+0

데코레이터 패턴도 제 대답 일 것입니다. GoF 책이나 Headfirst 디자인 패턴에 제시된 것과 똑같은 방식으로 사용할 필요가 없습니다. 패턴은 요리법이 아닙니다. 자신의 필요에 맞게 모양을 만드십시오. – Sandman

0

나는 Command 패턴을 생각하고 있습니다.

이 같은 보일 것이다 : 즉

public class MyClass { 

    interface Command { 
    void execute(int a); 
    } 

    static class DoThisSimpleCommand implements Command { 
    void execute(int a) { 
     // do this simple 
    } 
    } 

    static class DoThisAnotherWayCommand implements Command { 
    void execute(int a) { 
     // do this another way 
    } 
    } 

    private Command doThisCommand; 

    MyClass() { 
    initDoThisCommand(); 
    } 

    private void initDoThisCommand() { 
    if (config.getDoThisMethod().equals("simple")) { 
     doThisCommand = new DoThisSimpleCommand(); 
    } else { 
     doThisCommand = new DoThisAnotherWayCommand(); 
    } 
    } 

    void doThis(int a) { 
    doThisCommand.execute(a); 
    } 
} 

을; 당신은 구현시 인스턴스화하는 구현체에 책임을 위임합니다. 이제는 doThis가 위임하고 실제 기능은 깨끗하게 자체 클래스에 보관됩니다.

1

사실이 목적을 위해 빌더 패턴이라고하는 또 다른 패턴이 있습니다. 일반적으로 클래스가있을 때 유용하지만 각각의 '구성'옵션은 일부 조합에서만 선택적으로 지정할 수 있습니다. (http://en.wikipedia.org/wiki/Builder_pattern,하지만 그건 정확하게 내 시나리오를 설명하지 않습니다.).

빌드 할 클래스와 빌더라는 두 클래스를 작성하십시오. 빌더 클래스는 어떤 옵션 조합이 선택인지, 어떤 조합이 합리적인지 등을 처리하는 클래스입니다.

예를 들어 샐러드를 나타 내기를 원하지만 특정 재료 만 '좋은 맛'이므로 그것들 만 만들어야한다.

Class Salad { 
    private Veggie v; 
    private Egg e; 
    private Meat m; 
    // etc etc, lots of properties 
    //constructor like this is nice 
    Salad(SaladBuilder builder) { 
     //query the builder to actually build the salad object. 
     //getVeggie() will either return the supplied value, 
     //or a default if none exists. 
     this.v = builder.getVeggie(); 
     //rest of code omitted 
    } 

    //otherwise this constructor is fine, but needs a builder.build() method 
    Salad(Veggie v, Meat m, Egg e) { //code omitted 
    } 
} 

class SaladBuilder { 
    //some default, or left to null depending on what is needed 
    private Veggie v = SOME_DEFAULT_VEGGIE; 
    private Egg e; 
    private Meat m; 
    // etc etc, lots of properties. 

    //similar functions for each ingredient, 
    //or combination of ingredients that only make sense together 
    public SaladBuilder addIngredient(Meat m) { 
     this.m = m; 
     return this; 
    } 

    public SaladBuilder addIngredient(Veggie v) { 
     this.v = v; 
     return this; 
    } 

    public Salad build(){ 
     // essentially, creates the salad object, but make sure optionals 
     // are taken care of here. 
     return new Salad(getBeggie(), getMeat(), getEgg()); 
    } 
} 

/오프 기능을 켜 옵션에 대한

Salad s = new SaladBuilder().addIngredient(v).addIngredient(m).build(); 
2

사용 예, 나는 데코레이터가 @Thomas 오웬스가 말한대로 갈 수있는 방법이라고 생각합니다. 나는 기능을 바꾸는 옵션에 대해 좀 더 걱정하고있다. 이러한 작업을 연결할 수없는 경우 데코레이터는 작동하지 않습니다.

public void ActionABC() 
{ 
    if (options.DoA) 
    { 
     A(); 
    } 

    if (options.DoB) 
    { 
     B(); 
    } 

    if (options.DoC) 
    { 
     C(); 
    } 
} 

public void ActionCAB() 
{ 
    if (options.DoC) 
    { 
     C(); 
    } 

    if (options.DoA) 
    { 
     A(); 
    } 

    if (options.DoB) 
    { 
     B(); 
    } 
} 

이 구성의 순서는 각 작업 방법에 대한 차이로 실내 장식으로 처리하기 어려울 것입니다 : 예를 들어, 코드처럼 보이는 경우.

20 개 이상의 옵션을 사용하면 켜짐/꺼짐으로 가정 할 때 400 가지 이상의 가능한 조합이 있습니다. 나는이 모든 조합이 똑같이 가능한 것은 아니라고 생각합니다. Decorator를 통해 처리 할 수없는 사항에 대해서는 설정 조합에 매핑되는 작업 모드에 대해 생각하는 것이 좋습니다. 가장 기대되는 모드 만 지원하십시오. 모드 수가 적 으면 서브 클래 싱을 사용하여 이것을 처리 한 다음 Decorator를 사용하여 사용자가 선택한 모드를 나타내는 하위 클래스에 기능을 추가하십시오.팩토리를 사용하여 구성을 기반으로 적절한 클래스를 선택하고 빌드 할 수 있습니다.

본질적으로, 저는 여러분이 구축 할 때 융통성과 수반되는 복잡성이 필요한지 고려하고 싶을 것이라고 생각합니다. 더 적은 수의 사용 모드로 구성 옵션을 접어서 구성 옵션의 수를 줄이는 것을 고려하십시오.

+0

20+ 부울 옵션은 백만 가지 이상의 조합입니다. – erickson

0

상호 의존적 인 옵션에 대한 규칙을 어떻게 적용 하시겠습니까? 옵션을 동적으로 추가 할 수있는 경우 명령 패턴 인 경우 여러 명의 호출자가 있어야하거나 실행해야하는 명령을 작성하기 위해 case 문을 구현해야 할 수 있습니다.

1

어때? 다음과 같은 유형을 바탕으로

IdMap elementsById = (options.useIdAttribute) ? new IdMapImpl() : new NullIdMap(); 

public Element getElementById(final string id) { 
    return elementsById.get(id); 
} 

:

interface IdMap { 
    Element get(String id); 
} 

class NullIdMap implements IdMap { 
    public Element get(final String id) { 
     throw new Exception(/* Error message */); 
    } 
} 

class IdMapImpl implements IdMap { 
    Map<String, Element> elements = new HashMap<String, Element>(); 

    public Element get(final String id) { 
     rejectEmpty(id); 
     return elements.get(id.toLowerCase()); 
    } 
} 

는 여기에서 우리는 useIdAttribute이 비활성화 된 곳 NullObject 패턴의 사용은 특별한 경우를 처리 할 수 ​​있습니다. 물론 상충 관계가 있습니다. 파서 클래스 자체는 더욱 표현력이 강하지 만 1보다는 4 가지 유형이 있습니다.이 리팩토링은 get 메소드에 대한 과도한 것처럼 보일 수 있지만 'put()'메소드를 추가하면 단일 클래스 (NullIdMap)에서 '특별한 경우'논리를 지역화하는 이점이 있습니다.

는 [rejectEmpty는 빈 문자열을 전달하면 예외가 발생 도우미 메서드입니다.]

+0

이 예제는 C#보다는 Java로되어 있지만 여전히 유용합니다. – johnstok

0

여기에 유용 할 수 있습니다 전략 또는 정책 패턴 같은 것을 사용. 런타임시 특정 데이터의 구성 또는 (존재하지 않는) 존재와 같은 것에 기반한 알고리즘의 다른 구현을 캡슐화하거나 스왑 아웃하는 좋은 방법입니다.

시나리오에서 파싱 클래스가 일반적으로 동일한 유형의 동작 (무언가가 있음)이 있지만 내부 알고리즘이 변경되는 경우 구현을 기반으로 할 수 있습니다.

http://en.wikipedia.org/wiki/Strategy_pattern

관련 문제