一、工厂模式概述
工厂模式属于创建型设计模式,主要用于对象的创建,将对象的创建与使用分离,提高代码的灵活性和可维护性。
为什么需要工厂模式?
- 解耦:将对象的创建与使用分离
- 降低代码重复:集中管理对象的创建逻辑
- 提高扩展性:新增产品类时不影响现有代码
- 统一管理:可以对创建过程进行统一控制
二、简单工厂模式(Simple Factory)
1. 定义
定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例通常具有共同的父类或接口。
2. 结构
- 工厂类(Factory):负责创建具体产品
- 抽象产品(Product):定义产品的接口
- 具体产品(ConcreteProduct):实现抽象产品接口的具体类
// 抽象产品
interface Product {
void use();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 简单工厂
class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
return null;
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Product product = SimpleFactory.createProduct("A");
product.use(); // 输出: 使用产品A
}
}
4. 优缺点
优点:
- 客户端无需知道具体产品类名,只需知道对应参数
- 实现了对象的创建和使用的分离
缺点:
- 工厂类集中了所有产品的创建逻辑,职责过重
- 新增产品需要修改工厂类,违反开闭原则
- 难以扩展复杂的产品结构
5. 面试常见问题
- 简单工厂模式违反了哪些设计原则?(开闭原则)
- 简单工厂模式和直接new一个对象有什么区别?(解耦、集中管理)
- 简单工厂模式适用于什么场景?(创建对象较少且不频繁变化)
三、工厂方法模式(Factory Method)
1. 定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
2. 结构
- 抽象工厂(Factory):声明工厂方法
- 具体工厂(ConcreteFactory):实现工厂方法,返回具体产品
- 抽象产品(Product):定义产品接口
- 具体产品(ConcreteProduct):实现产品接口
// 抽象产品
interface Product {
void use();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 抽象工厂
interface Factory {
Product createProduct();
}
// 具体工厂A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactoryA();
Product product = factory.createProduct();
product.use(); // 输出: 使用产品A
}
}
4. 优缺点
优点:
- 用户只需关心所需产品对应的工厂,无需关心创建细节
- 新增产品时只需添加对应的工厂类,符合开闭原则
- 可以形成基于产品的等级结构
缺点:
- 类的个数容易过多,增加系统复杂度
- 增加了系统的抽象性和理解难度
5. 面试常见问题
- 工厂方法模式如何解决简单工厂模式的问题?(通过多态延迟实例化到子类)
- 工厂方法模式和简单工厂模式的主要区别是什么?(是否使用继承和多态)
- JDK中哪些地方使用了工厂方法模式?(如Collection.iterator(), URLStreamHandlerFactory)
四、抽象工厂模式(Abstract Factory)
1. 定义
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2. 结构
- 抽象工厂(AbstractFactory):声明创建一系列产品的方法
- 具体工厂(ConcreteFactory):实现创建具体产品的方法
- 抽象产品(AbstractProduct):为每种产品声明接口
- 具体产品(ConcreteProduct):实现抽象产品接口的具体类
// 抽象产品A interface AbstractProductA { void use(); } // 具体产品A1 class ProductA1 implements AbstractProductA { @Override public void use() { System.out.println("使用A1产品"); } } // 具体产品A2 class ProductA2 implements AbstractProductA { @Override public void use() { System.out.println("使用A2产品"); } } // 抽象产品B interface AbstractProductB { void operate(); } // 具体产品B1 class ProductB1 implements AbstractProductB { @Override public void operate() { System.out.println("操作B1产品"); } } // 具体产品B2 class ProductB2 implements AbstractProductB { @Override public void operate() { System.out.println("操作B2产品"); } } // 抽象工厂 interface AbstractFactory { AbstractProductA createProductA(); AbstractProductB createProductB(); } // 具体工厂1 class ConcreteFactory1 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ProductA1(); } @Override public AbstractProductB createProductB() { return new ProductB1(); } } // 具体工厂2 class ConcreteFactory2 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ProductA2(); } @Override public AbstractProductB createProductB() { return new ProductB2(); } } // 客户端使用 public class Client { public static void main(String[] args) { AbstractFactory factory = new ConcreteFactory1(); AbstractProductA productA = factory.createProductA(); AbstractProductB productB = factory.createProductB(); productA.use(); // 输出: 使用A1产品 productB.operate(); // 输出: 操作B1产品 } }
4. 优缺点
优点:
隔离了具体类的生成,客户端不需要知道具体类名 易于交换产品系列,只需改变具体工厂
缺点:
- 增加新的产品等级结构很复杂,需要修改抽象工厂和所有具体工厂
- 增加了系统的抽象性和理解难度
- 保证客户端始终只使用同一个产品族中的对象
5. 面试常见问题
- 抽象工厂模式和工厂方法模式的主要区别是什么?(产品族 vs 单一产品)
- 抽象工厂模式适用于什么场景?(需要创建一系列相关或依赖的对象)
- Spring框架中哪里使用了抽象工厂模式?(如BeanFactory)
五、三种工厂模式对比
特性 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|---|
复杂度 | 低 | 中 | 高 |
可扩展性 | 差(修改工厂类) | 好(新增工厂类) | 好(新增工厂类) |
适用场景 | 创建对象较少 | 单一产品等级结构 | 多个产品等级结构 |
设计原则 | 违反开闭原则 | 符合开闭原则 | 符合开闭原则 |
灵活性 | 低 | 中 | 高 |
六、实际应用场景
-
JDBC中的工厂模式:
DriverManager.getConnection()
是简单工厂DataSource
是工厂方法- 不同数据库的驱动是抽象工厂的实现
-
Spring框架:
BeanFactory
是工厂方法模式ApplicationContext
扩展了工厂方法模式- 不同环境的配置可以看作抽象工厂
-
日志框架:
LoggerFactory.getLogger()
是工厂方法- 不同日志实现(Log4j, JUL, SLF4J)是具体工厂
一、策略模式概述
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。
为什么需要策略模式?
避免条件语句:消除大量的if-else或switch-case语句 算法复用:相同算法可以在不同环境中使用 扩展性好:新增策略不影响现有代码
二、策略模式结构
1. 核心角色
- 策略接口(Strategy):定义所有支持的算法的公共接口
- 具体策略(ConcreteStrategy):实现策略接口的具体算法类
- 上下文(Context):持有一个策略对象的引用,通过策略接口与具体策略交互
- 符合开闭原则:对扩展开放,对修改关闭
三、策略模式优缺点
优点
算法自由切换:策略类之间可以自由切换 避免多重条件判断:消除了大量的条件语句 扩展性良好:增加新策略很容易,符合开闭原则 算法复用:相同算法可以在不同环境中使用
缺点
四、实际应用场景
- 客户端必须知道所有策略类:客户端需要了解不同策略的区别
- 策略类数量增加:每个策略都是一个类,可能增加系统复杂性
- 通信开销:如果策略需要访问上下文数据,可能需要复杂的接口设计
- 支付系统:多种支付方式的选择与切换
- 排序算法:根据不同场景选择快速排序、归并排序等
- 游戏AI:NPC根据情境选择不同行为策略
- 折扣系统:不同促销活动采用不同的折扣策略
- 路由算法:根据网络状况选择最优路由策略
1. 策略模式和工厂模式有什么区别?
回答要点:
工厂模式是创建型模式,关注对象的创建;策略模式是行为型模式,关注行为的封装和切换 工厂模式解决"如何创建对象"的问题,策略模式解决"如何选择算法"的问题 工厂模式在创建后一般不改变对象,策略模式可以动态切换策略
2. 策略模式和状态模式有什么区别?
回答要点:
3. 什么情况下应该使用策略模式?
回答要点:
回答要点:
- 策略模式客户端需要知道所有策略,状态模式客户端不感知状态变化
- 策略模式策略之间独立,状态模式状态之间可能相互转换
- 策略模式关注算法选择,状态模式关注状态变化引起的行为变化
- 当一个系统有许多类,它们的区别仅在于行为不同时
- 需要动态地在几种算法中选择一种时
- 需要避免使用多重条件判断语句时
- 希望算法可以独立于使用它的客户端变化时
- 对扩展开放:新增策略只需添加新策略类,无需修改现有代码
- 对修改关闭:上下文类不需要因为新增策略而修改
- 通过面向接口编程实现,依赖抽象而非具体实现
4. 策略模式如何符合开闭原则?
回答要点:
- 对扩展开放:新增策略只需添加新策略类,无需修改现有代码
- 对修改关闭:上下文类不需要因为新增策略而修改
- 通过面向接口编程实现,依赖抽象而非具体实现
5. 策略模式有哪些缺点?如何解决?
回答要点:
6. 请举例说明你在项目中如何使用策略模式?
回答示例:
"在我参与的电商项目中,我们使用策略模式实现了多种折扣计算方式。定义了一个DiscountStrategy接口,有会员折扣、满减折扣、促销折扣等实现。结账时根据活动类型自动选择对应策略计算最终价格。这样新增折扣类型时只需添加新策略类,不影响现有代码,也便于进行单元测试。"
- 客户端必须了解所有策略:可以通过工厂模式或依赖注入隐藏创建细节
- 策略类数量增加:合理设计策略层次结构,使用组合而非继承
- 通信开销:设计良好的上下文接口,或使用共享数据对象
一、责任链模式概述
责任链模式(Chain of Responsibility)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
为什么需要责任链模式?
- 解耦请求发送者和接收者:发送者无需知道具体由哪个对象处理
- 动态组合处理流程:可以灵活地增加或修改处理链
- 多对象处理请求:允许多个对象都有机会处理请求
- 符合单一职责原则:每个处理器只关注自己负责的部分
二、责任链模式优缺点
优点
降低耦合度:请求发送者无需知道具体哪个对象会处理 动态组合:可以运行时动态改变链中的成员或调整顺序 灵活性高:新增处理者或改变处理顺序都很方便 单一职责:每个处理者只关注自己负责的部分
缺点
三、实际应用场景
- 请求可能未被处理:没有保证请求一定会被处理
- 性能影响:长链可能导致请求处理延迟
- 调试困难:请求传递过程不易跟踪和调试
- 循环引用风险:不正确的链设置可能导致循环引用
- 审批流程:多级审批系统,如请假审批、费用报销
- 异常处理:Java异常处理机制(try-catch-finally)
- 过滤器/拦截器:Servlet Filter、Spring Interceptor
- 日志处理:不同级别的日志由不同处理器处理
- 游戏事件处理:游戏中的输入事件传递处理
七、面试常见问题及回答
1. 责任链模式和装饰器模式有什么区别?
回答要点:
责任链模式是行为型模式,关注请求的传递和处理;装饰器模式是结构型模式,关注对象功能的动态添加 责任链模式处理者可以终止传递,装饰器模式通常会继续传递 责任链模式处理者通常不关心前驱后继的具体类型,装饰器模式装饰器知道被装饰对象
2. 如何避免责任链过长导致的性能问题?
回答要点:
3. 责任链模式如何保证请求一定会被处理?
回答要点:
4. 什么情况下不适合使用责任链模式?
回答要点:
5. 责任链模式如何支持处理器的动态添加和移除?
回答要点:
6. 请举例说明你在项目中如何使用责任链模式?
回答示例:
"在我们项目的风控系统中,我们使用责任链模式实现了多层次的风险检查。每个风险检查点(如黑名单检查、信用评分检查、交易频率检查等)都是一个独立的处理器。当发起交易时,请求会依次通过这些检查点。每个检查点只关注自己负责的风险维度,通过则传递给下一个检查点,不通过则终止流程并返回风险提示。这种设计使我们能够灵活地添加新的风险检查点而不影响现有逻辑。"
- 限制链的最大长度
- 对频繁调用的处理器进行缓存
- 使用异步处理机制
- 定期评估和优化处理链结构
- 对处理器进行性能监控和分析
- 在链尾设置默认处理器处理所有未被处理的请求
- 使用模板方法模式确保处理逻辑的一致性
- 定义明确的处理结果返回机制
- 请求必须有且只有一个处理器处理时
- 处理顺序需要严格固定且不会变化时
- 性能要求极高,无法接受链式调用的开销时
- 处理逻辑过于简单,不需要多对象协作时
- 维护处理器集合而非固定引用
- 提供添加/移除处理器的方法
- 使用组合模式管理复杂处理器结构
- 可以结合工厂模式管理处理器实例
- 可以引入"终结处理器"概念确保链的完整性