java策略设计模式

三思方举步,百折不回头      2022-04-30     392

关键词:

1、概述

     策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。

    其实不要被晦涩难懂的定义所迷惑,策略设计模式实际上就是定义一个接口,只要实现该接口,并对接口的方法进行实现,那么不同的实现类就完成了不同的算法逻辑,而使用该接口的地方,则可以根据需要随意更改实现类,因为它们的接口一样额。

    因此策略设计模式有三个角色:

        抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口实现。此角色给出所有的具体策略类所需的接口。

        具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

        策略上下文(Context)角色:持有一个Strategy的引用,并且在某处调用了算法。

    注意:算法定义其实就是接口的方法,算法具体逻辑就是接口实现类中对方法的具体实现。 


2、代码示例

    首先定义一个算法接口。Strategy接口的run方法就是一种算法的定义。

package com.yefengyu.pattern.strategy;

public interface Strategy
{
    String run();
}

    其次,编写两个类实现Strategy接口,每个实现类都重写run方法(下面只是简单的返回)。实际上不同实现类的run方法是某种算法(业务领域)的不同实现。

package com.yefengyu.pattern.strategy;

public class ConcreteStrategyOne implements Strategy
{
    @Override
    public String run()
    {
       //此处只是简单的返回
        return "ConcreteStrategy  One 实现的算法";
    }
}
package com.yefengyu.pattern.strategy;

public class ConcreteStrategyTwo implements Strategy
{
    @Override
    public String run()
    {
       //此处只是简单的返回
        return "ConcreteStrategy  Two 实现的算法";
    }
}

    接着编写一个策略上下文角色,它持有Strategy接口。下面的StrategyContext类就是策略上下文角色,它有一个Strategy类型的属性,并且通过构造函数传入(也可以是其它方式)。在StrategyContext中有一个execute方法,该方法我们想象它是很复杂的,在其中某一步使用到了策略算法。

package com.yefengyu.pattern.strategy;

public class StrategyContext
{
    private Strategy strategy;

    public StrategyContext(Strategy strategy)
    {
        this.strategy = strategy;
    }

    public void execute()
    {
       //StrategyContext 的 execute 有许多事情要做
        System.out.println("------");

       //天啊,终于执行到算法这里了
        String result = strategy.run();
       System.out.println(result);

       //后续还有好多操作。。。
        System.out.println("======");
    }
}

    编写客户端来演示:生成两个不同的Strategy的实现类的对象,分别通过构造函数传入到策略上下文角色中,StrategyContext通过执行execute就调用到具体的算法(run)。

package com.yefengyu.pattern.strategy;

public class Client
{
    public static void main(String[] args)
    {
        Strategy strategy = new ConcreteStrategyOne();
        StrategyContext strategyContext = new StrategyContext(strategy);
        strategyContext.execute();

        strategy = new ConcreteStrategyTwo();
        strategyContext = new StrategyContext(strategy);
        strategyContext.execute();
    }
}

   上面的代码中,StrategyContext通过构造函数传入不同的实现类,即可在其execute方法中调用不同的算法。


3、JDK中的策略设计模式-ThreadPoolExecutor

    ThreadPoolExecutor使用方式如下:首先定义一个ThreadPoolExecutor对象,通过调用execute方法来执行,注意execute方法需要传入Runnable。

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());

executor.execute(new Runnable()
{
    @Override
    public void run()
    {
        System.out.println("hello ThreadPoolExecutor");
    }
});
    找到ThreadPoolExecutor的源码,来看看ThreadPoolExecutor是如何使用策略设计模式的。ThreadPoolExecutor类有很多构造方法,但是完成具体功能的是如下构造器:
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
     构造器的其它内容无需关注。只需要注意构造器的最后一个参数和代码的最后一行,就可以明白ThreadPoolExecutor有一个RejectedExecutionHandler 类型的成员变量 handler,而构造函数则为其赋值。
    线程池一般先new一个ThreadPoolExecutor对象,然后调用execute方法执行任务,因此我们看看execute方法的实现。
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
    上面的代码无需仔细看,注意代码中多次调用了reject方法,该方法源码如下,实际就是调用了ThreadPoolExecutor类中RejectedExecutionHandler类型的成员变量handler的方法。
final void reject(Runnable command)
{
    handler.rejectedExecution(command, this);
}
    查看下RejectedExecutionHandler发现它是一个接口,只有一个方法。
package java.util.concurrent;

public interface RejectedExecutionHandler 
{
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

    有了接口,那么实现类在哪里呢?还是在ThreadPoolExecutor类中,只是使用了静态内部类的形式:

public static class CallerRunsPolicy implements RejectedExecutionHandler {

        public CallerRunsPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

    public static class AbortPolicy implements RejectedExecutionHandler {

        public AbortPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

    public static class DiscardPolicy implements RejectedExecutionHandler {

        public DiscardPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {

        public DiscardOldestPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

   经过上面的一系列源码探究,我终于要使出对比大法了。

    (a): RejectedExecutionHandler接口中的rejectedExecution就是策略模式中的算法,此处算法实际叫做拒绝策略:如果线程数量大于等于 maximumPoolSize,且 workQueue 已满,则使用拒绝策略处理新任务。

    (b): CallerRunsPolicy 、AbortPolicy 、AbortPolicy 、DiscardOldestPolicy 是拒绝策略算法的具体实现。和上面的演示示例相比,此处直接在策略上下文环境直接使用静态内部类的方式实现了抽象的策略,也就是接口中算法的定义。

    (c): ThreadPoolExecutor就是策略的上下文(Context)角色。它持有一个RejectedExecutionHandler的引用,使用构造函数对其赋值,其中有方法execute作为环境上下文的主要入口,并且调用了算法。

    (d): 客户端:见本节最上面的代码,在new ThreadPoolExecutor对象的时候可以传入拒绝策略,在使用ThreadPoolExecutor时可以通过传入不同的拒绝策略来到达不同的效果,正是策略模式期望的效果。


4、JDK中的策略设计模式-Thread与Runnable

     特别注意:在https://www.cnblogs.com/yefengyu/p/10520531.html文章中,我讲解了Thread是模板设计模式的一种实现,那里前提是使用Thread方式创建线程,而不是使用Runnable方式。

    通过上面线程池的对比试验之后,这次分析换一种思路,我们现在把模板设计模式往Thread和Runnable身上套。

   (a): 策略算法、也就是抽象策略角色:Runnable接口及其方法run方法。

@FunctionalInterface
public interface Runnable 
{
    public abstract void run();
}

    (b): 算法具体实现                   

    一般由程序员创建一个类实现Runnable接口,重写run方法。

package com.yefengyu.pattern.strategy;

public class MyRunnable implements Runnable
{
    @Override
    public void run()
    {
        System.out.println("hello Runnable ");
    }
}

    (c): 策略上下文  

    Thread类持有Runnable接口,并且通过构造函数传入。start方法中隐式调用了Thread类的run方法。

    持有抽象策略接口

private Runnable target;

     通过构造函数为抽象策略接口赋值

public Thread(Runnable target)
{
     init(null, target, "Thread-" + nextThreadNum(), 0);
}

     策略上下文一般都有一个主要的方法入口,在方法的某一步调用了算法。

public synchronized void start() {

    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}

private native void start0(); 
 

   start方法是主要的方法入口,它调用了start0方法,而start0是本地方法,最后还是调用了Thread的run方法(细节自行分析),而Thread的run方法调用了Runnable的run方法。也就是最终调用到了算法。

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

   (d): 客户端使用                            

     public Thread(Runnable target)  使用构造方法传入,调用start方法启动线程。

Thread thread = new Thread(new MyRunnable());

thread.start();

    通过上面的分析,我们平常使用的线程也使用了策略设计模式,只是这里的策略具体实现交由程序员实现,JDK为我们提供了策略接口、策略上下文等。


5、总结

    策略设计模式一般实现步骤如下:

    a、编写策略接口

    b、编写策略实现类

    c、编写策略执行上下文,一般这个类持有一个策略接口属性,通过构造函数为其赋值,并且该类有一个主要的入口函数,该函数有很多个操作步骤,其中某一个步骤要使用算法(也就是接口方法)。

    d、客户端在使用的时候,首先使用策略执行上下文这个类的构造函数传入策略实现类,接着调用策略执行上下文这个类的主要入口函数,就可以执行到算法。通过构造函数传入不同的策略实现类,就可以更换程序的算法逻辑。

    策略设计模式缺点就是客户端需要知道所有的策略实现类。

 

java设计模式之策略模式

    策略模式属于对象的行为模式,策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化。策略模式使这些算法在客户端调用它们... 查看详情

java设计模式之策略模式

...此模式让算法的变化,不会影响到使用算法的客户(大话设计模式)。策略模式UML图      策略模式代码   古代的各种计谋都是一种策略,这次我们的例子就拿其中一种离间计来写的,理解起来非常容易,代码... 查看详情

java-设计模式-策略模式

策略模式其实就是对各种行为算法的包装,各种不同的行为的算法的分离。属于对象的行为模式。650)this.width=650;"src="http://www.jasongj.com/img/designpattern/strategy/Strategy.png"alt="StrategyPatternClassDiagram"/>策略模式类图,比较简单,没有自己... 查看详情

java设计模式之策略模式

1.策略模式的介绍  策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。简单的... 查看详情

java设计模式—策略模式

定义    通过选择策略类,来执行不同算法分支。核心是通过注入对象,改变行为。此模式其实就是springIOC思想。优点   1.算法可以自由的切换。   2.避免使用多重条件判断。   3.便于扩展:增加一个策略只需... 查看详情

java设计模式—策略模式

定义    通过选择策略类,来执行不同算法分支。核心是通过注入对象,改变行为。此模式其实就是springIOC思想。优点   1.算法可以自由的切换。   2.避免使用多重条件判断。   3.便于扩展:增加一个策略只需... 查看详情

java策略模式

策略模式策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。这个模式涉及到三个... 查看详情

java策略模式,comparator

package 设计模式.策略模式;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.Iterator;import java.util.List;/** * @dep 查看详情

java描述设计模式(22):策略模式

本文源码:GitHub·点这里||GitEE·点这里一、生活场景每年双十一,各大电商平台会推出不同的满减策略,当用户的消费金额满一定额度后,会进行减去一定的优惠额度,从而来一波清仓甩卖,使用策略模式来描述该流程。publicclassC01_I... 查看详情

java设计模式7.策略模式模板方法模式观察者模式

策略模式策略模式的用意,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。环境角色:持有一个抽象策略角色的引用。抽象策略角色:... 查看详情

java策略模式(代码片段)

查看详情

java之策略模式(大话设计模式)

温故而知新,每次读设计模式都会有不同的体验,坚持每天一个设计模式,直到熟练运用设计模式。策略模式定义了很多完成相同工作的算法,但实现不同,它可以以相同的方式调用所有的算法,减少了算法和调用算法的耦合。... 查看详情

策略设计模式详解c/java/js/go/python/ts不同语言实现

简介策略模式(StrategyPattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。当你想使用对象中各种不同的算法变体,使用if...else所带来的复杂和难以维护... 查看详情

java设计模式—策略模式

定义    通过选择策略类,来执行不同算法分支。核心是通过注入对象,改变行为。此模式其实就是springIOC思想。优点   1.算法可以自由的切换。   2.避免使用多重条件判断。   3.便于扩展:增加一个策略只需... 查看详情

java设计模式-策略模式

  第一次好好的看设计模式,可能有不对的地方,大佬们可以下面指出,感谢!  -----------分割线----------  场景:商城打折活动,但是我不确定是打几折,因为换季可能打折力度不一样,8折,9折,满300减50等等等等,这... 查看详情

java策略设计模式

...sp;  实际上不要被晦涩难懂的定义迷惑,其实策略设计模式就是定义一个接口,那么只要实现该接口的类,对这些方法进行实现,那么不同的实现类就完成了不同的算法逻辑,而使用该接口的地方,可以根据需要随意更改... 查看详情

java设计模式1-策略模式

策略模式:分别封装行为接口,实现算法族,超类里放行为的接口对象,在子类里具体设定行为对象.原则就是:分离变化部分,封装接口,基于接口编程各种功能.为什么要用策略模式{继承带来的问题:(为策略模式)对类的局部改动,尤其是... 查看详情

策略模式—java实现(转)

 1.现实需求客户有了新的需求,这时我们直接新增策略即可,改很少的代码。基本符合我们面向对象原则中的开闭原则(对扩展开放,对修改关系),实现了高内聚低耦合。2.策略模式定义策略模式,又叫算法簇模式,就是... 查看详情