设计模式8:策略模式

策略模式是一种行为设计模式,让你能在运行时改变对象的行为,主要用于算法的选择和替换。

意图:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式可以使算法可独立于使用它的客户而变化。

解决什么问题:策略模式主要用来解决在软件开发中经常遇到的一些问题,比如一个对象有很多行为,而且这些行为在不同的情况下需要进行切换,或者这些行为之间存在复杂的规则。

优点:

你可以在运行时切换对象的行为。
你可以将算法的实现和使用分离开来。
你可以通过独立策略来消除大型条件基础语句。
开闭原则。你可以在不修改原有代码的情况下引入新的策略。
缺点:

如果策略数量过多,复用策略的概率就会变得很小。
客户必须知道所有的策略类,并自行决定使用哪一个策略类。

示例

classicswarm 的调度器中,调度的策略就是基于策略模式实现的:

定义策略类:

// PlacementStrategy is the interface for a container placement strategy.
type PlacementStrategy interface {
	// Name of the strategy
	Name() string
	// Initialize performs any initial configuration required by the strategy and returns
	// an error if one is encountered.
	// If no initial configuration is needed, this may be a no-op and return a nil error.
	Initialize() error
	// RankAndSort applies the strategy to a list of nodes and ranks them based
	// on the best fit given the container configuration.  It returns a sorted
	// list of nodes (based on their ranks) or an error if there is no
	// available node on which to schedule the container.
	RankAndSort(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error)
}

var (
	strategies []PlacementStrategy
	// ErrNotSupported is the error returned when a strategy name does not match
	// any supported placement strategy.
	ErrNotSupported = errors.New("strategy not supported")
	// ErrNoResourcesAvailable is the error returned when there are no resources
	// available to schedule a container. This can occur if there are no nodes in
	// the cluster or if no node contains sufficient resources for the container.
	ErrNoResourcesAvailable = errors.New("no resources available to schedule container")
)

func init() {
	strategies = []PlacementStrategy{
		&SpreadPlacementStrategy{},
		&BinpackPlacementStrategy{},
		&RandomPlacementStrategy{},
	}
}

// New creates a new PlacementStrategy for the given strategy name.
func New(name string) (PlacementStrategy, error) {
	if name == "binpacking" { //TODO: remove this compat
		name = "binpack"
	}

	for _, strategy := range strategies {
		if strategy.Name() == name {
			log.WithField("name", name).Debugf("Initializing strategy")
			err := strategy.Initialize()
			return strategy, err
		}
	}

	return nil, ErrNotSupported
}

// List returns the names of all the available strategies.
func List() []string {
	names := []string{}

	for _, strategy := range strategies {
		names = append(names, strategy.Name())
	}

	return names
}

spread 策略实现类:

// SpreadPlacementStrategy places a container on the node with the fewest running containers.
type SpreadPlacementStrategy struct {
}

// Initialize a SpreadPlacementStrategy.
func (p *SpreadPlacementStrategy) Initialize() error {
	return nil
}

// Name returns the name of the strategy.
func (p *SpreadPlacementStrategy) Name() string {
	return "spread"
}

// RankAndSort sorts nodes based on the spread strategy applied to the container config.
func (p *SpreadPlacementStrategy) RankAndSort(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
	// for spread, a healthy node should decrease its weight to increase its chance of being selected
	// set healthFactor to -10 to make health degree [0, 100] overpower cpu + memory (each in range [0, 100])
	const healthFactor int64 = -10
	weightedNodes, err := weighNodes(config, nodes, healthFactor)
	if err != nil {
		return nil, err
	}

	sort.Sort(weightedNodes)
	output := make([]*node.Node, len(weightedNodes))
	for i, n := range weightedNodes {
		output[i] = n.Node
	}
	return output, nil
}

random 策略实现类:

/ RandomPlacementStrategy randomly places the container into the cluster.
type RandomPlacementStrategy struct {
	r *rand.Rand
}

// Initialize a RandomPlacementStrategy.
func (p *RandomPlacementStrategy) Initialize() error {
	p.r = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
	return nil
}

// Name returns the name of the strategy.
func (p *RandomPlacementStrategy) Name() string {
	return "random"
}

// RankAndSort randomly sorts the list of nodes.
func (p *RandomPlacementStrategy) RankAndSort(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
	for i := len(nodes) - 1; i > 0; i-- {
		j := p.r.Intn(i + 1)
		nodes[i], nodes[j] = nodes[j], nodes[i]
	}
	return nodes, nil
}

在调度器中包含了一个策略:


type Scheduler struct {
	sync.Mutex

	strategy strategy.PlacementStrategy
	filters  []filter.Filter
}