Sign in to follow this  

Design Patterns

This topic is 4180 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

I love this book, (ISBN-0-201-63361-2) I've never thought about design in terms of "encapsulating variation" until reading this. Most of my question in how to properly design a system has been answer by the concepts presented in this book. Anyway, I know I haven't mastered them as I have not had the chance to even try them in C++ (although I did some stuff with javascript using notepad :) ), but here are some of the patterns that I think are the most useful: Adapter - STL function adapters are definately useful, I can also think of many other applications. I also love the fact that it allows me more breathing room with interfaces. If the interface doesn't fit every dependant use, then an adapter can fix that. Bridge - still unsure on what this one is, but I once used a templated object to define the methods for accessing user input. That allowed me to decouple my input system from the version of Direct Input that I was relying on. To me that seems like a bridge, but that book's example is nothing like that. State - better way of implementing state machines. Seeing this leads me to believe that most uses of switch statements could redone using polymorphic objects that encapsulate the variations instead of using branching code. Factory/Abstract Factory - if I saw the code for something like this without the explanation I would've thought it was a stupid waste. However after reading I can imagine a few uses and it inspired a new idea for me that may eliminate the need for singletons. The idea which I came up with for a new design patter may be a "builder" however nothing in the book matches it. Regardless, programming a particle system for an arcade javascript game that I made, (using notepad, I needed to keep my skills up :) ) brought me to the idea of a Particle Emitter (which came from here, suggested by a member of this forum). I realized that my interpretation of a Particle Emitter was a "Generator" (well, that's what I'll call it as it seems fair enough) of objects of the type "Particle". So here is what I define as the "Generator" pattern. Generator - Intent: Separate the initialization of objects which depend on the same set of shared data, from the process of creating them. Thus allowing instances to be created without the need of forwarding all of the initializing values to every location where these objects must be created. - Motivation: Particles within a particle system are intialized with a complex set of ranges (min velocity, max velocity, min x bound, max x bound) that determine how a given particle will behave. Furthermore, particles and many other types of objects often need reference to all other objects of their type within a given collection. This requires that the objects are instantiated with those values. Passing on this data manually in every location where these objects are created can become messy. It also tightens the coupling between the implementation of the object and its creation. By using a "Generator", the method of creating an object can be changed without having to alter implementation code everywhere else within the program. Example 1 code: Particle Emitter
class Generator()
{
public:
  Generator(int minx_,int maxx_,int miny_,int maxy_,int minv_,int maxv_)
  {
    minx=minx_; // minimum x value
    maxx=maxx_; // maximum x value
    miny=miny_; // minimum y value
    maxy=maxy_; // maximum y value
    minv=minv_; // minimum velocity value
    maxv=maxv_; // maximum velocity value
  }

  int minx; // minimum x value
  int maxx; // maximum x value
  int miny; // minimum y value
  int maxy; // maximum y value
  int minv; // minimum velocity value
  int maxv; // maximum velocity value

  Particle &Generate() // generates a new particle
  {// assume Rand(min,max) returns a random int between the values min and max
    Particle p(Rand(minx,maxx),Rand(miny,maxy),Rand(minv,maxv),m_particles);
    return p;
  }
};

class Particle()
{
public:
  Particle(int x,int y,int v);
  ...
}


int main
{
  Generator g(10,100,  50,500, 10,15); // create new generator
  ...
  // to generate a new particle
  Particle p=g.Generate();
  ...
}


Example 2 code: Texture Generator
class Generator()
{
public:
  std::map<string,texture> m_map; // map of all textures by filename

  Texture *Generate(string filename) // generates a new texture
  {
    Texture *t=&m_map[filename];
    if (!t->isLoaded()) // check if texture is loaded already
      t->Load(filename);
    return t;
  }
};



int main
{
  Generator g(); // create new generator
  ...
  // to generate a new texture or reference an existing one if that is already loaded
  Texture *t=g.Generate("test.png");
  ...
}


I would implement that last example with smart pointers, but it's just an example and I don't want to make it too complex. Anyway, I cannot wait until I'm back in America doing C++ and using design patterns.

Share this post


Link to post
Share on other sites
What exactly is the difference between your Generator and a "standard" Factory? I'm not exactly sure that there is a new "pattern" at work here. But that doesn't really matter - you should not feel yourself restricted by the GoF book in your programming, right? :) It's only a distillation of common programming practices, though it's useful for amateurs like us since we don't have to discover all this stuff the hard way.

Share this post


Link to post
Share on other sites
I'm sorry to say that you haven't invented anything new, the word generator is already reserved for several different meanings depending on the context in the domain of programming.

If you where familiar with the C++ standard library containers & algorithms you would have already come cross one the meanings i.e the Generator Concept.


Furthermore your code has a serious flaw, returning a reference to a local variable with automatic storage duration.

Share this post


Link to post
Share on other sites
Quote:
Original post by lightbringer
What exactly is the difference between your Generator and a "standard" Factory? I'm not exactly sure that there is a new "pattern" at work here. But that doesn't really matter - you should not feel yourself restricted by the GoF book in your programming, right? :) It's only a distillation of common programming practices, though it's useful for amateurs like us since we don't have to discover all this stuff the hard way.

The factory method in the book does not have any member variables. It also creates more than one kind of object. The factory method is used for creating a family of objects. My concept is for creating objects based on the same set of initilizing data.

As for the comment on my code returning a reference to variable with temporary storage... I agree, and I expect that there are other errors as well since I have no access to a compiler to work out the details. I was sure there was a practical and safe some way in C++ to create a new object and return it by refference without using pointers.

Anyway, I forgot two patterns that I also had a great appreciation for:

Decorator - beautiful concept, and it will probably be used in my scene graph as well as in my GUI.

Visitor - from this pattern I see the potential to outsource serialization to an external (visitor) object which could make serialization of complex trees of polymorphic heirarchal data easy. The one primary issue that would make that difficult is getting access to private data to serializers without destroying encapsulation. I imagine that I could use a friend class that can forward such access after being passed a refference to the class being accessed. Such a friend class could be passed on to a visitor thus preserving encapsulation. Of course that would greatly increase the amount of required classes, however it could allow for more operations beyond serialization visitors.

[Edited by - T1Oracle on June 16, 2006 3:55:57 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by T1Oracle
The factory method in the book does not have any member variables. It also creates more than one kind of object. The factory method is used for creating a family of objects. My concept is for creating objects based on the same set of initilizing data.

Choosing between subclasses is one method of customization a Factory has available to it. Passing in different initialization patterns is another. Don't pay too much attention to Factory not holding any data in their examples (which are not intended to exhaust all possibilities for each pattern). In practice, it's quite common for a Factory to hold private data detailing how to construct objects.

A very important concept to grasp in object oriented programming is that there is no fundamental difference between "objects of different subclasses" and "objects with different parameters". Here, I'll demonstrate.


class Adder
{
public:
virtual int AddTo(int i);
};

class ThreeAdder
{
public:
virtual int AddTo(int i) { return i+3; }
};

class FiveAdder
{
public:
virtual int AddTo(int i) { return i+5; }
};

class AdderFactory
{
public:
Adder* CreateAdder(int addend) { ... }
}

int main()
{
AdderFactory af;
Adder* a1 = af.CreateAdder(5);
Adder* a2 = af.CreateAdder(3);
}




class Adder()
{
public:
Adder(int addend) : m_addend(addend) { }

AddTo(int i) { return i+addend; }
};

class AdderFactory
{
public:
Adder* CreateAdder(int addend) { ... }
}

int main()
{
AdderFactory af;
Adder* a1 = af.CreateAdder(5);
Adder* a2 = af.CreateAdder(3);
}


What's the difference between these two setups (other than the contrived limitations of the first one)? Nothing. In each case, the returned Adder has been customized in some way. These two possibilities are hardly the only options for customization, by the way. Consider how the Strategy pattern could be applied here.

Share this post


Link to post
Share on other sites
Quote:
Original post by T1Oracle
Quote:
Original post by lightbringer
What exactly is the difference between your Generator and a "standard" Factory? I'm not exactly sure that there is a new "pattern" at work here. But that doesn't really matter - you should not feel yourself restricted by the GoF book in your programming, right? :) It's only a distillation of common programming practices, though it's useful for amateurs like us since we don't have to discover all this stuff the hard way.

The factory method in the book does not have any member variables. It also creates more than one kind of object. The factory method is used for creating a family of objects. My concept is for creating objects based on the same set of initilizing data.

The fact that sometimes you have variables and sometimes you don't have them is an implementation detail - it change nothing to the actual abstraction.

Of course, the factory in the book is very simple - hiding the real purpose of the pattern with uneeded complexity would have been a bad thing. The same can be said for the book's singleton: it don't have any method but GetInstance().

So far, your pattern is a specialized example of the factory pattern. For example:
class TextureFactory
{
std::map<std::string, Texture> mTextures;
public:
Texture *create(const std::string& filename)
{
Texture *t = &mTextures[filename];
if (!t->isLoaded()) {
t->Load(filename);
}
return t;
}
};

I just changed the name, and it is still a good name, isn't it?

Regards,

And while I don't support the war, I'd like to wish you good luck.

Share this post


Link to post
Share on other sites
Quote:
Original post by T1Oracle
Visitor - from this pattern I see the potential to outsource serialization to an external (visitor) object which could make serialization of complex trees of polymorphic heirarchal data easy. The one primary issue that would make that difficult is getting access to private data to serializers without destroying encapsulation. I imagine that I could use a friend class that can forward such access after being passed a refference to the class being accessed. Such a friend class could be passed on to a visitor thus preserving encapsulation. Of course that would greatly increase the amount of required classes, however it could allow for more operations beyond serialization visitors.


It's no easier than a recursive call to a common base method, but it sure makes the distinction between data and logic cleaner. For private data, one thing you could probably do is pass it as an argument to the accept*() methods of your visitor.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Thank you to all who replied, I am learning from your responses.
Quote:
Original post by lightbringer
Quote:
Original post by T1Oracle
Visitor - from this pattern I see the potential to outsource serialization to an external (visitor) object which could make serialization of complex trees of polymorphic heirarchal data easy. The one primary issue that would make that difficult is getting access to private data to serializers without destroying encapsulation. I imagine that I could use a friend class that can forward such access after being passed a refference to the class being accessed. Such a friend class could be passed on to a visitor thus preserving encapsulation. Of course that would greatly increase the amount of required classes, however it could allow for more operations beyond serialization visitors.


It's no easier than a recursive call to a common base method, but it sure makes the distinction between data and logic cleaner. For private data, one thing you could probably do is pass it as an argument to the accept*() methods of your visitor.

Thank you, that is probably a better idea it definately sounds like it would take less code.

As for the reference to the use of common base methods... The issue with that is the amount of foresight needed to keep that up. You may want to add new methods of serialization, or add a method for cloning objects (to clone an entire tree for an example), eitherway it allows for greater extensibility.

Also I remember one popular web article that described using maps to discern which concrete class is being referenced by the abstract base. With a visitor object, double dispatch is applied (another awesome concept that I wish I knew about earlier) thus resolving the issue of finding out which concrete class is in use.

Regardless, serialization definately sounds easier than many other sources I have seen makes it out to be. There are two hurdles in serilization as I see it:
1) A method for converting concrete classes into the output stream format. - This can be done by members or visitors.
2) Correct identification of the current concrete class being serialized into the output stream format. - This is easily handled by appliying double dispatch to the executed serialization methods.

Share this post


Link to post
Share on other sites
Honestly, it looks like all you are doing is overcomplicating currying or partial function application. The examples in the book seem a little off to show you exactly what a factory can be, because they don't have to create more than one kind of object, of course, and they can definately include initialization data for the objects they create.

"Generator" is a different concept entirely, and I know it as an iterable object that generates its values on request with a co-routine kind of function, such as with Python.

Share this post


Link to post
Share on other sites

This topic is 4180 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this