当我们使用继承不能很好的解决问题时,比如一些行为在子类不停的改变,并且让所有的子类都有这些行为是不恰当的,可以考虑把会变化的部分取并封装起来,这样就可以保证其他部分不受影响,这样做的好处是代码变化引起的不经意后果变少,而且系统也会变得更有弹性。
策略模式设计原则:针对接口编程,而不是针对实现编程
所谓针对接口编程,接口是一个概念,也是java的interface的构造,
“针对接口编程”关键就在于多态。利用多态,程序可以针对超类型编
程,执行时会根据实际状况执行到真正的行为
下面引用《设计模式》一书中的例子:
有一个鸭子实体,有飞和叫两种行为,但是不同的鸭子,对于这两个行为的具体实现是不一样,具体如下:
- 模型鸭不会飞,也不会叫
- 绿头鸭只会飞,不会叫
- 黄头鸭会飞,也会发出咕咕的叫声
- 黑头鸭会飞,也会发出吱吱的叫声
下面是具体的代码实现:
/**
* 鸭子的父类
*/
public abstract class Duck {
QuackBehavior quackBehavior;//负责叫的行为接口
FlyBeahavior flyBeahavior;//负责飞的行为接口
public Duck() {
}
public void performFly() {
flyBeahavior.fly();//委托给具体 行为
}
public void performQuack() {
quackBehavior.quack();//鸭子对象不用亲自处理咕咕叫的行为,而是委托给quackBehavior引用的对象
}
public void swim() {
//因为所有的鸭子都会游泳,所以不用单独抽象出来一种行为
//这种设计就是策略模式,将变化的地方封装为接口
System.out.println("所有的鸭子都会游泳,即使是模型鸭");
}
public void display() {
}
}
/**
* 飞这一动作的接口
*/
public interface FlyBeahavior {
void fly();
}
飞又有两种行为即会飞、不会飞,所以飞行行为有两个实现类会飞和不会飞
/**
* 会飞
*/
public class FlyWIthWings implements FlyBeahavior {
@Override
public void fly() {
System.out.println("我会飞");
}
}
/**
* 不会飞
*/
public class FlyWithNone implements FlyBeahavior {
@Override
public void fly() {
System.out.println("我不会飞");
}
}
/**
* 叫这一行为的接口
*/
public interface QuackBehavior {
void quack();
}
叫分为咕咕叫、吱吱叫、不会叫
/**
* 会叫
*/
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("我会咕咕的叫");
}
}
/**
* 会吱吱叫
*/
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("我会吱吱的叫");
}
}
/**
* 不会叫
*/
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("我不会叫");
}
}
下面我们实现黄头鸭–会飞,也会咕咕的叫
/**
* 黄头鸭--会飞也会咕咕的叫
*/
public class YellowDuck extends Duck {
public YellowDuck() {//在此处实例化会飞也会叫的具体行为
quackBehavior = new Quack();
flyBeahavior = new FlyWIthWings();
}
public void display() {
System.out.println("我是黄头鸭");
}
}
我们测试运行一下试试:
/**
* 运行主类
*/
public class MainClass {
public static void main(String[] args) {
Duck yellowDuck = new YellowDuck();
yellowDuck.performFly();
yellowDuck.performQuack();
}
}
至此我们实现了通过接口来实现变化地方的封装,但是在鸭子里建立了一堆动态功能还未用到,这样的话就太可惜了,所以我们通过设定方法来设定鸭子的具体行为,由此引出了动态设定行为
/**动态设定**/
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public void setFlyBeahavior(FlyBeahavior flyBeahavior) {
this.flyBeahavior = flyBeahavior;
}
最终Duck类如下:
/**
* 鸭子的父类
*/
public abstract class Duck {
QuackBehavior quackBehavior;//负责叫
FlyBeahavior flyBeahavior;//负责飞
public Duck() {
}
public void performFly() {
flyBeahavior.fly();
}
public void performQuack() {
quackBehavior.quack();//鸭子对象不用亲自处理咕咕叫的行为,而是委托给quackBehavior引用的对象
}
public void swim() {
System.out.println("所有的鸭子都会游泳,即使是橡皮鸭");
}
public void display() {
}
/**动态设定**/
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public void setFlyBeahavior(FlyBeahavior flyBeahavior) {
this.flyBeahavior = flyBeahavior;
}
}
/**
* 模型鸭
*/
public class ModelDuck extends Duck {
@Override
public void display() {
System.out.println("我是一只模型鸭子");
}
public ModelDuck() {
flyBeahavior = new FlyWithNone();
quackBehavior = new MuteQuack();
}
}
此时这只鸭子还不会飞,不过我们动态的给他设置一些行为,比如我们给这个模型加一个火箭的飞行动力
/**
* 拥有火箭般的飞行动力
*/
public class FlyRocketPowered implements FlyBeahavior {
@Override
public void fly() {
System.out.println("我拥有火箭一般的飞行动力");
}
}
我们测试一下
/**
* 运行主类
*/
public class MainClass {
public static void main(String[] args) {
// Duck yellowDuck = new YellowDuck();
// yellowDuck.display();
// yellowDuck.performFly();
// yellowDuck.performQuack();
Duck model=new ModelDuck();
model.display();
model.performFly();
//接下来动态的设定火箭动力
model.setFlyBeahavior(new FlyRocketPowered());
model.performFly();
}
}
通过这个运行结果我们可以看出我们如果想动态的改变鸭子的行为,只需调用set方法即可
而且 策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户
下一篇:《设计模式02——观察者模式》