NOTE
Strategy Pattern
The strategy pattern selects one of several algorithms for accomplishing the same task according to a type or context, with Java and Go examples.
This is a historical learning note and may contain outdated or incomplete understanding.
1. Definition
There are multiple different ways to accomplish the same thing. Select a different algorithm according to the type; using if/else is too cumbersome.
The difference from the state pattern is that every strategy in the strategy pattern is used to accomplish the same thing, while the state pattern is used to accomplish different things.
2. UML
3. Java
3.1. Client
public class Client
{
public static void main(String[] args)
{
double price = 100.0;
StrategyContext context = new StrategyContext();
// Existing customer, small amount
context.setStrategy(new OldCustomerFew());
context.getPrice(price);
// New customer, small amount
context.setStrategy(new NewCustomerFewer());
context.getPrice(price);
}
}
3.2. Context
public class StrategyContext
{
private Strategy strategy;
public Strategy getStrategy()
{
return strategy;
}
public void setStrategy(Strategy strategy)
{
this.strategy = strategy;
}
public void getPrice(double price)
{
System.out.println(this.strategy.getPrice(price));
}
}
3.3. Strategy
public interface Strategy
{
double getPrice(double price);
}
public class NewCustomerFewer implements Strategy
{
@Override
public double getPrice(double price)
{
return price * 0.95;
}
}
public class NewCustomerMany implements Strategy
{
@Override
public double getPrice(double price)
{
return 0.9 * price;
}
}
public class OldCustomerFew implements Strategy
{
@Override
public double getPrice(double price)
{
return price * 0.85;
}
}
4. Golang
4.1. Strategy
/* User discount strategy */
type IStrategy interface {
GetPrice(price float64) float64
}
// Strategy with a larger discount for existing users
type OldCustomerMuchStrategy struct {
}
func NewOldCustomerMuch() *OldCustomerMuchStrategy {
return &OldCustomerMuchStrategy{}
}
func (o OldCustomerMuchStrategy) GetPrice(price float64) float64 {
return price * 0.5
}
// Strategy with a smaller discount for new users
type NewCustomerMuchStrategy struct {
}
func NewNewCustomerFew() *NewCustomerMuchStrategy {
return &NewCustomerMuchStrategy{}
}
func (o NewCustomerMuchStrategy) GetPrice(price float64) float64 {
return price * 0.8
}
4.2. Strategy Manager
/* Strategy manager */
type StrategyContext struct {
strategy IStrategy
}
func NewStrategyContext() *StrategyContext {
return &StrategyContext{}
}
func (s *StrategyContext) Strategy() IStrategy {
return s.strategy
}
func (s *StrategyContext) SetStrategy(strategy IStrategy) {
s.strategy = strategy
}
// Apply the discount
func (s *StrategyContext) GetPrice(price float64) {
fmt.Printf("Discounted price: %f
", s.strategy.GetPrice(price))
}
4.3. Client
func main() {
price := 100.0
strategyContext := 策略模式.NewStrategyContext()
strategyContext.SetStrategy(策略模式.NewOldCustomerMuch())
strategyContext.GetPrice(price)
strategyContext.SetStrategy(策略模式.NewNewCustomerFew())
strategyContext.GetPrice(price)
}
5. Example
Template method + simple factory + strategy pattern.

- pb.go
package pb
type SwitchRoomReq struct {
}
type SwitchRoomRsp struct {
}
- abstract_template.go
package template
import (
"context"
"fmt"
"test/template/pb"
)
/* Implements IFetchDataService */
type abstractFetchDataService struct {
IFetchDataSteps
req *pb.SwitchRoomReq
rsp *pb.SwitchRoomRsp
basicInfo interface{}
extraInfo interface{}
}
func (t *abstractFetchDataService) checkParam(ctx context.Context) error {
fmt.Println("default checkParam")
return nil
}
func (t *abstractFetchDataService) getBasicInfo(ctx context.Context) error {
fmt.Println("default getBasicInfo")
return nil
}
func (t *abstractFetchDataService) getExtraInfo(ctx context.Context) error {
fmt.Println("default getExtraInfo")
return nil
}
func newTemplate(iSteps IFetchDataSteps) *abstractFetchDataService {
return &abstractFetchDataService{IFetchDataSteps: iSteps}
}
func (t *abstractFetchDataService) HandleRequest(ctx context.Context, req *pb.SwitchRoomReq, rsp *pb.SwitchRoomRsp) error {
t.req = req
t.rsp = rsp
if err := t.IFetchDataSteps.checkParam(ctx); err != nil {
return err
}
if err := t.IFetchDataSteps.getBasicInfo(ctx); err != nil {
return err
}
if err := t.IFetchDataSteps.getExtraInfo(ctx); err != nil {
return err
}
return nil
}
- content_read_template_impl.go
package template
import (
"context"
"fmt"
)
const TypeContentReadService = 2
var contentReadServiceConstructorFunc fetchDataServiceConstructor = func() IFetchDataService {
return NewFetchDataFromContentReadService()
}
func init() {
GlobalServiceFactory.RegisterService(TypeContentReadService, contentReadServiceConstructorFunc)
}
type FetchDataFromContentReadService struct {
*abstractFetchDataService
}
func (f *FetchDataFromContentReadService) checkParam(ctx context.Context) error {
fmt.Println("checkParam")
return f.abstractFetchDataService.checkParam(ctx)
}
func (f *FetchDataFromContentReadService) getBasicInfo(ctx context.Context) error {
fmt.Println("getBasicInfo")
return f.abstractFetchDataService.getBasicInfo(ctx)
}
func NewFetchDataFromContentReadService() *FetchDataFromContentReadService {
service := &FetchDataFromContentReadService{}
service.abstractFetchDataService = newTemplate(service)
return service
}
- factory.go
package template
import "fmt"
var GlobalServiceFactory = newServiceFactory()
type fetchDataServiceConstructor = func() IFetchDataService
type serviceFactory struct {
serviceMap map[int]fetchDataServiceConstructor
}
func newServiceFactory() *serviceFactory {
return &serviceFactory{serviceMap: make(map[int]fetchDataServiceConstructor)}
}
func (s *serviceFactory) GetServiceByType(serviceType int) (IFetchDataService, error) {
serviceConstructor, ok := s.serviceMap[serviceType]
if ok {
return serviceConstructor(), nil
}
err := fmt.Errorf("GetServiceByType: serviceType %v invalid", serviceType)
return nil, err
}
func (s *serviceFactory) RegisterService(serviceType int, serviceConstructor fetchDataServiceConstructor) {
s.serviceMap[serviceType] = serviceConstructor
}
- itemplate.go
package template
import (
"context"
"test/template/pb"
)
type IFetchDataService interface {
HandleRequest(ctx context.Context, req *pb.SwitchRoomReq, rsp *pb.SwitchRoomRsp) error
}
type IFetchDataSteps interface {
checkParam(ctx context.Context) error
getBasicInfo(ctx context.Context) error
getExtraInfo(ctx context.Context) error
}
- template_test.go
package template
import (
"context"
"testing"
)
func TestTemplate(t *testing.T) {
service, _ := GlobalServiceFactory.GetServiceByType(TypeContentReadService)
service.HandleRequest(context.Background(), nil, nil)
}
// Output
checkParam
default checkParam
getBasicInfo
default getBasicInfo
default getExtraInfo
Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub