cft

Design Patterns Saga: The Beginning

Overview of Factory Design Pattern


user

Gene Zeiniss

3 years ago | 9 min read

A little while ago I overheard a discussion about polymorphism. Specifically: is it still a favorite interview topic for Java developer positions. Opinions ranged from “it’s a must”, to “never used it”.

And I thought: I want to write about polymorphism. I shared my thoughts with my friend. He just blurted out in a heartbeat: design patterns! Design patterns, no doubt, the most fascinating topic in object-oriented programming. So, here I am!

This article is the beginning of the Design Patterns Saga. Let’s refresh our knowledge of polymorphism to lay down the basics for the pattern.

What Do We Know About Polymorphism?

Polymorphism, according to Oxford, is the condition of occurring in several different forms. In word-for-word translation from Greek, polymorphism means many (poly) forms (morphs). I found about 40 synonyms to polymorphism.

The list starts with variety, array, assortment; and it’s heating up to mishmash, soup, stew. I like food allegories. Perhaps because I like to eat.

Think about sushi. According to the “Know Your Sushi” article, the main types are Sashimi (my favorite), Nigiri, Chirashi, and Maki.

Sushi shows up in a few different forms. This concept of transformation is core to object-oriented programming and is key to programming in Java.

Polymorphism in Java in a Few Words

Polymorphism in Java allows us to perform the same action in different ways. Mind your choices! There are two types of Polymorphism:

Static Polymorphism or Overloading is the existence of multiple functions in the same class, having the same name but different parameters. For example, class Self-Assembly Sushi can include function for vegetarian sushi and for fish-based sushi.

Dynamic Polymorphism or Overriding goes up a level in sushi assembly. Class California Roll can implement Maki method and return an inside-out sushi roll with avocado, imitation crab, cucumber, and tobiko. Dragon Roll class will implement the same method by adding the shrimp tempura and thin slices of avocado on top of the roll. In other words, it is a process when the method described in one class is overridden (extended or implemented) by other classes.

The bottom line, Polymorphism, is what makes Java code flexible and potentially extensible.

The great advantage and purpose of using Polymorphism are to decouple the client class from the implementation code. Guess what, most design patterns use some form of Polymorphism to express specific implementations out of generic solutions.

So What Are Design Patterns?

Think about design patterns like Grandma’s recipe for a favorite dish. If your Grandma is Asian you can just continue with the sushi example.

My Russian Granny makes pirozhki (boat-shaped buns with a variety of fillings). She has experimented with and fried, cooked, and baked pirozhki over and over again. After a while, baking was preferred because it creates the best tasting dish and it’s more gentle for our on-diet figure.

All my family loves Granny’s pirozhki. Every one of my friends loves them as well. If you ever have dinner with my family, you will too. This recipe is ready to be distributed, so other people would enjoy it.

Grandma is an analogy for developers, who over the years, have experimented with many different solutions to a recurring specific problem (From all of us, thank you!).

A design pattern is a pirozhki’s receipt or practical proven solution to a given problem. Whoever did this really knows his onions.

Design Pattern Categories

The authors of the book “Design Patterns: Elements of Reusable Object-Oriented Software”, also known as the Gang of Four, classified the design patterns into a few main categories.

The first category is Creational Patterns. The category name kind of speaks for itself. It contains several patterns to tackle how you handle new objects creation. We’ll get back to these in a moment.

Another pattern category is Structural Patterns, which describe relationships between objects and how subclasses and classes should interact to achieve a particular design goal. I can compare the structural pattern to food pairing. A flavor pairing determines which ingredients may be combined together to form a suitable relationship.

Some ingredients are mixed together, till they are nearly indistinguishable, such as mixing blood oranges, carrot, and tomato smoothie.

Others are combined but still maintain some independence, such as a salad of mixed vegetables. Some food just pairs well with others and is not physically combined, such as wine and cheese. Or, if you are looking for a tasty combination of sweet and salty ingredients, you can pair chocolate with caviar 😋. Truly, I just found this combination in Foodpairing (for foodies between us).

The relationships among the food are defined by the pairings. Similarly, in software, each structural pattern determines these various suitable relationships among the objects.

The final pattern category involves Behavioral Patterns. These patterns focus on how independent objects work towards a common goal. Take pirozhki as a goal. The cooking team includes only two people, Granny and you, when each one has a role.

Grandma bakes and, from time to time, grumbles that you are underfoot. You are trying to help with all one’s might, till you get kicked out from the kitchen, and finally, you eat.

Well done! Did you see what we did there? Several participants, one goal. Each participant has a specific role in the plan to achieve the goal.

The Gang of Four identified twenty three design patterns. I feel like going through a selection of these patterns. Let’s start with the Factory Pattern.

Factory Design Pattern

What are your associations with the word factory? Someplace where workers manufacture goods. This is exactly this. The Factory Pattern is a creational pattern, whose purpose is to create objects.

Just like a factory in the real world. In this pattern, the object creation happens in the factories, without exposing the creation logic to the client. Imagine that your software implements a sushi bar and you want to create sushi to serve at your bar.

There are many different types of sushi, but let’s start with the California and Dragon rolls that were mentioned in the polymorphism example.

A lot of the sushi rolls we’ve become familiar with are a Western take on Japanese Maki sushi. Therefore, to implement the sushi bar software, you need a Maki interface and concrete California and Dragon classes implementing Maki.

Let’s say that all you need to create a maki roll is to add fish, fillings, and the topic. Can you smell polymorphism? Kidding, this is an overriding example we mentioned earlier.

public interface Maki {
void addFish();
void addFillings();
void addTopics();
}

public class CaliforniaRoll implements Maki {
@Override
public void addFish() { log.info("california roll fish: imitation crab"); }
@Override
public void addFillings() { log.info("california roll fillings: avocado and cucumber"); }
@Override
public void addTopics() { log.info("california roll topics: no topic"); }
}

public class DragonRoll implements Maki {
@Override
public void addFish() { log.info("dragon roll fish: shrimp tempura"); }
@Override
public void addFillings() { log.info("dragon roll fillings: avocado and cucumber"); }
@Override
public void addTopics() { log.info("dragon roll topics: slices of avocado along with tobiko"); }
}

Now, you need a method. The method will first declare the maki variable, which will refer to the maki object to be created.

Think about it, like maki base, which is traditionally made with a sheet of nori, wrapped around a layer of rice. California Roll, for example, is an inside-out sushi roll with a layer of rice on the outside and a sheet of nori on the inside. The roll type parameter will determine which maki roll is actually instantiated.

When you have a base for your roll, you can call methods to add fish, fillings, and topics. These methods do not care what type of roll is created. All they want is a maki base to operate on.

The method looks like this.

public Maki createRoll(RollType rollType) {
// create concrete instantiation of maki object
Maki maki = Match(rollType).of(
Case($(RollType.CALIFORNIA_ROLL), new CaliforniaRoll()),
Case($(RollType.DRAGON_ROLL), new DragonRoll()),
Case($(), () -> {
log.info("provided sushi type is undefined");
return null; }));
// assembly sushi
maki.addFish();
maki.addFillings();
maki.addTopics();
return maki;
}

Your brilliant chief adds more and more roll types to the menu. Spicy Tuna Roll, Spider Roll. In this example, the list of conditionals grows and grows as new roll types are added.

Maki maki = Match(rollType).of(
Case($(RollType.CALIFORNIA_ROLL), new CaliforniaRoll()),
Case($(RollType.DRAGON_ROLL), new DragonRoll()),
Case($(RollType.SPICY_TUNA_ROLL), new SpicyTunaRoll()),
Case($(RollType.SPIDER_ROLL), new SpiderRoll()),
Case($(), () -> {
log.info("provided sushi type is undefined");
return null;
})
);

Notice that what we do with the roll after creating it doesn’t change. Each roll needs to contain fish, fillings, and topics. This is all getting pretty complicated. To decouple maki instantiation from the client, you can delegate the responsibility of maki base creation, based on provided roll type, to Sushi Factory.

public class SushiFactory {
public Maki createMakiBase(RollType rollType) {
return Match(rollType).of(
Case($(RollType.CALIFORNIA_ROLL), new CaliforniaRoll()),
Case($(RollType.DRAGON_ROLL), new DragonRoll()),
Case($(RollType.SPICY_TUNA_ROLL), new SpicyTunaRoll()),
Case($(RollType.SPIDER_ROLL), new SpiderRoll()),
Case($(), () -> {
log.info("provided sushi type is undefined");
return null;
}));}
}

In general, a factory object is an instance of such a class, which has a method to create product objects (maki base, in our case). Now, this Sushi Factory can be used by the Sushi Bar service. In other words, the sushi bar is now a client of the sushi factory.

public class SushiBarService {
private final SushiFactory sushiFactory;

public Maki createRoll(RollType rollType) {
Maki maki = sushiFactory.createMakiBase(rollType);
maki.addFish();
maki.addFillings();
maki.addTopics();
return maki;
}
}

Here is a UML diagram of the Sushi Bar service you just implemented.

Let’s look, what have you gained here? The Sushi Bar service and it’s method may not be the only client of Sushi Factory. Other clients, such as Sushi Delivery and Sushi Takeaway may use Sushi Factories to create maki as well.

Since all of the actual maki creation happens in the Sushi Factory, you can simply add new roll types to your factory or to change the way the rolls are instantiated, without modifying the client’s code.

That’s all! Now you know your onions 🤓. And I know that I feel like ordering sushi delivery. This post made me hungry. Bon appetit!

Upvote


user
Created by

Gene Zeiniss

Java developer, backend guild master at a fintech startup, blogger (http://medium.com/@genezeiniss) 🤓


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles