工厂方法模式实战之某商场一次促销活动

目录

      • 1.5.1、前言
      • 1.5.2、实战场景简介
      • 1.5.3、开发环境
      • 1.5.4、用传统的if-else语句实现
        • 1.5.4.1、工程结构
        • 1.5.4.2、if-else需求实现
        • 1.5.4.3、测试验证
      • 1.5.5、工厂模式优化代码
        • 1.5.5.1、工程结构
        • 1.5.5.2、代码实现
          • 1.5.5.2.1、定义各种商品发放接口及接口实现
          • 1.5.5.2.2、定义工厂及统一的发放接口及接口实现
        • 1.5.5.3、测试验证
      • 1.5.6、总结

1.5.1、前言

        本文接上一篇设计模式之工厂方法模式(Factory Method Pattern)介绍工厂方法模式的原理之后接着以一个具体的应用场景(营销场景业务)如何编码实现工厂方法模式。
        为了可以让整个学习的案例更加贴近实际开发,这⾥模拟互联⽹中在营销场景下的业务。由于营销场景的复杂、多变、临时的特性,它所需要的设计需要更加深⼊,否则会经常⾯临各种紧急CRUD操作,从⽽让代码结构混乱不堪,难以维护。

1.5.2、实战场景简介

        某地一商店为了回馈老客户,针对满足条件的老客户发放兑换卡、实物商品、优惠券三种奖品。其业务场景图如下:
图片1

1.5.3、开发环境

        本文将采用Java语言,基于JDK17基础环境、Maven、Idea等工具进行开发实现。JDK环境的安装请参考JDK安装部署。具体如下:

环境名称版本号
JDK1.8.0_202
Maven3.6.3
Idea2019.3.5

1.5.4、用传统的if-else语句实现

        如果不考虑任何扩展性,只为了尽快满⾜需求,那么对这么⼏种奖励发放只需使⽤ifelse语句判断,调⽤不同的接⼝即可满⾜需求。可能这也是⼀些刚⼊⻔编程的⼩伙伴,常⽤的⽅式。接下来我们就先按照这样的⽅式来实现业务的需求。

1.5.4.1、工程结构

        我们将借助Idea工具采用多模块的SpringBoot项目的方式构建工程结构,不会借助Idea工具创建多模块的SpringBoot项目的朋友请参考博文如何借助Idea创建多模块的SpringBoot项目,这里不再赘述,最终创建的工程结构如下图所示:
工程结构
        该工程结构按照严格的Spring MVC进行分层构建,主要包括:控制层、数据库访问层、数据实体和业务对象层、接口及接口实现层、启动类、资源文件及配置文件、单元测试类、pom文件等。而本示例主要用到了奖品入参对象AwardReqDTO、奖品出参对象AwardResDTO、兑换卡发放服务接口对象CardGiveService、优惠券发放服务接口对象CouponGiveService、实物商品发放服务接口对象GoodsGiveService、奖品发放服务接口对象AwardGiveService、兑换卡发放服务接口实现对象CardGiveServiceImpl、优惠券发放服务接口实现对象CouponGiveServiceImpl、实物商品发放服务接口实现对象GoodsGiveServiceImpl、奖品发放服务接口实现对象AwardGiveServiceImpl以及一个单元测试对象AwardGiveServiceTest,工程源码下载地址。

1.5.4.2、if-else需求实现
package cn.dz.smart.service.impl;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.AwardGiveService;
import cn.dz.smart.service.CardGiveService;
import cn.dz.smart.service.CouponGiveService;
import cn.dz.smart.service.GoodsGiveService;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @ClassName AwardGiveServiceImpl.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放服务接口实现层
 * @CreateTime 2024年06月04日 22:56:00
 */
@Slf4j
@Service
public class AwardGiveServiceImpl implements AwardGiveService {

    /**
     * @Description: 发放奖品
     * @Params: param 奖品发放参数
     * @Return: 是否发放成功
     * @Author: wzd
     * @Date: 2024年6月4日23:34:41
     */
    @Override
    public AwardResDTO give(AwardReqDTO param) {
        AwardResDTO result = null;
        if (Objects.nonNull(param)){
            String json = JSONUtil.toJsonStr(param);
            log.info("开始给用户编号为:{}的用户发放奖品。参数:{}", param.getUId(), json);
            if (param.getAwardType() == 1) {
                //发放优惠券
                CouponGiveService couponGiveService = new CouponGiveServiceImpl();
                Boolean give = couponGiveService.give();
                result = new AwardResDTO();
                if (give){
                    result.setCode("000");
                    result.setMessage("优惠券发放成功。");
                } else{
                    result.setCode("001");
                    result.setMessage("优惠券发放失败。");
                }
            } else if (param.getAwardType() == 2){
                //发放实物商品
                GoodsGiveService goodsGiveService = new GoodsGiveServiceImpl();
                Boolean give = goodsGiveService.give();
                result = new AwardResDTO();
                if (give){
                    result.setCode("000");
                    result.setMessage("实物商品发放成功。");
                } else{
                    result.setCode("001");
                    result.setMessage("实物商品发放失败。");
                }
            } else if (param.getAwardType() == 3){
                //发放兑换卡
                CardGiveService cardGiveService = new CardGiveServiceImpl();
                Boolean give = cardGiveService.give();
                result = new AwardResDTO();
                if (give){
                    result.setCode("000");
                    result.setMessage("兑换卡发放成功。");
                } else{
                    result.setCode("001");
                    result.setMessage("兑换卡发放失败。");
                }
            }
        }
        return result;
    }
}
  • 如上就是使⽤ if-else ⾮常直接的实现出来业务需求的非常糟糕的代码,如果仅从业务⻆度看,研发如期甚⾄提前实现了功能。
  • 那这样的代码⽬前来看并不会有什么问题,但如果在经过⼏次的迭代和拓展,接⼿这段代码的研发将⼗分痛苦。᯿构成本⾼需要理清之前每⼀个接⼝的使⽤,测试回归验证时间⻓,需要全部验证⼀次。这也就是很多⼈并不愿意接⼿别⼈的代码,如果接⼿了⼜被压榨开发时间。那么可想⽽知这样的 if-else 还会继续增加。这也是if-else实现的致命缺陷,因为每增加一个奖品发放类型就得增加一段if-else语句逻辑,也得重新测试回归验证一次。
1.5.4.3、测试验证

        写⼀个单元测试来验证上⾯编写的接⼝⽅式,养成单元测试的好习惯会为你增强代码质量。单元测试类代码如下:

package cn.dz.smart.test;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.AwardGiveService;
import cn.dz.smart.service.impl.AwardGiveServiceImpl;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @ClassName AwardGiveServiceTest.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放测试类
 * @CreateTime 2024年06月04日 22:58:00
 */
@Slf4j
@SpringBootTest
public class AwardGiveServiceTest {

    /**
     * @Description: 奖品发放单元测试方法
     * @Params: 无
     * @Return: 无
     * @Author: wzd
     * @Date: 2024年6月15日22:19:50
     */
    @Test
    public void testGive(){
        AwardGiveService awardGiveService = new AwardGiveServiceImpl();
        System.out.println("111");
        log.info("模拟发放优惠券测试");
        AwardReqDTO param1 = new AwardReqDTO();
        param1.setUId("10001");
        param1.setAwardType(1);
        param1.setAwardCode("EGM1023938910232121323432");
        param1.setBizId("791098764902132");
        AwardResDTO give1 = awardGiveService.give(param1);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param1));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give1));

        log.info("模拟发放实物商品测试");
        AwardReqDTO param2 = new AwardReqDTO();
        param2.setUId("10002");
        param2.setAwardType(2);
        param2.setAwardCode("9820198721311");
        param2.setBizId("1023000020112221113");
        AwardResDTO give2 = awardGiveService.give(param2);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param2));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give2));

        log.info("模拟发放兑换卡测试");
        AwardReqDTO param3 = new AwardReqDTO();
        param3.setUId("10003");
        param3.setAwardType(3);
        param3.setAwardCode("AQY1xjk8LO975YUio");
        param3.setBizId("102300002011");
        AwardResDTO give3 = awardGiveService.give(param3);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param3));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give3));
    }
}

        测试结果如下:

模拟发放优惠券测试
开始给用户编号为:10001的用户发放奖品。参数:{"awardCode":"EGM1023938910232121323432","uId":"10001","bizId":"791098764902132","awardType":1}
请求参数:{"awardCode":"EGM1023938910232121323432","uId":"10001","bizId":"791098764902132","awardType":1}
 测试结果:{"code":"000","message":"优惠券发放成功。"}
模拟发放实物商品测试
开始给用户编号为:10002的用户发放奖品。参数:{"awardCode":"9820198721311","uId":"10002","bizId":"1023000020112221113","awardType":2}
请求参数:{"awardCode":"9820198721311","uId":"10002","bizId":"1023000020112221113","awardType":2}
测试结果:{"code":"000","message":"实物商品发放成功。"}
模拟发放兑换卡测试
开始给用户编号为:10003的用户发放奖品。参数:{"awardCode":"AQY1xjk8LO975YUio","uId":"10003","bizId":"102300002011","awardType":3}
请求参数:{"awardCode":"AQY1xjk8LO975YUio","uId":"10003","bizId":"102300002011","awardType":3}
 测试结果:{"code":"000","message":"兑换卡发放成功。"}
  • 运⾏结果正常,满⾜当前所有业务产品需求,写的还很快。但!实在难以为维护!

1.5.5、工厂模式优化代码

        接下来使⽤⼯⼚⽅法模式来进⾏代码优化,也算是⼀次很⼩的重构。整理᯿构会你会发现代码结构清晰了、也具备了下次新增业务需求的扩展性。但在实际使⽤中还会对此进⾏完善,⽬前的只是抽离出最核⼼的部分体现到你⾯前,⽅便学习。

1.5.5.1、工程结构

        工程结构与上面的if-else实现的工程结构类似,只是新增了公共接口对象CommodityService和工厂对象AwardFactory,具体如下:
工程结构1

  • ⾸先,从上⾯的⼯程结构中你是否⼀些感觉,⽐如:它看上去清晰了、这样分层可以更好扩展了、似乎可以想象到每⼀个类做了什么。
  • 如果还不能理解为什么这样修改,也没有关系。因为你是在通过这样的⽂章,来学习设计模式的魅⼒。并且再获取源码后,进⾏实际操作⼏次也就慢慢掌握了⼯⼚模式的技巧,,工程源码下载地址。
1.5.5.2、代码实现
1.5.5.2.1、定义各种商品发放接口及接口实现

        定义公共的奖品发奖接⼝,代码具体如下:

package cn.dz.smart.service;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;

/**
 * @ClassName CommodityService.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放服务接口层
 * @CreateTime 2024年06月15日 21:47:00
 */
public interface CommodityService {

    /**
     * 奖品发放接口
     */
    AwardResDTO give(AwardReqDTO param);
}
  • 所有的奖品⽆论是实物、虚拟还是第三⽅兑换卡,都需要通过我们的程序实现此接⼝进⾏处理,以保证最终⼊参出参的统⼀性。

  • 接⼝的⼊参包括; ⽤户ID 、 奖品ID 、 业务ID 以及 扩展字段 ⽤于处理发放实物商品时的收货地址等。
            定义各类奖品发奖接⼝对象及接口实现对象,代码具体如下:

  • 兑换卡发放服务接口

package cn.dz.smart.service;

/**
 * @ClassName CardGiveService.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 兑换卡发放服务接口层
 * @CreateTime 2024年06月15日 21:51:00
 */
public interface CardGiveService extends CommodityService {
}
  • 兑换卡发放服务接口实现
package cn.dz.smart.service.impl;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.CardGiveService;
import org.springframework.stereotype.Service;

/**
 * @ClassName CardGiveServiceImpl.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 兑换卡发放服务接口实现层
 * @CreateTime 2024年06月15日 21:53:00
 */
@Service
public class CardGiveServiceImpl implements CardGiveService {

    /**
     * @Description: 发放兑换卡
     * @Params: 无
     * @Return: 是否发放成功
     * @Author: wzd
     * @Date: 2024年6月15日21:58:29
     */
    @Override
    public AwardResDTO give(AwardReqDTO param) {
        return new AwardResDTO("000","兑换卡发放成功。");
    }
}
  • 优惠券发放服务接口
package cn.dz.smart.service;

/**
 * @ClassName CouponGiveService.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 优惠券发放服务接口层
 * @CreateTime 2024年06月15日 21:52:00
 */
public interface CouponGiveService extends CommodityService {
}
  • 优惠券发放服务接口实现
package cn.dz.smart.service.impl;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.CouponGiveService;
import org.springframework.stereotype.Service;

/**
 * @ClassName CouponGiveServiceImpl.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 优惠券发放服务接口实现层
 * @CreateTime 2024年06月15日 21:57:00
 */
@Service
public class CouponGiveServiceImpl implements CouponGiveService {

    /**
     * @Description: 发放优惠券
     * @Params: 无
     * @Return: 是否发放成功
     * @Author: wzd
     * @Date: 2024年6月15日21:58:29
     */
    @Override
    public AwardResDTO give(AwardReqDTO param) {
        return new AwardResDTO("000","优惠券发放成功。");
    }
}
  • 实物商品发放服务接口
package cn.dz.smart.service;

/**
 * @ClassName GoodsGiveService.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 实物商品发放服务接口层
 * @CreateTime 2024年06月15日 21:52:00
 */
public interface GoodsGiveService extends CommodityService {
}
  • 实物商品发放服务接口实现
package cn.dz.smart.service.impl;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.GoodsGiveService;
import org.springframework.stereotype.Service;

/**
 * @ClassName GoodsGiveServiceImpl.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 实物商品发放服务接口实现层
 * @CreateTime 2024年06月15日 21:59:00
 */
@Service
public class GoodsGiveServiceImpl implements GoodsGiveService {

    /**
     * @Description: 发放实物商品
     * @Params: 无
     * @Return: 是否发放成功
     * @Author: wzd
     * @Date: 2024年6月15日21:58:29
     */
    @Override
    public AwardResDTO give(AwardReqDTO param) {
        return new AwardResDTO("000","实物商品发放成功。");
    }
}
  • 从上⾯可以看到每⼀种奖品的实现都包括在⾃⼰的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。
  • 后续在新增的奖品只需要按照此结构进⾏填充即可,⾮常易于维护和扩展。
  • 在统⼀了⼊参以及出参后,调⽤⽅不在需要关⼼奖品发放的内部逻辑,按照统⼀的⽅式即可处理。
1.5.5.2.2、定义工厂及统一的发放接口及接口实现

        定义工厂,代码具体如下:

package cn.dz.smart.factory;

import cn.dz.smart.service.CommodityService;
import cn.dz.smart.service.impl.CardGiveServiceImpl;
import cn.dz.smart.service.impl.CouponGiveServiceImpl;
import cn.dz.smart.service.impl.GoodsGiveServiceImpl;

import java.util.Objects;

/**
 * @ClassName AwardFactory.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 商店奖品工厂类
 * @CreateTime 2024年06月15日 22:04:00
 */
public class AwardFactory {

    /**
    * @Description: 根据奖品类型创建奖品发放工厂服务类
    * @Params: awardType 奖品类型,1优惠券、2实物商品、3第三⽅兑换卡
    * @Return: 奖品发放工厂服务类
    * @Author: wzd
    * @Date:
    */
    public CommodityService getAwardFactory(Integer awardType){
        if (Objects.isNull(awardType)) return null;
        if (1==awardType) return new CouponGiveServiceImpl();
        if (2==awardType) return new GoodsGiveServiceImpl();
        if (3==awardType) return new CardGiveServiceImpl();
        throw new RuntimeException("不存在的商品服务类型");
    }
}

        定义统一的发放接口,代码具体如下:

package cn.dz.smart.service;

/**
 * @ClassName AwardGiveService.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放服务接口层
 * @CreateTime 2024年06月15日 22:13:00
 */
public interface AwardGiveService extends CommodityService {
}

        定义统一的发放接口实现,代码具体如下:

package cn.dz.smart.service.impl;

import cn.dz.smart.factory.AwardFactory;
import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.AwardGiveService;
import cn.dz.smart.service.CommodityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @ClassName AwardGiveServiceImpl.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放服务接口实现层
 * @CreateTime 2024年06月15日 22:11:00
 */
@Slf4j
@Service
public class AwardGiveServiceImpl implements AwardGiveService {

    /**
     * @Description: 发放奖品
     * @Params: param 奖品发放参数
     * @Return: 是否发放成功
     * @Author: wzd
     * @Date: 2024年6月15日22:15:08
     */
    @Override
    public AwardResDTO give(AwardReqDTO param) {
        //实例化工厂
        AwardFactory factory = new AwardFactory();
        //根据奖品类型创建对应的奖品发放服务类
        CommodityService service = factory.getAwardFactory(param.getAwardType());
        if (Objects.isNull(service)) return null;
        return service.give(param);
    }
}
  • 这⾥我们定义了⼀个工厂类,在⾥⾯按照类型实现各种商品的服务。可以⾮常⼲净整洁的处理你的代码,后续新增的商品在这⾥扩展即可。如果你不喜欢 if 判断,也可以使⽤ switch 或者 map 配置结构,会让代码更加⼲净。
  • 另外很多代码检查软件和编码要求,不喜欢if语句后⾯不写扩展,这⾥是为了更加⼲净的向你体现
    逻辑。在实际的业务编码中可以添加括号。
1.5.5.3、测试验证

        单元测试类代码如下:

package cn.dz.smart.test;

import cn.dz.smart.pojo.dto.AwardReqDTO;
import cn.dz.smart.pojo.dto.AwardResDTO;
import cn.dz.smart.service.AwardGiveService;
import cn.dz.smart.service.impl.AwardGiveServiceImpl;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @ClassName AwardGiveServiceTest.java
 * @Author wzd
 * @Version 1.0.0
 * @Description 奖品发放测试类
 * @CreateTime 2024年06月15日 22:19:00
 */
@Slf4j
@SpringBootTest
public class AwardGiveServiceTest {

    /**
     * @Description: 奖品发放单元测试方法
     * @Params: 无
     * @Return: 无
     * @Author: wzd
     * @Date: 2024年6月15日22:19:50
     */
    @Test
    public void testGive(){
        AwardGiveService awardGiveService = new AwardGiveServiceImpl();
        System.out.println("111");
        log.info("模拟发放优惠券测试");
        AwardReqDTO param1 = new AwardReqDTO();
        param1.setUId("10001");
        param1.setAwardType(1);
        param1.setAwardCode("EGM1023938910232121323432");
        param1.setBizId("791098764902132");
        AwardResDTO give1 = awardGiveService.give(param1);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param1));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give1));

        log.info("模拟发放实物商品测试");
        AwardReqDTO param2 = new AwardReqDTO();
        param2.setUId("10002");
        param2.setAwardType(2);
        param2.setAwardCode("9820198721311");
        param2.setBizId("1023000020112221113");
        AwardResDTO give2 = awardGiveService.give(param2);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param2));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give2));

        log.info("模拟发放兑换卡测试");
        AwardReqDTO param3 = new AwardReqDTO();
        param3.setUId("10003");
        param3.setAwardType(3);
        param3.setAwardCode("AQY1xjk8LO975YUio");
        param3.setBizId("102300002011");
        AwardResDTO give3 = awardGiveService.give(param3);
        log.info("请求参数:{}", JSONUtil.toJsonStr(param3));
        log.info("测试结果:{}", JSONUtil.toJsonStr(give3));
    }
}

        测试结果如下:

模拟发放优惠券测试
开始给用户编号为:10001的用户发放奖品。参数:{"awardCode":"EGM1023938910232121323432","uId":"10001","bizId":"791098764902132","awardType":1}
请求参数:{"awardCode":"EGM1023938910232121323432","uId":"10001","bizId":"791098764902132","awardType":1}
 测试结果:{"code":"000","message":"优惠券发放成功。"}
模拟发放实物商品测试
开始给用户编号为:10002的用户发放奖品。参数:{"awardCode":"9820198721311","uId":"10002","bizId":"1023000020112221113","awardType":2}
请求参数:{"awardCode":"9820198721311","uId":"10002","bizId":"1023000020112221113","awardType":2}
测试结果:{"code":"000","message":"实物商品发放成功。"}
模拟发放兑换卡测试
开始给用户编号为:10003的用户发放奖品。参数:{"awardCode":"AQY1xjk8LO975YUio","uId":"10003","bizId":"102300002011","awardType":3}
请求参数:{"awardCode":"AQY1xjk8LO975YUio","uId":"10003","bizId":"102300002011","awardType":3}
 测试结果:{"code":"000","message":"兑换卡发放成功。"}
  • 运⾏结果正常,既满⾜了业务产品需求,也满⾜了⾃⼰对代码的追求。这样的代码部署上线运⾏,内⼼不会恐慌,不会觉得半夜会有电话。
  • 另外从运⾏测试结果上也可以看出来,在进⾏封装后可以⾮常清晰的看到⼀整套发放奖品服务的完整性,统⼀了⼊参、统⼀了结果。

1.5.6、总结

  • 从上到下的优化来看,⼯⼚⽅法模式并不复杂,甚⾄这样的开发结构在你有所理解后,会发现更加简单了。
  • 那么这样的开发的好处知道后,也可以总结出来它的优点; 避免创建者与具体的产品逻辑耦合 、 满足单⼀职责,每⼀个业务逻辑实现都在所属⾃⼰的类中完成 、 满足开闭原则,无需更改使⽤调⽤⽅就可以在程序中引⼊新的产品类型 。但这样也会带来⼀些问题,⽐如有⾮常多的奖品类型,那么实现的子类会极速扩张。因此也需要使⽤其他的模式进⾏优化,这些在后续的设计模式中会逐步涉及到。
  • 从案例入手看设计模式往往要⽐看理论学的更加容易,因为案例是缩短理论到上⼿的最佳⽅式,如果你已经有所收获,⼀定要去尝试实操。
  • 完整的工程源码下载地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/714296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

项目经理,请勇敢Say No~

为什么要say no? 培养say no的勇气 优雅的say no! say no 三部曲,项目经理,你准备好了吗? 为什么要say no? 保护项目完整性的屏障 项目管理的核心在于平衡时间、成本与质量三大要素,任何一项的…

STL——set、map、multiset、multimap的介绍及使用

文章目录 关联式容器键值对树形结构与哈希结构setset的介绍set的使用set的模板参数列表set的构造set的使用set的迭代器使用演示 multisetmultiset演示 mapmap的定义方式map的插入map的查找map的[ ]运算符重载map的迭代器遍历multimapmultimap的介绍multimap的使用 在OJ中的使用…

全球“抱团”美股,美股“抱团”AI

内容提要 过去一个月内,全球约有300亿美元新资金流入股票基金,其中高达94%投向了美国资产;一季度,海外投资者购入了1870亿美元美国公司债券,同比增长61%。 文章正文 尽管美国面临债务问题和大选带来的政治分歧&#…

索引-定义、创建(CREATE INDEX)、删除(DROP INDEX)

一、概述 1、索引是SQL语言定义的一种数据对象,是大多数DBMS为数据库中基本表创建的一种辅助存取结构,用于响应特定查询条件进行查询时的查询速度,DBMS根据查询条件从数据库文件中,选择出一条或者多条数据记录以供检索&#xff0…

【JS重点17】原型继承

目录 一:什么是原型继承 二:通过赋值方式实现原型继承 三:通过构造函数实现原型继承 四:如何赚钱 一:什么是原型继承 通过往构造函数上的原型对象添加属性和方法,再new一个实例对象,从而实例…

18. 第十八章 继承

18. 继承 和面向对象编程最常相关的语言特性就是继承(inheritance). 继承值得是根据一个现有的类型, 定义一个修改版本的新类的能力. 本章中我会使用几个类来表达扑克牌, 牌组以及扑克牌性, 用于展示继承特性.如果你不玩扑克, 可以在http://wikipedia.org/wiki/Poker里阅读相关…

CSS期末复习速览(二)

1.元素显示模式分为三种&#xff1a;块元素&#xff0c;行内元素&#xff0c;行内块元素 2.块元素&#xff1a;常见的块元素&#xff1a;<h1>~<h6> <p> <div> <ul> <ol> <li>&#xff0c;特点&#xff1a;自己独占一行&a…

需求:如何给文件添加水印

今天给大家介绍一个简单易用的水印添加框架&#xff0c;框架抽象了各个文件类型的对于水印添加的方法。仅使用几行代码即可为不同类型的文件添加相同样式的水印。 如果你有给PDF、图片添加水印的需求&#xff0c;EasyWatermark是一个很好的选择&#xff0c;主要功能就是传入一…

嵌入式实训day5

1、 from machine import Pin import time # 定义按键引脚控制对象 key1 Pin(27,Pin.IN, Pin.PULL UP) key2 Pin(26,Pin.IN, Pin.PULL UP)led1 Pin(15,Pin.ouT, value0) led2 Pin(2,Pin.ouT, value0) led3 Pin(0,Pin.ouT, value0) # 定义key1按键中断处理函数 def key1 ir…

2.线上论坛项目

一、项目介绍 线上论坛 相关技术&#xff1a;SpringBootSpringMvcMybatisMysqlSwagger项目简介&#xff1a;本项目是一个功能丰富的线上论坛&#xff0c;用户可编辑、发布、删除帖子&#xff0c;并评论、点赞。帖子按版块分类&#xff0c;方便查找。同时&#xff0c;用户可以…

【CT】LeetCode手撕—121. 买卖股票的最佳时机

目录 题目1- 思路2- 实现⭐121. 买卖股票的最佳时机——题解思路 2- ACM实现 题目 原题连接&#xff1a;121. 买卖股票的最佳时机 1- 思路 模式识别 模式1&#xff1a;只能某一天买入 ——> 买卖一次 ——> dp 一次的最大利润 动规五部曲 1.定义dp数组&#xff0c;确…

跻身中国市场前三,联想服务器的“智变”与“质变”

IDC发布的《2024年第一季度中国x86服务器市场报告》显示&#xff0c;联想服务销售额同比增长200.2%&#xff0c;在前十厂商中同比增速第一&#xff0c;并跻身中国市场前三&#xff0c;迈入算力基础设施“第一阵营”。 十年砺剑联想梦&#xff0c;三甲登榜领风骚。探究联想服务器…

IDEA模版快速生成Java方法体

新建模版组myLive 在模版组下新建模版finit 在模版text内输入以下脚本 LOGGER.info("$className$.$methodName$>$parmas1$", $parmas2$); try {} catch (Exception e) {LOGGER.error("$className$.$methodName$>error:", e); }LOGGER.info("$c…

redis未授权到getshell

0 前言 现在是redis数据库未授权访问到getshell的部分了,不好意思&#xff0c;因为个人原因&#xff0c;和上篇mysql的getshell文章间隔较久. 1 漏洞产生原因 redis安装完之后&#xff0c;默认情况下绑定在 0.0.0.0:6379&#xff0c;且没有对登录IP做限制&#xff0c;并且没…

T113 Tina5.0 添加板级支持包

文章目录 环境介绍添加板级支持包修改板级文件验证总结 环境介绍 硬件&#xff1a;韦东山T113工业板 软件&#xff1a;全志Tina 5.0 添加板级支持包 进入源码目录<SDK>/device/config/chips/t113/configs&#xff0c;可以看到有如下文件夹&#xff1a; 复制一份evb1_…

python15 数据类型 集合类型

集合类型 无序的不重复元素序列 集合中只能存储不可变的数据类型 声明集合 使用 {} 定义 与列表&#xff0c;字典一样&#xff0c;都是可变数据类型 代码 集合类型 无序的不重复元素序列 集合中只能存储不可变的数据类型 声明集合 使用 大括号{} 定义 与列表&#xff0c;字典一…

linux驱动学习(十)之内存管理

一、linux内核启动过程中&#xff0c;关于内存信息 1、内核的内存的分区 [ 0.000000] Memory: 1024MB 1024MB total ---> 1G [ 0.000000] Memory: 810820k/810820k available, 237756k reserved, 272384K highmem [ 0.000000] Virtual kernel memory layout: 内…

UnityAPI学习之碰撞检测与触发检测

碰撞检测 发生碰撞检测的前提&#xff1a; 1. 碰撞的物体需要有Rigidbody组件和boxcllidder组件 2. 被碰撞的物体需要有boxcollider组件 示例1&#xff1a;被碰撞的物体拥有Rigidbody组件 两个物体会因为都具有刚体的组件而发生力的作用&#xff0c;如下图所示&#xff0c…

人工智能模型组合学习的理论和实验实践

组合学习&#xff0c;即掌握将基本概念结合起来构建更复杂概念的能力&#xff0c;对人类认知至关重要&#xff0c;特别是在人类语言理解和视觉感知方面。这一概念与在未观察到的情况下推广的能力紧密相关。尽管它在智能中扮演着核心角色&#xff0c;但缺乏系统化的理论及实验研…

Elasticsearch 8.1官网文档梳理 - 十一、Ingest pipelines(管道)

Ingest pipelines 管道&#xff08;Ingest pipelines&#xff09;可让让数据在写入前进行常见的转换。例如可以利用管道删除文档&#xff08;doc&#xff09;的字段、或从文本中提取数据、丰富文档&#xff08;doc&#xff09;的字段等其他操作。 管道&#xff08;Ingest pip…