1.前言
在现实生活中常常遇到多种可选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等。在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如果使用多重条件转移语句实现,不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
2.概念
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户(支付方式,有微信,有支付宝等都可以支付,但是不管你选择哪种策略都是支付)。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
3.模式的结构和实现
策略模式是将一组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性,现在我们来分析其基本结构和实现方法。
3.1 模式的结构
策略模式的主要角色如下。
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境类(Context):持有一个策略类的引用,最终给客户端调用。
3.2模式的实现
场景:假如老板叫你做鸭子的通关游戏,鸭子分很多种,比如有模型鸭,有机器鸭,有家养的土鸭等,鸭子的外观也不一样,并且,有些鸭子不会飞,有些会飞(用翅膀飞,机器鸭用火箭动力飞),并且飞的方式不一样,有些鸭子不会叫,有些鸭子会叫(嘎嘎叫),并且叫的方式也不一样,那么当你选中不用类型的鸭子去通关游戏,就会体现出不同的风格的游戏
按需求画出类图
Duck是个抽象类,子类分别有MachineDuck(机器鸭),ModelDuck(模型鸭),SoilDuck(家养土鸭),因为飞(fly)和叫(quack)的行为各都不一样,如果把飞和叫的行为都写在Duck抽象类里面,那么就是继承,那么子类都要去重写飞(fly)和叫(quack)方法,所以把飞和叫的行为定义为一个接口,这样就可以更好的运用组合,而不是用继承!
1. 创建一个抽象类Duck
//Duck.ts
export abstract class Duck {
public flyBehavior: pattern.IflyBehavior;
public quckBehavior: pattern.IquackBehavior;
constructor(fly: pattern.IflyBehavior, quck: pattern.IquackBehavior) {
this.flyBehavior = fly;
this.quckBehavior = quck;
}
public abstract display();
//执行叫的动作
public performQuck() {
this.quckBehavior.quack();
}
//执行飞的动作
public performFly() {
this.flyBehavior.fly();
}
//设置叫的方式
public setFlyBehavior(type: pattern.IflyBehavior) {
return this.flyBehavior = type;
}
//设置飞行的方式
public setQuackBehavior(type: pattern.IquackBehavior) {
return this.quckBehavior = type;
}
}
2. 创建IflyBehavior(飞行为)和IquackBehavior(叫行为)的接口
//IflyBehavior.ts
export interface IflyBehavior {
fly();
}
//IquackBehavior.ts
export interface IquackBehavior{
quack();
}
3.分别实现这两个接口
//FlyNoWay.ts
import{IflyBehavior} from "./IflyBehavior"
export class FlyNoWay implements IflyBehavior{
public fly(){
console.log('我不会飞')
}
}
//FlyRocketPower.ts
import{IflyBehavior} from "./IflyBehavior"
export class FlyRocketPower implements IflyBehavior{
public fly(){
console.log('我用火箭动力飞行')
}
}
//FlyWithWings.ts
import{IflyBehavior} from "./IflyBehavior"
export class FlyWithWings implements IflyBehavior{
public fly(){
console.log('我用翅膀飞')
}
}
//Quack.ts
import{IquackBehavior} from "./IquackBehavior"
export class Quack implements IquackBehavior{
public quack(){
console.log('我会嘎嘎叫')
}
}
//MuteQuack.ts
import{IquackBehavior} from "./IquackBehavior"
export class MuteQuack implements IquackBehavior{
public quack(){
console.log('我不会叫')
}
}
4.分别实现Duck的子类
//MachineDuck.ts
import { Duck } from "./Duck"
import { FlyRocketPower } from "./FlyRocketPower";
import { Quack } from "./Quack";
export class MachineDuck extends Duck {
constructor(){
super(new FlyRocketPower(),new Quack())
}
public display() {
console.log('我是机器鸭外观')
}
}
//ModelDuck.ts
import { Duck } from "./Duck"
import { FlyNoWay } from "./FlyNoWay"
import { MuteQuack } from "./MuteQuack";
export class ModelDuck extends Duck {
constructor() {
super(new FlyNoWay(), new MuteQuack());
}
public display() {
console.log('我是模型鸭的外观')
}
}
//SoilDuck.ts
import { Duck } from "./Duck"
import { FlyWithWings } from "./FlyWithWings";
import { Quack } from "./Quack";
export class SoilDuck extends Duck {
constructor(){
super(new FlyWithWings(),new Quack());
}
public display() {
console.log('我是家养土鸭的外观')
}
}
5.最后实现环境类,最终给客户端调用
//Context.ts
export class Context {
private duck: pattern.Duck;
public getDuck() {
return this.duck;
}
public setDuck(duck: pattern.Duck) {
this.duck = duck;
}
public duckMethod(): void {
//执行鸭的外观显示
this.duck.display();
//执行鸭的飞行行为
this.duck.performFly();
//执行鸭的叫行为
this.duck.performQuck();
}
}
//Index.controller.ts 调用
import { SoilDuck } from "./SoilDuck"
import { Context } from "./Context"
export class IndexController implements angular.IController {
constructor() {
let duck = new Context();
//设置鸭的类型
duck.setDuck(new SoilDuck());
//执行鸭的方法
duck.duckMethod();
// 我是家养土鸭的外观
//我用翅膀飞
//我会嘎嘎叫
}
}
总结优缺点:
优点:
多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
缺点:
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
策略模式造成很多的策略类,增加维护难度。