CodeBlanks Logo Icon CodeBlanks

Summary - Design patterns

Combine Singleton, Factory and Strategy to write a payment system.

Summary: combining patterns

In this summary you will combine three patterns in a payment system. You will use Singleton for a single logger, Factory for a method that produces the requested discount type, and Strategy for different discount algorithms.

The Logger must be a single instance because all payments write to the same source. The Discount abstract class is the discount strategy; NoDiscount and TenPercent subclasses apply different algorithms. The createDiscount("ten") call returns the right strategy. The patterns work together: Factory builds Strategy, Singleton logs.

Practice Write the payment system

Define a Logger Singleton with a log(const std::string&) method that prints std::cout << msg;. An abstract Discount class has virtual int apply(int price) const = 0;. NoDiscount returns the price unchanged; TenPercent returns price - price / 10. A createDiscount(const std::string&) Factory method returns TenPercent for "ten". In main compute a 100 unit price with 10 percent discount and log it (output 90).

Starter code

main.cpp
#include <iostream>
#include <memory>
#include <string>

// Define the Logger Singleton class
// Define the Discount abstract class and NoDiscount/TenPercent classes
// Add the createDiscount Factory method

int main() {
    // Compute a 100 unit price with TenPercent, log it
    return 0;
}

Sample solution

main.cpp
#include <iostream>
#include <memory>
#include <string>

class Logger {
public:
    static Logger& instance() {
        static Logger l;
        return l;
    }
    void log(const std::string& msg) { std::cout << msg; }
private:
    Logger() = default;
};

class Discount {
public:
    virtual ~Discount() = default;
    virtual int apply(int price) const = 0;
};

class NoDiscount : public Discount {
public:
    int apply(int price) const override { return price; }
};

class TenPercent : public Discount {
public:
    int apply(int price) const override { return price - price / 10; }
};

std::unique_ptr<Discount> createDiscount(const std::string& type) {
    if (type == "ten") return std::make_unique<TenPercent>();
    return std::make_unique<NoDiscount>();
}

int main() {
    auto d = createDiscount("ten");
    int total = d->apply(100);
    Logger::instance().log(std::to_string(total));
    return 0;
}

Expected output

90

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Why is the Logger a Singleton in this system?

  • To run faster
  • So all payments write to the same source Correct answer
  • To use less memory
  • To be compatible with the Factory

Explanation: Singleton gives one logger; all records go to the same place.

2 Match the pattern to its role

Term Meaning
Logger (Singleton) Single logger
createDiscount (Factory) Builds a strategy
class Discount (Strategy) Discount algorithm
TenPercent Concrete strategy

3 Complete the createDiscount call

Question
auto d = ____("ten");
Answer
auto d = createDiscount("ten");

Explanation: createDiscount is the Factory method; it builds a strategy.

All lessons in Advanced