6

I'm looking to abstract a helper method. The method needs to be able to take in an object, do things with it depending on the type of object, and return a value. Would it be better to do something like this:

interface ICanDo
{
 string DoSomething();
}

string DoThings(ICanDo mything)
{
 return mything.DoSomething();
}

Or is it better to do something like this:

interface IStrategy
{
 string DoSomething(object o);
}

string DoThings(object mything, IStrategy strategy)
{
 return strategy.DoSomething(mything);
}

Is the latter even using a strategy pattern, since the strategy isn't being built into the class?

Is there a better way to do this I'm not thinking of? Would it be better to build the strategy into the class, using a wrapper for any class that needs to have DoThings run on it?

Sorry--I'm new to this pattern and trying to figure out where and how to use it best.

This is what I ended up putting together. I'm unsure if this follows good development principles.

class IndexWrapper
{
    public interface IDocumentable
    {
        Document BuildDocument();
    }

    public interface IDocumentBuilder
    {
        Type SupportedType { get; }

        Document BuildDocument(object o);
    }

    public class StringDocumentBuilder : IDocumentBuilder
    {
        public Type SupportedType { get { return typeof(string); } }

        public Document BuildDocument(object o)
        {
            Document doc = new Document();
            doc.Add(new Field("string", o as string, Field.Store.YES, Field.Index.ANALYZED));
            return doc;
        }
    }

    public static class IndexableFactory
    {
        public static IDocumentable GetIndexableObject(object o)
        {
            return GetIndexableObject(o, DocumentBuilderFactory.GetBuilder(o));
        }

        public static IDocumentable GetIndexableObject(object o, IDocumentBuilder builder)
        {
            return new IndexableObject(o, builder);
        }
    }

    public static class DocumentBuilderFactory
    {
        private static List<IDocumentBuilder> _builders = new List<IDocumentBuilder>();

        public static IDocumentBuilder GetBuilder(object o)
        {
            if (_builders.Count == 0)
            {
                _builders = Assembly.GetExecutingAssembly()
                                   .GetTypes()
                                   .Where(type => typeof(IDocumentBuilder).IsAssignableFrom(type) && type.IsClass)
                                   .Select(type => Activator.CreateInstance(type))
                                   .Cast<IDocumentBuilder>()
                                   .ToList();
            }

            return _builders.Where(builder => builder.SupportedType.IsAssignableFrom(o.GetType())).FirstOrDefault();
        }
    }

    private class IndexableObject : IDocumentable
    {
        object _o;
        IDocumentBuilder _builder;

        public IndexableObject(object o) : this(o, DocumentBuilderFactory.GetBuilder(o)) { }
        public IndexableObject(object o, IDocumentBuilder builder)
        {
            _o = o;
            _builder = builder;
        }

        virtual public Document BuildDocument()
        {
            return _builder.BuildDocument(_o);
        }
    }
}
Josh
  • 632
  • 7
  • 13
  • 4
    Aren't the two essentially the same? You just renamed the interface and added a new parameter on DoThings. If you're new to patterns, I suggest you choose the simpler ones first, those that you understand and make sense to your needs. Following a pattern just for the sake of using one will make your code even more complicated. – Adrian M Feb 25 '13 at 06:19
  • 1
    @AdrianM The first locks the method into the class, requiring that you override the method to get new functionality and also requiring that the class have ICanDo implemented. The second provides the method separately, allowing any class to be provided as a parameter, provided the strategy works with the class. My examples are poor, though, as I was curious between implementing the strategy directly in the class vs. providing it separately. I'll correct the first. My apologies! – Josh Feb 25 '13 at 06:50
  • Looking about, isn't the second example the same as described here? [link](http://www.codeproject.com/Articles/52807/Design-Patterns-Part-1-The-Strategy-Pattern) The one big difference being that in the implementation above, IStrategy would have to verify that mything was the correct type, which seems problematic at best. – Josh Feb 25 '13 at 07:11
  • OK. First, change your examples to be more concrete. 'mything' should be a specific class that either implements ICanDo, if you choose the former option, or compatible with IStrategy if you choose the latter. Then, read this http://en.wikipedia.org/wiki/Strategy_pattern – Adrian M Feb 25 '13 at 09:21
  • @AdrianM I've edited my post to include what I put together. I chose to use the factory and strategy patterns, as they seemed to best fit my needs. I feel this should nicely take care of what I need done, but I'm unsure if it's overcomplicating the code... – Josh Feb 26 '13 at 02:30

1 Answers1

5

When in doubt, keep the KISS mantra in your mind - Keep It Short and Simple. Patterns can be very useful, but often they're only useful in specific cases and add unnecessary complexity otherwise.

In my experience, the strategy pattern is useful for when you have multiple different backends to choose from for a class. For example, say you have a logging class that your program uses to print debug information. Maybe in some cases, you want to log to a file. Maybe you'd like to log to a console. Perhaps you'd even like to log to a remote server with a proprietary protocol you company made!

So, your logging class may look like this:

interface IOutputWriter
{
    void WriteLn(string message);
}

class ConsoleWriter : IOutputWriter
{
    public ConsoleWriter()
    {

    }

    public void WriteLn(string message)
    {
        Console.WriteLine(message);
    }
}

class NetworkWriter : IOutputWriter
{
    public NetworkWriter()
    {

    }

    public void WriteLn(string message)
    {
        //Crazy propietary server protocol action
    }
}

class Logger
{
    IOutputWriter writer;
    public Logger(IOutputWriter writer)
    {
        this.writer = writer;
    }

    public void Log(string message)
    {
        writer.WriteLn(message + "Date");
    }
}

With the end result that your program code looks like this:

class Program
{
    static void Main(string[] args)
    {
        Logger logger = new Logger(new ConsoleWriter());
        logger.Log("Test");
    }
}

The benefit is that if you want to use your crazy networking protocol, you can do it without even looking at the logging class. You just have to make a new class with your IOutputWriter interface and tell your logger to use your custom backend. The strategy pattern is essentially defining reusable interfaces and then using those interfaces to decouple algorithms from each other.

Jacob Adamson
  • 116
  • 2
  • 2