在引入策略模式之前. 先用一个小项目解释为什么要有策略模式. 这是一个模拟鸭子的小游戏. 最开始需求为, 所有的鸭子都会叫以及都会游泳且叫声游泳姿势都一样. 因此我们用原始 OO 的思维去编写代码时很容易做到
public abstract class Duck{public void yuck(){
System.out.println("嘎嘎叫");
}
public void swim(){
Systrm.out.println("i am swim");
}
public abstract void display();
}
也就是封装一个超类. 然后设置两个子类通用的方法, 叫和游泳. 此时再编写程序需要的各种不同的鸭子子类. 他们都能够叫和游泳. 此时编写一个测试类.
public class Test{
public static void main(String[] args){
Duck gDuck = new GreenDuck();
Duck rDuck = new RedDuck();
gDuck.display();
gDuck.yuck();
gDuck.swim();
rDuck.display();
rDuck.yuck();
rDuck.swim();
}
}
public class GreenHeadDuck extends Duck{
@override
public void display(){
System.out.println("绿头鸭");
}
}
public class RedHeadDuck extends Duck{
@override
public void display(){
System.out.println("红头鸭");
}
}
测试类的结果为: 绿头鸭 嘎嘎叫 i am swim 红头鸭 嘎嘎叫 i am swim. 这个完全没有问题. 好的, 现在游戏上线, 市场火热, 新需求来了. 鸭子要会飞. 那我们继续用 OO 的思维去想,
那么可以给超类 Duck 中设置一个通用的 fly 方法. 让所有的鸭子都会飞. 让我们把 Duck 的代码改一下. 改成下面的
public abstract class Duck {
public void yuck() {
System.out.println("嘎嘎叫");
}
public void swim() {
Systrm.out.println("i am swim");
}
public abstract void display();
public void fly() {
System.out.println("i am flying");
}
}
好的, 现在的确实现了鸭子会飞的功能, 但是这也引起了一些问题. 因为事实上不是所有的鸭子都会飞. 你在超类中定义了方法. 影响的是全部的子类. 容易导致溢出的问题, 也就是. 有些子类不需要. 但是你给它强制加了. 好, 那如果现在有些子类不会飞. 我们继续用 OO 的思维去解决. 那么可以在不会飞的子类中用重写去覆盖掉父类给他的方法. 我们加入绿头鸭不会飞, 我们修改以下绿头鸭的代码如下
public class GreenHeadDuck extends Duck{
@override
public void display(){
System.out.println("绿头鸭");
}
@override
public void fly(){
System.out.println("i can't fly")
}
}
好, 可以通过在子类中覆盖来实现有些鸭子会费飞有些不会飞这个需求. 但是在我们这个例子中鸭子种类少, 这么玩没有关系. 但如果由 2000 种鸭子呢. 难道你要一个一个的去覆盖子类中的方法.
很明显就能看出单纯的面向对象, 也就是 OO 的思想在设计这个需求的时候并不完善. 那也有些人说, 那我不在超类 Duck 中定义 fly 的方法. 我在子类中去定义, 谁会飞我就给谁定义. 这其实和上面一样. 如果子类基数大. 那么你写的重复代码也会很多. 那么 OO 中的代码复用就完全没有体现出来了.
因此, 我们分析以下上面 OO 设计的问题所在. 为什么会说这个设计不好. 原因是在于它的行为和客户类没有分离. 也就是 "干什么" 和 "怎么干" 没有分离开来. 例如上面的例子. 客户类中的 fly 是干什么, 但是怎么干, fly 里面怎么定义代码, 他们没有分离. 全部堆在客户类中, 耦合度太高. 导致, 你的 "怎么干" 放在客户类中定义, 无论你放在超类还是子类. 都容易出问题, 因此就导出了策略模式这个概念
策略模式是干嘛的, 是去耦合的. 它所做的就是把 "干什么" 和 "怎么干" 分离开来. 把 "怎么干" 封装到一个策略接口或者抽象类中. 然后编写一系列的怎么干算法封装起来. 客户类中只需要调用这个算法就 ok 了
策略模式的两种标准点的定义:
图视:
环境对象 (Context): 该类中实现了对抽象策略中定义的接口或者抽象类的引用.
抽象策略对象 (Strategy): 它可由接口或抽象类来实现.
具体策略对象 (ConcreteStrategy): 它封装了实现同不功能的不同算法.
策略模式定义了一系列的算法, 并将每一个算法封装起来, 而且使它们还可以相互替换, 策略模式让算法独立于使用它的客户而独立变化. 使这些算法在客户端调用它们的时候能够互不影响地变化.
策略模式: 分别封装行为接口, 实现算法族, 超类中放行为接口对象. 在子类中的构造方法中去实例化行为对象. 原则就是: 分离变化部分, 封装接口, 基于接口编程各种功能族, 让行为算法的变化独立于算法的使用者.
使用策略模式的步骤: 1. 分析变化的行为, 拿出来进行封装成一个策略接口或者抽象类. 然后定义该行为的多种实现算法. 形成算法族 2. 在客户超类中放策略接口. 3. 在子类的构造方法中去具体实例化具体的策略算法
下面让我们看看用策略模式修改模拟鸭子的这个项目.
1. 封装变化的部分. 当前鸭子的变化行为为 fly. 有些会飞, 有些不会飞. 有些会飞的好, 有些飞的不好. 因此封装 fly 这个行为为一个策略接口. FlyObject
public interface FlyObject {
void fly();
}
接下来, 编写项目中实现 fly 这个行为的多种算法. 例如不会飞 nofly, 飞的好, good fly, 飞得不好 badfly. 这三个行为族
public class NoFly implements FlyObject{
@override
public void fly(){
System.out.println("不会飞")
}
}
public class GoodFly implements FlyObject{
@override
public void fly(){
System.out.println("Good fly");
}
}
public class BadFly implements FlyObject{
@override
public void fly(){
System.out.println("bad fly");
}
}
之后在客户类的超类也就是 Duck 中放置策略接口, 也就是 FlyObject.
package com.lyh.strategy.duck;
import com.lyh.strategy.flybehavior.FlyBehavior;
import com.lyh.strategy.yuckbehavior.YuckBehavior;
public abstract class Duck {
FlyBehavior flybehavior;
YuckBehavior yuckbehavior;
public Duck() {};
public void fly() {
flybehavior.fly();
}
public void yuck() {
yuckbehavior.yuck();
}
public abstract void display();
public void setFlyBehavior(FlyBehavior fb) {
this.flybehavior = fb;
}
public void setYuckBehavior(YuckBehavior yb) {
this.yuckbehavior = yb;
}
// 这个方法子类通用, 不会变化
public void swim() {
System.out.println("i am swim");
}
}
在客户类的子类中去实例化具体的策略接口算法.
package com.lyh.strategy.duck;
import com.lyh.strategy.flybehavior.GoodFlyBehavior;
import com.lyh.strategy.yuckbehavior.GaGaYuckBehavior;
public class GreenHeadDuck extends Duck {
public GreenHeadDuck() {
flybehavior = new GoodFlyBehavior();
yuckbehavior = new GaGaYuckBehavior();
}
@Override
public void display() {
System.out.println("绿头鸭");
}
}
package com.lyh.strategy.duck;
import com.lyh.strategy.flybehavior.NoFlyBehavior;
import com.lyh.strategy.yuckbehavior.NoYuckBehavior;
public class RedHeadDuck extends Duck {
public RedHeadDuck() {
flybehavior = new NoFlyBehavior();
yuckbehavior = new NoYuckBehavior();
}
@Override
public void display() {
System.out.println("红头鸭");
}
}
因此, 通过这种方式, 能够实现客户类中的行为和客户类相分离. 把变化的行为独立出来. 抽象成接口. 这样如果有新的行为需求, 可以直接在行为族中加入. 如果有新的客户类对象. 也可以让他们
具体选择的去实现行为族. 达到降低耦合度的目的.
但是, 策略模式也有缺点. 也就是客户类必须了解策略行为族中的每一个算法的实现以及原理. 只有全部知晓. 客户类才能去区别调用他们而不会出错. 这时策略模式必须要做到的一点.
我们什么时候使用策略模式: 这个没有具体的需求. 一般而言. 如果你定义的抽象父类中的一些方法是可变的. 子类中不一定需要这些方法的时候. 建议用策略模式将变化的行为封装起来. 这其实也是一种方法的封装. 你将行为封装, 那么其他的类也可以去使用.
来源: https://www.cnblogs.com/exceptionblog/p/8372941.html