2013-08-12 2 views
2

여러 위치에서 데이터를 쿼리 할 수 ​​있고 첫 번째 null이 아닌 인스턴스에서 반환해야하는 경우 if/else 문을 사용하지 않는 방법이 있습니까? 여러 공급 업체를 사용할 때 if/else를 피하는 방법은 무엇입니까?

나는 3 개 다른 위치 (사용자 환경 설정, 그룹 설정 및 시스템 환경 설정)에서 사용자에 대한 기본 설정을로드 할 노력하고있어. 예를 들어 :

Preference getPreference(User user, Preference.Type type) { 

    Preference preference = getUserPreferenceFor(user, type); 
    if (preference != null) { 
     return preference; 
    } 

    preference = getGroupPreferenceFor(user, type); 
    if (preference != null) { 
     return preference; 
    } 

    return getSystemPreferenceFor(user, type); 
}  

나는이 방법을 함께 확인하고 체인이 그 동작이 동일하게 유지 할 수 있도록하지만, 코드는이 추악한 중복을하지 않았을 경우 다음을 사용하지 않도록하고 싶습니다.

나는 몇 가지 해결책을 생각했지만 우아한 파업으로 생각하지는 않습니다. 예를 들어, 한 가지 방법은 이러한 각각의 메소드를 디폴트 자체를 제공하는 것이지만, 그것은 if 구문을 분산시키는 것입니다.

Preference getPreference(User user, Preference.Type type) { 
    Preference preference = getUserPreferenceFor(user, type); 
    if (preference != null) { 
     return preference; 
    } 

    return getGroupPreferenceFor(user, type); 
} 

Preference preference getGroupPreferenceFor( 
    if (preference != null) { 
     return preference; 
    } 

    return getSystemPreferenceFor(user, type); 
} 

또 다른 방법은, 일부 바이더 인터페이스를 사용하여 각각의 서브 클래스를 생성하고 첫 번째 null 이외가 발견 될 때까지 제공을 반복하는 것입니다.

public interface PreferenceProvider { 
    Preference getPreference(User user, Preference.Type type); 
} 

public class UserPreferenceProvider implements PreferenceProvider { 
    public Preference getPreference(User user, Preference.Type type) { 
     ... 
    } 
} 
... group and system provider the same way 

final static PreferenceProvider[] providers = new PreferenceProvider[] { 
    new UserPreferenceProvider(), 
    new GroupPreferenceProvider(), 
    new SystemPreferenceProvider() 
}; 

Preference getPreference(User user, Preference.Type type) { 
    Preference preference = null; 
    for (PreferenceProvider provider : providers) { 
     preference = provider.getUserPreferenceFor(user, type); 
     if (preference != null) { 
      return preference; 
     } 
    } 
} 

마지막 하나는 충분히 가까운하지만 여전히이 널 (null) 검사가 있습니다 (I 피해야하고 싶은) 나는이 문제를 해결하는 몇 가지 디자인 패턴이있다 꽤 확신 해요.

Preference getPreference(User user, Preference.Type type) { 

    Preference preference = getUserPreferenceFor(user, type); 
    if (preference == null) { 
     preference = getGroupPreferenceFor(user, type); 
     if (preference == null) { 
      preference = getSystemPreferenceFor(user, type); 
     } 
    } 
    return preference; 
} 

그것은 여전히 ​​if의를 사용하지만 나에게 충분히 우아한 외모 : 난 그냥

+0

; 대신 가장 간단한 형태의 가독성을 제안 할 것입니다. (예 : alfasin의 답) – devsnd

+0

위임자를 매개 변수로 전달할 수있는 자바의 능력에 따라 (C#에서는 할 수 있습니다) 일반 null-check 함수를 만들고 대부분의 ifs. – Fendy

+0

코드 줄을 너무 많이 추가하는 것을 피하려면 어떻게해야합니까? 가치가있는 것이 코드 기반으로 가져 왔습니까? – GManNickG

답변

2

에 대해 무엇을 어느 ... 기억이 안나요.

+0

+1 메서드의 중간에서 return 문을 제거합니다. – Kalaji

+0

@Kalaji의 yeps, 내 목록에 팁 # 1의 : http://alfasin.com/20-tips-for-becoming-a-better-programmer/) – alfasin

+0

마지막'if' 문'이 있어야 ==' '! =' – Kalaji

0

다른 Provider 구현을 사용하여 두 번째 아이디어를 얻었으나 두 번째 방법을 각 Provider에 추가하여 값을 제공 할 수 있는지 알아볼 수 있습니다. 그런 다음 대신 다음과 같이 널 반환 값을 검사의 방법을 사용할 수 있습니다 :

for (PreferenceProvider provider : providers) { 
    if (provider.canProvideAPreferenceFor(user, type)) { 
     return provider.getUserPreferenceFor(user, type); 
    } 
} 

return null; 
+0

나는 내 질문에이 기본 설정을 데이터베이스, 웹 서비스 등에서 가져올 수 있어야한다고 생각한다. 두 번 가져 오거나 초기 가져 오기를 캐시해야하므로 상태 저장 서비스에는 적합하지 않을 것이다. – Ragnar

+0

아, 그 정도면 충분합니다. 너무 많은 작업이 시작될 수도 있지만 각 사용에 대해 새로운 제공자 집합을 만들고 호출 할 수있는'canProvideAPreferenceFor'는'Provider' 내부에 값을 캐시 할 수 있고, null이 아닌 경우 true를 리턴하고 리턴 할 수 있습니다 요청시 'getUserPreferenceFor'에서 가져옵니다. –

0

당신은 환경 설정의 주어진 세트 기본 클래스를 변경할 수 있습니다.

Preference getPreference(User user, Preference.Type type) { 

    Preference preference = new Preference(getSystemPreferenceFor(user, type)); 

    preference.merge(getGroupPreferenceFor(user, type)); 
    preference.merge(getUserPreferenceFor(user, type)); 

    return preference; 

}

이제 널 (null)를 확인해야합니다 병합하지만 단수 예이다. Preference.Preference()는 적절한 경우 getSystemPreference를 기본값으로 사용할 수 있습니다.

이렇게하면 하나의 기본 설정 집합 만 사용된다는 전제가 변경됩니다.

+0

실제로 사용하지 않으면 리소스에 액세스하지 않는 것이 좋습니다. 즉, 대부분의 경우 그룹 기본 설정을 가져 와서 사용자 기본 설정으로 덮어 씁니다. – Ragnar

+0

@Ragnar 예, IMHO 환경 설정이 작동하는 방식입니다. –

0

나는이 일을 wrapper (decorator) 패턴을 사용하는 것이 좋습니다 것입니다.

각 체크를 단일 오브젝트로 랩핑 한 다음 준비된 오브젝트를 작성하면됩니다. Wikipedia에는 ​​그것을 사용하는 좋은 예가 있습니다.

행운을 비네. (: 많은 사람들이 대표단은 성능에 영향을했다,하지만 당신은 진실을 찾기 위해 스스로를 측정해야 주)

1

나는 그것을 대표단을 사용해볼 수 있습니다. 마지막으로 java를 사용한 이후로 이미 길기 때문에 컴파일되지 않을 수 있습니다.

public interface PreferenceGetter{ 
    Preference get(); 
} 

public Preference getPreferenceIfNull(Preference pref, PreferenceGetter func) { 
    if(pref == null){ 
     return func.get(); 
    } 
    else{ 
     return pref; 
    } 
} 

public Preference getPreference(User user, Preference.Type type){ 
    Preference preference = getUserPreferenceFor(user, type); 
    preference = getPreferenceIfNull(preference, new PreferenceGetter(){ 
      public Preference get(){ 
       getGroupPreferenceFor(user, type); 
      } 
     }); 
    preference = getPreferenceIfNull(preference, new PreferenceGetter(){ 
      public Preference get(){ 
       getSystemPreferenceFor(user, type); 
      } 
     }); 
    return preference 
} 

업데이트 :

(ArtemStorozhuk에 의해 제안) 대신 대표단 Decorator 패턴을 사용하여 대안이있다. C#에서는 Decorator 대신 위임을 사용하는 것이 더 좋습니다. 매개 변수 전달 기능이 있기는하지만 java에서는 다를 수 있습니다. 또한 Decorator은 이해하기가 어렵습니다.

public interface PreferenceGetter{ 
    Preference get(Preference pref, PreferenceGetter func); 
} 

public class NullPreferenceGetter implements PreferenceGetter{ 
    public Preference get(Preference pref, PreferenceGetter func){ 
     return null; 
    } 
} 

public class UserPreferenceGetter implements PreferenceGetter{ 
    public UserPreferenceGetter(PreferenceGetter decorated){ 
     this.decorated = decorated; 
    } 
    private PreferenceGetter decorated; 
    public Preference get(Preference pref, PreferenceGetter func){ 
     Preference result = getUserPreferenceFor(user, type); 
     if(result == null){ 
      result = decorated.get(pref, func); 
     } 
     return result; 
    } 
} 

public class GroupPreferenceGetter implements PreferenceGetter{ 
    public GroupPreferenceGetter(PreferenceGetter decorated){ 
     this.decorated = decorated; 
    } 
    private PreferenceGetter decorated; 
    public Preference get(Preference pref, PreferenceGetter func){ 
     Preference result = getGroupPreferenceFor(user, type); 
     if(result == null){ 
      result = decorated.get(pref, func); 
     } 
     return result; 
    } 
} 

사용 : 당신은 기본적으로 자바의 방법은 일류 객체가 아니라는 것을 극복하기 위해 노력하고

public Preference getPreference(User user, Preference.Type type){ 
    PreferenceGetter getter = new UserPreferenceGetter(
     new GroupPreferenceGetter(
      new SystemPreferenceGetter(
       new NullPreferenceGetter()) 
      ) 
     ); 
    return getter.get(user, type); 
} 
+0

이것은 실제로 작동 할 수 있습니다 ... 그리고 빈 함수 호출에 대해 몇 나노초를 보내는 데 신경 쓰지 않아도됩니다. 나는 이것이'Suppiler.of (Preference.class) .from (new UserPreference()) 나 (new GroupPreference()) 나 (new SystemPreference())와 같이 함께 묶일 수 있을지 궁금하다. get()' – Ragnar

+0

In C# 확장으로 인해 가능합니다. 그 이름은'Monad'이며 자바에서 이에 상응하는 것을 모릅니다. 그것은 권장되지 않습니다. 그것은 하나의 라이너이며 예외가 발생하면 그 라인을 참조하십시오. – Fendy

관련 문제