Chapter 3: Creational Patterns

0:00 / 0:00
Report an issue

Welcome to Last Minute Lecture.

This free chapter overview is designed to help students review and understand key concepts.

These summaries supplement not replaced the original textbook and may not be redistributed or resold.

For complete coverage, always consult the official text.

Welcome to the Deep Dive.

Today we're tackling a really foundational chapter for modern object -oriented design.

Creational patterns.

Exactly.

We're talking about how build objects but in a way that your main application doesn't even care how they're actually being manufactured.

That's the mission really.

Our sources come directly from the definitive design patterns catalog and we're diving into the five essential creational patterns.

And the goal at the end of the day is to make your system independent of how its objects are created, composed, and represented.

It sounds like the key to flexible software.

It is.

It's And when you look at these patterns, there are really two big ideas that keep coming up, right?

Two recurring themes.

First, they all encapsulate the knowledge of which concrete classes are being used.

So your code never has to know the specific class name.

Precisely.

And the second theme is that they hide how those instances are actually built and put together.

When you look at the family tree, you see a split.

Right.

The class creational versus object creational.

Exactly.

The class creational patterns, they use inheritance.

So subclassing is how you get variation.

And the object creational ones.

They use composition.

They delegate the dirty work of instantiation to a whole other object.

It's like the difference between following a family recipe yourself versus just using a specialized machine in a factory.

That's a great analogy.

And to show why this matters so much, we're using the source materials running example, which is building a Okay, simple enough.

Imagine you've got a basic maze.

It's built from a few components like maze, room, wall, and door.

All inheriting from some abstract mapsite class.

Pretty standard stuff.

So the inflexible way to build, say, a simple two room maze is to have one big function like mazegame .createMaze.

And inside that function, you're just hard coding everything.

New room one, new wall, new door connecting them.

And that's where the pain starts.

Oh, absolutely.

Because what happens when your team wants to launch an enchanted maze expansion pack?

Suddenly you need an enchanted room or a door needing spell.

If that createMaze function has hundreds of these hard coded new calls, you have to go back and rewrite the whole thing.

The whole thing just to change the theme of the game.

And that's the exact barrier that inflexibility that these patterns are designed to just completely break down.

So let's look at the first fix, the most approachable one, right?

The one that uses inheritance.

The factory method.

OK, the factory method.

It's often called a virtual constructor.

The intent is simple.

Define an interface for creating an object, but let the subclasses decide which actual class to instantiate.

So the main class knows when to create something, but not what to create.

Exactly.

The creator defers instantiation to its children.

The classic example is in application frameworks.

Think about a generic application class, right?

The framework knows the user clicked filed new.

It knows it needs a new document, but it can't possibly know if you're building a word processor that needs a text document or a graphics program that needs a drawing document.

If it hard coded new text document, the framework would be useless for anything else.

So the solution is that the abstract application class defines an abstract function, the factory method.

Let's call it create document.

And then my specific app, say my word processor app, just overrides that one method to return a new my document.

That's it.

The framework has a hook and the subclass provides the object.

It's very clean.

So how does this fix our maze game?

Well, instead of hard coding new room,

the maze game class now gets virtual factory methods.

Make room, make wall, make door.

The main create maze logic, the complex part, stays exactly the same.

It just calls these make functions instead of new.

Ah, I see.

So if we want a bombed maze game, we don't touch create maze at all.

That one line.

We just make a subclass, bond a maze game, and we only override, say, make wall to return a new bombed wall instance.

And you're done.

Flexibility through inheritance.

But I can see the trade off.

If you have like 10 different components, you might end up with dozens of subclasses of maze game, which feels like it could get messy.

A total sprawl, which sets us up perfectly for our next pattern, where we move away from inheritance and towards composition.

The abstract factory.

Also known as a kit.

The intent here is to provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Okay, families.

That word seems key.

It's everything.

This is for when you need to enforce consistency across a whole set of components.

The classic motivation for this is a UI toolkit, isn't it?

It is.

Imagine you need to support different look and feel standards like motif and presentation manager.

You don't just want one motif button.

You need the whole family.

A motif button, a motif scroll bar, a motif window.

They all have to match.

Exactly.

So you define an abstract widget factory with methods like create scroll bar and create window.

Then you create concrete factories, motif widget factory, PM widget factory, and your application only ever talks to the abstract factory.

So the client code never sees a concrete class name.

It just says, Hey, factory, give me a scroll bar.

And you can swap the entire look and feel of your app just by plugging in a different factory object at the start.

It's incredibly powerful for maintaining consistency.

But there's a big liability, a major trade off we have to talk about.

There is the abstract factory interface kind of fixes the set of products you can create.

So what happens if six months later, the UI standard adds a new

That's the problem.

You have to go back and change the abstract factory interface itself to add a create slider method.

Which means you have to update every single one of your concrete factory classes to implement it.

Supporting new kinds of products becomes difficult.

It's a significant constraint.

So back in the maze, we'd pass a maze factory object into create maze.

Right.

And create maze would call factory dot make room and factory dot make wall.

You pass in an enchanted maze factory and you're guaranteed to get a consistently enchanted maze.

Okay, so if abstract factory is about product consistency, what's next?

The builder pattern.

And this one is about product complexity and its representation.

Its whole intent is to separate the construction of a complex object from how it's actually represented.

So the same recipe, the same construction process can create wildly different final things.

Exactly.

The classic example is a parser for a complex document like RTF rich text format.

The RTF reader is what we call the director.

It knows the strict algorithm for reading the file token by token,

but it doesn't build the final product itself.

It delegates that it delegates to a text converter, which is the builder.

If you plug in an ACI converter, it just pulls out the plain text and ignores all the formatting.

But if you use, say, a text converter as the builder, the exact same sequence of commands the director will produce a beautifully formatted document.

The parsing logic never changes, but the final product is completely different.

That's a huge difference from abstract factory, which just gives you the finished object in one go.

Builder is step by step.

And the final result can be a totally different type of object, which is where the real power lies.

So for our maze, we'd have a maze builder interface with steps like build maze, build room, and build door in.

And create maze becomes the director.

It just calls those steps in order.

Build room one, build room two, build a door between them.

A standard maze builder would actually build a maze, but you could have an exotic one, right?

Like the counting maze builder from the text.

Oh, that's a brilliant example.

The director calls builder dot build room one.

And the counting maze builder doesn't build anything.

It just increments a counter.

So the director follows this complex construction process, but the final product from the counting maze builder is just an integer, a number.

It's a completely different representation.

That deep separation of the recipe from the final dish is the core of the builder pattern.

All right, let's shift gears again.

We've managed families and complex construction now for prototype.

Prototype is the king of runtime configuration.

What's the intent here?

You specify the kinds of objects to create using a prototypical instance, and then you create new objects by just copying it.

By cloning it.

By cloning it.

This is how you escape from creating huge parallel class hierarchies.

So instead of subclassing a graphic tool for every type of musical note you want to create, you just have one graphic tool and you parameterize it with a prototype object, like an instance of a whole note.

When the user clicks, the tool just calls clone on that prototype.

Super simple for the client.

The only requirement is that the prototype objects have to know how to clone themselves.

Right.

The benefits are huge.

You reduce subclassing, and you can even add or remove product types at runtime by just registering new prototypes.

But we have to talk about the tricky part of cloning.

The whole shallow copy versus deep copy problem.

Can you break that down for us?

Absolutely.

A shallow copy is like getting a duplicate key to a house.

If someone changes the lock on the house, both keys stop working.

Meaning the original object and the clone still share some internal data.

Right.

If you change the clone, you might accidentally change the original.

A deep copy is like getting an identical but separate house with its own lock and key.

The two are completely independent.

And for this to work, you almost always need a deep copy.

You do.

And the source also points out you often need a separate initialized method.

After you clone a door, for example, you still need to tell the new door which two rooms it connects.

So for the maze, we'd create a maze prototype factory.

And instead of subclassing it, we just feed it prototype objects in its constructor.

Exactly.

You'd initialize it with, say, new maze, new bomb wall, and new room within bomb.

When the client asks for a wall, the factory just calls clone on its prototype wall.

That's the ultimate runtime flexibility.

You can switch the entire game from a standard maze to a bombed maze just by passing a different set of objects to the factory's constructor.

No code changes to the factory itself.

None at all.

Okay, last one.

Singleton.

Finally, yes.

Singleton.

It's not so much about product variability, but it is 100 % about controlling creation.

The intent is simple.

A class can only have one instance.

Ever.

And you provide a single global way to get to it.

We need this for things that are naturally unique, like a printer spooler or a file system handler.

And we've all probably used global variables for this, but that doesn't stop someone from accidentally creating a second instance somewhere else in the code.

Which can lead to chaos.

So the trick is access control.

You make the constructor private or protected.

Which means no one outside the class can just use the new keyword to make one.

You've locked it down.

So the only way to get the instance is to a static member function, usually called instance.

And this is where lazy initialization comes in.

That's the key.

The function checks the static pointer.

If the instance is null, if it doesn't exist yet, the function creates it itself.

It's the only place allowed to call the private constructor.

And if it already exists, it just returns the one that's already there.

So you only build it if and when it's needed and only the very first time.

The real power, though, is when you use a singleton for a factory.

How so?

Well, you can make that instance function smarter.

It could, for example, read an environment variable, maybe something like Mesa style.

Ah, and based on that variable, it decides which subclass of factory to create the first time.

Exactly.

It can instantiate a bomb maze factory or an enchanted maze factory and return that as the single global instance for the entire application.

That's really powerful.

So you get global control, but without hard coding a specific class at compile time.

That's it.

Wow, that was a comprehensive tour.

Let's try to synthesize this.

What's the main dividing line between all these patterns?

It really still comes down to inheritance versus composition.

The factory method stands alone.

It relies on subclassing the creator.

It's great for simple localized changes, but as we said, it can lead to class sprawl.

And the others, abstract factory builder and prototype, they all rely on composition.

They use a separate delegate object to do the work.

Right.

They're often a bit harder to set up at first, but your flexibility later on is, well, it's exponential because you're just swapping objects at runtime, not rewriting code.

So whether you're building a UI, parsing a document or creating a game world,

these patterns let you build new things without tearing apart your core application logic.

That decoupling is where the real value is.

Okay.

So here's the provocative thought we want to leave you with.

The source material notes that the factory method is often the first one people reach for because it seems simpler,

fewer classes to start with.

It's an easy on -rep.

But what's the long -term cost?

If you know your product lines are going to change often, are you just setting yourself up to create dozens of tiny subclasses that do almost nothing?

When a single dynamically configured prototype factory might've saved you weeks of boilerplate coding down the road.

The real deep dive then isn't just knowing the patterns.

It's about anticipating where the change will come from.

Are you going to add new families of products?

That's abstract factory.

Or will you change the complex assembly process?

That's builder.

Choosing the right pattern is about predicting the future complexity.

A fantastic way to frame it.

That was a deep dive into decoupling creation from representation.

Hopefully this gave you the tools to decide when you need a builder, an abstract factory, or maybe a prototype.

And remember, that tight control over instantiation really is the key to building flexible, resilient software that can evolve over time.

Thanks for tuning into the deep dive.

We hope you feel thoroughly informed and ready to put these concepts into practice.

We'll catch you next time.

ⓘ This audio and summary are simplified educational interpretations and are not a substitute for the original text.

Chapter SummaryWhat this audio overview covers
Object creation mechanisms form a foundational concern in software architecture, and creational design patterns provide systematic approaches to managing how instances are instantiated, composed, and represented within a system. By abstracting the specifics of object construction, these patterns enable software to remain flexible and independent of concrete class implementations, supporting both static configuration and runtime adaptability. Five principal patterns constitute this category, each addressing distinct instantiation challenges. Abstract Factory establishes a contract for producing complete families of interrelated objects that function cohesively, ensuring consistency across product selections such as maintaining uniform appearance across all graphical interface components. Builder decouples the assembly sequence of intricate objects from their final form, enabling the same construction procedure to yield different outputs depending on which concrete builder executes the steps, exemplified by a document parser generating multiple output formats from identical source material. Prototype eliminates the need for parallel factory hierarchies by creating new instances through duplication of existing exemplars, thereby reducing implementation complexity and permitting straightforward runtime customization through state modification alone. Factory Method defers instantiation decisions to subclasses through an abstract creation method, granting derived classes authority over which concrete types to produce and serving as a customization mechanism within larger framework architectures. Singleton restricts class instantiation to a single object while furnishing a universally accessible reference point, preventing the namespace contamination and uncontrolled proliferation associated with global variables. These patterns achieve their flexibility by encapsulating instantiation logic, concealing concrete class dependencies, and allowing system behavior to be modified through configuration rather than code alteration. Practical application of these concepts becomes evident through the recurring example of constructing maze game components such as rooms, walls, and doors, demonstrating how each pattern transforms rigid, hard-coded construction procedures into adaptable, extensible systems capable of supporting new product types and behaviors without modification to existing client code.

Using this chapter to study? Last Minute Lecture is free and student-run. If it helped, consider supporting the project.

Support LML ♥