策略模式
策略模式又叫政策模式,它是将定义的算法家族分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户,属于行为型模式。
当存在大量if…else时,策略模式可以发挥作用,策略模式可以消除大量的if…else。
应用场景
1.针对同一类型问题,有多种处理方式,每一种都能独立解决问题。
2.算法需要自由切换的场景。
实际场景:移动支付、个人所得税(不同的薪资交不同的税)
通用类图
策略模式一般包含三种角色:
-
上下文角色(Context):用户操作入口,该角色持有抽象策略角色,解耦策略与客户端,使得当策略发生改变时不影响客户端的使用。
-
抽象策略角色(Strategy):规定策略或算法的行为
-
具体策略角色(ConcreteStrategy):具体的策略算法
案例演示
优惠促销
存在以下场景,用户进行购买商品时,商家可能会搞活动,而每个活动都有不同的优惠,比如:优惠券抵扣、返现促销、拼团优惠等等,使用策略模式设计该场景。
抽象策略角色
public interface IPromotionStrategy {
void doPromotion();
}
具体策略角色 返现促销
public class CashbackStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("返现,直接打款到支付宝账号");
}
}
具体策略角色 优惠券抵扣
public class CouponStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("使用优惠券抵扣");
}
}
具体策略角色 拼团优惠
public class GroupbuyStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("5人成团,可以优惠");
}
}
具体策略角色 无优惠
public class EmptyStrategy implements IPromotionStrategy {
public void doPromotion() {
System.out.println("无优惠");
}
}
标准上下文
public class PromotionContext {
private IPromotionStrategy iPromotionStrategy;
public PromotionContext(IPromotionStrategy iPromotionStrategy) {
this.iPromotionStrategy = iPromotionStrategy;
}
public void doPromotion(){
iPromotionStrategy.doPromotion();
}
}
由于策略模式的策略是由用户指定,因此需要先判断是什么策略,明显可知switch中是简单工厂模式,因此使用工厂替代上下文。
工厂替代上下文
工厂类
public class PromotionStrategyFacory {
private static Map<String,IPromotionStrategy> PROMOTIONS = new HashMap<String,IPromotionStrategy>();
static {
PROMOTIONS.put(PromotionKey.COUPON,new CouponStrategy());
PROMOTIONS.put(PromotionKey.CASHBACK,new CashbackStrategy());
PROMOTIONS.put(PromotionKey.GROUPBUY,new GroupbuyStrategy());
}
private static final IPromotionStrategy EMPTY = new EmptyStrategy();
private PromotionStrategyFacory(){}
public static IPromotionStrategy getPromotionStrategy(String promotionKey){
IPromotionStrategy strategy = PROMOTIONS.get(promotionKey);
return strategy == null ? EMPTY : strategy;
}
private interface PromotionKey{
String COUPON = "COUPON";
String CASHBACK = "CASHBACK";
String GROUPBUY = "GROUPBUY";
}
public static Set<String> getPromotionKeys(){
return PROMOTIONS.keySet();
}
}
在线支付
付款时,有多种选择,微信支付、支付宝支付、银联支付、京东支付等等,可使用策略模式设计。
抽象策略类
public abstract class Payment {
public abstract String getName();
//通用逻辑放到抽象类里面实现
public MsgResult pay(String uid, double amount){
//余额是否足够
if(queryBalance(uid) < amount){
return new MsgResult(500,"支付失败","余额不足");
}
return new MsgResult(200,"支付成功","支付金额" + amount);
}
protected abstract double queryBalance(String uid);
}
具体策略类 支付宝支付
public class AliPay extends Payment {
public String getName() {
return "支付宝";
}
protected double queryBalance(String uid) {
return 900;
}
}
具体策略类 京东支付
public class JDPay extends Payment {
public String getName() {
return "京东白条";
}
protected double queryBalance(String uid) {
return 500;
}
}
具体策略类 银联支付
public class UnionPay extends Payment {
public String getName() {
return "银联支付";
}
protected double queryBalance(String uid) {
return 120;
}
}
具体策略类 微信支付
public class WechatPay extends Payment {
public String getName() {
return "微信支付";
}
protected double queryBalance(String uid) {
return 263;
}
}
策略工厂
public class PayStrategyFactory {
public static final String ALI_PAY = "AliPay";
public static final String JD_PAY = "JdPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String UNION_PAY = "UnionPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String,Payment> strategy = new HashMap<String,Payment>();
static {
strategy.put(ALI_PAY,new AliPay());
strategy.put(JD_PAY,new JDPay());
strategy.put(WECHAT_PAY,new WechatPay());
strategy.put(UNION_PAY,new UnionPay());
}
public static Payment get(String payKey){
if(!strategy.containsKey(payKey)){
return strategy.get(DEFAULT_PAY);
}
return strategy.get(payKey);
}
}
支付结果
public class MsgResult {
private int code;
private Object data;
private String msg;
public MsgResult(int code, String msg, Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
@Override
public String toString() {
return "MsgResult{" +
"code=" + code +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
}
订单
public class Order {
private String uid;
private String orderId;
private double amount;
public Order(String uid, String orderId, double amount) {
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
public MsgResult pay(){
return pay(PayStrategyFactory.DEFAULT_PAY);
}
public MsgResult pay(String payKey){
Payment payment = PayStrategyFactory.get(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次交易金额为" + amount + ",开始扣款");
return payment.pay(uid,amount);
}
}
源码应用
spring下的Resource:根据不同的资源有不同的读取方法
spring实例化bean的类InstantiationStrategy:原生bean和代理bean
作业
1.改进优惠促销案例,通过配置文件存放策略关键字
properties文件读取工具类
public class PropertiesReader {
/**
* 读取properties
*
**/
public static Map<String,String> read(String propertiesLocation){
Map<String,String> map = new HashMap<>(4);
Properties properties = new Properties();
ClassPathResource classPathResource = new ClassPathResource(propertiesLocation);
try {
properties.load(classPathResource.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
for (String stringPropertyName : properties.stringPropertyNames()) {
map.put(stringPropertyName, (String) properties.get(stringPropertyName));
}
return map;
}
}
strategy.properties
#策略模式
CASHBACK=com.yjf.secondpart.design.pattern.strategy.homework.CashbackStrategy
GROUPBY=com.yjf.secondpart.design.pattern.strategy.homework.GroupbuyStrategy
COUPON=com.yjf.secondpart.design.pattern.strategy.homework.CouponStrategy
策略工厂类
public class PromotionStrategyFactory {
private PromotionStrategyFactory(){}
private static Map<String,IPromotionStrategy> map = new HashMap<>();
private static final String PROPERTYLOCATION = "strategy.properties";
private static EmptyStrategy emptyStrategy = new EmptyStrategy();
static {
Map<String, String> read = PropertiesReader.read(PROPERTYLOCATION);
for (Map.Entry<String, String> stringStringEntry : read.entrySet()) {
Object o = null;
try {
o = Class.forName(stringStringEntry.getValue()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
if(!(o instanceof IPromotionStrategy)){ continue; }
map.put(stringStringEntry.getKey(), (IPromotionStrategy) o);
}
}
public static IPromotionStrategy getPromotionStrategy(String promotionKey){
if(!map.containsKey(promotionKey)){
return emptyStrategy;
}else{
return map.get(promotionKey);
}
}
public static Set<String> getPromotionKeys(){
return map.keySet();
}
}
2.每个优惠促销方案都是独立有效的,这正是策略模式的意义所在,但是优惠又往往是同时出现的,结合装饰器模式完成多重优惠设计。
抽象策略类
public abstract class AbstractPromotionStrategy {
protected int amount;
public AbstractPromotionStrategy(int amount){
this.amount = amount;
}
int doPromotion(){
return amount;
}
}
具体策略类 无优惠
public class EmptyStrategy extends AbstractPromotionStrategy {
public EmptyStrategy(int amount) {
super(amount);
}
@Override
public int doPromotion() {
return super.doPromotion();
}
}
抽象装饰器
public abstract class AbstractPromotionDecorator extends AbstractPromotionStrategy{
protected AbstractPromotionStrategy abstractPromotionStrategy;
public AbstractPromotionDecorator(AbstractPromotionStrategy abstractPromotionStrategy) {
super(abstractPromotionStrategy.amount);
this.abstractPromotionStrategy = abstractPromotionStrategy;
}
@Override
int doPromotion() {
return abstractPromotionStrategy.doPromotion();
}
}
具体装饰器 组团优惠
public class GroupbuyStrategy extends AbstractPromotionDecorator {
public GroupbuyStrategy(AbstractPromotionStrategy abstractPromotionStrategy) {
super(abstractPromotionStrategy);
}
@Override
int doPromotion() {
return (int) (super.doPromotion() * 0.8f);
}
}
具体装饰器 优惠劵
public class CouponStrategy extends AbstractPromotionDecorator {
public CouponStrategy(AbstractPromotionStrategy abstractPromotionStrategy) {
super(abstractPromotionStrategy);
}
@Override
public int doPromotion() {
float v = new Random().nextFloat();
System.out.println(String.format("打%f折", v));
return (int)(super.doPromotion() * v);
}
}
责任链模式
责任链模式是将链中每一个节点看做是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一个节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,属于行为型模式。
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理。
应用场景
1.多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定
实际场景:发起一个工作流程
通用类图
责任链模式一般包含两种角色:
- 抽象处理者(Handler):定义一个处理请求的方法,并维护下一个处理节点的引用。
- 具体处理者(ConcreteHandler):对请求进行处理,如果不是该节点的职责,则进行转发。
案例演示
登录案例
一段登录逻辑,包括用户名密码判空,进行登录,权限检查,原代码如下。
用户
public class Member {
private String loginName;
private String loginPass;
private String roleName;
public Member(String loginName, String loginPass) {
this.loginName = loginName;
this.loginPass = loginPass;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getLoginPass() {
return loginPass;
}
public void setLoginPass(String loginPass) {
this.loginPass = loginPass;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
@Override
public String toString() {
return "Member{" +
"loginName='" + loginName + '\'' +
", loginPass='" + loginPass + '\'' +
", roleName='" + roleName + '\'' +
'}';
}
}
登录业务
public class MemberService {
public void login(String loginName,String loginPass){
if(StringUtils.isEmpty(loginName) ||
StringUtils.isEmpty(loginPass)){
System.out.println("用户名和密码为空");
return;
}
System.out.println("用户名和密码不为空,可以往下执行");
Member member = checkExists(loginName,loginPass);
if(null == member){
System.out.println("用户不存在");
return;
}
System.out.println("登录成功!");
if(!"管理员".equals(member.getRoleName())){
System.out.println("您不是管理员,没有操作权限");
return;
}
System.out.println("允许操作");
}
private Member checkExists(String loginName,String loginPass){
Member member = new Member(loginName,loginPass);
member.setRoleName("管理员");
return member;
}
public static void main(String[] args) {
MemberService service = new MemberService();
service.login("tom","666");
}
}
使用责任链模式,将逻辑切分成三段,三个职责节点进行处理。
抽象处理者
public abstract class Handler {
protected Handler next;
public void next(Handler next){ this.next = next;}
public abstract void doHandler(Member member);
}
具体处理者 非空校验
public class ValidateHandler extends Handler {
public void doHandler(Member member) {
if(StringUtils.isEmpty(member.getLoginName()) ||
StringUtils.isEmpty(member.getLoginPass())){
System.out.println("用户名和密码为空");
return;
}
System.out.println("用户名和密码不为空,可以往下执行");
next.doHandler(member);
}
}
具体处理者 登录校验
public class LoginHandler extends Handler {
public void doHandler(Member member) {
System.out.println("登录成功!");
member.setRoleName("管理员");
next.doHandler(member);
}
}
具体处理者 权限校验
public class AuthHandler extends Handler {
public void doHandler(Member member) {
if(!"管理员".equals(member.getRoleName())){
System.out.println("您不是管理员,没有操作权限");
return;
}
System.out.println("允许操作");
}
}
登录业务
public class MemberService {
public void login(String loginName,String loginPass){
Handler validateHandler = new ValidateHandler();
Handler loginHandler = new LoginHandler();
Handler authHandler = new AuthHandler();
validateHandler.next(loginHandler);
loginHandler.next(authHandler);
validateHandler.doHandler(new Member(loginName,loginPass));
}
}
当链中节点足够多时,当前链的结构就很复杂了,因此可以通过建造者模式构建复杂对象。
抽象处理者
public abstract class Handler<T> {
protected Handler next;
private void next(Handler next){ this.next = next;}
public abstract void doHandler(Member member);
public static class Builder<T>{
private Handler<T> head;
private Handler<T> tail;
public Builder<T> addHandler(Handler handler){
if (this.head == null) {
this.head = this.tail = handler;
return this;
}
this.tail.next(handler);
this.tail = handler;
return this;
}
public Handler<T> build(){
return this.head;
}
}
}
登录业务
public class MemberService {
public void login(String loginName,String loginPass){
Handler.Builder builder = new Handler.Builder();
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(new Member(loginName,loginPass));
}
}
源码应用
tomcat中的过滤器Filter
Netty中的ChannelHandler