是否有为类添加“选项”的模式?

时间:2008-10-18 10:49:54

标签: design-patterns configuration

我有一个类,我想允许几个(~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;
    }

7 个答案:

答案 0 :(得分:5)

Decorator pattern怎么样?它旨在为类动态添加行为。

答案 1 :(得分:2)

对于打开/关闭功能的选项,我认为装饰师可以像@Thomas Owens所说的那样。我更担心改变功能的选项。如果这些操作无法链接,装饰器很多都无法工作。例如,如果您的代码如下所示:

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

使用Decorator很难处理,因为每种Action方法的组合顺序都不同。

有20多个选项,假设它们是打开/关闭的,您有超过400种不同的可能组合。我怀疑并非所有这些组合都同样可能。对于您无法通过Decorator处理的事物,您可能需要考虑映射到设置组合的操作模式。仅支持最期望的那些模式。如果模式的数量很小,您可以使用子类化处理它,然后使用装饰器向表示用户所选模式的子类添加功能。您可以使用Factory根据配置选择和构建正确的类。

从本质上讲,我想我说你可能想要考虑一下你是否需要尽可能多的灵活性和随之而来的复杂性。考虑通过将配置选项折叠为更少数量的更可能使用的模式来减少配置选项的数量。

答案 2 :(得分: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();

答案 3 :(得分: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());
    }
}

这里我们使用NullObject模式来处理禁用useIdAttribute的特殊情况。当然有一个权衡 - 解析器类本身更具表现力,而现在有4种类型而不是1.这种重构对于get方法似乎有些过分,但是当你添加'put()'方法时它具有在单个类(NullIdMap)中本地化“特殊情况”逻辑的好处。

[rejectEmpty是一个帮助方法,如果传递一个空字符串则抛出异常。]

答案 4 :(得分: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只是代表,实际的功能被干净地存放在它自己的类中。

答案 5 :(得分:0)

您打算如何对相互依赖的选项实施规则?如果您可以动态添加选项,那么如果您使用命令模式,则可能会被迫拥有多个调用者,或者您可能必须实现一个case语句来构建您需要执行的命令。

答案 6 :(得分:0)

在此处使用类似策略或策略模式的内容可能很有用。这是一种很好的方法,可以根据运行时某些特定数据的配置或(非)存在等内容来封装或交换算法的不同实现。

在您的场景中,如果您的解析类通常具有相同类型的行为(某些内容,某些内容),但内部算法会发生变化,那么您可能希望将其作为实现的基础。

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