2

当数据可以从多个位置查询并且应该在第一个非空实例上返回时,有没有办法避免使用 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 检查并将这些方法链接在一起,以便行为保持不变,但代码不会有这种丑陋的重复。

我想到了几个解决方案,但这些解决方案都不是优雅的。例如,一种方法是让这些方法中的每一个都提供默认值本身,但这只会分散 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);
} 

另一种方法是使用一些提供者接口,为每个提供者创建子类并遍历提供者,直到找到第一个非空值。

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;
        }
    }
}

最后一个足够接近,但它仍然有这个空检查(我想避免),我非常有信心有一些设计模式可以解决这个问题。就是想不起来是哪一个...

4

5 回答 5

2

关于什么:

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;
}

它仍然使用ifs 但对我来说看起来足够优雅。

于 2013-08-12T18:52:39.677 回答
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
}

更新:

有一种使用Decorator模式代替委托的替代方法(如 ArtemStorozhuk 所建议的)。Decorator由于参数传递的能力,最好在 C# 中使用委托而不是在 C# 中,但在 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);
}
于 2013-08-12T19:45:33.403 回答
0

我建议使用包装器(装饰器)模式来做到这一点。

您只需将每个检查包装到一个对象中,然后创建准备好的装饰对象。维基百科有一个很好的使用例子。

祝你好运。

于 2013-08-12T19:40:17.847 回答
0

我会采用您的第二个想法来实现不同的Provider实现,但是为每个实现添加第二种方法Provider,可以用来确定它是否可以提供值。然后,您可以使用该方法而不是检查 null 返回值,如下所示:

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

return null;
于 2013-08-12T18:55:19.297 回答
0

您可以使用一组给定的 Preferences 使 Preferences 类可变。

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;

}

现在合并必须检查空值,但仅在单个实例中。如果合适,Preference.Preference() 可以默认为 getSystemPreference。

这确实改变了只使用一组偏好的前提。

于 2013-08-12T18:56:12.367 回答