Advertisement

Difference between Component Pattern and Builder Pattern?

Started by July 15, 2022 02:41 PM
22 comments, last by Oberon_Command 2 years, 4 months ago

Hello,

I am new to game development and doing some research I found that the Component pattern is largly used in game development. In a nutt shell you have an entity and start adding components to it, which is quite similar, if not identical to GOF's Builder pattern, where you a have a Builder class with some methods that allow to create different type of aggregates (or products, what ever you want to call it). Again, this is in nutt shell. There is also the concept of a Director in the builder, and patterns are nor set in stone so they can vary as long as the purpose is the same, etc….

Anyway, it seems to me that they are both the same but with a different name. What do you think?

R

They are not the same. The “component” pattern you're referring to applies to the actual object that was created. You take a base object (which in some cases isn't anything but an identifier) and glue behaviors and data to it at runtime, with the idea being that functionality that specific object supports doesn't need to exist until something requests it. It's a bulwark against objects getting too big and featureful (the "god object antipattern").

The GOF “builder” pattern is a fancy way to encapsulate constructor arguments in their own object; a builder is NOT the final object, but an object that is used to create another object, and builders typically start out as complete objects in themselves without gluing additional components to them at runtime. It has more in common with the factory pattern than runtime composition patterns like ECS. It's unclear to me where this “Director” thing you're referring to came from as a “builder” is just a class that configures another class on creation. Furthermore, a Builder (as opposed to a simple “arguments object” that happens to be passed to a constructor) is set up so that the setters for its fields can be chained and the user calls a “Build” method on the builder rather than invoking the desired object's constructor directly, eg.

GameObject obj = GameObjectBuilder()
    .SetPosition({0.0f, 0.0f, 0.0f})
    .SetVelocity({0.0f, 0.0f, 0.0f})
    .SetHealth(1.0f)
    .BuildObject(); 

This is somewhat less useful in more modern standards of C and C++ (and other languages) that support designated initializers, but may be useful when the initializers like SetHealth have complex logic that would only need to run on creation that GameObject itself wouldn't need to be concerned with. Another case where this is useful is where there is complex creation logic that doesn't belong in the constructor, eg. because it pulls in dependencies that are no longer used after creation or it can fail in a way that the constructor cannot meaningfully handle.

Advertisement

Builder is a creational design pattern.

“Component pattern” I'm not sure this is even a pattern. I always consider it to be similar to the Strategy Pattern but this is just my assumption. Reguardless, it is basically Composition over Inheritance.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

fleabay said:
“Component pattern” I'm not sure this is even a pattern.

I'm inclined to agree. “Component pattern” isn't really one pattern, but a family of them. There are a number of ways to use “components”; do you keep them in parallel arrays (ECS)? Do you put them directly in the object itself ("composition")? Do you have your components inherit from a virtual “Component” base class and then have a generic array of those on your object ("object-component"/"decorator pattern")? And every one of them with their own tradeoffs, of course.

interesting, thanks for the detailed explanation.

From GOF : “Director: constructs an object using the builder interface”. So you can have different concrete builders (CarBuilder, PlaneBuilder, etc..) that hold methods such as: buildEngine(), buildWheels(), etc.. so each method creates the pieces engines, wheels ,etc… What the director does is assemble the parts, and even more, allows you to set different assembling configurations.

So the concrete builders build the parts, not the integrity of what ever you are building. The director in the other hand, “directs how” the parts are assembled. For instance the Director can have a fullConstruct(), constructWihoutWheels(), constructOnlyBody(), etc… so basically you setting multiple configurations for the object you create.

That's why I though it was similar to component since you might do something like myCarA = Car(); myCarA.addWheels; myCarA.addBody, etc… and myCarB = Car(); and just myCarB.addBody() if you don't want a full construction. In other words, looks similar to having a director with different construct methods.

Does that comparison make any sense?

@fleabay

fleabay said:

Builder is a creational design pattern.

“Component pattern” I'm not sure this is even a pattern. I always consider it to be similar to the Strategy Pattern but this is just my assumption. Reguardless, it is basically Composition over Inheritance.

According to “Game Programming Patterns”, by Robert Nystrom, it is a pattern. It says that it is a pattern that “Allows a single Entity to span multiple domains without coupling the domains to each other”. So yes, composition over inheritance since you are not coupling the domains, but, applied to a Single Entity, which is what I would say makes it a pattern.

Advertisement

Have you read the original GOF book? It's a good read. The big thing they focused on was trying to give names to patterns, and once named they could be studied. We could cite books all day, but books aren't software and it's important to remember that. These books are about the study of software, and the discoveries and conclusions their authors made.

The GOF book was notable because it triggered the identification and naming of patterns. Before then since the mid 1970s people focused on algorithms and data structures. Those still exist, but the book encouraged a specific focus on the larger organizational and behavioral patterns. It wasn't new in 1995, people had been doing it before, but it was a catalyst for an additional focus in study.

The GOF book didn't have a “Component” but did have a “Composite”. Many game engines have adopted that as part of their “component” objects. The intent is the same as the one covered by GOF, “lets client treat individual objects as compositions of objects uniformly”.

The big benefit is that you can dump all of the things into a container and it doesn't matter what the things are, all of them share the same interface and can be used interchangeably.

  • In Unity that's usually a MonoBehaviour object and the assorted derived types, and the uniform interfaces like Start(), Update(), FixedUpdate(), LateUpdate() plus a ton of event handlers OnXxx, like OnGUI(), OnEnable(), OnDisable(), OnCollisionEnter(), OnCollisionExit(), and many more.
  • In Unreal that's usually one of two types, either an Actor object (usually given the “A” prefix") the assorted derived types like APawn, ACharacter, or a Component. Actors have a uniform interface with functions like BeginPlay(), Tick(), or GetComponents(). Components have a set of standard functions and a bunch of event handlers, but can range in functionality from AI to physics to lighting to cameras to models to many other functions.

In all cases, these are objects that are interchangeable and can be stored in a container. That's the key concept. You can have a container of MonoBehavior objects and neither know nor care what they are since they all implement the same interface. You can have a container of AActor objects, or UObject objects, you don't need to know nor care what they are since they all implement the same interface.

The GOF Builder pattern lets you create things without knowing the details of how they're built. The book's intent describes it as: “Separate the construction of a complex object from its representation so that the same construction process can create different representations”.

The benefit here is uniformity. It doesn't matter what the thing is, you can build them.

  • For Unity, no matter the object you can call Instantiate() and you've made a new one.
  • For Unreal, no matter the object you can use NewObject() and you've made a new one.

It doesn't matter what the object is, the Builder can build it. You might be using another fully-created object, a prefab, a blueprint, a class name, or something else, you do some work to get a data blob that can be created, call create, and you've got one.

Using other tools the Builder method might take a data structure describing the thing to be created, it might be a few parameters describing the thing, or it might be a small enough functionality that no additional parameters are needed for the Builder object to know what it needs to create and return to the caller. The software pattern is that the caller doesn't need to know the internal details of how other objects are created, it can call the Builder object with a blob of data saying what it wants, and the builder can do all the work and give back a new object.

Your “Director” isn't a GOF pattern, although I've seen many variations on it over the years. It might be a Command pattern, where a thing is directing flow. It might be a Mediator pattern, encapsulating how a bunch of objects interact with each other.

Usually the pattern in games is a Command pattern loop where everything follows a Chain of Responsibility pattern. The thread loops on it's commands, calling various Update() or Tick() methods. Each of those methods follow the Command pattern, doing their processing and dispatching their instructions to other objects. Those are typically commands or events, that follow a chain of responsibility until the thing happens.

For example, the “Director” here may be the a Command pattern loop to update the physics system periodically. That system has a collection of interested physics objects, and each one of them is processed with exactly the same Command. That is, each one follows the Composite pattern so they can fit in a container, and also follows the Command pattern. Exactly the same function or set of functions can be called on all the objects, regardless of if the object's final representation is a player pawn, a vehicle, or a tree, all the system knows is that it follows the interface and can accept the command. The command will likely also trigger events, maybe an event that it has entered a trigger volume. In that case, the object (whatever it is, maybe a player, vehicle, tree) receives the event and processes it. As the Chain of Responsibility pattern, if the Tree implementation doesn't handle it, the parent class is given the opportunity, perhaps it's an EnvironmentalObject in this code. If it doesn't have a handler, it passes up a step to the WorldObject in this code base, if that doesn't handle it then maybe the base object is given an opportunity, and if that doesn't handle it, the command gets discarded or is considered processed even though no actual processing was done on it.

Overall, the patterns are useful names to understand how pieces of software work together.

Unfortunately it is easy for programmers, especially beginner programmers, to think of these patterns in isolation. "This is a Command, that is a Mediator, this is an Observer, and they are nothing more." In reality it is a bundle of data and functions that can simultaneously follow commands, issue commands, observe and respond to other objects, and manage the state of elements. The same with other principles, “This is object oriented, that is data oriented, those are event oriented, and they are nothing more." In reality they can be many things all at once. This can be an object built with object-oriented principles, organized in a data-oriented way, which implements its actions in response to events.

They are useful topics to think about, but beware that you don't get so bogged down in thinking about the patterns that you forget first and foremost they're ways that things do stuff. How they do stuff is interesting, most things do more than one kind of stuff, and in the real world most complex things do many kinds of stuff simultaneously.

rudiHammad said:
From GOF : “Director: constructs an object using the builder interface”.

It appears you've misunderstood an example they used as a distinct pattern unto itself. I don't have a copy of GOF handy, but I don't recall “Director” being a design pattern in the GOF and it isn't listed among the 23 patterns they described here: https://en.wikipedia.org/wiki/Design_Patterns. I did however google that exact sentence you posted and that lead me to https://itnext.io/easy-patterns-builder-d85655bcf8aa,​ and https://www.d.umn.edu/~gshute/cs5741/patterns/builder.xhtml​ which use “director” as an example.

What makes something a “builder” object is not what it is called, but how it is structured. I could have called “GameObjectBuilder” something totally off the wall and different (such as “ThisIsNotABuilderISwear”) and it would be no less an example of the builder pattern. Do not conflate examples of patterns with the actual patterns they illustrate. That way lies much confusion.

rudiHammad said:
For instance the Director can have a fullConstruct(), constructWihoutWheels(), constructOnlyBody(), etc… so basically you setting multiple configurations for the object you create.

That's just a builder with multiple construct methods.

rudiHammad said:
That's why I though it was similar to component since you might do something like myCarA = Car(); myCarA.addWheels; myCarA.addBody, etc… and myCarB = Car(); and just myCarB.addBody() if you don't want a full construction. In other words, looks similar to having a director with different construct methods.

“Looks similar" is not the same thing as “is the same thing.” sin and cos “look similar” syntactically but your math will be very wrong if you confuse the two. It also appears to me you're conflating one specific use of the builder pattern (rather than the builder pattern itself, which is broader) with a particular type of runtime composition. So no, I don't find the comparison makes much sense.

rudiHammad said:
According to “Game Programming Patterns”, by Robert Nystrom, it is a pattern.

I assume you're referring to https://gameprogrammingpatterns.com/component.html?​​ I don't agree with his terminology here. “Component” is too high level to be listed as a design pattern IMO. Or at least one at the level of abstraction used by the GOF, which is the typical use of the word “pattern” in software development.

You will find that there is much disagreement over patterns in the software dev community, including over whether we should be using or even talking about patterns in the first place. ?

Hey guys, at no point I said Director is a pattern. It might have look like that because how I quoted it. Director is a Participant of the builder pattern.

@frob wow, that was a long answer XD!! thanks for your time

yes, I have read GOF and many other design pattern books, including game development patterns, where Component is describe as pattern as I mentioned earlier.And of course, I never thing in isolation of the patterns, in fact one of the greate things about GOF is that at the begging it shows an example on how they are connect and there is a section in each where they compare the pattern discribed in the lesson with others..

@oberon_command check this link if you want to learn on how GOF described the builder pattern uml https://sites.google.com/site/neurogencorp/whatyouknow/gof-design-patterns/creational-pattern/builder-design-pattern​ . The desciption there is brief(not from the book), but you can see the director class. Which is part of the builder pattern. I am surprised that you talk about builders and the term director didn't ring a bell since it is an important part of the pattern. If you didn't read GOF, I recomend you read it.

ps: thanks for teaching me the difference between cos and sin! now I Know I why I failed my math exams…XD

rudiHammad said:
I am surprised that you talk about builders and the term director didn't ring a bell since it is an important part of the pattern.

It isn't. As stated in my prior post, the “director” in that diagram has nothing to do with builder. It is just an example of something that uses a builder. Look at the other examples of the pattern on Wiki: https://en.wikipedia.org/wiki/Builder_pattern​​.​​ The director is just a wrapper around the builder itself that can be reused. I wouldn't say that it's any less a builder to not have a distinct director object.

This topic is closed to new replies.

Advertisement