Java-设计模式之策略模式

简介: 策略模式作为常用的设计模式之一,在我们开发过程中经常会被用到;本文从概念、角色、实例应用、设计原则、优缺点和应用场景等方面对策略模式进行了解读,希望可以对大家有所帮助;如果有问题,欢迎大家来指证和讨论;

一、策略模式概述:

首先我们来看一下什么是策略模式:

策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。                                 -阎宏博士 《JAVA与模式》

其实策略模式就是将每一个算法(变化的逻辑)封装起来,让其独立于使用它的客户而变化,并且使它们还可以相互替换。


二、策略模式的角色及其职责:

1、抽象策略角色[Strategy]策略类:

定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。

2、具体策略类[ConcreteStrategy]

实现了Strategy定义的接口,包装了相关的算法和行为,提供具体的算法实现。

3、上下文角色[Context]客户端的基类(可以是一个抽象类)

持有一个策略类的引用,最终给客户端调用,具有以下4点特征:

1)需要提供获取具体的算法(或策略类的方法

(2)内部维护一个策略类的实例;

3)负责动态设置运行时Strategy具体的实现算法;

4)负责跟策略类及其实现类之间的交互和数据传递;

4、角色设置如下图所示:

image.png


 

三、实例引入:

1、需求描述:

我们现在要开发一个类似于美团和饿了吗的外卖点餐平台,名字暂定为一米阳光。面向的客户主要有3类人,公司白领、农民伯伯和公司老板,客户在一米阳光平台点餐后,可以选择三种支付方式:支付宝、微信和信用卡。

需求实现:公司白领-小张使用支付宝支付100元,农民伯伯-老王使用微信支付50元,公司老板-李总使用信用卡支付200元。

2、需求分析:

在我们这个需求中,策略就是点餐费用的支付,具体的实现策略目前有三种:支付宝支付、微信支付和信用卡支付;

借助于策略模式,我们对于一米阳光平台的代码做了如下的设计:

(1)1个策略类(接口):PayStrategy

(2)3个具体策略类:AliPayWeChatPayCreditCardPay

(3)1个上下文类(客户基类、抽象类):Customer

(4)3个客户类:WhiteCollarFarmerBusinessMan

对应关系如下图所示:

image.png


四、代码呈现:

11个策略类(接口):PayStrategy

package com.strategy;

/**

* 支付策略类

* */

public interface PayStrategy {

   /**

    * 支付方法

    * customerName 客户姓名

    * money 客户支付金额

    * */

   String pay(String customerName, Integer money);

}


23个具体策略类:AliPayWeChatPayCreditCardPay

1AliPay

package com.strategy;

/**

* 支付宝支付

* */

public class AliPay implements PayStrategy{

   @Override

   public String pay(String customerName, Integer money) {

       StringBuffer result = new StringBuffer();

      result.append(customerName).append("您好,您在一米阳光平台使用");

       //todo 支付宝支付

       result.append("支付宝支付了");

      result.append(money).append("");

       return result.toString();

   }

}


2WeChatPay

package com.strategy;

/**

* 微信支付

* */

public class WeChatPay implements PayStrategy{

   @Override

   public String pay(String customerName, Integer money) {

       StringBuffer result = new StringBuffer();

      result.append(customerName).append("您好,您在一米阳光平台使用");

       //todo 微信支付

       result.append("微信支付了");

      result.append(money).append("");

       return result.toString();

   }

}


3CreditCardPay

package com.strategy;

/**

* 信用卡支付

* */

public class CreditCardPay implements PayStrategy{

   @Override

   public String pay(String customerName, Integer money) {

       StringBuffer result = new StringBuffer();

       result.append(customerName).append("您好,您在一米阳光平台使用");

       //todo 信用卡支付

       result.append("信用卡支付了");

      result.append(money).append("");

       return result.toString();

   }

}


31个上下文类(客户基类、抽象类):Customer

package com.customer;

 

import com.strategy.PayStrategy;

/**

* 客户

* */

public abstract class Customer{

 

   private String customerName;

 

   private Integer payMoney;

 

   public void setCustomerName(String customerName){

       this.customerName = customerName;

   }

 

   public void setPayMoney(Integer payMoney){

       this.payMoney = payMoney;

   }

 

   public String getCustomerName(){

       return this.customerName;

   }

 

   public Integer getPayMoney(){

       return this.payMoney;

   }

 

   private PayStrategy payStrategy;

 

   public String payInstance(PayStrategy payStrategy){

       return payStrategy.pay(customerName, payMoney);

   }

}


43个客户类:WhiteCollarFarmerBusinessMan

1WhiteCollar

package com.customer;

 

import com.customer.Customer;

/**

* 公司白领

* */

public class WhiteCollar extends Customer {

}


2Farmer

package com.customer;

import com.customer.Customer;

/**

* 农民伯伯

* */

public class Farmer extends Customer {

}


3BusinessMan

package com.customer;

/**

* 公司老板

* */

public class BusinessMan extends Customer {

}


5、客户端测试类:Client

package com.customer.test;

 

import com.customer.BusinessMan;

import com.customer.Farmer;

import com.customer.WhiteCollar;

import com.strategy.AliPay;

import com.strategy.CreditCardPay;

import com.strategy.PayStrategy;

import com.strategy.WeChatPay;

 

public class Client {

 

   public static void main(String[] args) {

 

       //公司白领-小张,支付宝支付,100

       //选择并创建需要使用的策略对象

       PayStrategy aliPay = new AliPay();

       //创建环境

       WhiteCollar whiteCollar = new WhiteCollar();

       //计算价格

      whiteCollar.setCustomerName("公司白领-小张");

       whiteCollar.setPayMoney(100);

       //支付

       String payResult1 = whiteCollar.payInstance(aliPay);

       //打印结果

       System.out.println(payResult1);

 

       //农民伯伯-老王,微信支付,50

       //选择并创建需要使用的策略对象

       PayStrategy weChatPay = new WeChatPay();

       //创建环境

       Farmer farmer = new Farmer();

       //计算价格

       farmer.setCustomerName("农民伯伯-老王");

       farmer.setPayMoney(50);

       //支付

       String payResult2 = farmer.payInstance(weChatPay);

       //打印结果

      System.out.println(payResult2);

 

 

       //公司老板-李总,信用卡支付,200

       //选择并创建需要使用的策略对象

       PayStrategy creditCardPay = new CreditCardPay();

       //创建环境

       BusinessMan businessMan = new BusinessMan();

       //计算价格

      businessMan.setCustomerName("公司老板-李总");

       businessMan.setPayMoney(200);

       //支付

       String payResult3 = businessMan.payInstance(creditCardPay);

       //打印结果

      System.out.println(payResult3);

   }

}


6、测试结果:

image.png


 

五、策略模式的设计原则:

策略模式的设计原则可以归纳为:变化的抽象成接口,面向接口编程而不是面向实现编程;

具体如下:

1、分开变化和不变化的部分,把变化的抽象成接口:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这个概念很简单,几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分;

比如在我们的一米阳光点餐平台中,固定的部分是每个客户都有姓名和支付费用两个属性,变化的部分是每个客户会采用不同的支付方式,从而我们需要把客户的支付方式封装成一个支付接口(策略类),并针对每一种支付方式设计了具体的支付策略/算法来实现这个支付接口(策略类);

2、针对接口编程,而不是针对实现编程:

关于接口编程和实现编程:

在我们开发过程中,面向接口编程比面向实现编程的可扩展性更好,比如在一米阳光点餐系统中:

1)如果我们采用面向实现编辑的方式,那么为了满足公司白领-小张使用支付宝支付的需求,需要给小张加一个支付方式的实现,同样农民伯伯-老王和公司老总-李总也需要单独加一个支付方式的实现,如果后面来了一个新客户,选择了与小张、老王或李总相同的支付方式,我们也需要给他添加一段支付的代码;后续随着我们的客户越来也多,重复的代码也会越来越多,后续的小伙伴维护起代码来就会很吃力;同时如果后续有新的需求,一个客户可以选择多种支付方式,那么我们客户侧的代码,还需要添加很多的if-else判断来支持,这样会造成客户侧支付相关的代码越来越多越来越乱,无法支持快速的扩展,后续的维护也是一个大问题;

2)如果我们采取面向接口实现,那我们就可以把支付相关的代码独立出来维护,后续新增支付方式,我们只需要新增一个具体的支付策略类;我们也可以通过在客户侧的简单调用,实现客户可以选择多种支付方式的需求;把变化的支付策略独立出来后,后续支付相关需求的变化对代码的侵入性和影响就很少了,代码的可扩展性就提高了;

3、多用组合,少用继承

使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态地改变行为;

“有一个”(has a)可能比“是一个”(is a)更好

在我们的一米阳光点餐平台中,每一个客户支付时都有一个PayStrategy,后续如果再给客户一个选择配送时间的SendStrategy,那么客户支付和配送时间选择的行为都可以委托它们代为处理;

客户在选择支付方式和配送时间时,我们就可以将两个类(PayStrategySendStrategy)结合起来使用,这就是组合(Composition)。这种作法和继承不同的地方在于:客户的行为不是继承而来,而是和适当的行为对象组合而来;

简而言之,在某些场景下,组合是比继承更优的解决方案,选择继承时,我们会受限于父类的既定方法,无法实现某些特定的扩展,但是组合可以让我们使用更简单的组装实现需求,而无需考虑父类的情况;


六、策略模式的优缺点

1、优点:

1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。

2)策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

3)使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

2、缺点:

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

2)策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。

 

七、策略模式的应用场景:

1、多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为;

2、需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现;

3、对客户隐藏具体策略(算法)的实现细节,彼此完全独立;

4具体使用场景:

1)支付平台需要给不同客户提供不同的支付方式或渠道,支付方式或渠道独立,每一种都是一个算法;

2)针对于不同消费者设置不同的会员等级和不同的优惠政策,会员等级和优惠政策相对独立,每一种都是一个算法;


结语:以上内容是自己对于策略模式的基本理解,如果有问题,欢迎大家来指证和讨论!

目录
相关文章
|
23天前
|
设计模式 Java Spring
Java 设计模式之责任链模式:优雅处理请求的艺术
责任链模式通过构建处理者链,使请求沿链传递直至被处理,实现发送者与接收者的解耦。适用于审批流程、日志处理等多级处理场景,提升系统灵活性与可扩展性。
175 2
|
23天前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
214 0
|
3月前
|
设计模式 缓存 Java
Java设计模式(二):观察者模式与装饰器模式
本文深入讲解观察者模式与装饰器模式的核心概念及实现方式,涵盖从基础理论到实战应用的全面内容。观察者模式实现对象间松耦合通信,适用于事件通知机制;装饰器模式通过组合方式动态扩展对象功能,避免子类爆炸。文章通过Java示例展示两者在GUI、IO流、Web中间件等场景的应用,并提供常见陷阱与面试高频问题解析,助你写出灵活、可维护的代码。
|
22天前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
219 35
|
22天前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
191 8
|
设计模式 算法 Java
Java工厂策略模式介绍
本文介绍Java中工厂模式与策略模式的结合应用,通过工厂创建策略对象,实现灵活、可扩展的支付、折扣等业务场景,提升系统解耦与维护性。
165 3
|
2月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
6月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
152 0
|
3月前
|
设计模式 安全 Java
Java设计模式(一):单例模式与工厂模式
本文详解单例模式与工厂模式的核心实现及应用,涵盖饿汉式、懒汉式、双重检查锁、工厂方法、抽象工厂等设计模式,并结合数据库连接池与支付系统实战案例,助你掌握设计模式精髓,提升代码专业性与可维护性。
|
3月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。