Code Monkey home page Code Monkey logo

blog's Introduction

blog's People

Contributors

phoenix-wong avatar

Watchers

 avatar

blog's Issues

设计模式-责任链模式

责任链模式

1. 什么是责任链

责任链设计模式是一种行为型设计模式,其主要目的是解耦请求发送者和请求接收者,让多个对象都有机会处理请求,从而避免请求发送者和接收者之间的紧耦合。

责任链模式的核心是一个链式结构,链中每个节点代表一个处理者对象,请求先经过第一个节点处理,如果该节点能够处理请求,则直接返回处理结果;否则,请求继续往下一个节点传递,直到找到能够处理该请求的节点为止。整个过程类似于流水线上的多个工作站,每个工作站负责一项工作,如果自己处理不了,就将工作交给下一个工作站,直到整个工作完成。

在责任链模式中,每个处理者对象都有一个指向下一个处理者对象的引用,这样就形成了一个处理者链。请求发送者只需要将请求发送给第一个节点即可,而不用关心请求会被哪个处理者对象处理。由于每个处理者对象都有机会处理请求,因此责任链模式可以实现请求的动态分配。

2. 优缺点

责任链模式的优点在于,它可以动态地添加、删除和调整处理者对象,从而灵活地构建处理链。同时,它也避免了请求发送者和接收者之间的紧耦合,增强了系统的灵活性和可扩展性。

不过,责任链模式也有一定的缺点,例如如果处理链过长或者处理时间过长,可能会对系统性能产生一定的影响。

3. 应用场景

在实际应用中,责任链模式常用于请求的预处理、请求的过滤、请求的分发等场景。例如,可以使用责任链模式来实现权限校验、日志记录、异常处理、请求重试等功能。同时,也可以将责任链模式与其他设计模式结合起来,例如装饰器模式、工厂模式、观察者模式等,从而实现更复杂的功能。

下单场景实战

1. 下单前置校验

在电商系统下单接口中,前置校验是非常重要的环节。下面是一个可能的校验步骤列表:

检查商品信息是否存在,包括商品名称、价格、规格等信息。
检查购买数量是否合法,是否超出了最大购买数量或最小购买数量的限制。
检查商品库存是否充足,以确保库存足够满足购买者的需求。
检查购买者的优惠券、积分等是否可以使用,以确保购买者能够享受相应的优惠或积分奖励。
检查收货地址信息是否完整和准确,以确保商品能够顺利地送达给购买者。
检查下单时间是否合法,例如检查购买者是否在限定的时间范围内下单。
真实电商场景中,验证逻辑绝对不仅仅是这些,过之而无不及。

对于完成这些前置校验逻辑,大部分程序员可能的代码思路如下:

public String createOrder(CreateOrderReqDTO xxx) {
    // 检查商品信息是否存在,包括商品名称、价格、规格等信息
  	// 检查购买数量是否合法,是否超出了最大购买数量或最小购买数量的限制
    // 检查商品库存是否充足,以确保库存足够满足购买者的需求
    // 检查购买者的优惠券、积分等是否可以使用,以确保购买者能够享受相应的优惠或积分奖励
    // 检查收货地址信息是否完整和准确,以确保商品能够顺利地送达给购买者
    // 检查下单时间是否合法,例如检查购买者是否在限定的时间范围内下单
    // ......
}

解决前置校验需求需要实现一堆逻辑,常常需要写上几百上千行代码。

为了避免这种代码臃肿的情况,我们可以运用责任链设计模式,对下单验证逻辑进行抽象。

2. 责任链重构

定义一个责任链处理器接口,所有子任务都实现该接口以处理具体的业务逻辑。

同时,为了方便对责任链流程中的任务进行顺序处理,我们需要继承 Spring 框架中的排序接口 Ordered。这将有助于保证责任链中的任务顺序执行。

public interface OrderCreateChainHandler<T> extends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);
}

public interface OrderCreateChainHandler<T> extends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);
}
创建个责任链上下文容器,用于存储与责任链相应的子任务。

public final class OrderCreateChainContext<T> implements CommandLineRunner {
    
    private final List<OrderCreateChainHandler> orderCreateChainHandlerContainer = new ArrayList();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(T requestParam) {
        // 此处根据 Ordered 实际值进行排序处理
        orderCreateChainHandlerContainer.stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
      	// 通过 Spring 上下文容器,获取所有 CreateOrderChainContext Bean
        Map<String, OrderCreateChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(OrderCreateChainHandler.class);
      	// 将对应 Bean 放入责任链上下文容器中
        chainFilterMap.forEach((beanName, bean) -> orderCreateChainHandlerContainer.add(bean););
    }
}

public final class OrderCreateChainContext<T> implements CommandLineRunner {
    
    private final List<OrderCreateChainHandler> orderCreateChainHandlerContainer = new ArrayList();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(T requestParam) {
        // 此处根据 Ordered 实际值进行排序处理
        orderCreateChainHandlerContainer.stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
      	// 通过 Spring 上下文容器,获取所有 CreateOrderChainContext Bean
        Map<String, OrderCreateChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(OrderCreateChainHandler.class);
      	// 将对应 Bean 放入责任链上下文容器中
        chainFilterMap.forEach((beanName, bean) -> orderCreateChainHandlerContainer.add(bean););
    }
}
实现 OrderCreateChainHandler 接口作为责任链处理器,每个具体的实现类负责执行特定的逻辑。

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}
通过责任链模式优化,创建订单接口前置校验代码从上千行缩减为行。

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderCreateChainContext<OrderCreateCommand> orderCreateChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        orderCreateChainContext.handler(requestParam);
    }
}

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderCreateChainContext<OrderCreateCommand> orderCreateChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        orderCreateChainContext.handler(requestParam);
    }
}

经过责任链模式的重构,你是否发现业务逻辑变得更加清晰易懂了?采用这种设计模式后,增加或删除相关的业务逻辑变得非常方便,不再需要担心更改上千行代码的几行代码,导致整个业务逻辑受到影响的情况。

责任链抽象

可能细心的小伙伴会发现一个问题,当业务使用越来越多的情况下,重复定义 OrderCreateChainHandler 以及 OrderCreateChainContext 会增加系统冗余代码量。

可以考虑将这两个基础类抽象出来,作为基础组件库中的通用组件,供所有系统下的业务使用,从而避免代码冗余。

如果想了解如何实现这一操作,请跟随马哥的思路继续往下阅读。

1. 抽象基础类

定义抽象责任链处理接口,等同于 OrderCreateChainHandler。

public interface AbstractChainHandler<T> extends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);
    
    /**
     * @return 责任链组件标识
     */
    String mark();
}

public interface AbstractChainHandler<T> extends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);
    
    /**
     * @return 责任链组件标识
     */
    String mark();
}

mark 方法是做什么的?接口增加 mark 方法,以便不同业务使用不同的标识。

假设项目中有两个业务场景:订单下单和用户创建都需要责任链模式去验证,那么在业务中进行调用责任链时传递不同的 mark 方法参数即可。

定义抽象责任链上下文,等同于 OrderCreateChainContext。可以看到保存责任链处理类的容器从 List 改为了 Map,这样可以方便扩展更多的不同业务责任链子类。

假设项目中有两个业务场景:订单下单和用户创建都需要责任链模式去验证,mark 就是用来进行分组,在业务中进行调用责任链时传递不同的 mark 方法参数,通过该参数找到对应的一组责任链具体实现类集合。

public final class AbstractChainContext<T> implements CommandLineRunner {
    
    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = Maps.newHashMap();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(String mark, T requestParam) {
        abstractChainHandlerContainer.get(mark).stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
        // 获取 Spring IOC 容器中所有 AbstractChainHandler 接口实现
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            if (abstractChainHandlers == null) {
                abstractChainHandlers = new ArrayList();
            }
            abstractChainHandlers.add(bean);
            // 根据 mark 标识将责任链模式分类,放入责任链容器上下文中
            abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);
        });
    }
}

这两个接口已经被放置在基础组件库中,如果业务需要使用责任链模式,则无需重新定义。

现在,让我们来看看业务代码需要编写哪些逻辑。

2. 抽象业务接口

让我们先进行头脑风暴,思考一下这个接口的用途是什么?

// 订单创建责任链过滤器
public interface OrderCreateChainFilter<T extends OrderCreateCommand> extends AbstractChainHandler<OrderCreateCommand> {
    
    @Override
    default String mark() {
        return OrderChainMarkEnum.ORDER_CREATE_FILTER.name();
    }
}

// 订单创建责任链过滤器
public interface OrderCreateChainFilter<T extends OrderCreateCommand> extends AbstractChainHandler<OrderCreateCommand> {
    
    @Override
    default String mark() {
        return OrderChainMarkEnum.ORDER_CREATE_FILTER.name();
    }
}

首先,如果没有 OrderCreateChainFilter 接口,会是什么样的场景?

由于责任链处理子类都需要依赖顶级抽象接口,因此要想知道某个业务场景下有多少具体子类是相当困难的。
由于责任链处理子类都需要实现 Mark 方法,实际上某一类责任链子类的 Mark 方法返回值是相同的。
通过在业务层面上抽象出一个具体业务责任链接口,就能很好地解决上述两个问题。

现在,让我们继续探讨责任链子类的编写。实际上,改动并不多,只需要将之前的 OrderCreateChainHandler 实现接口改为 OrderCreateChainFilter 即可。

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
				// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}

3. 业务使用

在具体业务场景中使用时,与之前相比并没有太大的差别。除了增加了 Mark 标识外,没有进行其他变更。

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final AbstractChainContext<OrderCreateCommand> abstractChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        abstractChainContext.handler(OrderChainMarkEnum.ORDER_CREATE_FILTER.name(), requestParam);
    }
}

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final AbstractChainContext<OrderCreateCommand> abstractChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        abstractChainContext.handler(OrderChainMarkEnum.ORDER_CREATE_FILTER.name(), requestParam);
    }
}

文末总结

本文详细介绍了责任链模式的概念,并通过电商下单场景模拟了真实使用场景。

为了复用责任链接口定义和上下文,我们通过抽象的方式将责任链门面接口加入到基础组件库中,实现快速接入责任链的目的。

虽然在本文中,我们没有使用 boolean 类型的返回值,而是通过异常来终止流程,但在后续的增强中,我们可以考虑添加布尔类型的返回值。

此外,我们还可以在 AbstractChainHandler 中增加是否异步执行的方法,以提高方法执行性能和减少接口响应时间。

架构设计总是在不断演进,本文的设计也有优化和进步的空间,让我们继续探索责任链模式的更多可能性。

k8s deployment.yaml配置文件字段详解

apiVersion: extensions/v1beta1  # 指定api版本,此值必须在kubectl api-versions中  
kind: Deployment  # 指定创建资源的角色/类型   
metadata:  # 资源的元数据/属性 
  name: demo  # 资源的名字,在同一个namespace中必须唯一
  namespace: default # 部署在哪个namespace中
  labels:  # 设定资源的标签
    app: nginx
    version: v1
spec: # 资源规范字段
  replicas: 1 # 声明副本数目
  revisionHistoryLimit: 3 # 保留历史版本
  selector: # 选择器
    matchLabels: # 匹配标签
      app: nginx
      version: v1
  minReadySeconds: 30 #定义新建的 Pod 经过多少秒后才被视为可用
  terminationGracePeriodSeconds: 30 #30秒内 (默认 30s) 还未完全停止,就发送 SIGKILL 信号强制杀死进程。
  progressDeadlineSeconds: 600 #升级过程中的最大时间(如果升级过程被暂停了,该时间也会同步暂停,时间不会一直增长)
  strategy: # 策略
    rollingUpdate: # 滚动更新
      maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavailable: 30% # 示在更新过程中能够进入不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
    type: RollingUpdate # 滚动更新策略
  template: # 模版
    metadata: # 资源的元数据/属性 
      annotations: # 自定义注解列表
        sidecar.istio.io/inject: "false" # 自定义注解名字
      labels: # 设定资源的标签
        app: nginx
        version: v1
    spec: # 资源规范字段
      containers:
      - name: nginx# 容器的名字   
        image: nginx:1.17.0 # 容器使用的镜像地址   
        imagePullPolicy: IfNotPresent 
        # 每次Pod启动拉取镜像策略,三个选择 Always、Never、IfNotPresent
        # Always,每次都检查;
        # Never,每次都不检查(不管本地是否有);
        # IfNotPresent,如果本地有就不检查,如果没有就拉取(手动测试时,已经 打好镜像存在docker容器中时,
	        # 使用存在不检查级别, 默认为每次都检查,然后会进行拉取新镜像,因镜像仓库不存在,导致部署失败)
        volumeMounts:       #文件挂载目录,容器内配置
        - mountPath: /data/     #容器内要挂载的目录
          name: share       #定义的名字,需要与下面vloume对应
        resources: # 资源管理
          limits: # 最大使用
            cpu: 300m # CPU,1核心 = 1000m
            memory: 500Mi # 内存,1G = 1000Mi
          requests:  # 容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
            cpu: 100m
            memory: 100Mi
        livenessProbe: # pod 内部健康检查的设置
          httpGet: # 通过httpget检查健康,返回200-399之间,则认为容器正常
            path: /healthCheck # URI地址
            port: 8080 # 端口
            scheme: HTTP # 协议
            # host: 127.0.0.1 # 主机地址
          initialDelaySeconds: 30 # 表明第一次检测在容器启动后多长时间后开始
          timeoutSeconds: 5 # 检测的超时时间
          periodSeconds: 30 # 检查间隔时间
          successThreshold: 1 # 成功门槛
          failureThreshold: 5 # 失败门槛,连接失败5次,pod杀掉,重启一个新的pod
        readinessProbe: # Pod 准备服务健康检查设置
          httpGet:
            path: /healthCheck
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 5
        #也可以用这种方法   
        #exec: 执行命令的方法进行监测,如果其退出码不为0,则认为容器正常   
        #  command:   
        #    - cat   
        #    - /tmp/health   
        #也可以用这种方法   
        #tcpSocket: # 通过tcpSocket检查健康  
        #  port: number 
        ports:
          - name: http # 名称
            containerPort: 8080 # 容器开发对外的端口 
            protocol: TCP # 协议
      imagePullSecrets: # 镜像仓库拉取密钥
        - name: harbor-certification
      volumes:      #挂载目录在本机的路径
      - name: share #对应上面的名字
        hostPath:
          path: /data   #挂载本机的路径
      affinity: # 亲和性调试
        nodeAffinity: # 节点亲和力
          requiredDuringSchedulingIgnoredDuringExecution: # pod 必须部署到满足条件的节点上
            nodeSelectorTerms: # 节点满足任何一个条件就可以
            - matchExpressions: # 有多个选项,则只有同时满足这些逻辑选项的节点才能运行 pod
              - key: beta.kubernetes.io/arch
                operator: In
                values:
                - amd64

解决强一致性和最终一致性场景

强一致性
比如订单创建和取消时对库存和优惠券系统的调用,如果不能保证强一致,可能导致库存超卖或优惠券重复使用。 对于强一致性场景,我们采用Seata的AT模式来处理,下面的示意图取自seata官方文档。
Pasted image 20230413182843

最终一致性场景
比如支付成功后通知发货系统发货,确认收货后通知积分系统发放积分,只要保证能够通知成功即可,不需要同时成功同时失败。 对于最终一致性场景,我们采用的是本地消息表方案:在本地事务中将要执行的异步操作记录在消息表中,如果执行失败,可以通过定时任务来补偿。
Pasted image 20230413182939

@EventListener事务未提交

如果并发量比较大 数据库来不及修改 状态未修改 导致后面逻辑没执行

修改方案:改用@TransactionalEventListener 确保事务提交后执行 TransactionPhase.AFTER_COMMIT

设计模式-装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许你动态地向对象添加职责而不改变其接口。这个模式通过创建一个装饰类来包裹原始类,并在装饰类中添加新的功能。

下面是一个 Java 实现装饰器模式的示例,展示了如何在不修改现有代码的情况下扩展对象的功能。

示例场景

假设我们有一个简单的接口 Coffee,表示一种咖啡饮料,并有一个具体的实现类 SimpleCoffee。我们将通过装饰器模式为咖啡添加不同的配料,如牛奶和糖。

1. 定义接口和具体实现

首先定义一个 Coffee 接口和一个 SimpleCoffee 实现:

// Coffee 接口
public interface Coffee {
    String getDescription();
    double getCost();
}

// SimpleCoffee 类
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple coffee";
    }

    @Override
    public double getCost() {
        return 5.0;
    }
}

2. 创建装饰器类

接下来,我们创建一个抽象装饰器类 CoffeeDecorator,它实现 Coffee 接口,并持有一个 Coffee 对象:

// CoffeeDecorator 抽象类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

3. 实现具体的装饰器类

现在,我们可以创建具体的装饰器类,比如 MilkDecoratorSugarDecorator,来为咖啡添加配料:

// MilkDecorator 类
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with milk";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 1.5;
    }
}

// SugarDecorator 类
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with sugar";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }
}

4. 使用装饰器

最后,我们可以使用装饰器来动态地为咖啡添加配料:

public class Main {
    public static void main(String[] args) {
        // 创建一个简单的咖啡
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println(simpleCoffee.getDescription() + " $" + simpleCoffee.getCost());

        // 添加牛奶
        Coffee milkCoffee = new MilkDecorator(simpleCoffee);
        System.out.println(milkCoffee.getDescription() + " $" + milkCoffee.getCost());

        // 添加糖
        Coffee milkAndSugarCoffee = new SugarDecorator(milkCoffee);
        System.out.println(milkAndSugarCoffee.getDescription() + " $" + milkAndSugarCoffee.getCost());
    }
}

输出结果

Simple coffee $5.0
Simple coffee, with milk $6.5
Simple coffee, with milk, with sugar $7.0

总结

通过装饰器模式,我们可以在不修改现有类的情况下,为对象动态地添加新的职责。这种模式特别适用于需要扩展对象功能但又不希望影响其他对象的场景。在这个例子中,我们使用装饰器模式为咖啡添加配料,展示了如何通过组合对象来实现功能扩展。

和生成器模式区别

装饰器模式(Decorator Pattern)和生成器模式(Builder Pattern)是两种不同的设计模式,它们解决不同类型的问题,并且有不同的用途和实现方式。以下是这两种模式的详细区别和用途:

装饰器模式 (Decorator Pattern)

定义和用途

装饰器模式是一种结构型设计模式,允许你动态地向对象添加行为,而不改变其接口。这种模式通过创建装饰类来包裹原始类,并在装饰类中添加新的功能。

主要特点

  • 动态扩展对象功能:可以在运行时通过组合不同的装饰器类,动态地为对象添加不同的行为。
  • 遵循开放/关闭原则:不需要修改现有类就能扩展功能。
  • 透明性:装饰器类实现与被装饰类相同的接口,所以在使用时透明。
  • 组合使用:可以多个装饰器类组合使用,为对象添加多个功能。

示例

以下是一个使用装饰器模式为咖啡添加配料的示例:

// Coffee 接口
public interface Coffee {
    String getDescription();
    double getCost();
}

// SimpleCoffee 类
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple coffee";
    }

    @Override
    public double getCost() {
        return 5.0;
    }
}

// CoffeeDecorator 抽象类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

// MilkDecorator 类
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with milk";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 1.5;
    }
}

// SugarDecorator 类
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with sugar";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }
}

生成器模式 (Builder Pattern)

定义和用途

生成器模式是一种创建型设计模式,允许你一步一步创建复杂对象。该模式将对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示。

主要特点

  • 复杂对象的分步骤创建:将对象创建过程分成多个步骤,使得创建过程更加清晰和可控。
  • 解耦构建过程和表示:将对象的构建过程与最终对象的表示分离,可以在不改变构建逻辑的情况下生成不同的表示。
  • 可读性和可维护性:通过分步骤的构建方式,代码的可读性和可维护性得以提高。

示例

以下是一个使用生成器模式构建复杂 House 对象的示例:

// House 类
public class House {
    private String foundation;
    private String structure;
    private String roof;
    private String interior;

    // 私有构造函数,确保对象只能通过生成器创建
    private House(HouseBuilder builder) {
        this.foundation = builder.foundation;
        this.structure = builder.structure;
        this.roof = builder.roof;
        this.interior = builder.interior;
    }

    @Override
    public String toString() {
        return "House with foundation: " + foundation + ", structure: " + structure + ", roof: " + roof + ", interior: " + interior;
    }

    // 生成器类
    public static class HouseBuilder {
        private String foundation;
        private String structure;
        private String roof;
        private String interior;

        public HouseBuilder setFoundation(String foundation) {
            this.foundation = foundation;
            return this;
        }

        public HouseBuilder setStructure(String structure) {
            this.structure = structure;
            return this;
        }

        public HouseBuilder setRoof(String roof) {
            this.roof = roof;
            return this;
        }

        public HouseBuilder setInterior(String interior) {
            this.interior = interior;
            return this;
        }

        public House build() {
            return new House(this);
        }
    }
}

// 使用生成器模式创建 House 对象
public class Main {
    public static void main(String[] args) {
        House house = new House.HouseBuilder()
                .setFoundation("Concrete")
                .setStructure("Wood")
                .setRoof("Tiles")
                .setInterior("Modern")
                .build();

        System.out.println(house);
    }
}

对比和总结

  • 目的

    • 装饰器模式:动态地为对象添加行为或职责,而不改变其接口。
    • 生成器模式:一步一步地构建复杂对象,将对象的构建过程与其表示分离。
  • 使用场景

    • 装饰器模式:当你需要在不修改现有代码的情况下扩展对象功能时。
    • 生成器模式:当你需要创建一个包含多个步骤或配置复杂对象时。
  • 实现方式

    • 装饰器模式:使用装饰器类包裹原始类,动态地添加功能。
    • 生成器模式:使用生成器类一步步设置对象的属性,最终构建出复杂对象。

通过这两个模式的比较和示例,可以看出它们在解决不同问题时各自的优势和适用场景。

和策略模式区别

装饰器模式(Decorator Pattern)和策略模式(Strategy Pattern)是两种不同的设计模式,它们的目的、使用场景和实现方式都有所不同。以下是详细的区别和使用示例:

装饰器模式 (Decorator Pattern)

定义和用途

装饰器模式是一种结构型设计模式,允许你动态地向对象添加职责而不改变其接口。它通过创建装饰类来包裹原始类,并在装饰类中添加新的功能。

主要特点

  • 动态扩展功能:可以在运行时动态地为对象添加行为。
  • 透明性:装饰类与被装饰类实现相同的接口,所以使用时透明。
  • 组合使用:可以组合多个装饰器类,为对象添加多种功能。

使用场景

  • 当你需要为一个类添加额外的职责,但不想修改类的定义时。
  • 当扩展功能可以用多个装饰器组合完成时。

示例

// Coffee 接口
public interface Coffee {
    String getDescription();
    double getCost();
}

// SimpleCoffee 类
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple coffee";
    }

    @Override
    public double getCost() {
        return 5.0;
    }
}

// CoffeeDecorator 抽象类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

// MilkDecorator 类
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with milk";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 1.5;
    }
}

// SugarDecorator 类
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", with sugar";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;
    }
}

策略模式 (Strategy Pattern)

定义和用途

策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端变化。

主要特点

  • 算法的封装和独立:每个策略类封装一个具体的算法,算法可以独立于客户端变化。
  • 策略的可替换性:不同策略类实现相同的接口,可以在运行时动态替换。
  • 减少条件语句:避免使用大量条件语句来选择不同的行为。

使用场景

  • 当你需要在运行时动态选择算法时。
  • 当多个类只在行为上有所不同,可以使用策略模式将这些行为封装为策略类。

示例

// 策略接口
public interface PaymentStrategy {
    void pay(int amount);
}

// 具体策略类 - 信用卡支付
public class CreditCardPaymentStrategy implements PaymentStrategy {
    private String cardNumber;

    public CreditCardPaymentStrategy(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

// 具体策略类 - PayPal支付
public class PayPalPaymentStrategy implements PaymentStrategy {
    private String email;

    public PayPalPaymentStrategy(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal.");
    }
}

// 上下文类
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

// 使用策略模式
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 使用信用卡支付
        cart.setPaymentStrategy(new CreditCardPaymentStrategy("1234-5678-9012-3456"));
        cart.checkout(100);

        // 使用PayPal支付
        cart.setPaymentStrategy(new PayPalPaymentStrategy("[email protected]"));
        cart.checkout(200);
    }
}

对比和总结

  • 目的

    • 装饰器模式:动态地为对象添加行为或职责,而不改变其接口。
    • 策略模式:定义一系列算法,并将它们封装起来使它们可以互相替换。
  • 使用场景

    • 装饰器模式:当你需要动态地为对象添加功能时。
    • 策略模式:当你需要在运行时动态选择算法时。
  • 实现方式

    • 装饰器模式:通过创建装饰类包裹原始类,动态地添加功能。
    • 策略模式:通过定义策略接口和具体策略类,将算法封装起来并使其可替换。
  • 透明性

    • 装饰器模式:装饰类与被装饰类实现相同的接口,所以使用时透明。
    • 策略模式:策略类通过上下文类调用,策略的具体实现对客户端透明。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.