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. 结论

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

相关推荐
不务专业的程序员--阿飞4 分钟前
【SQL 如何解锁递归】
java·数据库·sql
嘵奇10 分钟前
Spring Boot拦截器详解:原理、实现与应用场景
java·spring boot·后端
八股文领域大手子11 分钟前
Java死锁排查:线上救火实战指南
java·开发语言·面试
jackson凌17 分钟前
【Java学习笔记】finalize方法
java·笔记·学习
fanTuanye21 分钟前
redis 缓存穿透,缓存击穿,缓存雪崩
java·redis·缓存
神秘的t38 分钟前
Spring Web MVC————入门(2)
java·spring·mvc
开开心心就好1 小时前
高效全能PDF工具,支持OCR识别
java·前端·python·pdf·ocr·maven·jetty
冷心笑看丽美人1 小时前
Spring MVC数据绑定和响应 你了解多少?
java·spring·mvc
XQ丶YTY1 小时前
大二java第一面小厂(挂)
java·开发语言·笔记·学习·面试
一零贰肆1 小时前
深入理解SpringBoot中的SpringCache缓存技术
java·springboot·springcache·缓存技术