views:

252

answers:

7

I have a class on which I want to allow several (~20+) configuration options. Each option turns on or off a piece of functionality, or otherwise alters operations. To facilitate this, I coded a separate options class with default values. However, I had to litter my code with guard conditions to determine how methods should behave. I am almost done, but now the code seems to smell.

Is there a preferred method/pattern to implement a class like this?

EDIT: More specifically, I am working on a parsing class. Each option configures mutually exclusive portions of the basic parsing algorithm. For example I have several areas in my class that look like the below:

 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;
    }
+5  A: 

How about the Decorator pattern? It's designed for dynamically adding behavior to a class.

Thomas Owens
I thought about this, but I'll review the again code to see if it will fit. Initially I thought it would be a bit overkill, and I wanted to avoid class bloat.
Nescio
Decorator pattern would be my answer too. You don't have to use it in the exact same way in which it is presented in the GoF book or Headfirst design patterns - patterns aren't recipes. Shape it to fit your needs.
Sandman
A: 

I'm thinking of a Command pattern.

It would look something like:

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

In other words; you delegate responsibility to an implementation which you instantiate on construction. Now your doThis just delegates and the actual functionality is cleanly stowed away in it's own class.

extraneon
+1  A: 

There is indeed another pattern called the builder pattern exactly for this purpose. Its usually, useful when you have a class but each 'configuration' option could be made optional perhaps in some combinations only. (http://en.wikipedia.org/wiki/Builder_pattern, but that doesnt exactly describe my scenario tho).

You create two classes - the class you want to build, and the builder. The builder class is the class that takes care of working out which combinations of options are optional, which combination makes sense, etc etc.

e.g., you want to represent a salad - but only certain ingredients 'tastes good', so only those should be made.

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

usage example

Salad s = new SaladBuilder().addIngredient(v).addIngredient(m).build();
Chii
+2  A: 

For the options that turn on/off functionality, I think Decorator is the way to go as @Thomas Owens says. I'm a little more concerned about the options that alter functions. Decorator many not work if these operations cannot be chained. For example, if your code looks like:

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

This would be difficult to handle with Decorator as the order of composition differs for for each Action method.

With 20+ options, assuming that they are on/off, you have over 400 different possible combinations. I suspect that not all of these combinations are equally likely. For things that you can't handle via Decorator, you might want to think about modes of operation that map on to combinations of settings. Support only those modes that are most expected. If the number of modes is small you could handle this with subclassing, then use the Decorators to add functionality to the subclass representing the mode the user has chosen. You could use a Factory to select and build the proper class based on the configuration.

In essence, I guess I'm saying that you may want to consider if you need as much flexibility, and attendant complexity, as you are building. Consider reducing the number of configuration options by collapsing them into a smaller number of more likely to be used modes.

tvanfosson
20+ Boolean options is over a million combinations, isn't it?
erickson
A: 

How do you intend to enforce rules for options that are interdependent? If you can add options dynamically you may be forced to have multiple invokers if you the command pattern, or you may have to implement a case statement for building out what commands you need execute.

David Robbins
+1  A: 

What about something like this?

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

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

Based on the following types:

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

Here we make use of the NullObject pattern to handle the special case where useIdAttribute is disabled. Of course there is a trade-off - the parser class itself is more expressive, while there are now 4 types rather than 1. This refactoring may seem like overkill for just the get method, but when you add a 'put()' method etc. it has the benefit of localisng the 'special case' logic in a single class (NullIdMap).

[rejectEmpty is a helper method that throws an exception if passed an empty string.]

johnstok
This example is in Java rather than C#, but it's still instructive.
johnstok
A: 

Using something like a strategy or policy pattern might be useful here. It's a nice way to encapsulate or swap out different implementations of an algorithm based on things like configuration or (non)existence of some particular data at runtime.

In your scenario, if your parsing class typically has the same types of behavior (something in, something out) but the internal algorithms change, it's something you might want to base an implementation on.

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

joshua.ewer