Java实现常见的工厂模式(包含在Springboot中实战开发)

Java实现工厂模式

文章目录

  • Java实现工厂模式
    • [1. 概念](#1. 概念)
    • [2. 工厂模式的三种实现方式](#2. 工厂模式的三种实现方式)
      • [2.1 简单工厂模式](#2.1 简单工厂模式)
        • 1.定义产品接口
        • [2. 实现具体产品类](#2. 实现具体产品类)
        • [3. 实现简单工厂类](#3. 实现简单工厂类)
        • [4. 客户端代码](#4. 客户端代码)
        • 5.运行结果
      • [2.2 工厂方法模式](#2.2 工厂方法模式)
        • [1. 定义产品接口](#1. 定义产品接口)
        • [2. 实现具体产品类](#2. 实现具体产品类)
        • [3. 创建工厂接口](#3. 创建工厂接口)
        • [4. 实现具体工厂类](#4. 实现具体工厂类)
        • [5. 客户端代码](#5. 客户端代码)
        • [6. 运行结果](#6. 运行结果)
      • [2.3 抽象工厂模式](#2.3 抽象工厂模式)
        • [1. 创建家具的抽象产品接口](#1. 创建家具的抽象产品接口)
        • [2. 创建具体产品类](#2. 创建具体产品类)
        • 3.创建抽象工厂类
        • [4. 实现具体的工厂类](#4. 实现具体的工厂类)
        • [5. 客户端代码](#5. 客户端代码)
        • [6. 运行结果](#6. 运行结果)
    • [3. 三种方式的优缺点对比总结](#3. 三种方式的优缺点对比总结)

1. 概念

定义 :工厂模式(Factory Pattern)是一种创建型设计模式,定义了一个创建对象的接口,但由子类来决定实例化哪个具体类。
使用场景

需要生成复杂对象,但不希望客户端依赖这些对象的具体类时。

当多个类具有相似功能,但只在某些细节上有区别时,工厂模式能够统一这些不同点。

2. 工厂模式的三种实现方式

通常有三种实现方式:简单工厂模式、工厂方法模式、抽象工厂模式。

举个生活中的例子:假设你有一个家具公司,你可以生产两种风格的家具------现代风格和维多利亚风格,每种风格都有沙发、椅子等家具

2.1 简单工厂模式

简单工厂将所有创建产品的逻辑集中在一个工厂类中。我们可以基于家具厂的案例,使用简单工厂模式来实现家具的创建。

概念:简单工厂模式由一个工厂类根据传入的参数,决定创建哪一种产品对象。它不属于23种经典的设计模式,但经常作为工厂模式的基础。

1.定义产品接口
java 复制代码
// 椅子接口
interface Chair {
    void create();
}

// 沙发接口
interface Sofa {
    void create();
}
2. 实现具体产品类

分别实现现代风格和维多利亚风格的椅子和沙发。

java 复制代码
// 现代风格的椅子
class ModernChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的椅子");
    }
}

// 维多利亚风格的椅子
class VictorianChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的椅子");
    }
}

// 现代风格的沙发
class ModernSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的沙发");
    }
}

// 维多利亚风格的沙发
class VictorianSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的沙发");
    }
}
3. 实现简单工厂类

这里我们创建一个工厂类 FurnitureFactory,它根据传入的参数来创建不同类型的家具。

java 复制代码
// 简单家具工厂类
class FurnitureFactory {
    // 静态方法,根据产品类型创建相应的家具
    public static Chair createChair(String type) {
        if (type.equalsIgnoreCase("modern")) {
            return new ModernChair();
        } else if (type.equalsIgnoreCase("victorian")) {
            return new VictorianChair();
        } else {
            throw new IllegalArgumentException("未知的椅子类型: " + type);
        }
    }

    public static Sofa createSofa(String type) {
        if (type.equalsIgnoreCase("modern")) {
            return new ModernSofa();
        } else if (type.equalsIgnoreCase("victorian")) {
            return new VictorianSofa();
        } else {
            throw new IllegalArgumentException("未知的沙发类型: " + type);
        }
    }
}
4. 客户端代码

客户端可以通过 FurnitureFactory 提供的静态方法创建产品。

java 复制代码
public class FurnitureShop {
    public static void main(String[] args) {
        // 客户希望购买一张现代风格的椅子
        Chair chair = FurnitureFactory.createChair("modern");
        chair.create();

        // 客户希望购买一张维多利亚风格的沙发
        Sofa sofa = FurnitureFactory.createSofa("victorian");
        sofa.create();
    }
}
5.运行结果
创建了一张现代风格的椅子
创建了一张维多利亚风格的沙发

代码分析

  1. 简单工厂类 FurnitureFactory :工厂类提供了两个静态方法 createChaircreateSofa,通过传入的参数决定要创建的具体产品。简单工厂的核心思想就是将对象的创建逻辑集中在一个地方。
  2. 具体产品类 :具体产品类如 ModernChairVictorianChair 分别实现了 Chair 接口,提供了具体的产品行为。
  3. 客户端:客户端通过工厂方法调用相应的产品,无需了解产品的具体实现细节。

优缺点

优点:

  • 简化了客户端代码:客户端不需要直接创建对象,只需通过工厂来获取产品。
  • 集中管理产品创建逻辑:所有创建逻辑都集中在一个工厂类中,便于维护和修改。

缺点:

  • 工厂类的职责较重:随着产品类型的增加,工厂类会变得越来越复杂,违反了单一职责原则。
  • 可扩展性差 :如果要添加新的产品类型,工厂类需要修改,不符合开闭原则

通过简单工厂模式,我们将所有的创建逻辑集中到了 FurnitureFactory 中,并且通过参数化的方式控制创建不同的产品。这适合于产品种类较少且变化不频繁的情况。

2.2 工厂方法模式

概念:工厂方法模式是一种创建型设计模式,它将对象的创建过程推迟到子类中处理,使得客户端不直接依赖于具体产品类。每个具体工厂都会负责创建一种具体产品,这样就符合开闭原则,并且提供了更好的扩展性。

在上面的家具厂案例中,我们可以通过工厂方法模式来为不同的家具(如椅子和沙发)创建不同的工厂。每个工厂只负责创建一种产品。

1. 定义产品接口

首先,我们依然保留 ChairSofa 接口,定义它们的 create() 方法。

java 复制代码
// 椅子接口
interface Chair {
    void create();
}

// 沙发接口
interface Sofa {
    void create();
}
2. 实现具体产品类

分别实现现代风格和维多利亚风格的椅子和沙发。

java 复制代码
// 现代风格的椅子
class ModernChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的椅子");
    }
}

// 维多利亚风格的椅子
class VictorianChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的椅子");
    }
}

// 现代风格的沙发
class ModernSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的沙发");
    }
}

// 维多利亚风格的沙发
class VictorianSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的沙发");
    }
}
3. 创建工厂接口

工厂方法模式的核心思想是,每个具体工厂负责创建一种产品,因此我们需要为每种产品创建不同的工厂接口

java 复制代码
// 椅子工厂接口
interface ChairFactory {
    Chair createChair();
}

// 沙发工厂接口
interface SofaFactory {
    Sofa createSofa();
}
4. 实现具体工厂类

我们为现代风格和维多利亚风格的家具分别实现不同的工厂类,每个工厂只负责创建一种风格的产品。

java 复制代码
// 现代风格椅子工厂
class ModernChairFactory implements ChairFactory {
    @Override
    public Chair createChair() {
        return new ModernChair();
    }
}

// 维多利亚风格椅子工厂
class VictorianChairFactory implements ChairFactory {
    @Override
    public Chair createChair() {
        return new VictorianChair();
    }
}

// 现代风格沙发工厂
class ModernSofaFactory implements SofaFactory {
    @Override
    public Sofa createSofa() {
        return new ModernSofa();
    }
}

// 维多利亚风格沙发工厂
class VictorianSofaFactory implements SofaFactory {
    @Override
    public Sofa createSofa() {
        return new VictorianSofa();
    }
}
5. 客户端代码

客户端不直接创建产品,而是通过具体工厂创建产品。

java 复制代码
public class FurnitureShop {
    public static void main(String[] args) {
        // 客户希望购买一张现代风格的椅子
        ChairFactory chairFactory = new ModernChairFactory();
        Chair chair = chairFactory.createChair();
        chair.create();

        // 客户希望购买一张维多利亚风格的沙发
        SofaFactory sofaFactory = new VictorianSofaFactory();
        Sofa sofa = sofaFactory.createSofa();
        sofa.create();
    }
}
6. 运行结果
java 复制代码
创建了一张现代风格的椅子
创建了一张维多利亚风格的沙发

代码分析

  1. 工厂接口 ChairFactorySofaFactory:每个产品(椅子和沙发)有各自的工厂接口,它们定义了创建产品的方法。
  2. 具体工厂类 :如 ModernChairFactoryVictorianSofaFactory,这些类实现了相应的工厂接口,并负责创建具体产品。
  3. 客户端:客户端通过具体工厂创建产品,而不直接依赖于产品类。这样可以通过工厂接口动态选择要创建的产品,而无需修改客户端代码。

优缺点

优点:

  • 符合开闭原则:可以轻松扩展新的工厂和产品,而不影响现有代码。
  • 简化客户端代码:客户端只依赖工厂接口,不关心具体的产品类,解耦了对象创建的过程。
  • 更好的扩展性:每增加一种新的产品,只需添加对应的工厂类,不需要修改已有的工厂逻辑。

缺点:

  • 增加了类的数量:每增加一种产品或风格,都需要新建一个工厂类,可能会导致类的数量较多。

通过工厂方法模式,我们将对象的创建过程推迟到了具体工厂中,使得代码更易于扩展,客户端代码也更加简洁。

2.3 抽象工厂模式

概念:抽象工厂模式是一种创建型设计模式,它通过为创建一系列相关或依赖对象提供一个接口,而无需指定它们的具体类。

举个生活中的例子:假设你有一个家具公司,你可以生产两种风格的家具------现代风格和维多利亚风格,每种风格都有沙发、椅子等家具。这时,抽象工厂模式就是一个合适的设计选择。

1. 创建家具的抽象产品接口
java 复制代码
// 椅子接口
interface Chair {
    void create();
}

// 沙发接口
interface Sofa {
    void create();
}
2. 创建具体产品类

分别实现现代风格和维多利亚风格的椅子和沙发。

java 复制代码
// 现代风格的椅子
class ModernChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的椅子");
    }
}

// 维多利亚风格的椅子
class VictorianChair implements Chair {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的椅子");
    }
}

// 现代风格的沙发
class ModernSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张现代风格的沙发");
    }
}

// 维多利亚风格的沙发
class VictorianSofa implements Sofa {
    @Override
    public void create() {
        System.out.println("创建了一张维多利亚风格的沙发");
    }
}
3.创建抽象工厂类

FurnitureFactory 设计为一个抽象类,定义抽象方法用于创建产品。

java 复制代码
// 抽象家具工厂类
abstract class FurnitureFactory {
    public abstract Chair createChair();
    public abstract Sofa createSofa();
}
4. 实现具体的工厂类
java 复制代码
// 现代风格家具工厂
class ModernFurnitureFactory extends FurnitureFactory {
    @Override
    public Chair createChair() {
        return new ModernChair();
    }

    @Override
    public Sofa createSofa() {
        return new ModernSofa();
    }
}

// 维多利亚风格家具工厂
class VictorianFurnitureFactory extends FurnitureFactory {
    @Override
    public Chair createChair() {
        return new VictorianChair();
    }

    @Override
    public Sofa createSofa() {
        return new VictorianSofa();
    }
}
5. 客户端代码

客户端依然可以通过工厂类来创建不同风格的家具。

java 复制代码
public class FurnitureShop {
    private Chair chair;
    private Sofa sofa;

    public FurnitureShop(FurnitureFactory factory) {
        chair = factory.createChair();
        sofa = factory.createSofa();
    }

    public void showFurniture() {
        chair.create();
        sofa.create();
    }

    public static void main(String[] args) {
        // 客户希望购买一套现代风格的家具
        FurnitureFactory modernFactory = new ModernFurnitureFactory();
        FurnitureShop modernShop = new FurnitureShop(modernFactory);
        modernShop.showFurniture();

        System.out.println("----------------------------");

        // 客户希望购买一套维多利亚风格的家具
        FurnitureFactory victorianFactory = new VictorianFurnitureFactory();
        FurnitureShop victorianShop = new FurnitureShop(victorianFactory);
        victorianShop.showFurniture();
    }
}
6. 运行结果
java 复制代码
创建了一张现代风格的椅子
创建了一张现代风格的沙发
----------------------------
创建了一张维多利亚风格的椅子
创建了一张维多利亚风格的沙发

代码分析

  1. 抽象工厂类 FurnitureFactory :我们使用抽象类而不是接口来定义工厂类,并在其中定义了两个抽象方法 createChair()createSofa(),让子类负责具体实现。
  2. 具体工厂类 ModernFurnitureFactoryVictorianFurnitureFactory :每个具体工厂继承了抽象工厂 FurnitureFactory,并实现了创建产品的具体方法。
  3. 客户端代码:客户端使用具体工厂来获取产品实例,通过工厂调用生成椅子和沙发。

优缺点

优点

  • 可以为子类提供部分默认实现(虽然这个例子中没有使用)。如果工厂类有一些通用的功能可以在多个具体工厂中复用,那么抽象类会更加方便。

  • 提供了创建一组相关产品的接口,符合高内聚低耦合原则。

  • 扩展性好,增加新的产品族时只需增加新的工厂和产品类。

缺点:

  • 当需要增加新的产品等级结构时,所有的工厂类都需要修改。
  • Java 的单继承限制仍然存在,如果具体工厂类已经继承了其他类,就无法再继承 FurnitureFactory

3. 三种方式的优缺点对比总结

  • 简单工厂模式

    • 适合简单场景,但不符合开闭原则。
    • 工厂类承担了过多的职责,耦合度较高。
  • 工厂方法模式

    • 符合开闭原则和单一职责原则,适合扩展性强的场景。
    • 需要为每个产品创建相应的工厂,可能造成类爆炸。
  • 抽象工厂模式

    • 适合产品族的扩展,提供高灵活性。
    • 实现复杂,增加新的产品等级时,修改成本较高。

4.SpringBoot中工厂模式实战

项目中有一个登录的需求,有手机号码登录和用户名登录两种方式,这里就可以使用工厂模式进行实现,避免了以往ifelse的弊端,提成高逼格,哈哈

接口和实现:

java 复制代码
public interface UserLoginService {

    AuthUser login(LoginBo loginBo);
}

@Service
public class PhoneLoginServiceImpl implements UserLoginService {
    @Override
    public AuthUser login(LoginBo loginBo) {
        System.out.println("phone login");
        AuthUser authUser = new AuthUser();
        authUser.setId("1");
        authUser.setName("zhangsan");
        authUser.setPassword("123456");
        return authUser;
    }

}

@Service
public class UsernameLoginServiceImpl implements UserLoginService {
    @Override
    public AuthUser login(LoginBo loginBo) {
        System.out.println("username login");
        AuthUser authUser = new AuthUser();
        authUser.setId("1");
        authUser.setName("username");
        authUser.setPassword("123456");
        return authUser;
    }

}

工厂类:

java 复制代码
@Component
public class LoginFactory implements ApplicationContextAware {

    private Map<String, UserLoginService> LOGIN_TYPE_MAP = new HashMap<>();

    /**
     * 初始化不同登录类型处理类
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LOGIN_TYPE_MAP.put(AuthConstant.PHONE_MESSAGE_CODE, applicationContext.getBean(PhoneLoginServiceImpl.class));
        LOGIN_TYPE_MAP.put(AuthConstant.USERNAME_PASSWORD, applicationContext.getBean(UsernameLoginServiceImpl.class));
    }

    public UserLoginService getInstance(String loginType) {
        return LOGIN_TYPE_MAP.get(loginType);
    }

}

public class AuthConstant {
    String USERNAME_PASSWORD = "USERNAME_PASSWORD";
    String PHONE_MESSAGE_CODE = "PHONE_MESSAGE_CODE";
}

测试:

java 复制代码
@RestController
public class BasicController {

    @Autowired
    private LoginFactory loginFactory;

    @PostMapping("/logintest")
    void login(@RequestBody  LoginBo loginBo){
        UserLoginService userLoginService = loginFactory.getInstance(loginBo.getLoginType());
        userLoginService.login(loginBo);
    }

}

5. 结论

工厂模式在实际开发中非常常见,不同场景下选择合适的模式尤为重要。在需要生成大量复杂对象时,工厂方法模式和抽象工厂模式是非常好的选择;而对于简单的对象生成需求,简单工厂模式足以应对。

相关推荐
无忧无虑Coding2 小时前
pyinstall 打包Django程序
后端·python·django
·云扬·3 小时前
Java IO 与 BIO、NIO、AIO 详解
java·开发语言·笔记·学习·nio·1024程序员节
求积分不加C3 小时前
Spring Boot中使用AOP和反射机制设计一个的幂等注解(两种持久化模式),简单易懂教程
java·spring boot·后端
枫叶_v3 小时前
【SpringBoot】26 实体映射工具(MapStruct)
java·spring boot·后端
东方巴黎~Sunsiny3 小时前
java-图算法
java·开发语言·算法
2401_857617624 小时前
汽车资讯新趋势:Spring Boot技术解读
java·spring boot·后端
小林学习编程5 小时前
从零开始理解Spring Security的认证与授权
java·后端·spring
写bug的羊羊5 小时前
Spring Boot整合Nacos启动时 Failed to rename context [nacos] as [xxx]
java·spring boot·后端
努力的小陈^O^6 小时前
docker学习笔记跟常用命令总结
java·笔记·docker·云原生
童先生6 小时前
如何将java项目打包成docker 镜像并且可运行
java·开发语言·docker