Jump to content
  • Advertisement
Sign in to follow this  
Spinewire

Complex Entity Creation -- Expanding on Factories?

This topic is 3795 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everyone, I've been wracking my brain over object generation in my object-oriented framework for my 2D game. Currently, my game objects are based around a couple role-specific Entity classes, which are simple state machines (and also subjects, as per the Observer design pattern), and various tools and observers I can tie to those entities, such as a Sprite classes, which gather information from the Entity and render them on screen, and Agent classes, which receive key or (eventually) network input and use those events to manipulate the entity they're observing. Now I'm working on a Director object, which can receive events from the Entity and then go on to create other entities with accompanying Sprites, Agents, and Directors (as needed) in the case, for instance, of firing a weapon and needing both a projectile entity and a sprite to represent it on-screen. My problem is with everything all of these objects need to know for creation, and where all of that information is scattered within my framework. I'm keeping Entities, Sprites, Agents, Directors and all of my 'node' objects as dumb as possible, to avoid storage redundancy and issues with updating after changing data. Anything they need to draw or update, for instance, they receive as pointer parameters for their Update() or OnEvent() functions, rather than storing pointers internally on initialization. I've reduced all initialization dependencies as much as possible, but there are some I simply can’t avoid. When creating a sprite, I need a pointer to the current DirectX device for loading the texture. (I’m using DirectX, but it’s not what I’m having trouble with, so I’m not using that forum.) Additionally, all Entities are registered with a World object which systematically updates them all, but to avoid mutual dependencies, the entities have no idea what World object they’re registered with. Same with Sprites and an ‘Engine’ object, and Agents with a ‘Controller’ object, etc. So there is no way that an Entity that needs another Entity to be created (gun firing a bullet) could pass the world in which to register the new projectile entity along with the event. So, whatever is creating that projectile has to already know it. That brings me, finally, to my question. How do standard factories receive and store all of the information they need to create an object? Currently, to create an entity, sprite, or agent, a factory might need the current DirectX device, the World object, the Engine object, the Controller object, and any of a number of other objects that cannot be passed in to the Create() method. Everything I’ve found on the Factory design pattern seems to assume that all of the objects they’re creating need no initialization parameters. Additionally, I need to expand on the Factory here to not only create single objects, but to actually make a system of objects, all linked up. Also, I have a second question, pointing back to how blind I want to keep my nodes. In the case of my Directors, which I don’t want inundated with a handful of ‘Factory’ pointers, what is a good method of making the Factories not only have access to the information they need, but also for the whole system to have access to the factories. (Preferably with minimal use of global variables or singletons.) (As a note, all of these design patterns I’m discussing are based on my understanding from http://www.c2.com/cgi-bin/wiki?DesignPatterns) Sorry if that was a gigantic essay, but I’m really boggling here. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Yes in an ideal world, we could ideally have objects with zero dependencies on others. In the real world cross dependencies are a real thing and. I gather your asking how can I create these cross dependencies with actually having any cross dependencies. I don't think it can be done.

I will use your direct example here -

So some common ways to keep the dependencies in check (and I am not advocating for any particular method here)

1. Global Variables are probably the most common way to allow global access.
2. Singletons.

If you refuse to make use of either of the above you are pretty much limited to

3. Registration of handlers. CDirectX::Init() registers itself with your sprite factory. And your factories register with other systems that require them.

I really hope this provides some assistance to you. Its quite shattering that there is really no ideal object orientated dependency free way to write a complex program. But we all live with it, the trick it to keep it maintainable as possible and document those dependencies so that some poor soul can work with your code and understand how and why those dependencies exist.

Share this post


Link to post
Share on other sites
To be honest, it sounds like your setup is a lot better than most. Actually having agents and sprites distinct from the object, using events for the communication... Most times these sort of things evolve out of necessity and as such are kinda sucky.

As for the specific questions:

Quote:

How do standard factories receive and store all of the information they need to create an object? Currently, to create an entity, sprite, or agent, a factory might need the current DirectX device, the World object, the Engine object, the Controller object, and any of a number of other objects that cannot be passed in to the Create() method. Everything I’ve found on the Factory design pattern seems to assume that all of the objects they’re creating need no initialization parameters.


Usually some initialization string (interpreter pattern) or a struct. IMO, having default constructable objects that are changed post-factory is better. Often times you need certain invariants to hold for the class, and those are often communicated via state change trigger. Having a set default state which is then changed via trigger leads to more reliable behavior.

Though as you point out, there are times where that is unavoidable (the DX device example is a good one). (imo) That can be passed as a initialization parameter of the factory itself. It can then relay that constant state to the things it constructs, akin to functors' bound parameters.

Quote:

In the case of my Directors, which I don’t want inundated with a handful of ‘Factory’ pointers, what is a good method of making the Factories not only have access to the information they need, but also for the whole system to have access to the factories. (Preferably with minimal use of global variables or singletons.)


Personally, I have a free method which takes an optional parameter as to what factory to use. It defaults to a 'default' factory which is just global. Because really, most every call you're going to make is to the common, global factory. This allows the freedom/flexibility to not do that if you need to, but not burden you when you don't.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spinewire
How do standard factories receive and store all of the information they need to create an object? Currently, to create an entity, sprite, or agent, a factory might need the current DirectX device, the World object, the Engine object, the Controller object, and any of a number of other objects that cannot be passed in to the Create() method. Everything I’ve found on the Factory design pattern seems to assume that all of the objects they’re creating need no initialization parameters. Additionally, I need to expand on the Factory here to not only create single objects, but to actually make a system of objects, all linked up.


My rule of thumb would be that if you find yourself having problems with the parameters to a factory, then you're trying to make the factory too generic. C++ already provides a very versatile object creation factory, called the new operator. Anything you build on top of that should necessarily restrict the scope somewhat.

A second consideration is that a factory should limit its scope to just creating objects. Not linking them to other objects, adding them to the world, loading their graphics and sounds, etc. Take away the factory's requirement to do much of this, and the number of parameters you need to supply drops. There's nothing wrong in an object creation process that is done in several short steps, if it allows each of those steps to be clearly-defined and to have minimal dependencies. It also makes it easier to customise behaviour later.

Quote:
Sorry if that was a gigantic essay, but I’m really boggling here. Thanks!


My guess is that you are perhaps overthinking things, and worrying too much about getting the perfect architecture. While this is commendable, it's often best to err on the side of practicality and refactor later, otherwise you get bogged down in abstractions (Agent? Director? Entity? these are all nothing-nouns) that don't actually add anything to the final product anyway. It's also hard to recommend suggestions to you when your classes are all quite vague - what object actually runs the game? Who owns the World object? Why can it not pass in the World as a parameter when updating Entities? Etc. Be very wary about letting your quest for perfect object-oriented code get in the way and actually make life harder for yourself.

Share this post


Link to post
Share on other sites
First, what is the point of the Abstract Factory design pattern?

See, in normal code, you would write something such as:
void theFunction()
{
frobnicate(new ClassName());
}


This, however, creates a strong dependency between your code and ClassName on the one hand (because you cannot use any other classes) and between your code and the ClassName() default constructor on the other hand (because you cannot call any other constructor). The dependency is strong because it cannot be changed without changing the function code. If the strong dependency is an issue (that is, if changing these two factors is a frequent requirement), then one can use the Abstract Factory design pattern to allow the caller to specify what object should be created, and with what constructor.

Some very basic examples of the Abstract Factory pattern in highly-expressive languages show how simple the concept actually is. In PHP, since it's possible to create classes at runtime and also load a class name from a variable, a function implementing the Abstract Factory pattern is simply:
function theFunction($factory) 
{
frobnicate(new $factory());
}


In a slightly less flexible language with functional capabilities, such as Objective Caml, an equivalent approach is also possible by using a function that creates a brand new object when called:
let theFunction(factory) =
frobnicate( factory() )


Of course, the pure and complete object-oriented version involves simulating the functional approach by creating an abstract factory class (to achieve polymorphism) and creating concrete subclasses to do the instantiating. This is due to the limitation of several main OO languages (C++ and Java, to name them) in terms of treating functions as first-class citizens, and the requirement to replace them with classes-with-a-virtual-method.

And this is how babies Abstract Factories are made.




And now, back to the problem at hand... you say that you wish the entire system to access all your factories. Can you think of a reason (that isn't silly) of allowing the squared_magnitude of your vector3d class to access the factories? The point is that the vast majority of your system does not need access to the factories. Even a given director does not necessarily need to have access to all the factories: a gun director, for instance, can only create bullets—in fact, the entire definition of a gun director is that it's a director configured to use a bullet factory!

So, the basic idea is that you have a set of factories on your left, all of them faithfully owned by an appropriate object, and a set of directors on your right, all of them in need of one or more factories to do their work? In fact, let us simplify this a bit further, without taking the 'Abstract Factory' solution for granted...

You have a set of directors, which react to things that happen in the world by creating new objects. However, since they are merely a part of the world, and not the world itself, it would be damaging to let them do the creation directly. Hence the two possibilities: either you delegate the creation power by means of the Abstract Factory pattern, or you let the world create the entities and have the directors merely specify the intermediary steps, by using the Abstract Factory pattern in the other direction. Basically, your director will return a list of Abstract Factories, and the world will then use these factories to create the objects and insert them where required. In pseudocode, the factory would store the director's arguments to the entity constructor when it's created, and would gather the world's arguments to the entity constructor when it's used:
class MustBeCreated : IEntity {
MustBeCreated(DataX x, DataY y);
}

class Factory : IFactory {
DataX x;
Factory(DataX x) { this.x = x; }
IEntity Create(DataY y) { return new MustBeCreated(this.x, y); }
}

class Director
{
DataX x;
List<IFactory> Think()
{
List<IFactory> list = new ArrayList<IFactory>();
list.Add(new Factory(this.x));
return list;
}
}

class World
{
DataY y;
List<Director> directors;
void Think()
{
foreach (Director d in directors)
foreach (IFactory factory in d.Think())
AddToSelf(factory.Create(y));
}
}


In this situation, the number of dependencies is drastically reduced, since you don't have to pass the required factories to every director: instead, the director returns the factories it had to use, and the world sorts the creations out.




As a summary: learn functional programming. This entire post is merely a verbose object-friendly way of saying "return a partially applied creation function from your director logic function". An Objective Caml example:

class mustBeCreated x y = object (* whatever *) end

class director = object
var x = (* whatever *)
method think = [new mustBeCreated x]
end

class world = object(self)
var y = (* whatever *)
var directors = (* whatever *)
method think =
List.iter
(fun d -> List.iter (fun f -> self # add (f y)) (d # think))
(directors)
end


Also, thinking of Abstract Factories as "those big factories created once and then used everywhere" betrays your procedural way of thinking. Factories can, and should, be used on a smaller scale and with shorter lifespans.

Share this post


Link to post
Share on other sites
Quote:
Original post by lubby
Yes in an ideal world, we could ideally have objects with zero dependencies on others.


That would be a sea of endless, boring pain. Consider some simple code, such as:
float average(float x, float y)
{
return (x + y) / 2.0f;
}


Now, this code has strong dependencies on the variable type float, the corresponding operator +, the corresponding operator /, and the constant 2.0f. Therefore, to eliminate all dependencies, you'd have to write:
template <typename Input, typename Output, typename Add, typename Halve>
Output average(Input x, Input y, Add add, Halve halve)
{
return halve(add(x,y));
}


And you can't use average in another function either, or you would create a dependency! You'd end up with programs being a heap of all-parametric code, with a single huge function that configures all the parameters locally and then runs a single fully configured function.

The point is, as the number of dependencies decreases (and thus the number of parameters increases), using the code for its purpose becomes more difficult (more configuration to be done) but its purpose covers a wider range of problems (because you have more liberty in specifying what to do). For instance, I could use the overly-parametric example average above to compute the geometric average (using a multiplication and a square root), but the point is that doing it as such would take more effort than writing it like that from the very beginning. The optimal module is the one that is narrow enough to be easy to use, yet flexible enough to solve many problems.

As such, reducing dependencies only has a point as long as configuring the parameter list doesn't take longer than rewriting the functionality. A good rule of thumb is to eliminate dependencies to mutable objects, because these tend to cause more pain than immutable objects. Another good rule is to think your of objects as tools, and determine how their user should be able to configure them to use them in his own universe, without having access to your entire code base.

Quote:
So some common ways to keep the dependencies in check (and I am not advocating for any particular method here)

1. Global Variables are probably the most common way to allow global access.
2. Singletons.

Be careful: neither global variables nor singletons actually eliminate any dependencies. They just hide the dependencies so they are harder to notice until you stumble into them (but they eliminate none of the problems related to the limited scope of your code). See, one can only reduce dependencies between a piece of code and something by removing all global references to that something, and instead keeping only local references. And as such, any form of global reference (such as those introduced by global variables and singletons) creates dependencies.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk

Now, this code has strong dependencies on the variable type float, the corresponding operator +, the corresponding operator /, and the constant 2.0f. Therefore, to eliminate all dependencies, you'd have to write:
...


Hi ToohrVyk, I appreciate what you are saying, but you may be reading too far into my comment. It's important that you apply what I am saying to the context of this thread. In fact without selectively quoting me the remainder of this comment did read. "In the real world cross dependencies are a real thing and. I gather your asking how can I create these cross dependencies with actually having any cross dependencies. I don't think it can be done." So I am not advocating the fact that an dependency free code base could ever be possible. In fact my statement was that it could not be done. Are we agreeing here? I think so.

Quote:
Original post by ToohrVykBe careful: neither global variables nor singletons actually eliminate any dependencies. They just hide the dependencies so they are harder to notice until you stumble into them (but they eliminate none of the problems related to the limited scope of your code). See, one can only reduce dependencies between a piece of code and something by removing all global references to that something, and instead keeping only local references. And as such, any form of global reference (such as those introduced by global variables and singletons) creates dependencies.


Again context, OP was discussing ways that those factories of his can access the dependencies. I provided three ways often used in the c/c++ language group to solve this. I don't belive that there are any other options available. Would love to know if there were though. OP is going way beyond basic and intrinsic types. Hes talking about much more functional systems here. Better examples to use are rendering systems, scenemanagers, and such.

It does not seem to me that you solved the issue, if object World contains the dependent types the dependencies have just been shuffled around into World. Really no difference. Everything that would be dependent on global, singletons or registered types is now dependent of world, which is dependent on those same types. So in fact again, we say the same thing. Dependencies can not be avoided, but we can do our best to reduce the number of dependencies.

Given the following two psuedo options I would prefer the later

SpriteFactory::Init()
{
// not much here
}

Sprite SpriteFactory::Create(...)
{
// I am dependent on D3D via one level of indirection, I am also dependent on World.
pWorld->pD3DDevice->CreateTexture();
}




or

SpriteFactorty::Init(IDirect3DDevice* pDevice, ...)
{
pD3DDevice = pDevice;
}

Sprite SpriteFactory::Create(...)
{
...
// I am directly dependent on Direct3D
pD3DDevice->CreateTexture();
...
}




This technique means that, yes the factory is still dependent on another type, but now we are directly dependent, and do not suffer a second level of indirection for our dependencies.

Share this post


Link to post
Share on other sites
Quote:
Original post by lubby
Hi ToohrVyk, I appreciate what you are saying, but you may be reading too far into my comment.


I was merely stating that what you called an ideal world would quite probably be hell. No need to take it personally!

Quote:
Again context, OP was discussing ways that those factories of his can access the dependencies. I provided three ways often used in the c/c++ language group to solve this. I don't belive that there are any other options available. Would love to know if there were though. OP is going way beyond basic and intrinsic types. Hes talking about much more functional systems here. Better examples to use are rendering systems, scenemanagers, and such.
I don't see your point here. You said that global variables and singletons can reduce dependencies, and I explained why they don't.

Quote:
Given the following two psuedo options I would prefer the later

I would prefer neither, and instead use the method I suggested above (which you apparently did not completely understand).

Quote:
It does not seem to me that you solved the issue, if object World contains the dependent types the dependencies have just been shuffled around into World. Really no difference. Everything that would be dependent on global, singletons or registered types is now dependent of world, which is dependent on those same types. So in fact again, we say the same thing. Dependencies can not be avoided, but we can do our best to reduce the number of dependencies.


No. You have built a straw man out of a completely idiotic solution, labeled it "ToohrVyk's solution", and are now attempting to imply that I was, for some reason or another, its original author. To quote what appears to be your opinion of my solution:

SpriteFactory::Init() { }

Sprite SpriteFactory::Create(...)
{
pWorld->pD3DDevice->CreateTexture();
}



The most glaring inconsistency here is that in the above code, the factory has a dependency on the world. However, in my proposal, the factory does not know anything about the existence of the world at all! Here's again for your viewing pleasure my proposed factory, adapted to your DirectX example:

class Factory : IFactory {
CreatorData data;
Factory(CreatorData data) { this.data = data; }
IEntity Create(D3DDevice dev) { return new MustBeCreated(data, dev); }
}



See? No world.

Now, let's get working on your 'preferred proposal':
SpriteFactorty::Init(IDirect3DDevice* pDevice, ...)
{
pD3DDevice = pDevice;
}

Sprite SpriteFactory::Create(...)
{
pD3DDevice->CreateTexture();
}



Since the sprite factory must be provided with the direct3d device, it cannot be created by the Director (otherwise the dependency between Director and Direct3D would remain) and must therefore be provided to the Director. This means that either the factory is accessed globally (creating an additional dependency between the Director and an instance of the factory that does not exist in my solution) or it is provided to the Director as a parameter in one way or another (meaning a potentially large number of parameters to be provided).

My solution allows the elimination of the Director-Direct3D dependency without either global access or mutiple parameter-passing, which is why it is superior in this particular case.

Share this post


Link to post
Share on other sites
I assure you that I am not taking your comments as a personal attack. I am simply clarifying and correcting a partial (mis)quote. If I were taking it personally I would be calling you all sorts of rude and inappropriate names. :-D

What you reference as the straw man - I misunderstood that you meant the dependent types lived in the world. Even now with your fresh example I still do not see how the dependencies are reduced. You say that Factory::Create() requires that the D3DDevice is passed as a parameter. Now the object calling Factory::Create() (World?) will still be dependent on Direct3D in that it is able to pass the D3DDevice parameter, along with the Object Factory itself.

Are you able to explain to me why this is better than having the dependent type registered with the Factory on creation. I am not yet nearly convinced the director pattern you are offering is any better.

On a side note, the D3DDevice needs to be passed on every call to the Create function. This is needless overhead (Although I do recognise the case where there may be multiple D3DDevices) but in general, by no means do I see this as a superior solution.

Share this post


Link to post
Share on other sites
Quote:
Original post by lubby
Even now with your fresh example I still do not see how the dependencies are reduced. You say that Factory::Create() requires that the D3DDevice is passed as a parameter. Now the object calling Factory::Create() (World?) will still be dependent on Direct3D in that it is able to pass the D3DDevice parameter, along with the Object Factory itself.


Ah, I see where the confusion comes from. The OP already had an object named 'World', and there was a terminology clash with my own 'World', which represented the object that has all the data required to create the entities requested by the Director. So, let's call instead my object the 'Simulation', and leave the term 'World' alone.

My point is this: someone, somewhere, has access to some of the data required to create Entities. We'll call this object the Simulation. In particular, one of the responsibilities of the Simulation is to hold the Direct3D device used by all objects within it. Then, there is the Director, which decides when objects are created, and also provides the rest of the data required to create Entities.

Therefore, our solution needs to, somehow, get together the data from the Simulation and from the Director, and create the object using that data. There are three possibilities: either the Simulation sends the data to the Director, which creates the object (this is done by sending the Director a Factory for every object type that the Director could wish to create), or the Director sends the Simulation the data and lets it create the object (this is done by returning a single Factory from the director), or the Director returns its data to a third party, which also gathers the data from the Simulation, and creates the object.

The current solution proposed by the OP and, so it seems, yourself, is the first one: the Simulation packs the required data into a large number of factories (one per object to create) and then provides every Director with the entire set of factories (just in case the Director needs to use one of them, although he doesn't have to). This can be done by a global variable (thereby greatly increasing the dependency and crippling the ability to reuse Director code) or by passing the factories as a parameter. In all situations, however, the addition of a new Entity type requires the programmer to alter the Simulation, since a new factory must be created for that type.

My proposed solution is the second one. The Director creates a new factory for the Entity it wishes to create, and returns that factory to the processing function, which forwards it up the call stack to the Simulation, and the Simulation creates the corresponding object by providing the missing data to the factory. This eliminates the need for passing a large number of factories around (since now all factories are being returned, and all have the same interface). It also solves the Open-Closed Principle violation, since now creating a new Entity type only requires changing the code of the Director which wishes to create that Entity, and not the code of the Simulation.

Of course, a variant on this would be to provide the Director with a single function that, when called, forwards a factory directly to the simulation for creating the requested Entity. It is equivalent, from a theoretical point of view, to the solution of returning the list of required entity creations.

The end result is:
  • Fewer argument passing (or global variables, depending on your factory implementations).
  • Improved respect for the Open/Closed principle.


Quote:
On a side note, the D3DDevice needs to be passed on every call to the Create function. This is needless overhead (Although I do recognise the case where there may be multiple D3DDevices) but in general, by no means do I see this as a superior solution.


If a single parameter in an object creation sequence was that big of an overhead, we would resort to global variables everywhere instead of passing arguments. Since dependency elimination can only be achieved by passing additional data as parameters or return values, it should come as no surprise that the solution with lower dependencies is also the one with more parameters being passed.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!