【问题标题】:Is this a case for dynamic classes or ...?这是动态类的情况还是......?
【发布时间】:2011-11-26 20:55:31
【问题描述】:

关于这个问题我什至不确定要搜索什么,所以我想我会在这里发布。

假设我有一堆接口,比如...

/// <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 的原因是能够拥有一个 List 并使用更具体的接口作为过滤器。也许下面的内容会更清楚:

/// <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 类的方法/属性。

我曾考虑使用字典和字符串属性查找来实现这一点,但感觉我可以通过这种方式编写更强类型的代码,并确保某个地方的简单拼写错误不会搞砸一切。

反正我猜总结是:

这是在对象上实现变量属性的糟糕方法吗?如果是这样,那会更好。如果没有,有没有办法像我在上面解释的那样在工厂中创建对象?

编辑:

我认为这样做的想法有些不错,这很酷。我想知道如何创建一个工厂,该工厂接受一个只有属性的接口的参数,并且这些属性只有 getter(我认为这是一个重要的点)以及属性的属性值,并返回一个实现接口并拥有所有定义的属性。

例如,

/// <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:如何使用装饰器?

根据我对装饰器的了解,您可以扩展对象的功能。这很酷。但是,您必须提前知道如何扩展该功能。你不能随意这样做。

考虑到我的代码库如上,我想使用装饰器。

我会说:

// 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.PropertyOnIEnjoyLookingDecoratorInterface 你失去了所有这些属性。这不是我想要的。保留属性的唯一方法是重定向

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:

感谢大家的cmets。我很高兴我发布了我对模式的无知;D 感谢您的澄清!我喜欢这个网站和所有可爱的人!

【问题讨论】:

  • 为什么你不能用你提到的方法结束InterestingObjects.OfType&lt;IProduceGoods&gt;()
  • @sll:我不知道怎么做工厂。这就是问题所在。或者如果这甚至是一个好方法。
  • 您希望所有这些接口之间的行为有何不同?除了吸气剂?
  • 几乎是吸气剂。有一些特殊对象将直接在代码中实现,它们的行为与 getter / setter 不同。工厂可以根据传递的参数查找这些。我会尝试进一步解释。
  • 看来你想要的是拥有一种单一类型的不同实例,如果你不打算有依赖于它的行为,你不需要用接口标记东西,但我没有你这么多的上下文,所以我可能遗漏了其他东西

标签: c# class dynamic properties factory


【解决方案1】:

我不喜欢 IMasterInterface 概念,但假设您只是对问题使用抽象术语 - 我可以看到这一点。我对您示例中的用法也有一点疑问:

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

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

“公主”不应该是“喜欢看和消费商品的东西”吗?

除此之外,我认为过滤实现的接口类型的方法很好。

【讨论】:

  • 我相信这样一种方式(通过为您今天需要的每个独特组合引入一个新界面,我的想法对吗?)您最终可能会遇到“界面炸弹/混乱”问题并且这个问题维护时间真的很辛苦
  • @Martin Clarke:我不确定你的意思,是不是有错误?你知道如何在这个例子中创建工厂吗?我不知道该怎么做。此外, ((princess is ... )) 只是一个评论。对 Create 的因子调用的结果应该是这样的(不是我会在代码中使用它)
【解决方案2】:

这是一个好方法。而且,坦率地说,我认为它没有任何问题。接口只是接口——它们只提供一种描述一组通用成员的方法。问题是您的问题非常笼统,很难理解您要达到的目标。

接受接口的工厂方法很奇怪。例如我们有以下代码:

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

var x = MyFactory.Create(IFoo);

工厂会创造什么? A 或 B 的实例?

无论如何,我认为我能提出的最佳建议是:不要想太多。

【讨论】:

  • 谢谢,我认为这也是个好主意。关键是我不想在您的示例中实现 A 类或 B 类。我希望工厂创建一些实现 Foo 的类,并且我将传递 Foo 属性的参数。我会更新问题以澄清。
【解决方案3】:

尝试研究装饰器设计模式。据我所知,它适用于您想到的那种情况,即可以包含从一组属性中提取的混合属性集的对象。在 Decorator 模型中,每个属性都是它自己的一个类,您可以有效地动态连接这些类以创建具有所需属性的对象。

要实现过滤,您需要某种迭代器来遍历装饰器链,以查看您所追求的是否包含在您正在测试的对象中。

我没有在 C# 中使用过装饰器,只在 C++ 中使用过,但我发现它是一种非常有用且灵活的前进方式。如果您发现自己创建了越来越多的表示“属性”类的交叉点的专业类,那么我认为装饰器可能会有所帮助。

有关此模式的信息,请参阅 Decorator Design Pattern in C#。 (购买并阅读 GoF 设计模式一书!)

【讨论】:

  • 我想过这一点,但我不想走一堆列表来查看是否包含该属性。不过,我会看看这个,看看它是否还不错。
  • +1。我认为这是您应该采取的方法,您想向对象添加某些行为。对于公主,您的工厂将返回用 EnjoyLookingAtGoodsDecorator 和 ConsumesGoodsDecorator 装饰的混凝土类型
  • 我不认为装饰器会起作用,因为我必须提前知道组合。我需要装饰器能够即时装饰任何接口组合。
  • 你能把这个定义好一点吗?装饰很简单,我不明白你所说的“即时”是什么意思
  • @SebastianPiu - 这一切都在装饰者模式中,当 GoF 已经这样做时,我没有太多意义。也有一些不错的网络资源 - 例如,这是我发现的一个很好的例子(当然是人为的,但我认为它说明了它是如何工作的):codeproject.com/KB/architecture/PatternsIntro_Decorator.aspx
【解决方案4】:

回答关于工厂的问题,您可以使用反射和/或原型模式来完成,但前提是接口的组合是唯一的。如果您有多个具体类可以满足请求的接口列表(例如,类 Prices 和 Prince),那么工厂将无法知道要创建哪个。

如果没有具体的类,并且您真的试图在运行时创建/组合/等一个对象,那么那是一匹不同颜色的马。您可能需要一个通用的基类(或者可能在您的主界面中提供支持)来帮助使用(如另一张海报所建议的)装饰器方法,或者可能使用 facet-type 模式。

【讨论】:

    【解决方案5】:

    根据您的更新,我发现您似乎不了解装饰器模式。

    我不知道这是否是最好的方法,就好像你在行为上没有任何区别并且你的接口是静态的(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>()
    

    编辑- 你可以这样破解它:

      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 则将其设置为 null。仍然很难在没有上下文的情况下弄清楚

    HTH

    【讨论】:

    • 漂亮。谢谢。我查找的任何示例都没有这样的实现,现在我看到它们都非常糟糕。 :D
    • 我不想要一个主接口,其中定义了所有方法,并在使用未定义的方法时抛出实现类。我有一组非常明确的接口,它们不重叠,但是当组合起来时,行为会得到扩展。例如,我可以有 IProduceGoods{ string WhatIProduce {get;} double HowManyPoundsIProduce {get;} int EverySoManyDays {get;}} 和 IConsumeGoods {string WhatIConsume{get;} double HowMuchIPayPerPound{get;}} 然后两者的组合有意义。让所有类都具有所有这些属性是没有意义的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 2013-09-04
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多