Шаблон стратегии или интерфейс?
Я ищу абстракцию вспомогательного метода. Метод должен иметь возможность принимать объект, делать что-то с ним в зависимости от типа объекта и возвращать значение. Было бы лучше сделать что-то вроде этого:
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 и сказать вашему регистратору использовать ваш пользовательский бэкэнд. Шаблон стратегии по существу определяет повторно используемые интерфейсы, а затем использует эти интерфейсы для отделения алгоритмов друг от друга.