2

For a project I'm working on, I need to create objects using different source data formats, more or less like this:

public class FooService
{
    protected DataFormat Format { get; set; }

    public FooService(DataFormat format = DataFormat.Json)
    {
        Format = format;
    }

    public Foo GetFoo(int id)
    {
        string content = GetContentInFormat("someuri/foo/" + id, Format);

        // Something here must create a "Foo" object
        // based on Format, which could be Json, Xml or other
    }
}

public enum DataFormat
{
    Json,
    Xml
} 

I know I could:

1) Have different methods and choose the right one based on Format:

switch (Format)
{
    case DataFormat.Xml:
       return CreateFooFromXml(content);
    case DataFormat.Json:
       return CreateFooFromJson(content);
    default:
       throw new Exception("Invalid format.");
}

Cons: there will be at least 8 different types that will be created this way, so I need something more extensible and maintainable.

2) Make FooService an interface or abstract class and implement concrete classes, one for each format.

Cons: business logic in all classes will always be the same, except for the class instantiation. It may be confusing having JsonFooService and XmlFooService.

I would like to know what's the best solution for this from an extensibility and maintainability point of view.

2
  • To clarify, you need a service that can create an object based on varying input (JSON, XML, etc)? So, the object returned will always be the same, and only the input will differ? This does sound a lot like you have things a little confused in your code, and with some reorganization, you will just need to use the strategy pattern' Commented Mar 26, 2012 at 21:42
  • @JustinPihony I know it can seem quite confusing, but yes, that's what I need. The problem is that there are many different objects that will be created using this method and I feel strategy is not the best fit. Commented Mar 27, 2012 at 2:51

5 Answers 5

3

Instead of making Format an enum, you could make it an interface (IFormat).

Then, for each format, create a concrete class (e.g., JsonFormat) that implements IFormat.

Each concrete class should have only what is unique to the particular format, such as how to find the element/record given an Id.

This is the "strategy pattern": http://en.wikipedia.org/wiki/Strategy_pattern

Sign up to request clarification or add additional context in comments.

2 Comments

I don't want to mix up the logic to get the data (code I posted) and the logic to create the instance from that. That's part of the problem.
It's part of the problem that "Foo" is not the only object that has to be created this way. Should I have a method in "JsonFormat" for each type that has to be created? I feel like something is wrong / missing.
2

You're clearly talking creational patterns here, but I don't think we know enough about the complexity of the Foo's to clearly recommend one over another. Luckily there aren't that many of the GoF creational patterns, so you can read up on all of them in fairly short order. It appears you already understand the factory-method pattern, so you might skip to abstract factory and builder to see whether they fit the bill better.

Tips from the builder wiki:

  • Builder focuses on constructing a complex object step by step. Abstract Factory emphasizes a family of product objects (either simple or complex). Builder returns the product as a final step, but as far as the Abstract Factory is concerned, the product gets returned immediately.
  • Builder often builds a Composite.
  • Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.
  • Sometimes creational patterns are complementary: Builder can use one of the other patterns to implement which components are built. Abstract Factory, Builder, and Prototype can use Singleton in their implementations.
  • Builders are good candidates for a fluent interface.

Hope that helps.

Comments

1

This is a typical use case for the strategy pattern, which deals with algorithms which can be selected at runtime. Switch-cases and long if/else if/else conditions can be transformed to elegant maintainable code

In case you want to dynamically switch out the data format generators, you can look at options like MEF which is a way to extend your application to discover new extensions without any configuration

Comments

0

specifically, you could use IOC (e.g. autofac) and some type of injection (as I see you're close to that thinking already),
to register all your available formats services on startup
(from config or simply an easy one-off initialization that's easy to maintain over time as you add new types)...
as e.g. implementing IOutputFormatService interface
You can then dynamically round them up (e.g. resolve, list out all implemented formats)
and each can 'do the job' via the common interface - or if you 'know them' you could also access them specifically, like IJsonOutputFormatInterface if needed.
If/when you add (or remove) new - it's just the configuration part that changes
And as others mentioned, you only need to code each service to do what's specific to it, format rendering etc.
note: reflection is not required, it's more a concept that's important

Comments

0

The pattern factory method is what you want. The pattern itself will not remove the switch statement, but it will only exist in one place.

However, you can combine the pattern with a bit of reflection to remove the switch statement.

Code (pseudocode)

public class FormatterFactory
{
  Dictionary<string, IFormatter> _formatters;

  public void FormatterFactory()
  {
      var baseType = typeof(IFormatter);
      var formatterTypes = AppDomain
            .GetExecutingAssembly()
            .GetTypes.Where(x=>baseType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);

       // convention based. Each formatter must be named like "JsonFormatter"
       foreach (var type in formatterTypes) 
       {
           _formatters.Add(type.Name.Replace("Formatter", "").ToLower(), type), 
       }
  }
  public IFormatter Create(string formatName)
  {
      var type = _formatters[formatName.ToLower());
      return (IFormatter)Activator.CreateInstance(type);

  }
}

Usage:

var factory = new FormatterFactory();
var json = factory.Create("json").Serialize(myObject);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.