java设计模式之策略模式

1、策略模式的定义

策略模式是一种行为型模式,它将对象和行为分开,将行为定义为一个行为接口和多个具体行为的实现(即多个策略)。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化。

2、策略模式的角色

Strategy: 策略接口,该接口定义若干个算法标识,即定义了若干个抽象方法。

ConcreteStrategy: 具体策略类,是实现策略接口的类,重写策略接口所定义的抽象方法。

Context: 环境类或上下文类,上下文是依赖于策略接口的类,也就是持有策略接口的引用,如下图的strategy成员变量。上下文需要提供一个方法,最终给客户端调用,在该方法中调用策略接口中定义的抽象方法,实际是调用具体策略类中重写的方法。

3、策略模式的优缺点

1)优点

1.策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

2.策略模式提供了管理相关的算法族的办法。

3.策略模式提供了可以替换继承关系的办法。

4.使用策略模式可以避免使用多重条件转移语句。

2)缺点

1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2.策略模式将产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

4、策略模式适用场景

在以下情况下可以使用策略模式:

1.如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

2.一个系统需要动态地在几种算法中选择一种。

3.如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

4.不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

在我们生活中比较常见的应用模式有:

1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式

2、电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式

5、使用策略模式的代码样例一

需求:编写两种算法,可以根据身高或体重进行排序。

策略1:根据身高从高到底排序。

策略2:根据体重从重到轻排序。

策略接口:

package com.design.mode.strategy.strategy01;

/**
 * @Author: 倚天照海
 * @Description: 抽象策略接口
 */
public interface CustomizedComparator<T> {

    int compare(T o1, T o2);

}

具体策略实现类,此处定义了两种策略,即两个策略实现类:

package com.design.mode.strategy.strategy01;

/**
 * @Author: 倚天照海
 * @Description: 具体策略类
 */
public class HeightComparator implements CustomizedComparator<Person> {

    /**
     * 根据身高从高到底排序
     * @param o1 待排序的新元素
     * @param o2 已排好序的旧元素
     * @return -1:交换o1和o2的位置,0和1:不交换o1和o2的位置
     */
    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getHeight() > o2.getHeight()){
            return -1;
        } else if (o1.getHeight() < o2.getHeight()){
            return 1;
        } else {
            return 0;
        }
        //return o2.getHeight().compareTo(o1.getHeight());
    }
}


package com.design.mode.strategy.strategy01;

/**
 * @Author: 倚天照海
 * @Description: 具体策略类
 */
public class WeightComparator implements CustomizedComparator<Person> {

    /**
     * 根据体重从重到轻排序
     * @param o1 待排序的新元素
     * @param o2 已排好序的旧元素
     * @return -1:交换o1和o2的位置,0和1:不交换o1和o2的位置
     */
    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getWeight() > o2.getWeight()){
            return -1;
        } else if (o1.getWeight() < o2.getWeight()){
            return 1;
        } else {
            return 0;
        }
        //return o2.getWeight().compareTo(o1.getWeight());
    }

}

上下文类或环境类:

package com.design.mode.strategy.strategy01;

/**
 * @Author: 倚天照海
 * @Description: 上下文环境
 * 与具体策略类交互,调用具体策略类方法
 * 这种写法的优缺点:
 * 优点:利于扩展,如果后期需要新增一个比较策略,在客户端创建新增的具体策略对象,
 * 作为参数传递给此处的上下文SortContext构造器即可。
 * 缺点:客户端与具体策略耦合,需要在客户端创建具体策略对象,作为参数传递给SortContext构造
 * 方法。另外,客户端需要知道所有的具体策略,并根据需求判断使用哪一个策略。
 */
public class SortContext<T> {

    private CustomizedComparator<T> comparator;

    public SortContext(CustomizedComparator<T> comparator) {
        this.comparator = comparator;
    }

    /**
     * 选择排序
     */
    public void sort(T[] a){
        for (int i = 0; i < a.length-1; i++) {
            int minIdx = i;
            for (int j = i+1; j < a.length; j++) {
                if (this.comparator.compare(a[j],a[j-1]) == -1){
                    minIdx = j;
                }
                swap(a,i,minIdx);
            }
        }
    }

    private void swap(T[] a, int i, int j){
        T temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

测试类:

package com.design.mode.strategy.strategy01;

import java.util.Arrays;

public class StrategyTest01 {

    public static void main(String[] args) {
        Person[] p = {new Person(10.0,20.0),new Person(15.0,15.0),new Person(20.0,10.0)};
        //根据实际需求,使用具体某个策略
        //CustomizedComparator<Person> comparator = new HeightComparator();
        CustomizedComparator<Person> comparator = new WeightComparator();
        SortContext<Person> context = new SortContext<Person>(comparator);
        context.sort(p);
        System.out.println(Arrays.toString(p));
    }

}

其中用到的实体类:

package com.design.mode.strategy.strategy01;

public class Person {

    private Double height;

    private Double weight;

    public Person() {
    }

    public Person(Double height, Double weight) {
        this.height = height;
        this.weight = weight;
    }

    public Double getHeight() {
        return height;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public Double getWeight() {
        return weight;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Person{" +
                "height=" + height +
                ", weight=" + weight +
                '}';
    }
}

6、使用策略模式的代码样例二

需求:模拟商品满减活动,有三种不同的策略:

1.不满100元,按照商品原价支付。

2.满100元,减10元。

3.满200元,减30元。

策略接口:

package com.design.mode.strategy.strategy02;

/**
 * @Author: 倚天照海
 * @Description: 抽象策略接口
 * 模拟商品满减活动,有三种不同的策略:
 * 1.不满100元,按照商品原价
 * 2.满100元,减10元
 * 3.满200元,减30元
 */
public interface ManJian {

    Integer minus(Goods goods);

}

三种具体的策略类:

package com.design.mode.strategy.strategy02;

/**
 * @Author: 倚天照海
 * @Description: 具体策略类
 */
public class DefaultManJian implements ManJian {

    @Override
    public Integer minus(Goods goods) {
        //todo 按照商品原价付款
        return goods.getPrice() * goods.getNumber();
    }
}
package com.design.mode.strategy.strategy02;

/**
 * @Author: 倚天照海
 * @Description: 具体策略类
 */
public class Man100Jian10 implements ManJian {

    @Override
    public Integer minus(Goods goods) {
        return goods.getPrice()*goods.getNumber() - 10;
    }

}
package com.design.mode.strategy.strategy02;

/**
 * @Author: 倚天照海
 * @Description: 具体策略类
 */
public class Man200Jian30 implements ManJian {

    @Override
    public Integer minus(Goods goods) {
        return goods.getPrice() * goods.getNumber() - 30;
    }

}

上下文类:

package com.design.mode.strategy.strategy02;

/**
 * @Author: 倚天照海
 * @Description: 上下文环境
 * 与具体策略类交互,调用具体策略类方法
 */
public class ManJianContext {

    private ManJian manJian;

    public ManJianContext() {
    }

    public ManJianContext(ManJian manJian){
        this.manJian = manJian;
    }

    /**
     * 写法一,这种写法的优缺点:
     * 优点:类似于简单工厂模式,使得客户端与具体策略类隔离,上下文与具体策略类交互
     * 缺点:不利于扩展,如果后期新增一种满减策略,例如满500减100,
     * 就需要修改此处上下文的代码,违反了开闭原则。
     */
    public Integer fuKuan(Goods goods){
        int price = goods.getPrice() * goods.getNumber();
        if (price < 100){
            this.manJian = new DefaultManJian();
        } else if (price < 200){
            this.manJian = new Man100Jian10();
        } else {
            this.manJian = new Man200Jian30();
        }
        return this.manJian.minus(goods);
    }

    /**
     * 写法二,这种写法的优缺点:
     * 优点:利于扩展,由客户端创建具体的策略对象,通过参数传递即可。
     * 缺点:客户端需要知道所有的策略,客户端与具体策略对象之间耦合度较高。
     */
    public Integer fuKuan(ManJian manJian, Goods goods){
        return manJian.minus(goods);
    }

}

测试类:

package com.design.mode.strategy.strategy02;

public class StrategyTest02 {

    public static void main(String[] args) {
        //客户端无需知道有哪些具体的策略,而是通过上下文与具体策略交互
        Goods goods = new Goods(50,1);
        //Goods goods = new Goods(50,3);
        //Goods goods = new Goods(50,4);
        ManJianContext context = new ManJianContext();
        Integer money = context.fuKuan(goods);
        System.out.println("购买商品付款金额======" + money);
    }
    
}

其中用到的实体类:

package com.design.mode.strategy.strategy02;

public class Goods {
    /**
     * 商品单价
     */
    private Integer price;
    /**
     * 商品数量
     */
    private Integer number;

    public Goods() {
    }

    public Goods(Integer price, Integer number) {
        this.price = price;
        this.number = number;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "price=" + price +
                ", number=" + number +
                '}';
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值