Java设计模式-策略模式
策略模式: 定义一系列的算法, 将其一个个封装起来, 并使它们可相互替换, 使得算法可独立于使用它的客户而变化.
(图片来源: 设计模式: 可复用面向对象软件的基础)
策略模式对应于解决某一问题的一个算法族, 允许用户从该算法族中任选一个算法解决该问题, 同时可以方便的更换算法或者增加新的算法. 并由客户端决定调用哪个算法(核心: 分离算法, 选择实现).
模式实现
案例: 商场打折 -策略可以简单分为: 原价购买、满减、返利三种策略:
Strategy
抽象策略: 定义算法族中所有算法的公共接口, Context使用这个接口来调用ConcreteStrategy定义的算法:
/**
* @author jifang
* @since 16/8/29 下午7:43.
*/
public interface Strategy {
double acceptCash(double money);
}
ConcreteStrategy
具体策略: 以Strategy接口实现某具体算法或行为:
// 正常收费
class Normal implements Strategy {
@Override
public double acceptCash(double money) {
return money;
}
}
// 打折收费
class Discount implements Strategy {
private double rate;
public Discount(double rate) {
if (rate > 1.0) {
throw new RuntimeException("折扣力度怎么能大于1.0?");
}
this.rate = rate;
}
@Override
public double acceptCash(double money) {
return money * rate;
}
}
// 返利收费
class Rebate implements Strategy {
private double cashState;
private double cashReturn;
public Rebate(double cashState, double cashReturn) {
this.cashState = cashState;
this.cashReturn = cashReturn;
}
@Override
public double acceptCash(double money) {
if (money > cashState) {
money -= Math.floor(money / cashState) * cashReturn;
}
return money;
}
}
Context
上下文:
维护一个Strategy对象的引用;
定义一个接口让Strategy访问它的数据;
public class Context {
private Strategy strategy;
public void setStrategy(Type type, double... args) {
if (type == Type.NORMAL) {
strategy = new Normal();
} else if (type == Type.DISCOUNT) {
strategy = new Discount(args[0]);
} else if (type == Type.REBATE) {
strategy = new Rebate(args[0], args[1]);
}
}
public double getResult(double money) {
return strategy.acceptCash(money);
}
public enum Type {
NORMAL(0, "正常"),
DISCOUNT(1, "打折"),
REBATE(2, "返利");
private int value;
private String desc;
Type(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
}
注: 将客户端需要选择具体Strategy的任务交给Context完成:
在基础策略模式中,选择所用具体Strategy实现的职责由Client承担, 并将其传递给Context, 这种方案对Client的负担较重, 因此将Context与简单工厂融合, 选择算法实现的工作改由Context负责.
Client
仅与Context交互: 通常有一系列的ConcreteStrategy可供选择.
public class Client {
@Test
public void client() {
double money = 1000;
Context context = new Context();
context.setStrategy(Context.Type.NORMAL);
System.out.println("原价: [" + context.getResult(money) + "]");
context.setStrategy(Context.Type.REBATE, 100, 20);
System.out.println("满100返20: [" + context.getResult(money) + "]");
context.setStrategy(Context.Type.DISCOUNT, 0.8);
System.out.println("6折优惠: [" + context.getResult(money) + "]");
}
}
小结
作用
析取算法: Strategy接口为Context定义了一个可重用的算法/行为, 继承/实现其有助于析取出算法族的公共功能, 且可减少算法与Client间的耦合.
消除条件语句: 避免将不同行为堆砌在一个类中, 将行为封装在独立的Strategy实现中, 可在Client中消除条件语句.
简化单元测试: 每个算法都有自己的类, 可以单独测试.
场景
当使用一个算法的不同变体, 且这些变体可以实现为一个算法族时;
算法的客户不需要知晓其内部数据, 策略模式可以避免暴露复杂的、与算法相关的数据结构;
一个类定义了多种行为, 且这些行为以多个条件语句形式出现, 可将相关行为各自的Strategy(如: Servlet-api service()方法).
相关模式
Flyweight: Strategy对象经常是很好的轻量级对象.
- JAVA_HOME环境变量失效的解决办法
- JBOSS EAP 6.0+ Standalone模式安装成Windows服务
- Django 设置media static
- Django---Ajax
- 利用Spring MVC搭建REST Service
- ehcache2.8.3入门示例:hello world
- day4、Linux基础题目
- 命令行执行Django脚本的方法
- Spring Security笔记:Hello World
- day5、文件乱码怎么解决
- javascript计算对象的长度
- Spring 4.0.2 学习笔记(2) - 自动注入及properties文件的使用
- day6、Linux下如何找出7天以前的文件删除
- django模板语法之include
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP实现微信退款功能
- PHP利用pdo_odbc实现连接数据库示例【基于ThinkPHP5.1搭建的项目】
- PHP登录验证功能示例【用户名、密码、验证码、数据库、已登陆验证、自动登录和注销登录等】
- 实现php删除链表中重复的结点
- Yii2.0框架实现带分页的多条件搜索功能示例
- 定位地理位置PHP判断员工打卡签到经纬度是否在打卡之内
- PHP APP微信提现接口代码
- thinkPHP5.1框架路由::get、post请求简单用法示例
- Python+Dlib+Opencv实现人脸采集并表情判别功能的代码
- Python爬虫实例——scrapy框架爬取拉勾网招聘信息
- php实现微信企业转账功能
- Laravel框架模型的创建及模型对数据操作示例
- Python环境管理virtualenv&virtualenvwrapper的配置详解
- Matplotlib自定义坐标轴刻度的实现示例
- Python基于time模块表示时间常用方法