策略模式

策略模式

​ 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改,我们创建表示各种策略的对象和运算规则随着策略对象的改变而改变。策略模式把对象本身和运算规则进行了分离。

一、使用场景

​ 我们知道 Java 中实现线程执行单元的方式另一种方式是实现 Runnable 接口,并重写其中的 run 方法,然后将对应的 Runnable 接口实现类作为 Thread 类构造方法的参数进行 Thread 的对象创建。这里其实就使用了策略模式。

Thread 部分源码:

public
class Thread implements Runnable {  
    ...
	/* What will be run. */
    private Runnable target;
    ...
        
    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
        
    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

我们可以看到 Thread 不仅实现类 Runnable 接口,同时还维护了一个 Runnable 属性,在重写的 run 方法中进行了一个判断,如果这个属性不为空我们就调用对应这个属性的 run 方法。

二、简单示例

​ 这里使用一个简单示例来演示,因为示例可以更侧重于对设计模式的体现,而实际使用场景不止需要考虑策略模式本身可能看着就有点疑惑。

首先我们定义一个策略接口

​ 对标 Runnable 接口中的 run 方法,对于 run 则是表示线程所需要执行的方法(可以理解为线程执行策略的一种抽象)

package strategypattern;

/**
 * 旅行策略接口
 */
public interface TravelStrategy {

    // 表示我们具体的旅行方式
    void travelAlgorithm ();
}
然后我们定义具体的策略

​ 这里就相当于你自己定义了类实现了 Runnable 接口并重写了 run 方法

package strategypattern;

/**
 * 表示坐汽车
 */
public class CarStrategy implements TravelStrategy{

    /**
     * 重写具体旅游策略
     */
    @Override
    public void travelAlgorithm() {
        System.out.println("坐汽车");
    }
}
package strategypattern;

/**
 * 表示坐高铁
 */
public class HighTrainStrategy implements TravelStrategy{

    /**
     * 重写具体旅游策略
     */
    @Override
    public void travelAlgorithm() {
        System.out.println("坐高铁");
    }
}
最后我们定义一个旅行者即使用这个策略的对象

​ 这个类就类似于 Thread 类

package strategypattern;

/**
 * 旅行者
 */
public class Traveler {

    /**
     * 首先在内部维护一个抽象策略接口对象
     */
    private TravelStrategy travelStrategy;

    public Traveler() { }

    /**
     * 这里我们使用构造方法给属性赋值,当然你也可以使用set方法
     * @param travelStrategy
     */
    public Traveler(TravelStrategy travelStrategy) {
        this.travelStrategy = travelStrategy;
    }

    /**
     * 这里我们旅行者的出行方式的方法
     */
    public void travelStyle () {
        travelStrategy.travelAlgorithm () ;
    }
}
测试
package strategypattern;

/**
 * @Package: strategypattern
 * @Author: yanjiali
 * @Created: 2024/8/26 22:04
 */
public class Main {
    public static void main(String[] args) {
        //比如我们想要使用汽车出行
        CarStrategy carStrategy = new CarStrategy();
        Traveler traveler = new Traveler(carStrategy);
        traveler.travelStyle();
    }
}

//对应输出:
坐汽车

三、总结

优点:
我们之前在选择出行方式的时候,往往会使用 if-else 语句,也就是用户不选择 A 那么就选择 B 这样的一种情况。这种情况耦合性太高了,而且代码臃肿,有了策略模式我们就可以避免这种现象;
策略模式遵循开闭原则,实现代码的解耦合。扩展新的方法时也比较方便,只需要继承策略接口就好了。

缺点:
客户端必须知道所有的策略类,并自行决定使用哪一个策略类;
策略模式会出现很多的策略类;
客户端在使用这些策略类的时候,这些策略类由于继承了策略接口,所以有些数据可能用不到,但是依然初始化了。

​ 简单来说,策略模式实现了具体策略和执行策略者的分离,使得我们在后续扩展时比直接写 if-else 要好,当新的具体策略需求到来时我们可以编写对应的策略实现类,而不需要对已有的代码进行修改。

posted @ 2024-08-27 09:19  如此而已~~~  阅读(39)  评论(0)    收藏  举报
//雪花飘落效果