开发者

Is this a case for dynamic classes or ...?

开发者 https://www.devze.com 2023-04-08 19:54 出处:网络
I\'m not really sure even what to search for regarding this issue, so I figured I\'d post it here. Let\'s say I have a bunch of interfaces like...

I'm not really sure even what to search for regarding this issue, so I figured I'd post it here.

Let's say I have a bunch of interfaces like...

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

Now, I have some combination that I know I want today, something like:

/// <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{ /*...*/ }

Now I'm happy that I've got my few classes, but tomorrow, I think, wouldn't it be nice to also have a class where someone actually buys from the Merchant so I'd go to my code and add

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

Now, I don't think I should have to do that...

What I would like to do is have a factory (or similar) thing and say:

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

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

Essentially, I'd like to tell the factory which interfaces to use to construct the object. I have containers that have lists of IMasterInterface

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

Now, here is where the meat of the question lies. The reason for having all the interesting classes implement the IMasterInterface was to be able to have a List and use the more specific interfaces as filters. Perhaps the following will make it more clear:

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

By filtering out to the more specific interface, I can now count on all the objects passed to DoSomethingWithListToAggregate(ICollection producers) having the methods / properties of the IProduceGoods class.

I thought about implementing this with dictionaries and string property look ups, but it feels like I can write more strongly typed code this way and ensure that a simple spelling mistake somewhere is not going to mess everything up.

Anyway, I guess the summary is:

Is this a poor way of implementing variable properties on a object and if so what would be a better. If not, is there a way to create the objects in a factory as I have tried to explain above?

EDIT:

I see some oks to the idea of doing this, and that's cool. I was wondering how to create a factory that takes arguments of an interface which only has properties and the properties only have getters (I think that's an important point) along with property values for the properties and returns a object that implements the interface and has all the properties defined.

For instance,

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

I'd pass the factory all the interfaces and their parameter values and it would create some class that implements those interfaces and has those values. Hopefully this is a little more clear?

EDIT 2: How to use Decorator?

From what I see about the decorator, you can extend the functionality of an object. That's cool. However, you have to know ahead of time how you are extending that functionality. You cannot do this arbitrarily.

Consider that my code base is as above and I want to use decorator.

I'd say:

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

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

EDIT 4:

I still don't think that'll work. In your example, I lose the contained classes interface, I have to redirect it down. For instance,

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

so when you d

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

you no longer can do princess.PropertyOnIEnjoyLookingDecoratorInterface you lose all those properties. This is not what I want. The only way to preserve the properties is to redirect

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

By doing the redirect, we have to implement the interface. Then the combinations have to all have code, which is what I'm trying to avoid. I don't to have constraints on the combinations of interfaces.

EDIT 5:

Perhaps I'm still not clear in my question. Imagine the interfaces as they are above with their properties filled in.

If the factory could do something like this:

/// <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
   }
}

I realize this is not how it will actually work, but illustrates the point I'm trying to make. I want the object to implement a dynamic combination of interfaces and have default values for the setters.

Even if I could do something like have the factory write a text file containing the code:

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

Then compile the class and somehow allow me to create instances of it. That's what I'm looking for. Hopefully that makes more sense.

EDIT 6:

This is the solution I'll probably go with since I'm not seeing an alternative at this point.

//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 pa开发者_JAVA技巧rt 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) }
}

Now my factory can just return these composition objects and as long as As is called, I can then downcast safely. I feel like there's probably a way to use generics to avoid the casting.

Any concrete classes implementing IMasterInterface can simply return themselves when As is called.

EDIT 3:

Thanks to everyone for comments. I'm glad I posted my ignorance of the pattern ;D Thanks for the clarification! I love this site and all you lovely people out there!!


Try looking into the Decorator Design Pattern. As far as I can see it is intended for the sort of case you have in mind, i.e. objects which can contain a mixed set of properties drawn from a set of properties. In the Decorator model, each property is a class of its own, and you effectively concatenate these classes dynamically to create objects with the properties you want.

To implement the filtering, you'd need to have some sort of iterator to walk the chain of Decorators to see if the one you are after is included for the object you are testing.

I haven't used Decorator in C#, only C++, but I have found it a very useful and flexible way forwards. If you find yourself creating more & more specialised classes which represent intersections of 'property' classes then I think Decorator may well help.

See Decorator Design Pattern in C# for info on this pattern. (And buy and read the GoF Design Patterns book!)


based on your update I see that you don't seem to understand the decorator pattern.

I don't know if this is the best way to go yet, as if you don't have any difference in behaviour and your interfaces are static (IenjoyLookingAtGoods, IConsumeGoods, etc) you can go ahead and implement these with properties on your abstract type, and just create new instances by setting them.

The decorators should be something like this

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

and if you want to get something that implements both you need to do something like this:

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

EDIT- you could hack it like this:

  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

but this is nasty, the most I read your question, the most seems that those interfaces make no sense. You are not adding behaviour at all. Why not have a class that implements IMasterInterface, and has 4 properties with the different interfaces (composition), if you don't want a princess to have IProduceGoods then set that as null. Still is hard to figure out without context

HTH


I don't like the IMasterInterface concept, but assuming that you're just using an abstract term for the question - I can see past that. I also have a niggling doubt about the usage in your example:

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

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

Shouldn't "princess" should be "somethingthatlikestolooksandconsumegoods"?

Other than that, I think the approach of filtering on the types of interface implemented is fine.


This is a good way. And, frankly, I do not see any problems with it. Interfaces are just interfaces - they only provide a way to describe a set of common members. The problem is that your question is quite generic and it is hard to understand what you are trying to achieve.

The factory method accepting interfaces is quite weird. For example we have the following code:

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

var x = MyFactory.Create(IFoo);

What would the factory create? Instance of A or B?

Anyway, I think that the best advise I can come up with is: Don't overthink it.


Answering the question abouth the factory, you could use reflection and/or the prototype pattern to accomplish, but only if the combination of interfaces is unique. If you have more than one concrete class that can satisfy the list of requested interfaces (say, classes Pricess and Prince) then the factory would have no way to know which to create.

If there are no concrete classes, and you are truely trying to create/compose/etc an object at runtime then that is a horse of a different color. You would likely need a common base class (or perhaps support in your master interface) to assist with this using (as another poster suggested) a decorator approach, or perhaps using a facet-type pattern.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号