策略模式及其在Java中的典型应用
1、什么是策略模式
策略模式的定义:定义算法族,分别封装起来,让他们之间可以互相替换,此模式的变化独立于算法的使用者。在策略模式中,一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式。策略模式的类图示例如下:
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象。策略对象改变context对象的执行算法。
2、策略模式的特性
(1)意图:
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
(2)主要解决:
在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
(3)何时使用:
一个系统有许多许多类,而区分它们的只是他们直接的行为。
(4)如何解决:
将这些算法封装成一个一个的类,任意地替换。
(4)关键代码:
实现同一个接口。
3、策略模式的优缺点及应用场景
(1)优点:
1)算法可以自由切换。
2)避免使用多重条件判断。
3)扩展性良好。
(2)缺点:
1)策略类会增多。
2)所有策略类都需要对外暴露。
(3)使用场景:
1)如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2)一个系统需要动态地在几种算法中选择一种。
3)如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
(4)注意事项:
如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
4、策略模式的应用实例
假设场景:假设在开发一款僵尸大战的游戏,有两种僵尸:普通僵尸和旗手僵尸,初始时他们的方法都一样:一步一步走,咬。但是后续随着僵尸种类的增加,会有多种僵尸,而且他们的方法实现也不一样,考虑如下:
4.1、原始方法实现示例
以下为传统直接实现的方式实现上述场景,我们每次要增加一个僵尸类时,都需要继承或重写一个类,类与类之间相互耦合,代码也逐渐变得难以理解,维护起来很麻烦。
package designpatterns.strategy.v1;
/**
* 〈一句话功能简述〉<br>
* 〈僵尸大战〉
*
* @author Jianf
* @create 2020/8/18
* @since 1.0.0
*/
public class ZombieTest {
public static void main(String[] args) {
AbstractZombie normalZombie = new NormalZombie();
AbstractZombie flagZombie = new FlagZombie();
normalZombie.display();
normalZombie.move();
normalZombie.attack();
System.out.println("-----------------------------");
flagZombie.display();
flagZombie.move();
flagZombie.attack();
}
}
//僵尸抽象类
abstract class AbstractZombie{
public abstract void display();
public void attack(){
System.out.println("咬。");
}
public void move(){
System.out.println("一步一步移动。。");
}
}
//普通僵尸类
class NormalZombie extends AbstractZombie{
@Override
public void display() {
System.out.println("我是普通僵尸");
}
}
class FlagZombie extends AbstractZombie{
@Override
public void display() {
System.out.println("我是旗手僵尸。。");
}
}
//假设后来又多了一些种类的僵尸,他们的这些行为不全部一样
//大头僵尸
class BigHeadZombie extends AbstractZombie{
@Override
public void display() {
System.out.println("我是大头僵尸。");
}
@Override
public void attack() {
//...
System.out.println("头撞。");
}
}
class XxxZombie extends BigHeadZombie{
@Override
public void move() {
System.out.println("xxx...");
}
}
//按照上面的逻辑,假设增加很多种类的僵尸,每次都需要继承或重写另一个僵尸类以实现其功能,代码将变得复杂难读
4.2 使用策略模式实现示例:
下面示例为使用策略模式来实现:我们每次新增一个僵尸类时,重写方法定义它的表现和行为,也可以在运行时通过动态修改其行为对象来实现对其行为的修改。各个僵尸类之间不耦合,具体实现算法可以相互替换。
package designpatterns.strategy.v2;
/**
* 〈一句话功能简述〉<br>
* 〈策略模式〉
*
* @author Jianf
* @create 2020/8/18
* @since 1.0.0
*/
public class StrategyTest {
public static void main(String[] args) {
//测试示例
Zombie zombie = new NormalZombie();
zombie.display();
zombie.attack();
zombie.move();
//更换攻击方式:传递算法,使用算法。。
zombie.setAttackable(new HitAttack());
zombie.attack();
}
}
//将共同特征进行抽象
interface Moveable{
void move();
}
interface Attackable{
void attack();
}
//抽象僵尸
abstract class Zombie{
abstract public void display();
Moveable moveable;
Attackable attackable;
abstract void move();
abstract void attack();
public Zombie() {
}
public Zombie(Moveable moveable, Attackable attackable) {
this.moveable = moveable;
this.attackable = attackable;
}
public Moveable getMoveable() {
return moveable;
}
public void setMoveable(Moveable moveable) {
this.moveable = moveable;
}
public Attackable getAttackable() {
return attackable;
}
public void setAttackable(Attackable attackable) {
this.attackable = attackable;
}
}
//普通僵尸
class NormalZombie extends Zombie{
public NormalZombie() {
super(new StepByStepMove(),new BiteAttack());
}
public NormalZombie(Moveable moveable, Attackable attackable) {
super(moveable, attackable);
}
@Override
public void display() {
System.out.println("我是普通僵尸。。");
}
@Override
void move() {
moveable.move();
}
@Override
void attack() {
attackable.attack();
}
}
//普通僵尸的行为
class StepByStepMove implements Moveable{
@Override
public void move() {
System.out.println("一步一步移动...");
}
}
class BiteAttack implements Attackable{
@Override
public void attack() {
System.out.println("咬");
}
}
//其他的攻击行为
class HitAttack implements Attackable{
@Override
public void attack() {
System.out.println("打。。");
}
}
//旗手僵尸
class FlageZombie extends Zombie{
public FlageZombie() {
super(new StepByStepMove(),new BiteAttack());
}
public FlageZombie(Moveable moveable, Attackable attackable) {
super(moveable, attackable);
}
@Override
public void display() {
System.out.println("我是旗手僵尸。。");
}
@Override
void move() {
moveable.move();
}
@Override
void attack() {
attackable.attack();
}
5、策略模式在JDK/Spring框架中的经典应用
(1) JDK中的Arrays类的sort()方法可以动态地传入比较器,不同的比较器可以有不同的实现算法,通过传入不同的比较器实现算法的替换,而该变化独立于算法的使用者,该方法源码如下:
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
我们传入的比较器只需要继承自Comparator<T>接口并在其方法compare等中实现我们的比较逻辑即可。
(2) org.springframework.beans.factory.support.InstantiationStrategy接口,源码如下:
/**
* Interface responsible for creating instances corresponding to a root bean definition.
*
* <p>This is pulled out into a strategy as various approaches are possible,
* including using CGLIB to create subclasses on the fly to support Method Injection.
*
*/
public interface InstantiationStrategy {
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
throws BeansException;
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, Object... args) throws BeansException;
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, Method factoryMethod, Object... args)
throws BeansException;
}
spring中该接口负责创建对应于根bean定义实例。创建方法可能多种,包括使用CGLIB来动态创建子类,以支持方法注入==>该接口被定义成策略。
本文源代码:
https://github.com/JianfuYang/2020-yjf-review/tree/master/src/designpatterns/strategy
声明:本文部分内容整理来源于网络,仅做个人学习使用!侵删~
本文部分内容参考链接:
https://www.runoob.com/design-pattern/strategy-pattern.html