第五篇:设计模式的选择和应用 - 智慧选择与合理实践
理解问题本质:首先,要深入理解面临的问题或需求。分析系统中的对象、它们之间的关系以及可能出现的变化点。了解哪些部分需要灵活性、扩展性或者解耦。
模式匹配:根据问题特征对照已知的设计模式,识别出可能适用的模式类型。例如,若遇到对象创建过程复杂且需要解耦,可以考虑使用工厂方法、抽象工厂或建造者模式;若需处理对象间复杂的交互逻辑,则可考虑责任链、中介者或观察者模式等。
权衡比较:对于多种可能适用的设计模式,进行对比分析,考虑每种模式带来的优缺点。如复用程度、代码可读性、维护成本、性能影响等因素。
实际场景结合:确保所选模式符合项目的具体应用场景,同时考虑到系统的未来发展变化。确保选择的设计模式能够适应业务需求的演变,而不是仅仅为了应用模式而模式化设计。
在选择和应用设计模式时,必须认识到每个模式都有其适用范围和局限性:
因此,在决定采用某个设计模式时,应综合评估该模式在当前场景下的价值,包括是否解决了特定问题、是否会带来其他潜在问题以及未来可能的需求变化。
Spring框架通过依赖注入(DI)实现控制反转(IoC),在实际应用中确实采用了工厂方法、策略和代理等模式的变体。下面分别以代码片段的形式说明:
1. 工厂方法模式的体现:BeanFactory与ApplicationContext
Spring中的BeanFactory
接口是IoC容器的基本实现,它扮演了抽象工厂角色,负责管理和创建各种对象(即bean)。而ApplicationContext
作为BeanFactory
的高级实现,更加强大且功能丰富。
// Spring BeanFactory 实现了工厂方法模式
public interface BeanFactory {
Object getBean(String name) throws BeansException;
// ... 其他获取bean的方法
}
// 通常我们使用 ApplicationContext 来获取 bean
public class ClassPathXmlApplicationContext implements ApplicationContext {
public Object getBean(String name) throws BeansException {
// ...
}
}
2. 策略模式的体现:例如数据源配置切换
Spring允许通过Java配置或XML配置定义多个数据源,并根据运行时条件选择不同的数据源策略。
@Configuration
public class DataSourceConfig {
@Autowired
private Environment env;
@Bean
@Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create()
.url(env.getProperty("primary.datasource.url"))
.username(env.getProperty("primary.datasource.username"))
.password(env.getProperty("primary.datasource.password"))
.build();
}
@Bean
public DataSource secondaryDataSource() {
return DataSourceBuilder.create()
.url(env.getProperty("secondary.datasource.url"))
.username(env.getProperty("secondary.datasource.username"))
.password(env.getProperty("secondary.datasource.password"))
.build();
}
// 在其他类中注入时可以按需选择数据源策略
@Service
public class SomeService {
@Autowired
private DataSource dataSource; // 这里注入的数据源可能是primaryDataSource也可能是secondaryDataSource
// 根据业务需求决定使用哪个数据源
// ...
}
}
3. 代理模式的体现:AOP和动态代理
Spring通过AOP(面向切面编程)机制实现了对目标对象的代理,如事务管理、日志记录等。
@Aspect
@Component
public class TransactionalAdvice {
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint joinPoint) {
// 开启事务的逻辑
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturning(Object result) {
// 提交事务的逻辑
}
// 其他通知...
}
@Service
public class UserServiceImpl implements UserService {
// 这个UserService的实例在Spring IoC容器中实际上是被代理的对象
@Override
public void addUser(User user) {
// 添加用户逻辑
}
}
在这个例子中,Spring通过生成代理对象来拦截目标方法调用,从而实现了诸如事务管理这样的横切关注点。这就是代理模式在Spring AOP中的应用。Spring既可以使用JDK动态代理,也可以使用CGLIB库进行代理实现。
由于文本交互的限制,下面将简要说明如何使用组合模式和观察者模式来实现电商购物车模块,并给出部分伪代码以展示关键逻辑。
1. 组合模式(Composite Pattern)
首先,定义一个商品接口Product
以及它的两种实现:单品SingleProduct
和套装BundleProduct
。套装内可以包含多个子商品项,即采用组合结构。
// 商品抽象类或接口
public abstract class Product {
protected double price;
public abstract void add(Product product);
public abstract void remove(Product product);
// ... 其他如获取价格、名称等方法
}
// 单品商品
public class SingleProduct extends Product {
private String name;
public SingleProduct(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public void add(Product product) {
throw new UnsupportedOperationException("单个商品不能添加子商品");
}
@Override
public void remove(Product product) {
throw new UnsupportedOperationException("单个商品没有子商品可移除");
}
// ...
}
// 套装商品,内部维护一个产品列表
public class BundleProduct extends Product {
private List<Product> productList = new ArrayList<>();
public void add(Product product) {
productList.add(product);
}
public void remove(Product product) {
productList.remove(product);
}
@Override
public double getPrice() {
double totalPrice = 0;
for (Product item : productList) {
totalPrice += item.getPrice();
}
return totalPrice;
}
// ...
}
2. 观察者模式(Observer Pattern)
定义一个Cart
购物车类,它持有商品集合并实现了观察者模式。当商品数量变化时,购物车总价自动更新。
import java.util.ArrayList;
import java.util.List;
// 购物车接口,同时作为观察目标(Observable)
public interface Cart extends ObserverTarget {
void addItem(Product product);
void removeItem(Product product);
double getTotalPrice();
// 注册和删除观察者的方法由ObserverTarget提供
}
// 购物车实现
public class ShoppingCart implements Cart {
private List<Product> cartItems = new ArrayList<>();
private List<Observer> observers = new ArrayList<>();
@Override
public void addItem(Product product) {
cartItems.add(product);
notifyObservers(); // 添加商品后通知所有观察者
}
@Override
public void removeItem(Product product) {
cartItems.remove(product);
notifyObservers(); // 移除商品后通知所有观察者
}
@Override
public double getTotalPrice() {
double totalPrice = 0;
for (Product item : cartItems) {
totalPrice += item.getPrice();
}
return totalPrice;
}
// 实现ObserverTarget接口中的注册与通知方法
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this); // 更新观察者(例如总价显示组件)
}
}
}
// 观察者接口
public interface Observer {
void update(Cart cart);
}
// 总价显示组件作为观察者
public class TotalPriceDisplay implements Observer {
@Override
public void update(Cart cart) {
double totalPrice = cart.getTotalPrice();
// 更新UI或其他操作,显示最新的总价
}
}
在这个例子中,当我们向购物车中添加或删除商品时,购物车会触发notifyObservers()
方法通知所有的观察者(如总价显示组件),观察者通过调用update()
方法来获取最新的购物车总价并进行相应更新。这样就实现了在添加或删除商品时实时计算并更新购物车总价的功能。
设计模式虽好,但如果滥用或过度设计,反而会导致代码过于复杂,增加理解和维护难度。以下是一些避免滥用和过度设计的建议:
总结来说,选择和应用设计模式的关键在于深入理解其原理,把握好适用的时机,并结合实际情况灵活运用,最终目的是为了提升软件的可读性、可维护性和可扩展性,而非盲目追求理论上的完美设计。