learn.lianglianglee.com/专栏/Java并发编程实战/30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html
2022-05-11 18:57:05 +08:00

1623 lines
32 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/Java并发编程实战/00 开篇词你为什么需要学习并发编程?.md.html">00 开篇词你为什么需要学习并发编程?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/01 如何制定性能调优标准?.md.html">01 如何制定性能调优标准?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/02 如何制定性能调优策略?.md.html">02 如何制定性能调优策略?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/03 字符串性能优化不容小觑百M内存轻松存储几十G数据.md.html">03 字符串性能优化不容小觑百M内存轻松存储几十G数据.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/04 慎重使用正则表达式.md.html">04 慎重使用正则表达式.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/05 ArrayList还是LinkedList使用不当性能差千倍.md.html">05 ArrayList还是LinkedList使用不当性能差千倍.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/06 Stream如何提高遍历集合效率.md.html">06 Stream如何提高遍历集合效率.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/07 深入浅出HashMap的设计与优化.md.html">07 深入浅出HashMap的设计与优化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/08 网络通信优化之IO模型如何解决高并发下IO瓶颈.md.html">08 网络通信优化之IO模型如何解决高并发下IO瓶颈.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/09 网络通信优化之序列化避免使用Java序列化.md.html">09 网络通信优化之序列化避免使用Java序列化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/10 网络通信优化之通信协议如何优化RPC网络通信.md.html">10 网络通信优化之通信协议如何优化RPC网络通信.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/11 答疑课堂深入了解NIO的优化实现原理.md.html">11 答疑课堂深入了解NIO的优化实现原理.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/12 多线程之锁优化深入了解Synchronized同步锁的优化方法.md.html">12 多线程之锁优化深入了解Synchronized同步锁的优化方法.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/13 多线程之锁优化深入了解Lock同步锁的优化方法.md.html">13 多线程之锁优化深入了解Lock同步锁的优化方法.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/14 多线程之锁优化(下):使用乐观锁优化并行操作.md.html">14 多线程之锁优化(下):使用乐观锁优化并行操作.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/15 多线程调优(上):哪些操作导致了上下文切换?.md.html">15 多线程调优(上):哪些操作导致了上下文切换?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/16 多线程调优(下):如何优化多线程上下文切换?.md.html">16 多线程调优(下):如何优化多线程上下文切换?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/17 并发容器的使用:识别不同场景下最优容器.md.html">17 并发容器的使用:识别不同场景下最优容器.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/18 如何设置线程池大小?.md.html">18 如何设置线程池大小?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/19 如何用协程来优化多线程业务?.md.html">19 如何用协程来优化多线程业务?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/20 磨刀不误砍柴工欲知JVM调优先了解JVM内存模型.md.html">20 磨刀不误砍柴工欲知JVM调优先了解JVM内存模型.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/21 深入JVM即时编译器JIT优化Java编译.md.html">21 深入JVM即时编译器JIT优化Java编译.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/22 如何优化垃圾回收机制?.md.html">22 如何优化垃圾回收机制?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/23 如何优化JVM内存分配.md.html">23 如何优化JVM内存分配.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/24 内存持续上升,我该如何排查问题?.md.html">24 内存持续上升,我该如何排查问题?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/25 答疑课堂:模块四热点问题解答.md.html">25 答疑课堂:模块四热点问题解答.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/26 单例模式:如何创建单一对象优化系统性能?.md.html">26 单例模式:如何创建单一对象优化系统性能?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/27 原型模式与享元模式:提升系统性能的利器.md.html">27 原型模式与享元模式:提升系统性能的利器.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/28 如何使用设计模式优化并发编程?.md.html">28 如何使用设计模式优化并发编程?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/29 生产者消费者模式:电商库存设计优化.md.html">29 生产者消费者模式:电商库存设计优化.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/Java并发编程实战/30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html">30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/31 答疑课堂:模块五思考题集锦.md.html">31 答疑课堂:模块五思考题集锦.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/32 MySQL调优之SQL语句如何写出高性能SQL语句.md.html">32 MySQL调优之SQL语句如何写出高性能SQL语句.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/33 MySQL调优之事务高并发场景下的数据库事务调优.md.html">33 MySQL调优之事务高并发场景下的数据库事务调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/34 MySQL调优之索引索引的失效与优化.md.html">34 MySQL调优之索引索引的失效与优化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/35 记一次线上SQL死锁事故如何避免死锁.md.html">35 记一次线上SQL死锁事故如何避免死锁.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/36 什么时候需要分表分库?.md.html">36 什么时候需要分表分库?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/37 电商系统表设计优化案例分析.md.html">37 电商系统表设计优化案例分析.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/38 数据库参数设置优化,失之毫厘差之千里.md.html">38 数据库参数设置优化,失之毫厘差之千里.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/39 答疑课堂MySQL中InnoDB的知识点串讲.md.html">39 答疑课堂MySQL中InnoDB的知识点串讲.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/41 如何设计更优的分布式锁?.md.html">41 如何设计更优的分布式锁?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/42 电商系统的分布式事务调优.md.html">42 电商系统的分布式事务调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/43 如何使用缓存优化系统性能?.md.html">43 如何使用缓存优化系统性能?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/44 记一次双十一抢购性能瓶颈调优.md.html">44 记一次双十一抢购性能瓶颈调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/加餐 什么是数据的强、弱一致性?.md.html">加餐 什么是数据的强、弱一致性?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/加餐 推荐几款常用的性能测试工具.md.html">加餐 推荐几款常用的性能测试工具.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/答疑课堂:模块三热点问题解答.md.html">答疑课堂:模块三热点问题解答.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/结束语 栉风沐雨,砥砺前行!.md.html">结束语 栉风沐雨,砥砺前行!.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>30 装饰器模式:如何优化电商系统中复杂的商品价格策略?</h1>
<p>你好,我是刘超。</p>
<p>开始今天的学习之前,我想先请你思考一个问题。假设现在有这样一个需求,让你设计一个装修功能,用户可以动态选择不同的装修功能来装饰自己的房子。例如,水电装修、天花板以及粉刷墙等属于基本功能,而设计窗帘装饰窗户、设计吊顶装饰房顶等未必是所有用户都需要的,这些功能则需要实现动态添加。还有就是一旦有新的装修功能,我们也可以实现动态添加。如果要你来负责,你会怎么设计呢?</p>
<p>此时你可能会想了,通常给一个对象添加功能,要么直接修改代码,在对象中添加相应的功能,要么派生对应的子类来扩展。然而,前者每次都需要修改对象的代码,这显然不是理想的面向对象设计,即便后者是通过派生对应的子类来扩展,也很难满足复杂的随意组合功能需求。</p>
<p>面对这种情况,使用装饰器模式应该再合适不过了。它的优势我想你多少知道一点,我在这里总结一下。</p>
<p>装饰器模式能够实现为对象动态添加装修功能,它是从一个对象的外部来给对象添加功能,所以有非常灵活的扩展性,我们可以在对原来的代码毫无修改的前提下,为对象添加新功能。除此之外,装饰器模式还能够实现对象的动态组合,借此我们可以很灵活地给动态组合的对象,匹配所需要的功能。</p>
<p>下面我们就通过实践,具体看看该模式的优势。</p>
<h2>什么是装饰器模式?</h2>
<p>在这之前,我先简单介绍下什么是装饰器模式。装饰器模式包括了以下几个角色:接口、具体对象、装饰类、具体装饰类。</p>
<p>接口定义了具体对象的一些实现方法;具体对象定义了一些初始化操作,比如开头设计装修功能的案例中,水电装修、天花板以及粉刷墙等都是初始化操作;装饰类则是一个抽象类,主要用来初始化具体对象的一个类;其它的具体装饰类都继承了该抽象类。</p>
<p>下面我们就通过装饰器模式来实现下装修功能,代码如下:</p>
<pre><code>/**
* 定义一个基本装修接口
* @author admin
*
*/
public interface IDecorator {
/**
* 装修方法
*/
void decorate();
}
/**
* 装修基本类
* @author admin
*
*/
public class Decorator implements IDecorator{
/**
* 基本实现方法
*/
public void decorate() {
System.out.println(&quot; 水电装修、天花板以及粉刷墙。。。&quot;);
}
}
/**
* 基本装饰类
* @author admin
*
*/
public abstract class BaseDecorator implements IDecorator{
private IDecorator decorator;
public BaseDecorator(IDecorator decorator) {
this.decorator = decorator;
}
/**
* 调用装饰方法
*/
public void decorate() {
if(decorator != null) {
decorator.decorate();
}
}
}
/**
* 窗帘装饰类
* @author admin
*
*/
public class CurtainDecorator extends BaseDecorator{
public CurtainDecorator(IDecorator decorator) {
super(decorator);
}
/**
* 窗帘具体装饰方法
*/
@Override
public void decorate() {
System.out.println(&quot; 窗帘装饰。。。&quot;);
super.decorate();
}
}
public static void main( String[] args )
{
IDecorator decorator = new Decorator();
IDecorator curtainDecorator = new CurtainDecorator(decorator);
curtainDecorator.decorate();
}
</code></pre>
<p>运行结果:</p>
<pre><code>窗帘装饰。。。
水电装修、天花板以及粉刷墙。。。
</code></pre>
<p>通过这个案例,我们可以了解到:如果我们想要在基础类上添加新的装修功能,只需要基于抽象类 BaseDecorator 去实现继承类,通过构造函数调用父类,以及重写装修方法实现装修窗帘的功能即可。在 main 函数中,我们通过实例化装饰类,调用装修方法,即可在基础装修的前提下,获得窗帘装修功能。</p>
<p>基于装饰器模式实现的装修功能的代码结构简洁易读,业务逻辑也非常清晰,并且如果我们需要扩展新的装修功能,只需要新增一个继承了抽象装饰类的子类即可。</p>
<p>在这个案例中,我们仅实现了业务扩展功能,接下来,我将通过装饰器模式优化电商系统中的商品价格策略,实现不同促销活动的灵活组合。</p>
<h2>优化电商系统中的商品价格策略</h2>
<p>相信你一定不陌生,购买商品时经常会用到的限时折扣、红包、抵扣券以及特殊抵扣金等,种类很多,如果换到开发视角,实现起来就更复杂了。</p>
<p>例如,每逢双十一,为了加大商城的优惠力度,开发往往要设计红包 + 限时折扣或红包 + 抵扣券等组合来实现多重优惠。而在平时,由于某些特殊原因,商家还会赠送特殊抵扣券给购买用户,而特殊抵扣券 + 各种优惠又是另一种组合方式。</p>
<p>要实现以上这类组合优惠的功能,最快、最普遍的实现方式就是通过大量 if-else 的方式来实现。但这种方式包含了大量的逻辑判断,致使其他开发人员很难读懂业务, 并且一旦有新的优惠策略或者价格组合策略出现,就需要修改代码逻辑。</p>
<p>这时,刚刚介绍的装饰器模式就很适合用在这里,其相互独立、自由组合以及方便动态扩展功能的特性,可以很好地解决 if-else 方式的弊端。下面我们就用装饰器模式动手实现一套商品价格策略的优化方案。</p>
<p>首先,我们先建立订单和商品的属性类,在本次案例中,为了保证简洁性,我只建立了几个关键字段。以下几个重要属性关系为,主订单包含若干详细订单,详细订单中记录了商品信息,商品信息中包含了促销类型信息,一个商品可以包含多个促销类型(本案例只讨论单个促销和组合促销):</p>
<pre><code>/**
* 主订单
* @author admin
*
*/
public class Order {
private int id; // 订单 ID
private String orderNo; // 订单号
private BigDecimal totalPayMoney; // 总支付金额
private List&lt;OrderDetail&gt; list; // 详细订单列表
}
/**
* 详细订单
* @author admin
*
*/
public class OrderDetail {
private int id; // 详细订单 ID
private int orderId;// 主订单 ID
private Merchandise merchandise; // 商品详情
private BigDecimal payMoney; // 支付单价
}
/**
* 商品
* @author admin
*
*/
public class Merchandise {
private String sku;// 商品 SKU
private String name; // 商品名称
private BigDecimal price; // 商品单价
private Map&lt;PromotionType, SupportPromotions&gt; supportPromotions; // 支持促销类型
}
/**
* 促销类型
* @author admin
*
*/
public class SupportPromotions implements Cloneable{
private int id;// 该商品促销的 ID
private PromotionType promotionType;// 促销类型 1\优惠券 2\红包
private int priority; // 优先级
private UserCoupon userCoupon; // 用户领取该商品的优惠券
private UserRedPacket userRedPacket; // 用户领取该商品的红包
// 重写 clone 方法
public SupportPromotions clone(){
SupportPromotions supportPromotions = null;
try{
supportPromotions = (SupportPromotions)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return supportPromotions;
}
}
/**
* 优惠券
* @author admin
*
*/
public class UserCoupon {
private int id; // 优惠券 ID
private int userId; // 领取优惠券用户 ID
private String sku; // 商品 SKU
private BigDecimal coupon; // 优惠金额
}
/**
* 红包
* @author admin
*
*/
public class UserRedPacket {
private int id; // 红包 ID
private int userId; // 领取用户 ID
private String sku; // 商品 SKU
private BigDecimal redPacket; // 领取红包金额
}
</code></pre>
<p>接下来,我们再建立一个计算支付金额的接口类以及基本类:</p>
<pre><code>/**
* 计算支付金额接口类
* @author admin
*
*/
public interface IBaseCount {
BigDecimal countPayMoney(OrderDetail orderDetail);
}
/**
* 支付基本类
* @author admin
*
*/
public class BaseCount implements IBaseCount{
public BigDecimal countPayMoney(OrderDetail orderDetail) {
orderDetail.setPayMoney(orderDetail.getMerchandise().getPrice());
System.out.println(&quot; 商品原单价金额为:&quot; + orderDetail.getPayMoney());
return orderDetail.getPayMoney();
}
}
</code></pre>
<p>然后,我们再建立一个计算支付金额的抽象类,由抽象类调用基本类:</p>
<pre><code>/**
* 计算支付金额的抽象类
* @author admin
*
*/
public abstract class BaseCountDecorator implements IBaseCount{
private IBaseCount count;
public BaseCountDecorator(IBaseCount count) {
this.count = count;
}
public BigDecimal countPayMoney(OrderDetail orderDetail) {
BigDecimal payTotalMoney = new BigDecimal(0);
if(count!=null) {
payTotalMoney = count.countPayMoney(orderDetail);
}
return payTotalMoney;
}
}
</code></pre>
<p>然后,我们再通过继承抽象类来实现我们所需要的修饰类(优惠券计算类、红包计算类):</p>
<pre><code>/**
* 计算使用优惠券后的金额
* @author admin
*
*/
public class CouponDecorator extends BaseCountDecorator{
public CouponDecorator(IBaseCount count) {
super(count);
}
public BigDecimal countPayMoney(OrderDetail orderDetail) {
BigDecimal payTotalMoney = new BigDecimal(0);
payTotalMoney = super.countPayMoney(orderDetail);
payTotalMoney = countCouponPayMoney(orderDetail);
return payTotalMoney;
}
private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
BigDecimal coupon = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.COUPON).getUserCoupon().getCoupon();
System.out.println(&quot; 优惠券金额:&quot; + coupon);
orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(coupon));
return orderDetail.getPayMoney();
}
}
/**
* 计算使用红包后的金额
* @author admin
*
*/
public class RedPacketDecorator extends BaseCountDecorator{
public RedPacketDecorator(IBaseCount count) {
super(count);
}
public BigDecimal countPayMoney(OrderDetail orderDetail) {
BigDecimal payTotalMoney = new BigDecimal(0);
payTotalMoney = super.countPayMoney(orderDetail);
payTotalMoney = countCouponPayMoney(orderDetail);
return payTotalMoney;
}
private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
BigDecimal redPacket = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.REDPACKED).getUserRedPacket().getRedPacket();
System.out.println(&quot; 红包优惠金额:&quot; + redPacket);
orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(redPacket));
return orderDetail.getPayMoney();
}
}
</code></pre>
<p>最后,我们通过一个工厂类来组合商品的促销类型:</p>
<pre><code>/**
* 计算促销后的支付价格
* @author admin
*
*/
public class PromotionFactory {
public static BigDecimal getPayMoney(OrderDetail orderDetail) {
// 获取给商品设定的促销类型
Map&lt;PromotionType, SupportPromotions&gt; supportPromotionslist = orderDetail.getMerchandise().getSupportPromotions();
// 初始化计算类
IBaseCount baseCount = new BaseCount();
if(supportPromotionslist!=null &amp;&amp; supportPromotionslist.size()&gt;0) {
for(PromotionType promotionType: supportPromotionslist.keySet()) {// 遍历设置的促销类型,通过装饰器组合促销类型
baseCount = protmotion(supportPromotionslist.get(promotionType), baseCount);
}
}
return baseCount.countPayMoney(orderDetail);
}
/**
* 组合促销类型
* @param supportPromotions
* @param baseCount
* @return
*/
private static IBaseCount protmotion(SupportPromotions supportPromotions, IBaseCount baseCount) {
if(supportPromotions.getPromotionType()==PromotionType.COUPON) {
baseCount = new CouponDecorator(baseCount);
}else if(supportPromotions.getPromotionType()==PromotionType.REDPACKED) {
baseCount = new RedPacketDecorator(baseCount);
}
return baseCount;
}
}
public static void main( String[] args ) throws InterruptedException, IOException
{
Order order = new Order();
init(order);
for(OrderDetail orderDetail: order.getList()) {
BigDecimal payMoney = PromotionFactory.getPayMoney(orderDetail);
orderDetail.setPayMoney(payMoney);
System.out.println(&quot; 最终支付金额:&quot; + orderDetail.getPayMoney());
}
}
</code></pre>
<p>运行结果:</p>
<pre><code>商品原单价金额为20
优惠券金额3
红包优惠金额10
最终支付金额7
</code></pre>
<p>以上源码可以通过 <a href="https://github.com/nickliuchao/decorator.git">Github</a> 下载运行。通过以上案例可知:使用装饰器模式设计的价格优惠策略,实现各个促销类型的计算功能都是相互独立的类,并且可以通过工厂类自由组合各种促销类型。</p>
<h2>总结</h2>
<p>这讲介绍的装饰器模式主要用来优化业务的复杂度,它不仅简化了我们的业务代码,还优化了业务代码的结构设计,使得整个业务逻辑清晰、易读易懂。</p>
<p>通常,装饰器模式用于扩展一个类的功能,且支持动态添加和删除类的功能。在装饰器模式中,装饰类和被装饰类都只关心自身的业务,不相互干扰,真正实现了解耦。</p>
<h2>思考题</h2>
<p>责任链模式、策略模式与装饰器模式有很多相似之处。平时,这些设计模式除了在业务中被用到以外,在架构设计中也经常被用到,你是否在源码中见过这几种设计模式的使用场景呢?欢迎你与大家分享。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/Java并发编程实战/29 生产者消费者模式:电商库存设计优化.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/Java并发编程实战/31 答疑课堂:模块五思考题集锦.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099719f58cb3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>