tags:

views:

116

answers:

3

Imagine an application based on options.

I want to add an exclamation mark to the end of every string (itself, an extremely easy task). However, I have an option in web.config or an XML file, so if the option is true, the exclamation is appended, otherwise it isn't.

I know how to check web.config or an xml file for the setting's value, however, what is the best way to do this? In the case of a string, it will be heavily used in any program.

I could write:

if (ExclamationIsSet) { // Append here }

// Otherwise it isn't set, so don't.

However, this isn't practical for a large (or even small) codebase. Is there a way to get rid of this manual checking? I've heard AOP or attributes may be able to solve this, but I haven't seen an example.

What methods could solve this problem?

Thanks

+2  A: 

I probably wouldn't use AOP here... my question is: when are you doing this appending? I would simply hook into a utility method at that point - i.e.

WriteOutput(someValue); // presumably with some other args

where WriteOutput has the burden of checking this flag - i.e. it isn't in a lot of places. You might also find that C# 3.0 extension methods have a role to play; for example, if you are writing to a TextWriter:

public static void WriteWithMarker(this TextWriter writer, SomeType value) {
    writer.Write(value);
    if(ExclamationIsSet) writer.Write(SomeExtraStuff);
}

then your code just (always) calls output.WriteWithMarker(value); - job done.

I would probably also ensure I minimise impact by storing this value once at init - static constructors are quite handy for that:

public static class MyUtiltiyClass {
    private static readonly bool exclamationIsSet;
    public static bool ExclamationIsSet {get{return exclamationIsSet;}}
    static MyUtiltiyClass() {
        exclamationIsSet = FindWhetherTheFlagIsSet();
    }
    public static void WriteWithMarker(this TextWriter writer, SomeType value) {
        writer.Write(value);
        if(ExclamationIsSet) writer.Write(SomeExtraStuff);
    }
    //etc
}

Perhaps with more context on what and where you are currently doing this it might become clearer....

Marc Gravell
+1 for the detailed walkthrough.
dotnetdev
+4  A: 

The operation can be described as:

public interface ITextDecorator
{
    string GetString(string input);
}

This encapsulates the how (Web.config, XML, etc.) and emphasizes the what (decorating a string).

Then, any class which might need to decorate text can take an instance of that interface:

public class Foo
{
    private ITextDecorator _textDecorator;

    public Foo(ITextDecorator textDecorator)
    {
        _textDecorator = textDecorator;
    }

    public void Bar(string text)
    {
        text = _textDecorator.GetString(text);

        // ...
    }
}

Example implementations of ITextDecorator might be:

public sealed class ExclamationPointTextDecorator : ITextDecorator
{
    public string GetString(string input)
    {
        return input + "!";
    }
}

public sealed class ConditionalTextDecorator : ITextDecorator
{
    private Func<bool> _condition;
    private ITextDecorator _innerTextDecorator;

    public ConditionalTextDecorator(Func<bool> condition, ITextDecorator innerTextDecorator)
    {
        _condition = condition;
        _innerTextDecorator = innerTextDecorator;
    }

    public string GetString(string input)
    {
        return _condition() ? _innerTextDecorator.GetString(input) : input;
    }
}

An example usage of these classes might be:

var textDecorator = new ConditionalTextDecorator(
    () => true,  // Check Web.config, an XML file, or any other condition
    new ExclamationPointTextDecorator());

var foo = new Foo(textDecorator);

foo.Bar("Test");

Notice the decoupling of the exclamation point appending from its conditional invocation. Both classes can now be reused independent of the other. This design style of fine-grained objects works best with an Inversion of Control (IoC) container. However, it is not required.

Bryan Watts
+2  A: 

You could wrap the check into a method:

private string SomeGoodMethodName(string text)
{
    if (text == null || text.EndsWith("!")) { return text; }

    return ConfigurationManager.AppSettings["addExclamation"] == "1" ? text + "!" : text;
}

...and then fetch your strings through that method. If needed you may want to look into the performance around ConfigurationManager.AppSettings and perhaps store this as a boolean to test against instead.

Fredrik Mörk
I just love the name SomeGoodMethodName ;-p
Marc Gravell
:o) Couldn't come up with a good one, so I left that for the readers' imagination to work with.
Fredrik Mörk
This is probably the best way - very quick too. I'm glad I made this thread as it stopped me looking into something potentially unnecessary (eg AOP).
dotnetdev