设计模式学习第二十二节 策略模式

概述

    策略模式(Strategy Pattern)基本介绍
    1、在策略模式中,定义算法族,分别封装起来。让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。是一种行为型模式。
    2、策略模式体现了几个设计原则:第一、把变化的代码从不变的代码中分离出来;第二、针对接口变成而不是具体的类(定义了策略接口);第三、多用组合/聚合,少用继承(客户端通过组合方式使用策略模式)。
    3、在策略模式中,定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类就是一种策略,为了保证这些策略使用时具有一致性,一般情况下一提供一个抽象的策略类来做规则定义,每种算法对应一个具体的算法类。
    4、策略模式的主要目的是将算法的定义与使用分开,也就是即哪个算法的行为和环境分开,将算法定义在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则,在出现新的算法类时,只需要增加一个新的具体策略类即可,
    类图描述
    在这里插入图片描述
    角色分析
    1、Context:环境类,使用算法的角色,它在实现某个方法时,可能使用多种策略,在环境类中聚合一个抽象策略类(拥有一个抽象策略类的成员变量),用于定义采用的策略。
    2、Strategy,抽象策略类,它为所支持的算法声明了抽象方法,是所有具体策略类的父类,环境类通过抽象策略中声明的方法,在运行时调用具体策略类中实现的算法。
    3、ConcreteStrategy,具体策略类,它实现在抽象策略类中声明的算法,在运行时,使用具体策略类中的某个方法实现业务逻辑的处理。

案例

    景区门票折扣方案,不同的用户给予不同的优惠策略,比如年费会员免费入场、学生半价入场、儿童半价入场,成人95折入场。

package com.example.pattern.strategy;

import lombok.Getter;
import lombok.Setter;

/**
 * 策略模式
 *
 * @author zjt
 * @date 2021-01-07
 */

interface Discount { // 抽象策略类
    double calculate(Double price);
}

@Getter
@Setter
public class Ticket { // 环境类 门票

    private double price;

    private Discount discount;

    public Ticket(double price) {
        this.price = price;
    }

    public double getPrice() {
        return discount.calculate(price);
    }

}


class StudentDiscount implements Discount { // 具体策略类 学生票

    @Override
    public double calculate(Double price) {
        return price * 0.5;
    }

}

class ChildDiscount implements Discount { // 具体策略类 儿童票

    @Override
    public double calculate(Double price) {
        return price * 0.5;
    }

}

class MemberDiscount implements Discount { // 具体策略类 会员票

    @Override
    public double calculate(Double price) {
        return 0;
    }

}

class AdultDiscount implements Discount { // 具体策略类 会员票

    @Override
    public double calculate(Double price) {
        return price * 0.95;
    }

}

class Client {
    public static void main(String[] args) {
        double price = 100;
        Ticket ticket = new Ticket(price);
        System.out.println("原价为:" + price);
        ticket.setDiscount(new AdultDiscount());
        price = ticket.getPrice();
        System.out.println("原价为:" + price);
    }
}

应用分析

public static void main(String[] args) {
        Integer[] arr = {1, 4, 2, 3};
        // 实现升序排序 反回-1放左边,返回1放右边
        // 说明
        // 1、实现了Comparator接口(类似与策略对象) 匿名类对象
        // 2、函数式接口实现 new Comparator<Integer>() {} 就是实现了策略接口的对象
        // 3、public int compare(Integer o1, Integer o2) {} 指定具体的处理方式
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                if (o1 > o2) {
                    return 1;
                } else {
                    return -1;
                }
            }
        };
        Arrays.sort(arr, comparator);
        // 调用方法的源码
        //public static <T> void sort(T[] a, Comparator<? super T> c) {
        //        if (c == null) {
        //            sort(a); // 默认方法
        //        } else {
        //            if (LegacyMergeSort.userRequested)
        //                legacyMergeSort(a, c); // 使用策略对象
        //            else
        //                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        //        }
        //    }
        System.out.println(Arrays.toString(arr));
        // 实现方式2 lambda 表达式 实现策略模式
        Arrays.sort(arr, (o1, o2) -> {
            if (o1 < o2) {
                return 1;
            } else {
                return -1;
            }
        });
        System.out.println(Arrays.toString(arr));
    }

题外话 Arrays.asList()方法不能add的小坑

    提到了Arrays工具类,前几天我在单元测试的时候发现了个问题,Arrays.asList()增加元素报错,顺便多提一点关于Arrays.asList()方法使用小坑,先看一段代码

    public static void main(String[] args) {
        Integer[] irr = {1, 2, 3, 4};
        List<Integer> list = Arrays.asList(irr);
        list.add(5);
    }
    // 运行报错
    // Exception in thread "main" java.lang.UnsupportedOperationException
	// at java.util.AbstractList.add(AbstractList.java:148)
	// at java.util.AbstractList.add(AbstractList.java:108)
	// 报错所在代码 是在AbstractList中抛出的错误,
	//  public boolean add(E e) {
    //    add(size(), e);
    //    return true;
    // }
    // public void add(int index, E element) {
    //    throw new UnsupportedOperationException();
    // }

    因为我自己没有仔细看源码,第一眼只看到了下面的代码,下意识的认为是返回的java.util.ArrayList,还困扰了我一会儿,java.util.ArrayList源码中找了一下,发现确实实现了新增的各种方法,最后才发现问题原因,在代码中备注一下。

 	@SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
    	// 这个ArrayList是Arrays工具类的内部静态类,它所实现的方法有限,
    	// 并没有实现 add() remove() 方法。需要额外操作还需要进一步转换
    	// 比如 List<Integer> list = new ArrayList<>(Arrays.asList(arr)) ;
        return new ArrayList<>(a); 
    }

    内部静态类代码就在asList() 方法的下面一行。记录一下,以后看代码还是要认真的多看看。

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
    	...... // 内容省略 实现的大致方法有 size(),toArray(),get(int index),
    		   // set(int index, E element),indexOf(Object o),contains(Object o)
    		   // sort(Comparator<? super E> c),replaceAll(UnaryOperator<E> operator)
    		   // forEach(Consumer<? super E> action)
    }

    好了,回归策略模式。

总结

    优点
    1、策略模式的关键是:分析项目中变化部分与不变的部分。
    2、策略模式提体现了,多用组合/聚合,少用继承,用行为的组合,而不是行为的继承。
    3、策略模式体现了“开闭原则”的支持,用户可以在不修改原有系统的基础上选择算法或者行为,也可以灵活的增加新的算法或行为。
    4、策略模式提供了管理算法族的方案,定义一个算法或者行为族,恰当的使用避免重复代码。提供了算法复用机制,不同的环境类可以方便的复用这些算法。同时也避免了大量的条件判断语句。
    5、策略模式提供了替换继承关系的办法:将算法封装在独立抽象策略类中,也就是可以独立与环境类去改变它,使得易于切换、理解和扩展。
    缺点:
    1、每增加一个策略,就要增加一个类,过多的策略会导致类的数目庞大。同时策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
    2、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值