商城购买会员打折满减优惠券商品

news/2024/12/29 17:13:42/

文章目录

  • 前言
  • 一、代码结构
  • 二、UML图
  • 三、代码实现
    • 3.1.domain
    • 3.2.enums
    • 3.3.strategy
    • 3.4.service
    • 3.5.config
  • 四、单元测试
  • 五、模式应用
  • 六、问题及优化思路
    • 6.1.问题
    • 6.2.优化
  • 总结


前言

使用策略模式、工厂方法模式、单例模式实现一些购买策略,需求:商城商品根据用户是否是会员,如果是会员则根据会员等级进行商品原价折扣,再根据商品是否参加满减,如果参加满减则对同一类型满减的商品进行合计去掉满减金额,然后再根据用户是否使用优惠券,在进行则扣。


一、代码结构

在这里插入图片描述


二、UML图

在这里插入图片描述


三、代码实现

3.1.domain

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;/*** 优惠券** @author 28382*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Coupon {/*** 优惠金额*/private BigDecimal amount;}
import com.mxf.code.product_coupon.enums.ProductTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author mxf* @version 1.0* @description: 满减* @date 2023/6/9*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FullReduction {private String productType;private List<FullReductionItem> fullReductionItemList;public static final Map<String, List<FullReductionItem>> FULL_REDUCTION_BY_PRODUCT_TYPE_MAP;// 默认数据static {FULL_REDUCTION_BY_PRODUCT_TYPE_MAP = new HashMap<>();List<FullReductionItem> fullReductionItemList = new ArrayList<>();fullReductionItemList.add(new FullReductionItem(new BigDecimal("300"), new BigDecimal("30")));fullReductionItemList.add(new FullReductionItem(new BigDecimal("200"), new BigDecimal("10")));FULL_REDUCTION_BY_PRODUCT_TYPE_MAP.put(ProductTypeEnum.PRODUCT_TYPE1.getType(), fullReductionItemList);List<FullReductionItem> fullReductionItemList2 = new ArrayList<>();fullReductionItemList2.add(new FullReductionItem(new BigDecimal("100"), new BigDecimal("5")));FULL_REDUCTION_BY_PRODUCT_TYPE_MAP.put(ProductTypeEnum.PRODUCT_TYPE2.getType(), fullReductionItemList2);}
}
import lombok.AllArgsConstructor;
import lombok.Data;import java.math.BigDecimal;/*** @author mxf* @version 1.0* @description: 满减信息* @date 2023/6/9*/
@Data
@AllArgsConstructor
public class FullReductionItem {/*** 满减门槛*/private BigDecimal threshold;/*** 满减金额*/private BigDecimal reduction;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 会员** @author 28382*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {/*** 会员等级*/private int level;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;/*** 商品** @author 28382*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {/*** 商品名称*/private String name;/*** 价格*/private BigDecimal price;/*** 会员商品,可参与会员打折*/private boolean isMemberProduct;/*** 类型*/private String type;
}

3.2.enums

import lombok.Getter;import java.math.BigDecimal;
import java.util.stream.Stream;/*** @author mxf* @version 1.0* @description: 会员等级枚举* @date 2023/6/9*/
@Getter
public enum MemberDiscountEnum {/*** 非会员*/NON_MEMBER(Integer.MIN_VALUE, new BigDecimal("1")),/*** 一级会员*/DISCOUNT_RATE_LEVEL1(1,new BigDecimal("0.9")),/*** 二级会员*/DISCOUNT_RATE_LEVEL2(2,new BigDecimal("0.85")),/*** 三级会员*/DISCOUNT_RATE_LEVEL3(3,new BigDecimal("0.8"));private final Integer level;private final BigDecimal discount;MemberDiscountEnum(Integer level, BigDecimal discount) {this.level = level;this.discount = discount;}public static BigDecimal getDiscountByLevel(Integer level) {return Stream.of(MemberDiscountEnum.values()).filter(e -> e.getLevel().equals(level)).map(MemberDiscountEnum::getDiscount).findFirst().orElse(new BigDecimal("1"));}
}
import lombok.Getter;
import lombok.NoArgsConstructor;/*** @author mxf* @version 1.0* @description: 商品类型枚举* @date 2023/6/9*/
@Getter
@NoArgsConstructor
public enum ProductTypeEnum {/*** 商品类型1*/PRODUCT_TYPE1("type1", "类型1"),/*** 商品类型2*/PRODUCT_TYPE2("type2", "类型2"),/*** 商品类型3*/PRODUCT_TYPE3("type3", "类型3");private String type;private String desc;ProductTypeEnum(String type, String desc) {this.type = type;this.desc = desc;}
}

3.3.strategy

import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;import java.math.BigDecimal;/*** 折扣策略*/
public interface DiscountStrategy {BigDecimal calculateDiscountPrice(Product product, Member member);
}
import com.mxf.code.product_coupon.domain.Product;import java.math.BigDecimal;
import java.util.List;/*** 满减策略接口** @author 28382*/
public interface FullReductionStrategy {BigDecimal calculateFullReductionPrice(List<Product> productList);
}
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;
import com.mxf.code.product_coupon.enums.MemberDiscountEnum;
import com.mxf.code.product_coupon.strategy.DiscountStrategy;import java.math.BigDecimal;/*** 会员折扣策略** @author 28382*/
public class MemberDiscountStrategy implements DiscountStrategy {@Overridepublic BigDecimal calculateDiscountPrice(Product product, Member member) {return product.getPrice().multiply(MemberDiscountEnum.getDiscountByLevel(member.getLevel()));}
}
import com.mxf.code.product_coupon.domain.FullReduction;
import com.mxf.code.product_coupon.domain.FullReductionItem;
import com.mxf.code.product_coupon.domain.Product;
import com.mxf.code.product_coupon.strategy.FullReductionStrategy;import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 类型满减策略** @author 28382*/
public class FullReductionForTypeStrategy implements FullReductionStrategy {@Overridepublic BigDecimal calculateFullReductionPrice(List<Product> productList) {BigDecimal total = BigDecimal.ZERO;Map<String, List<Product>> productMap = productList.stream().collect(Collectors.groupingBy(Product::getType));for (Map.Entry<String, List<Product>> entry : productMap.entrySet()) {String productType = entry.getKey();List<Product> products = entry.getValue();BigDecimal totalProductPrice = products.stream().map(Product::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);if (FullReduction.FULL_REDUCTION_BY_PRODUCT_TYPE_MAP.containsKey(productType)) {List<FullReductionItem> fullReductionItems = FullReduction.FULL_REDUCTION_BY_PRODUCT_TYPE_MAP.get(productType);BigDecimal maxReduction = BigDecimal.ZERO;for (FullReductionItem item : fullReductionItems) {if (totalProductPrice.compareTo(item.getThreshold()) >= 0 && item.getReduction().compareTo(maxReduction) > 0) {maxReduction = item.getReduction();}}total = total.add(totalProductPrice.subtract(maxReduction));} else {total = total.add(totalProductPrice);}}return total;}
}
import com.mxf.code.product_coupon.domain.Coupon;
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;import java.math.BigDecimal;
import java.util.List;/*** AbstractPriceCalculator 抽象类* 提供了基本的价格计算方法,其中包括判断是否是会员、是否有优惠券、计算总价等方法*/
public abstract class AbstractPriceCalculator {protected DiscountStrategy discountStrategy;protected FullReductionStrategy fullReductionStrategy;public AbstractPriceCalculator(DiscountStrategy discountStrategy, FullReductionStrategy fullReductionStrategy) {this.discountStrategy = discountStrategy;this.fullReductionStrategy = fullReductionStrategy;}protected BigDecimal getDiscountPrice(Product product, Member member) {return discountStrategy.calculateDiscountPrice(product, member);}protected BigDecimal getFullReductionPrice(List<Product> productList) {return fullReductionStrategy.calculateFullReductionPrice(productList);}protected boolean isMember(Member member) {return member != null && member.getLevel() > 0;}protected boolean hasCoupon(Coupon coupon) {return coupon != null && coupon.getAmount().compareTo(BigDecimal.ZERO) > 0;}public BigDecimal calculateTotalPrice(List<Product> productList, Member member, Coupon coupon) {BigDecimal totalPrice;for (Product product : productList) {if (isMember(member) && product.isMemberProduct()) {// 会员价BigDecimal discountPrice = getDiscountPrice(product, member);product.setPrice(discountPrice);}}// 满减totalPrice = getFullReductionPrice(productList);// 优惠券if (hasCoupon(coupon)) {totalPrice = totalPrice.subtract(coupon.getAmount());}return totalPrice;}
}

/*** 价格计算器* 在需要使用价格计算器的地方注入 PriceCalculator 实例,并调用其方法计算价格* @author 28382*/
public class PriceCalculator extends AbstractPriceCalculator {public PriceCalculator(DiscountStrategy discountStrategy, FullReductionStrategy fullReductionStrategy) {super(discountStrategy, fullReductionStrategy);}
}

3.4.service

在需要使用价格计算器的地方注入 PriceCalculator 实例,并调用其方法计算价格。

import com.mxf.code.product_coupon.domain.Coupon;
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;import java.math.BigDecimal;
import java.util.List;/*** @author mxf* @version 1.0* @description: 商品业务类* @date 2023/6/12*/
public interface ProductService {BigDecimal calculatePrice(List<Product> productList, Member member, Coupon coupon);
}
import com.mxf.code.product_coupon.domain.Coupon;
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;
import com.mxf.code.product_coupon.service.ProductService;
import com.mxf.code.product_coupon.strategy.PriceCalculator;import java.math.BigDecimal;
import java.util.List;/*** 商品业务实现类** @author 28382*/
public class ProductServiceImpl implements ProductService {private final PriceCalculator priceCalculator;public ProductServiceImpl(PriceCalculator priceCalculator) {this.priceCalculator = priceCalculator;}@Overridepublic BigDecimal calculatePrice(List<Product> productList, Member member, Coupon coupon) {return priceCalculator.calculateTotalPrice(productList, member, coupon);}
}

3.5.config

import com.mxf.code.product_coupon.domain.Coupon;
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;
import com.mxf.code.product_coupon.service.ProductService;
import com.mxf.code.product_coupon.strategy.PriceCalculator;import java.math.BigDecimal;
import java.util.List;/*** 商品业务实现类** @author 28382*/
public class ProductServiceImpl implements ProductService {private final PriceCalculator priceCalculator;public ProductServiceImpl(PriceCalculator priceCalculator) {this.priceCalculator = priceCalculator;}@Overridepublic BigDecimal calculatePrice(List<Product> productList, Member member, Coupon coupon) {return priceCalculator.calculateTotalPrice(productList, member, coupon);}
}

四、单元测试

import com.mxf.code.product_coupon.config.AppConfig;
import com.mxf.code.product_coupon.domain.Coupon;
import com.mxf.code.product_coupon.domain.Member;
import com.mxf.code.product_coupon.domain.Product;
import com.mxf.code.product_coupon.service.ProductService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;import static org.junit.Assert.assertEquals;@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AppConfig.class})
public class PriceCalculatorTests {@Autowiredprivate ProductService productService;@Testpublic void testCalculateTotalPrice() {List<Product> productList = new ArrayList<>();productList.add(new Product("product1", new BigDecimal("100"), false, "type1"));productList.add(new Product("product2", new BigDecimal("150"), true, "type1"));productList.add(new Product("product3", new BigDecimal("50"), true, "type1"));Member member = new Member();member.setLevel(2);Coupon coupon = new Coupon();coupon.setAmount(new BigDecimal("20"));BigDecimal totalPrice = productService.calculatePrice(productList, member, coupon);assertEquals(new BigDecimal("240.00"), totalPrice);}
}

五、模式应用

  1. 策略模式
    DiscountStrategy 和 FullReductionStrategy 接口以及它们的实现类 MemberDiscountStrategy 和 FullReductionForTypeStrategy 是策略模式的经典应用。通过定义不同的策略接口和具体实现类,可以方便地对不同的业务逻辑进行封装,并且可以在运行时动态地替换不同的策略,从而达到更灵活、更易于扩展的效果。

    在 PriceCalculator 类的构造方法中,我们将不同的策略实例注入进来,并保存在该对象中。在计算价格的时候,我们可以根据需要选择不同的策略进行计算。

  2. 工厂方法模式
    在 AppConfig 类中,我们使用了工厂方法模式来创建不同的 Bean 对象,并将它们注册到 IoC 容器中。例如 memberDiscountStrategy() 方法和 fullReductionForTypeStrategy() 方法分别创建了 MemberDiscountStrategy 和 FullReductionForTypeStrategy 的实例,并将它们注入到 PriceCalculator 的构造方法中。

  3. 单例模式
    在 Spring Boot 中,默认情况下所有的 Bean 都是单例的。因此,我们也可以将 PriceCalculator 设计为单例模式,保证该对象在整个应用程序中只创建一次。在 AppConfig 中,我们使用 @Bean 注解来创建 PriceCalculator 实例,并指定其作用域为 singleton。


六、问题及优化思路

6.1.问题

  • 在 PriceCalculator 中,我们硬编码了两种策略的实现类。如果需要新增一种策略或者更换现有的策略,就需要修改该类的代码。这违反了开闭原则,不利于代码的维护和扩展。
  • 在策略模式中,每个具体策略实现类都需要单独创建一个对象。这可能会导致内存的浪费,尤其是在系统中存在大量不同的策略实现类时。

6.2.优化

  • 使用工厂方法模式或者依赖注入来动态地创建策略实例,从而提高代码的扩展性。例如,可以将不同的策略实现类注册到 IoC 容器中,然后在 PriceCalculator 中根据需要动态获取对应的实例。
  • 使用享元模式来共享策略对象。在该模式中,相同的对象只需要创建一次,并在需要的时候进行共享和重用。可以将策略对象保存在一个 HashMap 中,并根据需要从中获取。这样可以节约内存,同时也能提高对象的复用性和性能。

总结

这次购买商品融入了一些常用的设计模式,可扩展的同时也存在一些不足,对此也提出了一些缺点和优化建议,具体可根据策略模式那篇博文进行适当改进


http://www.ppmy.cn/news/345500.html

相关文章

jenkins显示gitlab的认证报错

描述 使用“List Git branches (and more)”功能&#xff0c;显示gitlab的分支或者标签。报错“org.eclipse.jgit.api.errors.TransportException: http://gitlab.test.com/userCenter/userCenterServer: not authorized” 排查与解决 使用的凭证再gitlab是Maintainer身份&…

Agilent/安捷伦N4010A蓝牙测试仪

产品名称:无线连接测试仪 产品型号: N4010A 厂商品牌:安捷伦 技术指标: N4010A无线连接测试仪可灵活测量最新的无线标准并提高测试效率。 N4010A是一款通用的多格式无线连接测试解决方案&#xff0c;您可以对解决方案进行配置&#xff0c;使其服务于研发、集 成、验证或制造领…

信而泰耦合测试-网络测试仪实操

一、耦合测试原理 1.产生背景​ 常用测试无线设备过程中&#xff0c;将无线设备置于屏蔽箱中&#xff0c;通过无线网卡连接并运行iperf等类似软件的方式检测所述无线设备的吞吐量。 相关技术中将所述无线设备置于所述屏蔽箱中的检测方法&#xff0c;由于所述屏蔽箱存在信号反…

oracle的操作管理sql临时笔记

打印用户下所有的表&#xff1a; 所有信息&#xff1a;select * from all_tables a where a.OWNER upper(用户名); 仅表名&#xff1a;select table_name from all_tables a where a.OWNER upper(用户名);

批处理cmd:for语句的基本用法

一、基础语法 FOR %variable IN (set) DO command [command-parameters] #cmd窗口中 FOR %%variable IN (set) DO command [command-parameters] #批处理文件中之所以要区分cmd窗口和批处理文件两种环境&#xff0c;是因为在这两种环境下&#xff0c;命令语句表现出来的行…

如何避免被 Lombok 撞车,使用时需注意哪些问题?

近年来&#xff0c;Java 开发中使用 Lombok 这个工具已经成为了一种趋势。Lombok 通过简化代码实现了对 Java 开发中常用的重复代码的自动生成&#xff0c;但是却也因为其使用不当而引起了一些问题&#xff0c;例如代码风格不一致、编译错误和运行时异常等。在本文中&#xff0…

我就知道肯定有人想要这个

http://www.jandown.com/link.php?refg0SjztI3B4 提示&#xff1a;在www.graphis.ne.jp/sample/gragal/******/index.html可以看sample&#xff08;把星号替换成“gra_”后面的字符串&#xff0c;访问显然需要那个&#xff09;

Agth 辅助工具(GAL用agth进行自动翻译工具)

GAL用agth进行自动翻译图文详解攻略&#xff08;新手必看&#xff09; 推出以来 得到很多人的好评 还有 转载 由于某些原因 图片 文章 没了 想再写一篇 听听朋友意见: 涵的秋之回忆 15:55:16 我以前看哪个教程&#xff0c;研究了1个钟头都还心头纠结 所以呢 还不如 自己写个…