23.6. Strategy (GoF)

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?

Strategy

Context/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 (see Figure 23.8). 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 23.8. 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 23.9. 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 23.9. Strategy in collaboration.


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

Figure 23.10. Context object needs attribute visibility to its strategy.


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 PricingStrategyFactory 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 NextGen POS 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.

UML notation— Observe that in Figure 23.10 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 PricingStrategyFactory will be a singleton (one instance) and accessed via the Singleton pattern (see Figure 23.11).

Figure 23.11. Factory for strategies.


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

Figure 23.12. 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.

Summary

Protected Variations with respect to dynamically changing pricing policies has been achieved with the Strategy and Factory patterns. Strategy builds on Polymorphism and interfaces to allow pluggable algorithms in an object design.

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

Related Patterns


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.135.81