Java实现工厂模式
文章目录
- Java实现工厂模式
-
- [1. 概念](#1. 概念)
- [2. 工厂模式的三种实现方式](#2. 工厂模式的三种实现方式)
-
- [2.1 简单工厂模式](#2.1 简单工厂模式)
- [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. 三种方式的优缺点对比总结)
-
- 4.SpringBoot中工厂模式实战
- [5. 结论](#5. 结论)
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.运行结果
创建了一张现代风格的椅子
创建了一张维多利亚风格的沙发
代码分析
- 简单工厂类
FurnitureFactory
:工厂类提供了两个静态方法createChair
和createSofa
,通过传入的参数决定要创建的具体产品。简单工厂的核心思想就是将对象的创建逻辑集中在一个地方。 - 具体产品类 :具体产品类如
ModernChair
和VictorianChair
分别实现了Chair
接口,提供了具体的产品行为。 - 客户端:客户端通过工厂方法调用相应的产品,无需了解产品的具体实现细节。
优缺点
优点:
- 简化了客户端代码:客户端不需要直接创建对象,只需通过工厂来获取产品。
- 集中管理产品创建逻辑:所有创建逻辑都集中在一个工厂类中,便于维护和修改。
缺点:
- 工厂类的职责较重:随着产品类型的增加,工厂类会变得越来越复杂,违反了单一职责原则。
- 可扩展性差 :如果要添加新的产品类型,工厂类需要修改,不符合开闭原则。
通过简单工厂模式,我们将所有的创建逻辑集中到了 FurnitureFactory
中,并且通过参数化的方式控制创建不同的产品。这适合于产品种类较少且变化不频繁的情况。
2.2 工厂方法模式
概念:工厂方法模式是一种创建型设计模式,它将对象的创建过程推迟到子类中处理,使得客户端不直接依赖于具体产品类。每个具体工厂都会负责创建一种具体产品,这样就符合开闭原则,并且提供了更好的扩展性。
在上面的家具厂案例中,我们可以通过工厂方法模式来为不同的家具(如椅子和沙发)创建不同的工厂。每个工厂只负责创建一种产品。
1. 定义产品接口
首先,我们依然保留 Chair
和 Sofa
接口,定义它们的 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
创建了一张现代风格的椅子
创建了一张维多利亚风格的沙发
代码分析
- 工厂接口
ChairFactory
和SofaFactory
:每个产品(椅子和沙发)有各自的工厂接口,它们定义了创建产品的方法。 - 具体工厂类 :如
ModernChairFactory
和VictorianSofaFactory
,这些类实现了相应的工厂接口,并负责创建具体产品。 - 客户端:客户端通过具体工厂创建产品,而不直接依赖于产品类。这样可以通过工厂接口动态选择要创建的产品,而无需修改客户端代码。
优缺点
优点:
- 符合开闭原则:可以轻松扩展新的工厂和产品,而不影响现有代码。
- 简化客户端代码:客户端只依赖工厂接口,不关心具体的产品类,解耦了对象创建的过程。
- 更好的扩展性:每增加一种新的产品,只需添加对应的工厂类,不需要修改已有的工厂逻辑。
缺点:
- 增加了类的数量:每增加一种产品或风格,都需要新建一个工厂类,可能会导致类的数量较多。
通过工厂方法模式,我们将对象的创建过程推迟到了具体工厂中,使得代码更易于扩展,客户端代码也更加简洁。
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
创建了一张现代风格的椅子
创建了一张现代风格的沙发
----------------------------
创建了一张维多利亚风格的椅子
创建了一张维多利亚风格的沙发
代码分析
- 抽象工厂类
FurnitureFactory
:我们使用抽象类而不是接口来定义工厂类,并在其中定义了两个抽象方法createChair()
和createSofa()
,让子类负责具体实现。 - 具体工厂类
ModernFurnitureFactory
和VictorianFurnitureFactory
:每个具体工厂继承了抽象工厂FurnitureFactory
,并实现了创建产品的具体方法。 - 客户端代码:客户端使用具体工厂来获取产品实例,通过工厂调用生成椅子和沙发。
优缺点
优点:
-
可以为子类提供部分默认实现(虽然这个例子中没有使用)。如果工厂类有一些通用的功能可以在多个具体工厂中复用,那么抽象类会更加方便。
-
提供了创建一组相关产品的接口,符合高内聚低耦合原则。
-
扩展性好,增加新的产品族时只需增加新的工厂和产品类。
缺点:
- 当需要增加新的产品等级结构时,所有的工厂类都需要修改。
- 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. 结论
工厂模式在实际开发中非常常见,不同场景下选择合适的模式尤为重要。在需要生成大量复杂对象时,工厂方法模式和抽象工厂模式是非常好的选择;而对于简单的对象生成需求,简单工厂模式足以应对。