Шаблон стратегии или интерфейс?

Я ищу абстракцию вспомогательного метода. Метод должен иметь возможность принимать объект, делать что-то с ним в зависимости от типа объекта и возвращать значение. Было бы лучше сделать что-то вроде этого:

interface ICanDo
{
 string DoSomething();
}

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

Или лучше сделать что-то вроде этого:

interface IStrategy
{
 string DoSomething(object o);
}

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

Использует ли последний даже шаблон стратегии, поскольку стратегия не встроена в класс?

Есть ли лучший способ сделать это, о котором я не думаю? Было бы лучше встроить стратегию в класс, используя оболочку для любого класса, для которого нужно запустить DoThings?

Извините - я новичок в этом паттерне и пытаюсь выяснить, где и как его лучше всего использовать.

Это то, что я в итоге собрал. Я не уверен, если это следует хорошим принципам развития.

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

1 ответ

Решение

Если вы сомневаетесь, держите в уме мантру "ПОЦЕЛУЙ" - держите ее короткой и простой. Шаблоны могут быть очень полезными, но часто они полезны только в определенных случаях и добавляют ненужную сложность в противном случае.

По моему опыту, шаблон стратегии полезен для случаев, когда у вас есть несколько различных бэкэндов для выбора для класса. Например, скажем, у вас есть класс ведения журнала, который ваша программа использует для печати отладочной информации. Может быть, в некоторых случаях вы хотите войти в файл. Может быть, вы хотите войти в консоль. Возможно, вам даже захочется войти на удаленный сервер по проприетарному протоколу вашей компании!

Итак, ваш класс регистрации может выглядеть так:

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

С конечным результатом, что ваш программный код выглядит так:

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

Преимущество состоит в том, что если вы хотите использовать свой сумасшедший сетевой протокол, вы можете сделать это, даже не глядя на класс ведения журнала. Вам просто нужно создать новый класс с вашим интерфейсом IOutputWriter и сказать вашему регистратору использовать ваш пользовательский бэкэнд. Шаблон стратегии по существу определяет повторно используемые интерфейсы, а затем использует эти интерфейсы для отделения алгоритмов друг от друга.

Другие вопросы по тегам