avatarEsteban Thilliez

Summary

This article introduces design patterns, their types, and their importance in improving code quality, with examples of the Factory Method and Adapter patterns.

Abstract

Design patterns are reusable solutions to common problems in software design, providing a way to make code more flexible, reusable, and maintainable. They are divided into three categories: creational, structural, and behavioral patterns. The article explains the Factory Method and Adapter patterns as examples of creational and structural patterns, respectively. The Factory Method is used to create objects without specifying the exact class of object that will be created, while the Adapter pattern allows incompatible interfaces to work together. The article also mentions that there are many other design patterns, and each one solves a specific problem in software design.

Bullet points

  • Design patterns are reusable solutions to common problems in software design.
  • They are divided into three categories: creational, structural, and behavioral patterns.
  • The Factory Method is an example of a creational pattern that allows objects to be created without specifying the exact class of object that will be created.
  • The Adapter pattern is an example of a structural pattern that allows incompatible interfaces to work together.
  • Design patterns improve code quality by making it more flexible, reusable, and maintainable.
  • There are many other design patterns, each solving a specific problem in software design.

Improve your Python Coding with Design Patterns — Introduction

Common Design Solutions to Solve Common Design Problems

Photo by Olav Ahrens Røtne on Unsplash

This story is part of the “Design Patterns” series. You can find the other stories of this series here:

You can also find all the code used through this series on GitHub.

What are Design Patterns?

Design patterns are reusable solutions for the common problems occurring in software design. You know there are a thousand ways to solve a problem in programming, but some of them have been tested and proved to be very efficient. These are design patterns.

They are very abstract, which makes them independent from any programming language. They represent more an idea than an implementation.

They allow you to make your code more flexible, more reusable, more maintainable, etc… In a nutshell, they improve your code quality. They are also intensively used in the industry because when other programmers recognize a pattern in your code, they nearly instantly understand your code.

Types of Design Patterns

Design patterns are divided into different categories according to their purpose.

Creational Patterns

These patterns are used to improve the creation of objects. They abstract the instantiation process and make the system independent of how its objects are created, composed, and represented.

Here are the different creational design patterns:

  • Factory Method: provide an interface to create objects in a superclass, but subclasses can alter the type of objects created.
  • Abstract Factory: allows you to create families of related objects without specifying the concrete classes.
  • Builder: allows you to build complex objects step by step. It allows different representations of an object to be produced using the same code.
  • Prototype: used to copy objects without making the code dependent.
  • Singleton: pattern that ensures that a class has only one instance while providing a global access point.

As you can see each of these design patterns solves a problem you may encounter while coding.

I’ll make a story to explain each design pattern independently. In the meantime, let’s see the Factory Method, for example, to see what a pattern looks like.

The Factory Method

Imagine you’ve built an app that can be used to translate English into other languages. Currently, you have 5 languages implemented, and your code is dependent on these languages. If later you want to implement new languages, you have to refactor all the code.

A way to avoid this is to make the languages independent from the translator. That’s how the Factory Method can be used. Let’s check the code:

First, we create a common interface with a translatemethod. We make the interface abstract, and we define an abstract method to get the translations.

Then, we implement concrete translators inheriting from the Translator interface. This way, we just have to implement the _get_translations method.

Finally, we implement our factory. We just populate a dictionary of translators associating translators with their corresponding language, and we create a method to get the correct translator from a language.

If we want to add other translators later, we just have to create a new translator class and add the corresponding entry in the factory’s dictionary.

Now we can use our factory to create translators depending on a language and be sure to be able to translate text with them as they all follow the same interface.

There are many different ways to implement a Factory Method, and the way I’ve solved this problem using the Factory Method is just one among many others.

Structural Patterns

These are patterns used to assemble objects and classes into larger structures while maintaining flexibility.

Here is the list of structural patterns:

  • Adapter: used to make two incompatible systems work together.
  • Bridge: allows you to separate a large class into several related classes, allowing you to separate the abstraction and the implementation.
  • Composite: pattern that allows you to compose objects into a tree structure and work with it as if it were a single object.
  • Decorator: pattern that allows you to attach new behaviors to an object.
  • Flyweight: pattern used to decrease the memory usage of the program.
  • Facade: this pattern allows you to simplify a complex interface.
  • Proxy: a proxy allows to contain another object and to do something before or after the object’s request.

As for the creational patterns, we will see only one of these patterns, and I will make other stories for the others. Let’s see how an adapter works.

The Adapter

Imagine that you want to place objects in a round hole. You can put round objects as long as their radius is smaller than the radius of the hole. But you can’t put square objects. So let’s create an adapter to put square objects in this hole:

First, we create the RoundHole class. Then, we create our pegs. The first one is round and has a radius, and the second one is squared and has a width.

To fit the square peg into the round hole, we have to create an adapter converting a width into a radius.

Finally, we test that our adapter works correctly. If we try to fit a small square peg into a small hole, it works, but if the peg is too big, it doesn’t. Perfect!

In a nutshell, an Adapter follows the same interface as the one it should adapt to, and takes some elements from the interface it has to adapt. If it’s possible in your programming language, an Adapter usually inherits several interfaces (so it requires multiple-inheritance). But you can implement it as you want, you don’t always need to inherit from the two classes, or from one class.

Behavioral Patterns

These are patterns used to maintain effective communication and manage responsibilities between objects.

Here is the list of behavioral patterns:

  • Chain of Responsibility: pattern for passing requests along a chain of handlers. Each handler decides whether to pass the request to the next or to stop it.
  • Command: pattern to represent commands.
  • Iterator: pattern that allows you to browse collections without knowing their representation.
  • Mediator: pattern used to manage dependencies between objects.
  • Memento: pattern used to store states of objects.
  • Observer: pattern allowing you to send notifications when specific events occur in your app.
  • State: allows you to create classes that can change completely according to a state.
  • Strategy: allows you to separate a class doing different similar things into different classes called Strategies.
  • Template Method: a method whose specific parts and functionalities can be overridden and modified.
  • Visitor: separates algorithms from the objects on which they operate.

As an example, let’s see how a Template Method works.

Template Method

Imagine you’re building a game. You want a common AI for monsters, but some specific monsters must have specific behaviors. For example, a boss should run faster, or a sorcerer should cast spells and build towers.

The Template Method pattern can help you to do this.

First, we create a generic class defining methods that can be overridden. We can make this class abstract, but it’s not mandatory. For example, instead of GameAI , I could have named it MonstersAI and it would have not been abstract.

Then, we can create as many subclasses as we want and make them override some methods of the main class. This way, we can create monsters following the same interface, but whose behaviors may be different for some things.

Final Note

I hope this story was a good introduction to design patterns. They are definitely a must-know if you’re programming in an Object-Oriented programming language.

I think I will make one story per pattern, as there are so many things to say about them. So, this story can be seen as the introduction of the “Design Patterns Series”!

To explore the other stories of this story, click below!

To explore more of my Python stories, click here! You can also access all my content by checking this page.

If you want to be notified every time I publish a new story, subscribe to me via email by clicking here!

If you’re not subscribed to medium yet and wish to support me or get access to all my stories, you can use my link:

Python
Design Patterns
Programming
Coding
Software Engineering
Recommended from ReadMedium