Strategy (GoF) - UML

The next design problem to be resolved is to provide more complex pricing logic, such as a store - wide discount for the day, senior citizen discounts, and so forth.

The pricing strategy (which may also be called a rule, policy, or algorithm) for a sale can vary. During one period it may be 10% off all sales, later it may be $10 off if the sale total is greater than $200, and myriad other variations. How do we design for these varying pricing algorithms?

  • Name : Strategy
  • Problem : How to design for varying, but related, algorithms or policies? How to design for the ability to change these algorithms or policies?
  • Solution : Define each algorithm / policy / strategy in a separate class, with a common interface.

Since the behavior of pricing varies by the strategy (or algorithm), we create multiple SalePricingStrategy classes, each with a polymorphic getTotal method. Each getTotal method takes the Sale object as a parameter, so that the pricing strategy object can find the pre - discount price from the Sale, and then apply the discounting rule. The implementation of each getTotal method will be different: PercentDiscountPricingStrategy will discount by a percentage, and so on.

Figure 26.9 Pricing Strategy classes

A strategy object is attached to a context object - the object to which it applies the algorithm. In this example, the context object is a Sale. When a getTotal message is sent to a Sale, it delegates some of the work to its strategy object, as illustrated in Figure. It is not required that the message to the context object and the strategy object have the same name, as in this example (for example, getTotal and getTotal), but it is common. However, it is common - indeed. usually required - that the context object pass a reference to itself (this) on to the strategy object, so that the strategy has parameter visibility to the context object, for further collaboration.

Figure 26.10 Strategy in collaboration

Observe that the context object (Sale)needs attribute visibility to its strategy. This is reflected in the DCD in Figure.

Creating a Strategy with a Factory

There are different pricing algorithms or strategies, and they change over time. Who should create the strategy? A straightforward approach is to apply the Factory pattern again: A Pricing StrategyFactory can be responsible for creating all strategies (all the pluggable or changing algorithms or policies) needed by the application. As with the ServicesFactory, it can read the name of the implementation class of the pricing strategy from a system property (or some external data source), and then make an instance of it. With this partial data - driven design (or reflective design) one can dynamically change at any time - while the NextGenPOS application is running - the pricing policy, by specifying a different class of Strategy to create.

Observe that a new factory was used for the strategies; that is, different than the ServicesFactory. This supports the goal of High Cohesion - each factory is cohesively focused on creating a related family of objects.

Figure 26.11 Context object needs attribute visibility to its strategy

UML - Observe that in Figure the reference via a directed association is to the interface ISalePricingStrategy, not to a concrete class. This indicates that the reference attribute in the Sale will be declared in terms of the interface, not a class, so that any implementation of the interface can be bound to the attribute.

Note that because of the frequently changing pricing policy (it could be every hour), it is not desirable to cache the created strategy instance in a field of the PricingStrategyFactory, but rather to re - create one each time, by reading the external property for its class name, and then instantiating the strategy.

And as with most factories, the Pricing StrategyFactory will be a singleton (one instance) and accessed via the Singleton pattern

When a Sale instance is created, it can ask the factory for its pricing strategy, as shown in Figure.

Figure 26.12 Factory for strategies

Figure 26.13 Creating a strategy

Reading and Initializing the Percentage Value

Finally, a design problem that has been ignored until now is the issue of how to find the different numbers for the percentage or absolute discounts. For example, on Monday, the
PercentageDiscountPricingStrategy may have a percentage value of 10%, but 20% on Tuesday.

Note also that a percentage discount may be related to the type of buyer, such as a senior citizen, rather than to a time period.

These numbers will be stored in some external data store, such as a relational database, so they can be easily changed. So, what object will read them and ensure they are assigned to the strategy? A reasonable choice is the StrategyFactory itself, since it is creating the pricing strategy, and can know which percentage to read from a data store ("current store discount," "senior discount," and so forth).

Designs to read these numbers from external data stores vary from the simple to the complex, such as a plain JDBC SQL call (if Java technologies, as an example) or collaborating with objects that add levels of indirection in order to hide the particular location, data query language, or type of data store. Analyzing the variation and evolution points with respect to the data store will reveal if there is a need for protected variation. For example, we could ask, "Are we all comfortable with a long - term commitment to using a relational database that understands SQL?". If so, a simple JDBC call from within the StrategyFactory may suffice.

Related Patterns

Strategy is based on Polymorphism, and provides Protected Variations with respect to changing algorithms. Strategies are often created by a Factory.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd Protection Status

UML Topics