Text Adventure Library - Builder Pattern
Welcome back fellow developers! Today I am going over how I build complex objects for my text adventure. I created the Noun class as the base class for all objects in my game, there are three derived classes, Person, Place and Thing. Person is for characters, Place is for locations and Thing is for items. I use a ECS approach and I use a dictionary of type <string, object> called attributes for the components. The components define the objects, so Persons all need specific attributes that all Persons will need and the same for Places and Things. This all makes creating instances of these objects a complicated thing and I needed a way to simplify it. A common way to simplify the creation of objects is to use the factory pattern.
The Factory Pattern is a way to make creating objects easier. Instead of making each object directly, you ask a "factory" to make it for you. The factory knows how to make different kinds of objects and gives you the one you want. It's like ordering from a menu instead of cooking, you get what you want without having to do all the work of making it yourself.
The Factory Pattern is very useful and seemed like the right choice for me, but you can't customize it during the construction process. This makes it hard to create all the different objects in my game.
Imagine you're running a toy factory that produces different types of toys—cars, dolls, robots, and more. Using the Factory Pattern, you'd need a separate factory for each type of toy. This means if you have ten different types of toys, you'd need ten separate factories.
Now, imagine each factory needs to handle not just one but multiple variations of the same type of toy—for example, cars with different colors or features. You'd either need to create a multitude of factories, each handling a specific combination of features, or you'd end up with a single, overly complex factory that tries to handle all possible variations.
In either case, managing all these factories becomes cumbersome and unwieldy. Adding new types of toys or variations would require modifying existing factories or creating entirely new ones, leading to a tangled web of dependencies and making maintenance and scaling a nightmare.
Enter the Builder Pattern! The Builder Pattern is a creational design pattern used to construct complex objects step by step. It separates the construction of a complex object from its representation, allowing the same construction process to create different representations. The Builder Pattern is particularly suitable for constructing complex objects that require multiple steps or configurations. It excels in scenarios where object creation involves intricate initialization or dependencies.
Unlike the Factory Pattern, which creates objects in one go, the Builder Pattern facilitates incremental object construction. It achieves this by caching the reference to the object being built within the builder itself and passing the builder instance as an argument to subsequent builder methods. This allows each method to modify the partially constructed object and maintain its state throughout the construction process, ultimately resulting in a fully configured object when the construction is complete.
So it's decided, the builder pattern is the way to go for this situation. Lets write the code!
Sure, let's break down the NounBuilder
class and provide a detailed tutorial explaining each line:
public class NounBuilder<T> where T : Noun, new() { private T noun; private string[] requiredAttributes; // Constructor public NounBuilder(params string[] requiredAttributes) { noun = new T(); this.requiredAttributes = requiredAttributes; }
- The
NounBuilder<T>
class is declared as a generic class, allowing it to work with various types of objects, but uses thenew()
constraint ensures thatT
must be a subclass ofNoun.
- Two private fields are declared:
noun
, which holds the object being constructed, andrequiredAttributes
, which stores an array of required attribute names. I do this to ensure that the objects created have everything they need to function properly. - The constructor initializes a new instance of
T
and assigns it to thenoun
field. It also receives a variable number of strings representing required attribute names and stores them in therequiredAttributes
field.
// Method to set a single attribute public NounBuilder<T> WithAttribute(string key, object value) { noun.AddOrSetAttribute(key, value); return this; } // Method to set multiple attributes public NounBuilder<T> WithAttributes(Dictionary<string, object> attributes) { foreach (var kvp in attributes) { noun.AddOrSetAttribute(kvp.Key, kvp.Value); } return this; }
- The
WithAttribute
method sets a single attribute of the object being constructed. It takes a key-value pair representing the attribute name and value, calls theAddOrSetAttribute
method on thenoun
object, and returns the builder instance (this
) to support method chaining. - The
WithAttributes
method sets multiple attributes of the object being constructed. It iterates over the key-value pairs in the provided dictionary, calls theAddOrSetAttribute
method for each pair, and returns the builder instance (this
) to support method chaining.
The Builder Pattern allows chaining which enables the sequential invocation of builder methods to set various attributes or configurations of an object. Chaining allows developers to set object attributes or configurations one after the other without needing to store intermediate results in variables. Each method in the builder class returns a reference to the builder instance (this
), enabling subsequent method calls to be chained together seamlessly.
// Create a NounBuilder for constructing Person objects with required attributes "Name" and "Age" var builder = new NounBuilder<Person>("Name", "Age"); // Set attributes using method chaining var person = builder .WithAttribute("Name", "John Doe") .WithAttribute("Age", 30) .TryBuild();
The builder can support any number ofWithAttribute
calls to specialize the object anyway you want. I included The WithAttributes
method so I could also pass in dictionaries of attributes already defined to use as templates for specific objects.
Ensuring objects have all necessary attributes.
// Method to check if all required attributes are present private bool CheckRequiredAttributes() { var presentAttributes = noun.Attributes.Keys; return requiredAttributes.All(attr => presentAttributes.Contains(attr)); }
- The
CheckRequiredAttributes
method verifies whether all required attributes have been set for the object being constructed. - It retrieves the keys of the attributes currently set in the
noun
object and compares them with the required attribute names stored in therequiredAttributes
field. - If all required attributes are present, it returns
true
; otherwise, it returnsfalse
.
Building the Object
// Method to attempt building the object public T TryBuild() { if (CheckRequiredAttributes()) return Build(); else return null; } // Private method to finalize the construction process private T Build() { return noun; } }
- The
TryBuild
method is used to attempt building the object. It calls theCheckRequiredAttributes
method to verify if all required attributes are present. - If all required attributes are present, it calls the
Build
method to finalize the construction process and return the fully constructed object; otherwise, it returnsnull
. - The
Build
method is a private helper method that simply returns thenoun
object, representing the fully constructed object. I made the Build method private to prevent people from building improper objects, ones that do not have all the required attributes.
In conclusion, opting for the Builder Pattern over the Factory Pattern can significantly enhance the flexibility and precision of object construction in C#. While both patterns serve to simplify object creation, the Builder Pattern's ability to facilitate incremental construction makes it the right choice in this situation. Choose the Builder Pattern for incremental, customizable object construction, and elevate your coding experience to new heights. Happy building!
Endless Prose
The long awaited 3rd addition to the Epic Prose series!
Status | In development |
Author | logicandchaos |
Genre | Role Playing |
Tags | Text based |
More posts
- Finishing the LibraryApr 05, 2024
- Text Adventure Library - Slaying the Spaghetti Monster!Nov 22, 2023
- From Prototype to ProductionSep 25, 2023
- Redesign!Jul 22, 2023
- Improving the Name Generator using ChatGPTApr 20, 2023
- Creatures & Encounters!Mar 30, 2023
- Travelling Across The MapMar 23, 2023
- Exploring DungeonsMar 13, 2023
Leave a comment
Log in with itch.io to leave a comment.