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)针对于不同消费者设置不同的会员等级和不同的优惠政策,会员等级和优惠政策相对独立,每一种都是一个算法;


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

目录
相关文章
|
1月前
|
设计模式 算法 Kotlin
Kotlin - 改良设计模式 - 策略模式
Kotlin - 改良设计模式 - 策略模式
46 4
|
19天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
21天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
38 1
|
24天前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
28 2
|
29天前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
33 4
|
1月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
44 2
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
50 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文通过游泳运动员的案例,介绍策略模式及其在Kotlin中的改良应用,利用高阶函数简化代码结构,提高灵活性。
36 3
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文介绍策略模式在Kotlin中的应用,通过游泳运动员的例子,展示如何使用接口和高阶函数实现策略模式,使代码更简洁、灵活。
32 2