Classification of Design Patterns

Classification of Design Patterns

This post is a cross-post from www.ModernesCpp.com.

Patterns can be classified in various ways. The most prominent ones are the ones used in the books "Design Patterns: Elements of Reusable Object-Oriented Software" and "Pattern-Oriented Software Architecture, Volume 1".

Let me start in chronological order with the classification in the book Design Patterns: Elements of Reusable Object-Oriented Software.

Design Patterns: Elements of Reusable Object-Oriented Software

The following table provides the first overview of the 23 patterns presented in the book.

No alt text provided for this image

When you study the table, you can observe two classifications. First, creational patterns, structural patterns, and behavioral patterns, and second, class patterns and object patterns. The first classification is obvious, but not the second one.

Creational Patterns, Structural Patterns, and Behavioral Patterns

  • Creational patterns deal with object creation in a well-defined way.
  • Structural patterns provide mechanisms to organize class and objects for larger structures.
  • Behavioral patterns deal with communication patterns between objects.

The patterns that are written in bold fonts are the ones I used heavily in my past. Consequentially, I will write about them in future posts explicitly.

Destructional Patterns

You may see an asymmetry in this classification? Right! The book "Design Patterns: Elements of Reusable Object-Oriented Software" presents creational patterns, but not destructional patterns. So, what can you do?

Now, I come to the not-so-obvious classification. You can distinguish the scope of a pattern.

Class Patterns, and Object Patterns

I call class patterns and object patterns meta patterns in my patterns classes. I have two meta patterns in my mind when I want to solve a design challenge: inheritance versus composition. All 23 Design Patterns are only variations of the two key principles. Let me be more concrete. Inheritance is a class pattern, and composition is an object pattern.

Class Patterns

Class patterns apply classes and their subclass. They use the separation of interface and implementation and runtime dispatch with virtual function calls. Its functionality is hard-coded and available at compile time. They provide less flexibility and dynamic behavior, such as object patterns.

Object Patterns

Object patterns use the relationship of objects.

You build your abstraction by composing it out of basic building blocks. This composition can be done at runtime. Consequentially, object patterns are more flexible and delay the decision until runtime.

Honestly, inheritance is way too often used. Most of the time, the composition is the better choice.

Composition

When I gave my first design patterns classes around 2006, I gave them to the German automotive industry. To motivate composition, I created a generic car. Here it is.

#include <iostream>
#include <memory>
#include <string>
#include <utility>

struct CarPart{
    virtual int getPrice() const = 0;
};

struct Wheel: CarPart{
    int getPrice() const override = 0;
};

struct Motor: CarPart{
    int getPrice() const override = 0;
};

struct Body: CarPart{
    int getPrice() const override = 0;
};

// Trabi

struct TrabiWheel: Wheel{
    int getPrice() const override{
        return 30;
    }
};

struct TrabiMotor: Motor{
    int getPrice() const override{
        return 350;
    }
};

struct TrabiBody: Body{
    int getPrice() const override{
        return 550;
    }
};

// VW

struct VWWheel: Wheel{
    int getPrice() const override{
        return 100;
    }
};

struct VWMotor: Motor{
    int getPrice() const override{
        return 500;
    }
};

struct VWBody: Body{
    int getPrice() const override{
        return 850;
    }
};

// BMW

struct BMWWheel: Wheel{
    int getPrice() const override{
        return 300;
    }
};

struct BMWMotor: Motor{
    int getPrice() const override{
        return 850;
    }
};

struct BMWBody: Body{
    int getPrice() const override{
        return 1250;
    }
};

// Generic car
    
struct Car{
    Car(std::unique_ptr<Wheel> wh, std::unique_ptr<Motor> mo, std::unique_ptr<Body> bo): 
         myWheel(std::move(wh)), myMotor(std::move(mo)), myBody(std::move(bo)){}
         
    int getPrice(){
        return 4 * myWheel->getPrice() + myMotor->getPrice() + myBody->getPrice();
    }

private:
    std::unique_ptr<Wheel> myWheel;
    std::unique_ptr<Motor> myMotor;
    std::unique_ptr<Body> myBody;

};

int main(){
    
    std::cout << '\n';
    
    Car trabi(std::make_unique<TrabiWheel>(), std::make_unique<TrabiMotor>(), std::make_unique<TrabiBody>());
    std::cout << "Offer Trabi: " << trabi.getPrice() << '\n';
    
    Car vw(std::make_unique<VWWheel>(), std::make_unique<VWMotor>(), std::make_unique<VWBody>());
    std::cout << "Offer VW: " << vw.getPrice() << '\n';
    
    Car bmw(std::make_unique<BMWWheel>(), std::make_unique<BMWMotor>(), std::make_unique<BMWBody>());
    std::cout << "Offer BMW: " << bmw.getPrice() << '\n';
    
    Car fancy(std::make_unique<TrabiWheel>(), std::make_unique<VWMotor>(), std::make_unique<BMWBody>());
    std::cout << "Offer Fancy: " << fancy.getPrice() << '\n';
    
    std::cout << '\n';
    
}           

Okay, I know from international discussion in my design patterns classes that you know a BMW and a VW, but may have no idea of a Trabi. The same holds for many young people in Germany. Trabi is short for Trabant and is stands for small cars produced in the former East German.

No alt text provided for this image

Executing the program gives the expected result:

No alt text provided for this image

It's pretty straightforward to explain the program. The generic Car is a composition of four wheels, a motor, and a body. Each component is derived from the abstract base class CarPart and, therefore, has to implement the member function getPrice. The abstract base classes Wheel, Motor, and Body are not necessary but improve the structure of the car parts. When a customer wants to have a special car, the generic class Car delegates the getPrice call to its car parts.?

Of course, I applied bot meta patterns inheritance and composition together in this class to make the structure more type-safe and car parts easily pluggable.

A Thought Experiment

Now, let me reason more about composition and inheritance by answering the following questions:

  1. How many different cars can you make from existing vehicle components?
  2. How many classes do you need to solve the same complexity with inheritance?
  3. How easy/complex is it to use inheritance/composition to support a new car like Audi? For this, assume that all parts are at your disposal.
  4. How easy is it to change the price of a car part?
  5. Let's say a customer wants a new, fancy car assembled from existing car components. When do you need to decide to assemble the new car based on inheritance or composition? Which strategy is applied at compile time and which at run time?

Here is my reasoning:

  1. You can create 3 * 3 * 3 = 27 different cars out of the 14 components.
  2. You need 27 + 1?= 28 different classes to build 27 different cars. Each class has to encode its car parts into its class name, such as TrabiWheelVWMotorBMWBody, TrabiWheelVWMotorVWBody, TrabiWheelVWMotorTrabiBody, ... . This becomes pretty fast unmaintainable. The same complexity holds when you apply multiple inheritances and give TrabiWheelVWMotorBMWBody three base classes. In this case you would have to derive from TrabiWheel, VWMotor, and BMWBody. Additionally, you would have to rename the member function getPrice.
  3. In the composition strategy, you simply have to implement the three car parts for auto. This gives you the power to create 4 * 4?* 4 = 64 different cars from 17 components. On the contrary, with inheritance, you have to extend the inheritance tree in all necessary branches.
  4. It's pretty easy to change the price of a car part using composition. For inheritance, you have to traverse the entire inheritance tree and change the price in each spot.
  5. This is my main point. Thanks to composition, you can assemble the car parts during run time. In contrast, the inheritance strategy configures the car at compile time. Being a car seller means storing the car parts to assemble them when the customer comes. Using inheritance, you have to preproduce all configurations of your car.

Of course, this was only my thought experiment. But this should make one point clear.?To master combinatorial complexity, you have to compose your solution out of basic pluggable components. I call this the Lego principle.

What's next?

Also, the book "Pattern-Oriented Software Architecture, Volume 1" provides a very interesting classification of patterns. I will present it in my next post.


Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dr?ge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschl?ger, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Ralf Holly, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, and Stephen Totten.

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, Ralf Abramowitsch, John Nebel, and Mipko.

My special thanks to Embarcadero

My special thanks to PVS-Studio

?

Mentoring Program in English

Do you want to stay informed about my mentoring programs? Write to [email protected].

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

New

Contact Me

Modernes C++

要查看或添加评论,请登录

Rainer Grimm的更多文章

  • Reflection in C++26: Metafunctions

    Reflection in C++26: Metafunctions

    Reflection offers many metafunctions that run at compile time. The metafunctions are declared as consteval.

    1 条评论
  • Improve You Or Your Team

    Improve You Or Your Team

    Do you want to become a professional C++ developer? Here is your chance. Take one of my mentoring programs and try out…

  • Reflection in C++

    Reflection in C++

    After the search into the breadth starts today, the search into the depth: reflection. Reflection Reflection is the…

  • Embedded Programming with Modern C++

    Embedded Programming with Modern C++

    My most successful class is now available as mentoring. I have given this class around 100 times in the last few years.

  • An Overview of C++26: Concurrency

    An Overview of C++26: Concurrency

    Today, I will finish my overview of C++26 and write about concurrency. There are still two library features left before…

    1 条评论
  • Improve your Team

    Improve your Team

    Bring your team to the next level. Consider my mentoring programs.

  • My ALS Journey (14/n): My Status Quo

    My ALS Journey (14/n): My Status Quo

    My ALS progression seems to slow down, and Beatrix and I can the first time plan for the future. My ALS Journey so far…

    15 条评论
  • An Overview Of C++26: The Library – Math

    An Overview Of C++26: The Library – Math

    std::submdspan is s a subspan of an existing span (C++23). The subspan didn’t make it into C++23 and was added with…

  • Create Your Mentoring Program

    Create Your Mentoring Program

    Bring your team to the next level. Consider my mentoring programs.

  • An Overview of C++26: The Library

    An Overview of C++26: The Library

    In my last post, I overviewed C++26’s core language. Today, I continue with the library.

    1 条评论

社区洞察

其他会员也浏览了