Это случай для динамических классов или ...? - PullRequest
4 голосов
/ 28 сентября 2011

Я даже не уверен, что искать в связи с этой проблемой, поэтому я решил опубликовать это здесь.

Допустим, у меня есть несколько интерфейсов, таких как ...

/// <summary>
/// All interesting classes will implement this interface
/// </summary>
interface IMasterInterface {}

/// <summary>
/// Interface to represent someone that creates / produces goods
/// </summary>
interface IProduceGoods : IMasterInterface { int Prop1 {get;} }

/// <summary>
/// Interface to represent someone that buys / consumes goods
/// </summary>
interface IConsumeGoods : IMasterInterface { int Prop2 {get;} }

/// <summary>
/// Interface to represent someone that stores goods
/// </summary>
interface IStoreGoods : IMasterInterface { double Prop3 {get;} string name {get;}}

/// <summary>
/// Interface to represent someone that enjoys looking at goods
/// </summary>
interface IEnjoyLookingAtGoods : IMasterInterface { int Prop4 {get;} DateTime Prop5 {get;} }

Теперь у меня есть какая-то комбинация, которую я знаю сегодня, что-то вроде:

/// <summary>
/// Class to represent a farm which grows and stores crops
/// </summary>
class Farm : IProduceGoods, IStoreGoods {/*...*/}

/// <summary>
/// Class to represent a merchant who buys goods and stores them
/// </summary>
class Merchant : IConsumeGoods, IStoreGoods {/*...*/}

/// <summary>
/// Window Shopper represents someone who doesn't buy anything and only looks
/// </summary>
class WindowShopper : IEnjoyLookingAtGoods{ /*...*/ }

Теперь я счастлив, что у меня есть несколько моих классов, но завтра, я думаю, было бы неплохо также иметь класс, где кто-то фактически покупает у Продавца, так что я бы пошел к своему коду и добавить

/// <summary>
/// Princesses have lots of money to buy stuff and lots of time to look at stuff
/// </summary>
class Princess : IEnjoyLookingAtGoods, IConsumeGoods {/*...*/}

Так вот, я не думаю, что мне нужно это делать ...

Я бы хотел иметь фабричную (или похожую) вещь и сказать:

IMasterInterface princess = MyFactory.Create(IEnjoyLookingAtGoods, IEnjoyLookingAtGoodsParameters, IConsumeGoods, IConsumeGoodsParameters)

/// This should be true
((princess is IEnjoyLookingAtGoods) && (princess is IConsumeGoods))

По сути, я хотел бы сообщить фабрике, какие интерфейсы использовать для создания объекта. У меня есть контейнеры, которые имеют списки IMasterInterface

/// <summary>
/// My container class for interesting objects
/// </summary>
class InterestingObjectContainer
{ public ReadOnlyCollection<IMasterInterface> InterestingObjects {get;} }

Теперь, вот где лежит вопрос. Причиной того, что все интересные классы реализовали IMasterInterface, была возможность иметь список и использовать более специфические интерфейсы в качестве фильтров. Возможно, следующее станет более понятным:

/// <summary>
/// I want to see the net population of producers and get there total production
/// </summary>
class ProducerProductionCalculator
{
  // THIS IS WHERE THE MEAT OF THE QUESTION RESIDES!
  ProductionResults Calculate(InterestingObjectContainer interestingObject)
  {
    List<IProduceGoods> producers = interestingObject.InterestingObjects.OfType<IProduceGoods>(); // Perhaps more interest LINQ
    return DoSomethingWithListToAggregate(producers);
  }
}

Отфильтровав более конкретный интерфейс, теперь я могу рассчитывать на все объекты, переданные DoSomethingWithListToAggregate (производители ICollection) имеющие методы / свойства класса IProduceGoods.

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

Во всяком случае, я думаю, что резюме:

Это плохой способ реализации переменных свойств объекта и, если да, то что будет лучше. Если нет, то есть ли способ создать объекты на фабрике, как я пытался объяснить выше?

EDIT:

Я вижу некоторые мысли о том, чтобы сделать это, и это круто. Мне было интересно, как создать фабрику, которая принимает аргументы интерфейса, который имеет только свойства и свойства имеют только геттеры (я думаю, это важный момент) вместе со значениями свойств для свойств и возвращает объект, который реализует интерфейс и имеет все свойства определены.

Например,

/// <summary>
/// Factory to create any combination of properties
/// </summary>
class FactoryForInterestingObjects
{
  public static IMasterInterface Create(
  List<KeyValuePair</*what goes here is the interface that I want to use,
                      what goes here are the parameter values that 
                      should be returned by the getter */>> );
}

Я бы передал фабрике все интерфейсы и их значения параметров, и он создал бы некоторый класс, который реализует эти интерфейсы и имеет эти значения. Надеюсь, это немного понятнее?

РЕДАКТИРОВАТЬ 2: Как использовать Decorator?

Из того, что я вижу в декораторе, вы можете расширить функциональность объекта. Это круто. Однако вы должны заранее знать, как вы расширяете эту функциональность. Вы не можете сделать это произвольно.

Учтите, что моя кодовая база такая же, как и выше, и я хочу использовать декоратор.

Я бы сказал:

// Edited to be correct
class EnjoyLookingDecorator : IEnjoyLookingAtGoods
{
  private IMasterInterface instance;
  public EnjoyLookingDecorator(IMasterInterface wrappedObject)
  { this.instance = wrapped Object;}

  #region Implementation of IEnjoyLookingAtGoods
  /*...*/
  #endregion
}

РЕДАКТИРОВАТЬ 4:

Я все еще не думаю, что это сработает. В вашем примере я теряю содержащийся интерфейс классов, я должен перенаправить его вниз. Например,

class EnjoyLookingDecorator : IEnjoyLookingAtGoods
{
  private IMasterInterface instance;
  public EnjoyLookingDecorator(IMasterInterface concrete)
  { this.instance = concrete;}

  #region Implementation of IEnjoyLookingAtGoods here
  /*...*/
  #endregion

  bool Is<T>() //this should be in the IMasterInterface
  {
     return this is T or instance is T;
  }
}

class ConsumesGoodsDecorator : IConsumeGoods
{
  private IMasterInterface instance;
  public ConsumesGoodsDecorator (IMasterInterface concrete)
  { this.instance = concrete;}

  #region Implementation of IConsumeGoods here
  /*...*/
  #endregion

  bool Is<T>()
  {
     return this is T or instance is T;
  }
}

поэтому, когда вы d

IMasterInterface princess = new MasterClass() //whatever your concrete type is named
princess = new ConsumesGoodsDecorator(new EnjoyLookingDecorator(princess))

вы больше не можете делать princess.PropertyOnIEnjoyLookingDecorator. Интерфейс вы теряете все эти свойства. Это не то, что я хочу. Единственный способ сохранить свойства - это перенаправить

class ConsumesGoodsDecorator : IConsumeGoods, IEnjoyLookingAtGoods
{
  private IMasterInterface instance;
  public ConsumesGoodsDecorator (IMasterInterface concrete)
  { this.instance = concrete;}

  #region Implementation of IConsumeGoods here
  /*...*/
  #endregion

  #region Redirect all the IEnjoyLookingAtGoods Property Getters to instance
  /* ... */
  #endregion

  bool Is<T>()
  {
     return this is T or instance is T;
  }
}

Выполнив перенаправление, мы должны реализовать интерфейс. Тогда комбинации должны иметь код, который я стараюсь избегать. У меня нет ограничений на комбинации интерфейсов.

РЕДАКТИРОВАТЬ 5:

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

Если бы фабрика могла сделать что-то вроде этого:

/// <summary>
/// Factory to create any combination of properties
/// </summary>
class FactoryForInterestingObjects
{
  public static IMasterInterface Create(
    List<KeyValuePair<Type t, ArgSet customArguments>> interfaces))
  {
    object baseObject;
    foreach(KeyValuePair<Type, ArgSet> interface in interfaces)
    {
       AddInterface(interface, object);
    }
  }

  private static void AddInterface(KeyValuePair<Type, ArgSet> interface, ArgSet arguments)
  {
     // Delegate this to someone else
     if(interface.Key is typeof(IProduceGoods))
     {
       IProduceGoodsExtensions.AddInterface(o, interface.value);
     }
  }
}

public static class IProduceGoodsExtensions
{
   public static void AddInterface(object o, ArgSet arguments)
   {
      // do something to object to make it implement IProductGoods
      // and make all the getters return the arguments passed in ArgSet
   }
}

Я понимаю, что это не так, как на самом деле будет работать, но иллюстрирует мысль, которую я пытаюсь сделать. Я хочу, чтобы объект реализовал динамическую комбинацию интерфейсов и имел значения по умолчанию для сеттеров.

Даже если бы я мог сделать что-то наподобие фабрики, напишите текстовый файл с кодом:

/// <summary>
/// Auto Generated by Factory to create a new type on the fly
/// </summary>
class ClassImplementingIProduceGoodsAndIEnjoyLookingAtGoods : IProduceGoods, IEnjoyLookingAtGoods
{
  // From IProduceGoods
  public int Prop1 {get; private set;}
  // From IEnjoyLookingAtGoods
  public int Prop4 {get; private set;}
  public DateTime Prop5 {get; private set;}
  public ClassImplementingIProduceGoodsAndIEnjoyLookingAtGoods(int prop1, int Prop4 , DateTime Prop5)
  {
    this.Prop1 = prop1; this.Prop4 = Prop4; this.Prop5 = Prop5;
  }
}

Затем скомпилируйте класс и как-нибудь разрешите мне создавать его экземпляры. Это то, что я ищу. Надеюсь, это имеет больше смысла.

РЕДАКТИРОВАТЬ 6:

Это решение, с которым я, вероятно, пойду, так как на данный момент я не вижу альтернативы.

//Update Master Interface
interface IMasterInterface
{
   bool Is(Type t);
   IMasterInterface As(Type t);
}

/// <summary>
/// Class to build up a final object
/// </summary>
class CompositionObject : IMasterInterface
{
  ICollection<IMasterInterface> parts;
  CompositionObject(IMasterInterface object){ parts = new List<IMasterInterface(object);}
  bool Is(Type t)
  {
     foreach(IMasterInterface part in parts)
     { if (part is t) return true; // not sure on this off top of head
     }
      return false;
  }

  IMasterInterface As(Type t)
  {
    foreach(IMasterInterface part in parts)
    { if(part is t) return part; }
  }

  bool Add(IMasterInterface interface)
  { this.Is(typeof(interface)) return false; // don't add again
    this.parts.Add(interface) }
}

Теперь моя фабрика может просто возвращать эти составные объекты, и, пока вызывается As, я могу безопасно отключиться. Я чувствую, что, возможно, есть способ использовать дженерики, чтобы избежать кастинга.

Любые конкретные классы, реализующие IMasterInterface, могут просто возвращать себя при вызове As.

РЕДАКТИРОВАТЬ 3:

Спасибо всем за комментарии. Я рад, что опубликовал свое незнание шаблона; D Спасибо за разъяснения! Я люблю этот сайт и всех вас, милые люди там !!

Ответы [ 5 ]

2 голосов
/ 28 сентября 2011

на основе вашего обновления я вижу, что вы, похоже, не понимаете шаблон декоратора.

Я не знаю, является ли это лучшим способом, так как если у вас нет различий в поведении и ваши интерфейсы статичны (IenjoyLookingAtGoods, IConsumeGoods и т. Д.), Вы можете пойти дальше и реализовать их с помощью свойств на ваш абстрактный тип, и просто создайте новые экземпляры, установив их.

Декораторы должны быть примерно такими

class EnjoyLookingDecorator : IEnjoyLookingAtGoods
{
  private IMasterInterface instance;
  public EnjoyLookingDecorator(IMasterInterface concrete)
  { this.instance = concrete;}

  #region Implementation of IEnjoyLookingAtGoods here
  /*...*/
  #endregion

  bool Is<T>() //this should be in the IMasterInterface
  {
     return this is T || instance.Is<T>();
  }
}

class ConsumesGoodsDecorator : IConsumeGoods
{
  private IMasterInterface instance;
  public ConsumesGoodsDecorator (IMasterInterface concrete)
  { this.instance = concrete;}

  #region Implementation of IConsumeGoods here
  /*...*/
  #endregion

  bool Is<T>()
  {
     return this is T || instance.Is<T>();
  }
}

и если вы хотите получить что-то, что реализует оба, вам нужно сделать что-то вроде этого:

IMasterInterface princess = new MasterClass() //whatever your concrete type is named
princess = new ConsumesGoodsDecorator(new EnjoyLookingDecorator(princess))
var b = princess.Is<IEnjoysLookingAtGoods>()

Edit- Вы можете взломать это так:

  T As<T>()
  {
     return this is T ? this : instance.As<T>(); /*be careful here, the implementation in the concrete class should return null if he is not a T*/
  }

  /*continuing the example above*/
    var consumer = princess.As<IConsumesGoods>(); //this is good
    var producer = princess.As<IProducesGoods>(); //this will return null

но это противно, больше всего я читаю ваш вопрос, больше всего кажется, что эти интерфейсы не имеют смысла. Вы не добавляете поведение вообще. Почему бы не иметь класс, который реализует IMasterInterface и имеет 4 свойства с различными интерфейсами (композицией), если вы не хотите, чтобы у принцессы был IProduceGoods, тогда установите это значение как нулевое. Все еще трудно понять без контекста

НТН

2 голосов
/ 28 сентября 2011

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

Чтобы реализовать фильтрацию, вам необходимо иметь какой-то видитератор, чтобы пройти цепочку декораторов, чтобы увидеть, включен ли тот, который вы ищете, для объекта, который вы тестируете.

Я не использовал Decorator в C #, только в C ++, но я нашел его очень полезными гибкий путь вперед.Если вы обнаружите, что создаете все больше и больше специализированных классов, представляющих пересечения классов «свойств», то я думаю, что Decorator вполне может помочь.

См. Шаблон проектирования Decorator в C # для получения информации об этом шаблоне.(И купите, и прочитайте книгу «Образцы дизайна GoF!»)

0 голосов
/ 28 сентября 2011

Отвечая на вопрос о фабрике, вы можете использовать рефлексию и / или шаблон прототипа, но только если комбинация интерфейсов уникальна. Если у вас есть более одного конкретного класса, который может удовлетворять списку запрошенных интерфейсов (скажем, классы Pricess и Prince), то у фабрики не будет возможности узнать, что создать.

Если конкретных классов нет, и вы действительно пытаетесь создать / создать / и т.д. объект во время выполнения, тогда это лошадь другого цвета. Вам, вероятно, понадобится общий базовый класс (или, возможно, поддержка в вашем главном интерфейсе), чтобы помочь с этим, используя (как предложил другой автор) подход декоратора или, возможно, используя шаблон типа фасета.

0 голосов
/ 28 сентября 2011

Это хороший способ.И, честно говоря, я не вижу никаких проблем с этим.Интерфейсы - это просто интерфейсы - они обеспечивают только способ описания набора общих членов.Проблема в том, что ваш вопрос довольно общий и трудно понять, чего вы пытаетесь достичь.

Фабричный метод, принимающий интерфейсы, довольно странный.Например, у нас есть следующий код:

public class A : IFoo { }
public class B : IFoo { }

var x = MyFactory.Create(IFoo);

Что создаст завод?Экземпляр A или B?

Во всяком случае, я думаю, что лучший совет, который я могу придумать, это: не думайте об этом.

0 голосов
/ 28 сентября 2011

Мне не нравится концепция IMasterInterface, но при условии, что вы просто используете абстрактный термин для вопроса - я могу видеть это. У меня также есть сомнение в использовании в вашем примере:

IMasterInterface princess = MyFactory.Create(IEnjoyLookingAtGoods, IEnjoyLookingAtGoodsParameters, IConsumeGoods, IConsumeGoodsParameters)

/// This should be true
((princess is IEnjoyLookingAtGoods) && (princess is IConsumeGoods))

Разве "принцесса" не должна быть "чем-то, похожим на настоящие и потребляющие товары"?

Кроме этого, я думаю, что подход фильтрации по типам реализованного интерфейса хорош.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...