Java常用设计模式-策略模式

简介: Java常用设计模式-策略模式

策略模式是一个非常实用的设计模式,指定义了一类算法并将其封装起来,并使得它们之间可以灵活地切换,并且不影响客户端。

1,从一个例子开始

我们常常会在网上买东西,很多购物平台都会有着各种各样的优惠策略供你选择例如满减优惠、返现优惠等等。

假设现在要开发一个商城系统,并要开发优惠策略,需要实现不使用优惠满减优惠返现优惠三个策略。

这一步很简单,我们将优惠进行抽象,创建一个优惠接口Promotion如下:

package fun.swsk33site.strategy.promotion;

import fun.swsk33site.strategy.model.Order;

/**
 * 促销活动抽象接口
 */
public interface Promotion {

    /**
     * 执行促销策略
     *
     * @param order 传入订单进行相应折扣
     */
    void doPromotion(Order order);

}

然后创建该接口的实现类NoPromotionFullDiscountPromotionCashBackPromotion分别代表不使用优惠、满减优惠和返现优惠:

package fun.swsk33site.strategy.promotion.impl;

import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;

/**
 * 不使用优惠
 */
public class NoPromotion implements Promotion {

    @Override
    public void doPromotion(Order order) {
        System.out.println("不使用任何优惠");
    }

}
package fun.swsk33site.strategy.promotion.impl;

import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;

/**
 * 满减促销
 */
public class FullDiscountPromotion implements Promotion {

    @Override
    public void doPromotion(Order order) {
        // 满200减20
        if (order.getPrice() > 200) {
            order.setPrice(order.getPrice() - 20);
            System.out.println("使用了满200减20优惠");
        }
    }

}
package fun.swsk33site.strategy.promotion.impl;

import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;

/**
 * 返现促销
 */
public class CashBackPromotion implements Promotion {

    @Override
    public void doPromotion(Order order) {
        // 满200返现20
        if (order.getPrice() > 200) {
            // 返现20...
            System.out.println("使用了满200返现20到支付宝账户优惠");
        }
    }

}

最后创建客户端类执行支付逻辑:

Order order1 = new Order();
order1.setName("xxx");
order1.setPrice(123);
String strategy = "fulldiscount";
Promotion promotion = null;
if (strategy.equals("no")) {
    promotion = new NoPromotion();
} else if (strategy.equals("fulldiscount")) {
    promotion = new FullDiscountPromotion();
} else if (strategy.equals("cashback")) {
    promotion = new CashBackPromotion();
}
promotion.doPromotion(order1);

很显然这样写是完全不实用的,如果说促销活动越来越多,那么客户端的代码将会越来越复杂,越来越臃肿。

2,使用策略模式改造

我们可以单独创建一个类,这个类专门用于来根据传入参数选择不同的策略。

首先我们来创建一个枚举类型PromotionStrategy,用于作为选择优惠策略的参数:

package fun.swsk33site.strategy.promotion;

/**
 * 优惠策略枚举
 */
public enum PromotionStrategy {
    /**
     * 不使用优惠
     */
    NO,
    /**
     * 满减优惠
     */
    FULLDISCOUNT,
    /**
     * 返现优惠
     */
    CASHBACK
}

然后创建类PromotionContext,用于传入参数后选择相应的优惠策略,这个类就是我们策略模式的核心了:

package fun.swsk33site.strategy.promotion;

import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.impl.CashBackPromotion;
import fun.swsk33site.strategy.promotion.impl.FullDiscountPromotion;
import fun.swsk33site.strategy.promotion.impl.NoPromotion;

import java.util.HashMap;
import java.util.Map;

/**
 * 优惠策略选择上下文,用于选择优惠策略
 */
public class PromotionContext {

    // 用一个Map作为容器储存各个优惠策略的类,以枚举为参数取出
    private static Map<PromotionStrategy, Promotion> promotionMap = new HashMap<>();

    // 静态块用于初始化各个优惠策略实例
    static {
        promotionMap.put(PromotionStrategy.NO, new NoPromotion());
        promotionMap.put(PromotionStrategy.FULLDISCOUNT, new FullDiscountPromotion());
        promotionMap.put(PromotionStrategy.CASHBACK, new CashBackPromotion());
    }

    /**
     * 使用优惠
     *
     * @param order     传入要使用优惠的订单
     * @param strategy 传入优惠策略
     */
    public static void usePromotion(Order order, PromotionStrategy strategy) {
        Promotion getPromotion = promotionMap.get(strategy);
        getPromotion.doPromotion(order);
    }

}

可见,首先这个类中会把所有的优惠策略都实例化一遍并存入到一个Map中,每个优惠策略对应我们一个策略枚举值作为key,然后利用usePromotion方法,可以接受从客户端传来的优惠策略参数,然后选择相应的优惠策略。

可见,策略模式就是将选择策略的逻辑抽离到一个专门的类中,客户端就可以通过传参的形式更加灵活方便地选择策略。

我们来试一下子:

package fun.swsk33site.strategy;

import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;
import fun.swsk33site.strategy.promotion.PromotionContext;
import fun.swsk33site.strategy.promotion.PromotionStrategy;
import fun.swsk33site.strategy.promotion.impl.CashBackPromotion;
import fun.swsk33site.strategy.promotion.impl.FullDiscountPromotion;
import fun.swsk33site.strategy.promotion.impl.NoPromotion;

public class Client {

    /**
     * 客户端进行支付
     *
     * @param order     待支付账单
     * @param strategy 要使用的优惠策略
     */
    private static void doPayment(Order order, PromotionStrategy strategy) {
        System.out.println("账单:" + order.getName() + "准备支付");
        System.out.println("准备使用优惠");
        PromotionContext.usePromotion(order, strategy);
        System.out.println("支付完成:" + order.getPrice() + "元");
    }

    public static void main(String[] args) {
        // 模拟买东西
        Order order = new Order();
        order.setName("守望时空33购买的辛鹿咖啡豆:曼特宁拼配 深度烘焙1kg,意式极深炭烧 极深烘焙1kg,云南阿拉比卡庄园豆 深度烘焙454g");
        order.setPrice(202);
        // 进行支付,使用满减优惠策略
        doPayment(order, PromotionStrategy.FULLDISCOUNT);
    }

}

结果:

image.png

只需在进行支付的时候,调用策略选择类,利用枚举值传参,根据传参使用不同的优惠策略。

其实也可以不使用枚举传参,直接使用字符串或者常量等等方式都可以,不过枚举传参我认为可以降低错误的可能,实用一些。

3,总结

可见策略模式,在多策略的场景下是非常实用的。我们可以把策略模式的实现总结为以下几步:

  1. 抽象出策略接口,并实现不同的策略类(所有的策略)
  2. 编写出策略选择类,其中根据不同的传入参数,选择或者执行对应的策略
  3. 客户端调用策略选择类,传入参数执行对应策略

可以发现,策略模式是如何省略了大量if-else语句呢?很明显其核心借助了哈希表这种数据结构,将每种情况放入哈希表,直接根据条件取出,时间复杂度也为O(1),性能上也非常好。

策略模式增加了系统的可维护性,通常用在以下场景:

  • 一个系统需要在一类算法中动态地选择其中一种
  • 系统中有很多类,但是仅仅是行为不一样,需要根据情况使用其中的类

策略模式的优缺点也是很明显的:

  • 优点:

    • 符合开闭原则(尽量去扩展系统的功能而非去改写)
    • 避免了大量的if...else if...或者switch语句
  • 缺点:

    • 客户端需要知道所有的策略

最后附上整个示例的类图:

image.png

示例仓库地址

相关文章
|
27天前
|
设计模式 算法 Java
Java中的设计模式:提升代码质量的秘诀
【8月更文挑战第23天】在Java开发中,设计模式是提高代码可读性、可维护性和扩展性的强有力工具。本文通过浅显易懂的语言和实际案例,探讨几种常见的设计模式及其在Java中的应用,旨在帮助开发者更好地理解并运用这些模式来优化自己的代码结构。
39 2
|
5天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
1天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
5天前
|
设计模式 安全 算法
【Java面试题汇总】设计模式篇(2023版)
谈谈你对设计模式的理解、七大原则、单例模式、工厂模式、代理模式、模板模式、观察者模式、JDK中用到的设计模式、Spring中用到的设计模式
【Java面试题汇总】设计模式篇(2023版)
|
5天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
1天前
|
设计模式 人工智能 算法
PHP中的设计模式:策略模式的深入解析与实践软件测试中的人工智能革命:提升效率与准确性的新篇章
在PHP开发中,理解并运用设计模式是提升代码质量和可维护性的重要途径。本文聚焦于策略模式(Strategy Pattern),一种行为型设计模式,它允许在运行时选择算法或业务规则。通过本文,我们将深入探讨策略模式的定义、结构、使用场景以及如何在PHP项目中有效地实现和利用策略模式。不同于性能优化等技术性摘要,本文着重于提供对策略模式全面而实用的理解,助力开发者编写出更加灵活和可扩展的应用程序。 本文深入探讨了人工智能在软件测试领域的应用,揭示了其如何显著提高测试过程的效率和准确性。通过实际案例分析,展示了AI技术在自动化测试、缺陷检测及结果分析中的关键作用,并讨论了实施AI测试策略时面临的挑
10 3
|
5天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深度解析
在本文中,我们将深入探讨PHP编程中的策略模式,这是一种行为型设计模式,用于定义一系列算法,将每个算法封装起来,并使它们可以互换。通过详细讲解策略模式的概念、结构以及在PHP中的实现方式,我们希望能够帮助读者更好地理解和应用这一设计模式,提升代码的灵活性和可维护性。
14 1
|
20天前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
37 2
|
20天前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
30 1
|
20天前
|
设计模式 Java
重构你的代码:探索Java中的混合、装饰器与组合设计模式
【8月更文挑战第30天】在软件开发中,设计模式为特定问题提供了结构化的解决方案,使代码更易理解、维护及扩展。本文将介绍三种常用的 Java 设计模式:混合模式、装饰器模式与组合模式,并附有示例代码展示实际应用。混合模式允许通过继承多个接口或抽象类实现多重继承;装饰器模式可在不改变对象结构的情况下动态添加新功能;组合模式则通过树形结构表示部分-整体层次,确保客户端处理单个对象与组合对象时具有一致性。
15 1