12
\$\begingroup\$

I've been reading The Pragmatic Programmer for a few days and I've come across a reference to the strategy pattern. Looked it up in Design Patterns and figured I could refactor a piece of code I'm currently working on to comply with the strategy pattern.

I need to process text using many string algorithms much like Lucene analyze and tokenize text. This is for an importation routine. Strategy:

public class ChainOfProcessor: IStringProcessor
{
    public List<IStringProcessor> Processors;

    public ChainOfProcessor()
    {
        Processors = new List<IStringProcessor>();
    }

    public ChainOfProcessor Add<TProcessor>()
        where TProcessor: IStringProcessor, new()
    {
        return Add(new TProcessor());
    }

    public ChainOfProcessor Add(IStringProcessor processor)
    {
        Processors.Add(processor);
        return this;
    }

    public string Process(string input)
    {
        return Processors.Aggregate(input, (current, processor) => processor.Process(current));
    }
}

public interface IStringProcessor
{
    string Process(string input);
}

public class HtmlStripper: IStringProcessor
{
    public virtual string Process(string input)
    {
        var htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(input);
        return htmlDoc.DocumentNode.InnerText;
    }
}

public class HtmlDecoder: IStringProcessor
{
    public string Process(string input)
    {
        return HttpUtility.HtmlDecode(input);
    }
}

In Design Patterns, the authors use a Composition class and Compositor classes as examples. The client is allowed to chose a single Compositor through Composition to do whatever the Compositor is supposed to do. There is a subtle difference in how the client uses the Composition in my example. Instead of allowing a single IStringProcessor to be used through some Composition class, I allow many, to be executed one after the other through the concrete class ChainOfProcessor. Usage:

var text = new ChainOfProcessor()
    .Add<HtmlStripper>()
    .Add<HtmlDecoder>()
    .Process(html);
\$\endgroup\$

3 Answers 3

10
\$\begingroup\$

It seems like a you should implement Chain of responsibility as you are passing the same string to different kind of processor with a particular sequence

http://www.dofactory.com/Patterns/PatternChain.aspx#_self1

This is a gud pattern in your scenario.

One more thing your StringProcessorChain might not be needed in this case....

Let me know if you need a more highlight on same

\$\endgroup\$
2
  • \$\begingroup\$ It bothers me to write chaining code in every single processor. Also, usability is reduced on the client for large chains of processors; In the end it requires more code on the client and the framework. \$\endgroup\$ Commented Nov 8, 2012 at 19:34
  • 2
    \$\begingroup\$ You could make an abstract StringProcessorBase class that has the chaining code, and make your processors extend that... \$\endgroup\$
    – cHao
    Commented Nov 8, 2012 at 22:38
4
\$\begingroup\$

Minor stylistic things (not exposing a public List), etc. But otherwise, you're looking good:

public interface IStringProcessor
{
    string Process(string input);
}

public sealed class ChainOfProcessor : IStringProcessor
{
    private readonly IList<IStringProcessor> processors = new List<IStringProcessor>();

    public IList<IStringProcessor> Processors
    {
        get
        {
            return this.processors;
        }
    }

    public ChainOfProcessor Add<TProcessor>() where TProcessor : IStringProcessor, new()
    {
        return Add(new TProcessor());
    }

    public ChainOfProcessor Add(IStringProcessor processor)
    {
        this.processors.Add(processor);
        return this;
    }

    public string Process(string input)
    {
        return this.processors.Aggregate(input, (current, processor) => processor.Process(current));
    }
}

public class HtmlStripper : IStringProcessor
{
    public virtual string Process(string input)
    {
        var htmlDoc = new HtmlDocument();

        htmlDoc.LoadHtml(input);
        return htmlDoc.DocumentNode.InnerText;
    }
}

public sealed class HtmlDecoder : IStringProcessor
{
    public string Process(string input)
    {
        return HttpUtility.HtmlDecode(input);
    }
}
\$\endgroup\$
2
\$\begingroup\$

I would call this an implementation of the Composite pattern.

The strategy pattern is about choosing one from a selection of techniques to accomplish a goal. A common example is different formatting (xml or json) or different storage (db or file system). The encapsulation is around which choice is made.

In the composite pattern, an implementation of an interface delegates to N other implementation according to some algorithm (like "call all of them in order"). The encapsulation is cardinality of child objects being called. This is a very good example of exactly that.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.