C# factory - is upcast a must?
Asked Answered
A

6

18

Does the C# factory pattern require an upcast?

I want God in class library G to create an Adam in class library A without making G dependant on A. God produces Adams for consumption by Eve in class library E, and it's OK for Eve to know and depend on Adam. (edit - this sample keeps getting better and better :)

The solution I could think of is having an AdamFactory in A. This way AdamFactory knows Adam and can easily create it (possibly by just calling Adam's constructor). God receives an AdamFactory and can order it to CreateAdam.

Now, because God isn't allowed to know Adam, AdamFacotry's CreateAdam must return an object, and this requires Eve to up-cast the object returned by AdamFactory to an Adam.

This will work, I think. However, I feel uneasy about up-casting as it's a no-no. Is this really a must?

P.S. - No Blasphemy intended, and I apologize if someone's feelings were hurt. It seemed better to use God and Adam instead of Creator and Created because the two latter words are too similar to each other.

Edit: Re interfaces suggestion. Let's assume Adam has two methods: ProvideLove, ProvideFood and ProvideProtection (we're keeping this sample kis-safe :). Eve uses Adam for these two purposes, but of course God doesn't. So why provide God with the knowledge that AdamFactor returns something that implements an IAdam and not just an object? I don't get it!

Edit: The working code (with everybody in the same library, which my goal is to separate to different libraries) looks something like this:

Adam God.LoadAdam(AdamID theAdamID)
       var adam = new Adam(theAdamId, this)

Adam.Adam(AdamID theAdamID, God theGod)
      _god = theGod
      _mind  = theGod.LoadMind(theAdamId, this)

Mind God.LoadMind (AdamID theAdamID, Adam theAdam)
      var mind  = new Mind (theAdam)
      var mindId = new minId(theAdamId)
      mind.DeserializeFromFile(minId)

Mind.Mind (Adam theAdam)
      _adam = theAdam
Antisyphilitic answered 2/2, 2011 at 8:41 Comment(12)
I believe you mean to downcast the returned object to an Adam.Cartilaginous
I'd go for interfaces, as stated in the first answer. Anyway, +1 for nice question, very funny :-)Colorblind
Upvoted the question for the sample even before reading it to the end. :)Querist
haha nice story. So what was the question again?Amiraamis
@ Eamon Nerbonne:You're correct, though I don't like the "inverted" terminology.Antisyphilitic
My atheistic answer would be: why Eve doesn't instantiate Adam by herself? :-) I mean, why do you need a factory at all?Anisomerous
@Pieter888: Tnx. The question is: Is down-casting by Eve built in and mandatory?Antisyphilitic
If God doesn't know anything about Adam, what's the point of having Him create him, instead of just Eve using the AdamFactory?Miyokomizar
There's something wrong with the God class in this design. The God class should make explicit promises about what it returns (e.g. I will return an Adam), not return "something" and expect it to be right for Eve. God should know because he could not otherwise guarantee what he returns. I would instead connect Eve and AdamFactory in a more direct fashion (perhaps God passes an AdamFactory to Eve? Or Eve accesses it directly)Gala
@Simone: G is really a persistance class library, responsible for creating everthing. The way Adams are created depends on the type of persitance used: file system, database, etc. Eve should be shielded from this.Antisyphilitic
If you don't want G to be dependent on A then God cannot use AdamFactory defined in A. This is an awkward design anyway. Can you provide more concrete example of what you're trying to achieve and why you need God for that? :)Tinhorn
If God accepts an AdamFactory, where does AdamFactory come from?!Mesics
Q
9

I am not sure I completely understand the requirements but here is a suggestion:


//in assembly G
public abstract class HumanFactory<T>
{
    public abstract T CreateHuman();
}

//in assembly A
public class Adam { }
//in assembly A
public class AdamFactory : HumanFactory<Adam>
{
    public override Adam CreateHuman()
    {
        return new Adam();
    }
}

//in assembly G
public class God
{
    public T Create<T>(HumanFactory<T> factory)
    {
        return factory.CreateHuman();
    }
}

and the usage:


//Somewhere in assembly E
Adam adam = new God().Create(new AdamFactory());
Querist answered 2/2, 2011 at 9:0 Comment(4)
+1 given the whole set "Question + comments" I'd say that this is the most sensible solution. Nonetheless, there's something wrong in OP's design, IMHO.Anisomerous
@Anisomerous this design can have some uses. In order to be useful there should be generic constraints based on common interfaces or base classes for Adam and the other instances created by God. Then God can do some other work except blindly calling into the AdamFactory. Also the HumanFactory base class can have additional methods that God can use to do additional work.Querist
@Simone, God can make Adam live for every by sorting out the database side of things etc, Eve can't do this becouse she is fixed in a sigle (but moving) point on the time line.Pitchy
I think the interface might be improved as IFactory<in TSourceMaterial, out TCreatedResult> { TCreatedResult Create(TSourceMaterial src);}. The same interface definition should be usable to create a human given mud, or a Toyota Car given a Toyota Automotive Options Specification, though most implementations would only define it for a few particular types.Mencher
H
1

What about using interfaces so God knows IAdam, or something like IHuman ?

Headspring answered 2/2, 2011 at 8:44 Comment(1)
God doesn't have to use Adam. So how would God benefit from the interface?Antisyphilitic
P
1

I think you could use dependence injection. Try with an Inversion Of Control (IoC) container like Unity 2, StructureMap, Or Castle of Windsor.

Palila answered 2/2, 2011 at 8:51 Comment(1)
A very simple example would be really appreciatedAntisyphilitic
C
0

You're describing an abstract factory pattern, although the "child" factories (e.g. AdamFactory) normally have something in common so you'd expect them to produce something more like a common interface rather than just an object (see Davide's answer)

You're right to worry about the up-cast since that will tie Eve to the implementation of Adam which defeats the purpose of a factory (unless you're really using it as a builder).

Question is, why do you need the God class at all?

Coax answered 2/2, 2011 at 8:57 Comment(0)
T
0

Well, if God is a persistence class library as you mentioned in comment, then it should not influence the class design of the rest of the system.

So, I wouldn't add unnecessary interfaces. It's Ok to return an object from deserializer and downcast afterwards (e.g. BinaryFormatter, XmlSerializer).

The better way would be to make your deserializer generic.

Tinhorn answered 2/2, 2011 at 9:28 Comment(0)
H
0

OK, what about sprinkling in some generics here?

Let's say God may accept Factories of any type T that adhere to the following interface:

interface IFactory  {
  Type CreatedType { get; }
  object Create();
}

An abstract class to implement this may look like this:

abstract class AbstractFactory<T> : IFactory {
  public Type CreatedType { get { return typeof(T); }
  public virtual object Create() {
    return innerCreate();
  }
  protected abstract override T innerCreate();
}

Now, you can register factories with God:

God.RegisterFactory(new AdamFactory());

AdamFactory inherits from AbstractFactory<Adam>

God stores its factories in a dictionary where the key is the type returned by the IFactory interface

the Create Method now looks like that:

God.Create<Adam>();

God looks into its factories, sees there is one for typeof(T) of the generic method, retrieves the factory, calls create and downcasts to T.

What do you think?

Hudnall answered 2/2, 2011 at 9:45 Comment(1)
But then you're almost starting to build an IoC container - Admittedly, God is a nice name for an IoC container, but there are already a couple of those around..Hudnall

© 2022 - 2024 — McMap. All rights reserved.