Hej, właśnie obniżyłem ceny wszystkich produktów. Przygotujmy nasze umiejętności programowania na erę post-COVID. Więcej szczegółów »
Strategia

Strategia w języku C++

Strategia to behawioralny wzorzec projektowy zakładający przekształcenie zestawu zachowań w obiekty, które można stosować zamiennie w pierwotnym obiekcie.

Pierwotny obiekt, zwany kontekstem, przechowuje odniesienie do obiektu-strategii i deleguje mu działania związane z danym zachowaniem. Aby zmienić sposób, w jaki kontekst wykonuje swą pracę, należy zamienić bieżąco przypisany obiekt strategii na inny.

Użycie wzorca w języku C++

Złożoność:

Popularność:

Przykłady użycia: Wzorzec Strategia jest bardzo powszechny w kodzie C++. Często stosuje się go w różnych frameworkach by umożliwić użytkownikom zmianę funkcjonalności klasy bez konieczności rozszerzania jej.

Identyfikacja: Wzorzec Strategia można rozpoznać po obecności metody pozwalającej wykonywać faktyczną pracę zagnieżdżonemu obiektowi oraz po obecności settera umożliwiającego wymianę tego obiektu na inny.

Przykład koncepcyjny

Poniższy przykład ilustruje strukturę wzorca Strategia ze szczególnym naciskiem na następujące kwestie:

  • Z jakich składa się klas?
  • Jakie role pełnią te klasy?
  • W jaki sposób elementy wzorca są ze sobą powiązane?

main.cc: Przykład koncepcyjny

/**
 * The Strategy interface declares operations common to all supported versions
 * of some algorithm.
 *
 * The Context uses this interface to call the algorithm defined by Concrete
 * Strategies.
 */
class Strategy
{
public:
    virtual ~Strategy() {}
    virtual std::string DoAlgorithm(const std::vector<std::string> &data) const = 0;
};

/**
 * The Context defines the interface of interest to clients.
 */

class Context
{
    /**
     * @var Strategy The Context maintains a reference to one of the Strategy
     * objects. The Context does not know the concrete class of a strategy. It
     * should work with all strategies via the Strategy interface.
     */
private:
    Strategy *strategy_;
    /**
     * Usually, the Context accepts a strategy through the constructor, but also
     * provides a setter to change it at runtime.
     */
public:
    Context(Strategy *strategy = nullptr) : strategy_(strategy)
    {
    }
    ~Context()
    {
        delete this->strategy_;
    }
    /**
     * Usually, the Context allows replacing a Strategy object at runtime.
     */
    void set_strategy(Strategy *strategy)
    {
        delete this->strategy_;
        this->strategy_ = strategy;
    }
    /**
     * The Context delegates some work to the Strategy object instead of
     * implementing +multiple versions of the algorithm on its own.
     */
    void DoSomeBusinessLogic() const
    {
        // ...
        std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n";
        std::string result = this->strategy_->DoAlgorithm(std::vector<std::string>{"a", "e", "c", "b", "d"});
        std::cout << result << "\n";
        // ...
    }
};

/**
 * Concrete Strategies implement the algorithm while following the base Strategy
 * interface. The interface makes them interchangeable in the Context.
 */
class ConcreteStrategyA : public Strategy
{
public:
    std::string DoAlgorithm(const std::vector<std::string> &data) const override
    {
        std::string result;
        std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
            result += letter;
        });
        std::sort(std::begin(result), std::end(result));

        return result;
    }
};
class ConcreteStrategyB : public Strategy
{
    std::string DoAlgorithm(const std::vector<std::string> &data) const override
    {
        std::string result;
        std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
            result += letter;
        });
        std::sort(std::begin(result), std::end(result));
        for (int i = 0; i < result.size() / 2; i++)
        {
            std::swap(result[i], result[result.size() - i - 1]);
        }

        return result;
    }
};
/**
 * The client code picks a concrete strategy and passes it to the context. The
 * client should be aware of the differences between strategies in order to make
 * the right choice.
 */

void ClientCode()
{
    Context *context = new Context(new ConcreteStrategyA);
    std::cout << "Client: Strategy is set to normal sorting.\n";
    context->DoSomeBusinessLogic();
    std::cout << "\n";
    std::cout << "Client: Strategy is set to reverse sorting.\n";
    context->set_strategy(new ConcreteStrategyB);
    context->DoSomeBusinessLogic();
    delete context;
}

int main()
{
    ClientCode();
    return 0;
}

Output.txt: Wynik działania

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
abcde

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
edcba

  

Strategia w innych językach

Wzorce projektowe: Strategia w języku Java Wzorce projektowe: Strategia w języku C# Wzorce projektowe: Strategia w języku PHP Wzorce projektowe: Strategia w języku Python Wzorce projektowe: Strategia w języku Ruby Wzorce projektowe: Strategia w języku Swift Wzorce projektowe: Strategia w języku TypeScript Wzorce projektowe: Strategia w języku Go