一、引言
在软件开发中,我们常常需要根据不同的条件来创建对象或执行不同的操作。传统上,我们会使用大量的 if-else 或 switch-case 语句来实现这些逻辑,但这种方式会导致代码冗长、难以维护和扩展。工厂模式和策略模式是两种常用的设计模式,它们可以有效地解决这些问题。本文将详细介绍简单工厂模式、工厂方法模式、抽象工厂模式、策略模式以及工厂与策略模式的结合,并分析它们的区别、不足以及如何通过工厂与策略模式的结合来解决这些不足。
二、传统方式的问题
示例场景
假设我们要开发一个电商系统,根据用户选择的支付方式(支付宝、微信支付、银行卡支付)和支付环境(线上、线下)来执行相应的支付操作。使用传统 if-else 方式代码如下:
// 支付接口
interface Payment {
void pay(double amount);
}
// 支付宝支付类
class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
// 微信支付类
class WeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
// 银行卡支付类
class BankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
// 传统支付处理方式 - 充满 if-else
public class TraditionalPaymentHandler {
public static void processPayment(String paymentType, String environment, double amount) {
if ("alipay".equalsIgnoreCase(paymentType)) {
if ("online".equalsIgnoreCase(environment)) {
new Alipay().pay(amount);
} else if ("offline".equalsIgnoreCase(environment)) {
System.out.println("线下支付宝支付需额外操作");
new Alipay().pay(amount);
}
} else if ("wechat".equalsIgnoreCase(paymentType)) {
if ("online".equalsIgnoreCase(environment)) {
new WeChatPay().pay(amount);
} else if ("offline".equalsIgnoreCase(environment)) {
System.out.println("线下微信支付需扫码");
new WeChatPay().pay(amount);
}
} else if ("bankcard".equalsIgnoreCase(paymentType)) {
if ("online".equalsIgnoreCase(environment)) {
System.out.println("线上银行卡支付需输入验证码");
new BankCardPay().pay(amount);
} else if ("offline".equalsIgnoreCase(environment)) {
System.out.println("线下银行卡支付需刷卡");
new BankCardPay().pay(amount);
}
} else {
System.out.println("不支持的支付方式");
}
}
public static void main(String[] args) {
processPayment("alipay", "online", 100.0); // 输出: 使用支付宝支付 100.0 元
processPayment("wechat", "offline", 200.0); // 输出: 线下微信支付需扫码 使用微信支付 200.0 元
}
}
问题分析
代码冗长:随着支付方式和支付环境的增多,if-else 语句会急剧增长,代码可读性急剧下降。
难以维护:添加新的支付方式或支付环境需要修改 processPayment 方法,违反开闭原则。
行为耦合:支付方式的选择、支付环境的选择和支付行为的执行耦合在一起,不利于代码的复用和扩展。
三、工厂模式介绍及示例
1. 简单工厂模式
简单工厂模式通过一个工厂类根据不同条件创建不同对象,将创建逻辑集中,避免客户端直接使用 if-else 语句。
示例代码
// 支付接口
interface Payment {
void pay(double amount);
}
// 支付宝支付类
class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
// 微信支付类
class WeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
// 银行卡支付类
class BankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
// 简单支付工厂类(封装对象创建逻辑)
class SimplePaymentFactory {
public static Payment createPayment(String paymentType) {
switch (paymentType.toLowerCase()) {
case "alipay":
return new Alipay();
case "wechat":
return new WeChatPay();
case "bankcard":
return new BankCardPay();
default:
throw new IllegalArgumentException("不支持的支付方式: " + paymentType);
}
}
}
// 使用简单工厂模式后的支付处理(未解决支付环境问题)
public class SimpleFactoryPaymentHandler {
public static void main(String[] args) {
Payment alipay = SimplePaymentFactory.createPayment("alipay");
alipay.pay(100.0); // 输出: 使用支付宝支付 100.0 元
Payment wechatPay = SimplePaymentFactory.createPayment("wechat");
wechatPay.pay(200.0); // 输出: 使用微信支付 200.0 元
// 测试不支持的支付方式(会抛出异常)
// Payment unknownPay = SimplePaymentFactory.createPayment("paypal");
}
}
优点
集中了对象创建逻辑,减少了客户端代码中的条件判断。
不足
工厂类职责过重,添加新支付方式需修改其代码,违反开闭原则。
无法直接解决支付环境带来的条件判断问题。
2. 工厂方法模式
工厂方法模式定义创建对象的接口,由子类决定实例化的类,推迟类实例化到子类,解决了简单工厂模式违反开闭原则的问题。
示例代码
// 支付接口
interface Payment {
void pay(double amount);
}
// 支付宝支付类
class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
// 微信支付类
class WeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
// 银行卡支付类
class BankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
// 支付工厂接口(工厂方法模式的核心)
interface PaymentFactory {
Payment createPayment();
}
// 支付宝工厂类
class AlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new Alipay();
}
}
// 微信支付工厂类
class WeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPay();
}
}
// 银行卡支付工厂类
class BankCardPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new BankCardPay();
}
}
// 使用工厂方法模式后的支付处理(未解决支付环境问题)
public class FactoryMethodPaymentHandler {
public static void main(String[] args) {
// 使用支付宝支付
PaymentFactory alipayFactory = new AlipayFactory();
Payment alipay = alipayFactory.createPayment();
alipay.pay(100.0); // 输出: 使用支付宝支付 100.0 元
// 使用微信支付
PaymentFactory wechatPayFactory = new WeChatPayFactory();
Payment wechatPay = wechatPayFactory.createPayment();
wechatPay.pay(200.0); // 输出: 使用微信支付 200.0 元
// 使用银行卡支付
PaymentFactory bankCardPayFactory = new BankCardPayFactory();
Payment bankCardPay = bankCardPayFactory.createPayment();
bankCardPay.pay(300.0); // 输出: 使用银行卡支付 300.0 元
}
}
优点
每个支付方式有自己的工厂类,添加新支付方式只需创建新的工厂类,符合开闭原则。
不足
仍无法直接解决支付环境带来的条件判断问题。
类数量会增加,每增加一种产品类型,就需创建一个新的工厂类。
3. 抽象工厂模式
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。它为创建相关产品对象提供了更高级的抽象,适用于产品族的情况。
示例代码
// 基础支付接口
interface Payment {
void pay(double amount);
}
// 线上支付接口(扩展支付行为)
interface OnlinePayment extends Payment {
void onlinePayProcess();
}
// 线下支付接口(扩展支付行为)
interface OfflinePayment extends Payment {
void offlinePayProcess();
}
// 支付宝线上支付实现
class OnlineAlipay implements OnlinePayment {
@Override
public void pay(double amount) {
System.out.println("线上使用支付宝支付 " + amount + " 元");
}
@Override
public void onlinePayProcess() {
System.out.println("线上支付宝支付处理流程");
}
}
// 支付宝线下支付实现
class OfflineAlipay implements OfflinePayment {
@Override
public void pay(double amount) {
System.out.println("线下使用支付宝支付 " + amount + " 元");
}
@Override
public void offlinePayProcess() {
System.out.println("线下支付宝支付处理流程(如扫码)");
}
}
// 微信线上支付实现
class OnlineWeChatPay implements OnlinePayment {
@Override
public void pay(double amount) {
System.out.println("线上使用微信支付 " + amount + " 元");
}
@Override
public void onlinePayProcess() {
System.out.println("线上微信支付处理流程");
}
}
// 微信线下支付实现
class OfflineWeChatPay implements OfflinePayment {
@Override
public void pay(double amount) {
System.out.println("线下使用微信支付 " + amount + " 元");
}
@Override
public void offlinePayProcess() {
System.out.println("线下微信支付处理流程(如扫码)");
}
}
// 支付工厂接口(抽象工厂)
interface PaymentFactory {
Payment createPayment();
}
// 支付宝线上支付工厂
class OnlineAlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new OnlineAlipay();
}
}
// 支付宝线下支付工厂
class OfflineAlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new OfflineAlipay();
}
}
// 微信线上支付工厂
class OnlineWeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new OnlineWeChatPay();
}
}
// 微信线下支付工厂
class OfflineWeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new OfflineWeChatPay();
}
}
// 使用抽象工厂模式后的支付处理
public class AbstractFactoryPaymentHandler {
public static void main(String[] args) {
// 支付宝线上支付
PaymentFactory onlineAlipayFactory = new OnlineAlipayFactory();
OnlinePayment onlineAlipay = (OnlinePayment) onlineAlipayFactory.createPayment();
onlineAlipay.pay(100.0); // 输出: 线上使用支付宝支付 100.0 元
onlineAlipay.onlinePayProcess(); // 输出: 线上支付宝支付处理流程
// 微信线下支付
PaymentFactory offlineWeChatPayFactory = new OfflineWeChatPayFactory();
OfflinePayment offlineWeChatPay = (OfflinePayment) offlineWeChatPayFactory.createPayment();
offlineWeChatPay.pay(200.0); // 输出: 线下使用微信支付 200.0 元
offlineWeChatPay.offlinePayProcess(); // 输出: 线下微信支付处理流程(如扫码)
}
}
优点
可以创建一系列相关的产品对象,适用于产品族的情况。
客户端代码与具体产品类的耦合度降低,通过抽象工厂接口操作产品。
不足
增加了系统的抽象性和理解难度。
难以支持新种类的产品(如新的支付方式且需区分线上线下),若添加,需修改抽象工厂接口和所有的具体工厂类,违反开闭原则。
四、策略模式介绍及示例
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。在支付模块中,不同的支付方式就是不同的支付策略。
示例代码
// 支付策略接口(所有支付方式的共同抽象)
interface Payment {
void pay(double amount);
}
// 支付宝支付策略
class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
// 微信支付策略
class WeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
// 银行卡支付策略
class BankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
// 支付策略上下文类(封装策略的执行)
class PaymentContext {
private Payment paymentStrategy;
public PaymentContext(Payment paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void executePayment(double amount) {
paymentStrategy.pay(amount);
}
// 可选:动态切换策略的方法
public void setPaymentStrategy(Payment paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
}
// 使用策略模式后的支付处理
public class StrategyPaymentHandler {
public static void main(String[] args) {
// 使用支付宝支付
Payment alipay = new Alipay();
PaymentContext alipayContext = new PaymentContext(alipay);
alipayContext.executePayment(100.0); // 输出: 使用支付宝支付 100.0 元
// 动态切换为微信支付
Payment wechatPay = new WeChatPay();
alipayContext.setPaymentStrategy(wechatPay);
alipayContext.executePayment(200.0); // 输出: 使用微信支付 200.0 元
}
}
优点
将算法的选择和使用分离,客户端代码只需关心支付策略上下文,降低了代码的耦合度。
易于扩展,添加新的支付方式只需创建一个新的支付策略类,无需修改现有代码,符合开闭原则。
提高代码的可复用性,不同的支付策略可以在不同的场景中复用。
不足
仍无法直接解决支付环境带来的条件判断问题,客户端代码仍需根据支付环境选择不同的支付策略(可能仍需 if-else
)。
五、工厂加策略模式的结合及示例
将工厂模式和策略模式结合,可以充分发挥两者的优势。工厂模式负责创建支付策略对象,策略模式负责执行支付操作,同时可以结合支付环境来优化代码结构。
示例代码
import java.util.EnumMap;
import java.util.Map;
// 支付环境枚举(线上/线下)
enum PaymentEnvironment {
ONLINE, OFFLINE
}
// 支付策略接口
interface Payment {
void pay(double amount);
}
// -------- 支付宝支付策略 --------
class OnlineAlipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线上支付宝支付 " + amount + " 元");
}
}
class OfflineAlipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线下支付宝支付 " + amount + " 元(扫码完成)");
}
}
// -------- 微信支付策略 --------
class OnlineWeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线上微信支付 " + amount + " 元");
}
}
class OfflineWeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线下微信支付 " + amount + " 元(需扫码)");
}
}
// -------- 银行卡支付策略 --------
class OnlineBankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线上银行卡支付 " + amount + " 元(需输入验证码)");
}
}
class OfflineBankCardPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("线下银行卡支付 " + amount + " 元(需刷卡)");
}
}
// -------- 支付策略工厂(核心)--------
class PaymentStrategyFactory {
// 使用EnumMap存储支付策略(避免重复创建对象)
private static final Map<PaymentEnvironment, Map<String, Payment>> strategies = new EnumMap<>(PaymentEnvironment.class);
static {
// 初始化支付宝策略
Map<String, Payment> alipayStrategies = new EnumMap<>(PaymentEnvironment.class);
alipayStrategies.put("alipay", new OnlineAlipay());
alipayStrategies.put("alipay_offline", new OfflineAlipay());
strategies.put(PaymentEnvironment.ONLINE, alipayStrategies);
// 初始化微信策略(示例:使用匿名类)
Map<String, Payment> wechatStrategies = new EnumMap<>(PaymentEnvironment.class);
wechatStrategies.put("wechat", new OnlineWeChatPay());
wechatStrategies.put("wechat_offline", new OfflineWeChatPay());
strategies.put(PaymentEnvironment.OFFLINE, wechatStrategies);
}
public static Payment createPaymentStrategy(String paymentType, PaymentEnvironment environment) {
String key = paymentType.toLowerCase() + (environment == PaymentEnvironment.OFFLINE ? "_offline" : "");
Payment strategy = strategies.get(environment).get(key);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付方式或环境: " + paymentType + " + " + environment);
}
return strategy;
}
}
// -------- 支付上下文(策略模式)--------
class PaymentContext {
private Payment paymentStrategy;
public PaymentContext(Payment paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void executePayment(double amount) {
paymentStrategy.pay(amount);
}
// 动态切换策略
public void setPaymentStrategy(Payment paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
}
// -------- 客户端调用 --------
public class FactoryStrategyPaymentHandler {
public static void main(String[] args) {
// 示例1:线上支付宝支付
Payment alipayOnline = PaymentStrategyFactory.createPaymentStrategy("alipay", PaymentEnvironment.ONLINE);
PaymentContext context = new PaymentContext(alipayOnline);
context.executePayment(100.0); // 输出: 线上支付宝支付 100.0 元
// 示例2:线下微信支付
Payment wechatOffline = PaymentStrategyFactory.createPaymentStrategy("wechat", PaymentEnvironment.OFFLINE);
context.setPaymentStrategy(wechatOffline);
context.executePayment(200.0); // 输出: 线下微信支付 200.0 元(需扫码)
}
}
优势分析
对象创建与行为选择分离:工厂模式专注于支付策略对象的创建,根据支付方式和支付环境创建合适的支付策略对象;策略模式专注于支付行为的执行,通过支付策略上下文来执行支付操作,两者职责明确,代码结构清晰。
灵活性和可扩展性:添加新的支付方式时,只需在工厂类中添加相应的创建逻辑,并创建新的支付策略类(如果是抽象工厂模式下的产品族,则需按其规则添加);添加新的支付环境时,也只需在工厂类中调整创建逻辑,无需修改客户端代码和其他现有代码,符合开闭原则。
易于维护:代码的可读性和可维护性得到显著提高,当支付逻辑或支付环境处理逻辑发生变化时,只需修改对应的支付策略类或工厂类,不会影响其他部分的代码。
六、各模式区别与不足总结
模式 | 核心思想 | 优点 | 不足 |
---|---|---|---|
简单工厂模式 | 通过一个工厂类根据不同条件创建不同对象 | 集中了对象创建逻辑,减少客户端条件判断 | 工厂类职责过重,添加新类型需修改其代码,违反开闭原则;难以解决多维度条件判断(如支付环境) |
工厂方法模式 | 定义创建对象的接口,由子类决定实例化的类 | 每个类型有自己的工厂类,添加新类型只需创建新的工厂类,符合开闭原则 | 仍难以解决多维度条件判断;类数量增加,每增加一种产品类型,就需创建一个新的工厂类 |
抽象工厂模式 | 提供一个创建一系列相关或相互依赖对象的接口 | 可以创建一系列相关的产品对象,适用于产品族的情况;客户端代码与具体产品类的耦合度降低 | 增加了系统的抽象性和理解难度;难以支持新种类的产品(多维度下的新类型),若添加,需修改抽象工厂接口和所有的具体工厂类,违反开闭原则 |
策略模式 | 定义一系列算法,并将每个算法封装起来,使它们可以相互替换 | 将算法的选择和使用分离,降低代码耦合度;易于扩展,添加新算法只需创建新的策略类,符合开闭原则;提高代码可复用性 | 仍难以直接解决多维度条件判断(如支付环境),客户端代码可能仍需根据条件选择策略 |
七、结论
工厂模式和策略模式都是解决软件开发中常见问题的有效设计模式。
简单工厂模式、工厂方法模式和抽象工厂模式主要用于解决对象创建中的条件判断问题,策略模式主要用于解决对象行为选择中的条件判断问题。将工厂与策略模式结合,可以构建出更加灵活、可扩展和易于维护的代码架构,有效解决多维度条件判断带来的问题。
在实际开发中,应根据具体的需求和场景选择合适的设计模式或模式组合,以提高代码的质量和开发效率。通过合理运用工厂与策略模式的结合,可以有效消除代码中的 if-else 语句,使代码更加优雅和健壮。
如有其他问题报错或者需要进一步补充细节可以留言反馈。