Skip to content

工厂模式


基本介绍

核心思想:不直接在业务代码里 new 对象,而是把对象创建过程交给一个 “工厂类” 统一管理

这样做的好处是:业务代码不关心具体创建哪个实现类,只关心拿到一个可用对象

业务场景:一个业务中有很多不同的类型,每个类型都有自己对应的逻辑,使用 if - else 判断会显得代码冗长,且后续新增类型也不好维护,工厂模式可以把这些对象创建逻辑集中起来

优点如下

(1)对象创建逻辑集中管理

(2)业务代码不需要直接依赖具体实现类

(3) 新增实现类时,修改范围更可控

(4) 降低代码耦合度

缺点如下

(1)每次新增类型,通常都要修改工厂类

(2)如果类型很多,工厂类会越来越复杂

(3)简单工厂不完全符合开闭原则:对扩展开放,对修改关闭

标准工厂模式

实现思路

定义接口,编写不同的实现类去实现接口,并实现具体的业务逻辑,后续扩展只需要新增实现类即可

编写工厂模式,返回不同实现类的对象实例,用接口接收,利用 Java 的多态特性,实现方法的调用

代码示例

(1)定义接口

java
public interface PayService {
    void pay(BigDecimal amount);
}

(2)定义不同的实现类

java
public class AliPayService implements PayService {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("使用支付宝支付:" + amount);
    }
}

public class WeChatPayService implements PayService {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("使用微信支付:" + amount);
    }
}

(3)定义工厂类

java
public class PayServiceFactory {
    public static PayService getPayService(String payType) {
        if ("ALI_PAY".equals(payType)) {
            return new AliPayService();
        }

        if ("WECHAT_PAY".equals(payType)) {
            return new WeChatPayService();
        }

        if ("BANK_CARD".equals(payType)) {
            return new BankCardPayService();
        }

        throw new IllegalArgumentException("不支持的支付类型:" + payType);
    }
}

(4)应用示例

java
public class OrderService {
    public void payOrder(String payType, BigDecimal amount) {
        PayService payService = PayServiceFactory.getPayService(payType);
        payService.pay(amount);
    }
}

简单工厂 + 策略映射

需求分析

需要根据不同的锁类型创建不同的锁对象,且由用户传参动态选择

代码实现

思路分析:先定义一个枚举类,声明所有的类型,之后通过工厂模式声明并创建对象返回,后续业务中无需关注创建什么对象,只需要专注于如何拿到某个对象

(1)定义枚举类

java
public enum MyLockType {
    RE_ENTRANT_LOCK,
    FAIR_LOCK,
    READ_LOCK,
    WRITE_LOCK,
    ;
}

(2)定义工厂类

MyLockFactory 内部持有了一个 Map,key 是锁类型枚举,值是创建锁对象的 Function,注意这里不是存锁对象,因为锁对象必须是多例的,不同业务用不同锁对象,同一个业务用相同锁对象

MyLockFactory 内部的 Map 采用了 EnumMap,只有当 Key 是枚举类型时可以使用 EnumMap,其底层不是 hash 表,而是简单的数组,由于枚举项数量固定,因此这个数组长度就等于枚举项个数,然后按照枚举项序号作为角标依次存入数组。这样就能根据枚举项序号作为角标快速定位到数组中的数据

java
@Component
public class MyLockFactory {

    private final Map<MyLockType, Function<String, RLock>> lockHandlers;

    public MyLockFactory(RedissonClient redissonClient) {
        this.lockHandlers = new EnumMap<>(MyLockType.class);
        this.lockHandlers.put(RE_ENTRANT_LOCK, redissonClient::getLock);
        this.lockHandlers.put(FAIR_LOCK, redissonClient::getFairLock);
        this.lockHandlers.put(READ_LOCK, name -> redissonClient.getReadWriteLock(name).readLock());
        this.lockHandlers.put(WRITE_LOCK, name -> redissonClient.getReadWriteLock(name).writeLock());
    }

    public RLock getLock(MyLockType lockType, String name){
        return lockHandlers.get(lockType).apply(name);
    }
}

(3)应用示例

java
@RequiredArgsConstructor
public class MyLockAspect {

    private final MyLockFactory lockFactory;

    public Object tryLock(MyLock myLock) {
        // 通过工厂模式创建锁对象
        RLock lock = lockFactory.getLock(myLock.lockType(), myLock.name());
    }
}

// MyLock 是一个注解,定义了一些属性
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLock {
    String name();

    long waitTime() default 1;

    long leaseTime() default -1;

    TimeUnit unit() default TimeUnit.SECONDS;

    MyLockType lockType() default MyLockType.RE_ENTRANT_LOCK;

    MyLockStrategy lockStrategy() default MyLockStrategy.FAIL_AFTER_RETRY_TIMEOUT;
}