重构获得模式 Refactoring to Patterns
面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”指的是那些可以满足“应对变化,提高复用”的设计。
现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点,然后在变化点处应用设计模式,从而更好地应对需求的变化”。“什么时候、什么地点应用设计模式”比“理解设计模式结构本身”更为重要。
设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发倡导的“Refactoring to Patterns”是目前普遍公认的最好的获得设计模式的方法。
软件设计的复杂性
设计模式的原则
封装变化角度对设计模式的分类
组件协作:
Template Method
Strategy
Observer / Event
单一职责:
Decorator
Bridge
对象创建:
Factory Method
Abstract Factory
Prototype
Builder
对象性能:
Singleton
Flyweight
接口隔离:
Façade
Proxy
Mediator
Adapter
状态变化:
Memento
State
数据结构:
Composite
Iterator
Chain of Responsibility
行为变化:
Command
Visitor
领域对象:
Interpreter
重构的关键技法
>静态 → 动态
>早绑定 → 晚绑定
>继承 → 组合
>编译时依赖 → 运行时依赖
>紧耦合 → 松耦合
学完设计模式,其实你会发现这八个点其实只是在不同的侧面来说明同一个问题。
“组件协作”模式:
现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用间的松耦合,是二者之间协作时常用的模式。>典型模式Template,MethodStrategy,Observer/Event
Template Method
动机(Motivation)
- 在软件构建过程中,对于某一项任务,它常常有稳定的结构,但各个子步骤却有很多改变的需求,或者由于固有原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现
- 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤化或者晚期实现需求 ?
要点总结
- Template Method模式是一种非常基础性的设计模式象系统中有着大量的应用。用最简洁的机制(接口方法)为很多应用程序框架提供了灵活的扩展点,是代码复用实现的基本实现结构。
- 除了可以灵活应对子步骤的变化外“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
- 在具体实现方面,被Template Method调用的方法可以实现,也可以没有任何实现(抽象方法),但它们设置为protected方法。
Strategy
动机(Motivation)
- 在软件构建过程中,某些对象使用的算法可能多种多样,经常改,如果将这些算法都编码到对象中,将会使对象变得异常复杂变而且有时候支持不使用的算法也是一个性能负担。
- 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题 ?
1. 定义策略接口
1. 定义策略接口
首先,我们定义一个策略接口 PaymentStrategy
,这是所有支付策略的共同接口。
// PaymentStrategy.java
public interface PaymentStrategy {
void pay(int amount);
}
2. 实现具体策略
接着,我们创建几个实现了 PaymentStrategy
接口的具体策略类:
// CreditCardPayment.java
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String cardHolderName;
public CreditCardPayment(String cardNumber, String cardHolderName) {
this.cardNumber = cardNumber;
this.cardHolderName = cardHolderName;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using Credit Card.");
}
}
// PayPalPayment.java
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using PayPal.");
}
}
3. 创建上下文类
上下文类 ShoppingCart
使用一个策略来进行支付。策略对象在运行时可以替换,因此 ShoppingCart
可以动态地改变它的支付行为。
// ShoppingCart.java
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public ShoppingCart(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
4. 使用策略模式
最后,我们来看一下如何使用策略模式:
public class StrategyPatternDemo {
public static void main(String[] args) {
// 使用信用卡支付
PaymentStrategy creditCardPayment = new CreditCardPayment("1234 5678 9012 3456", "John Doe");
ShoppingCart cart = new ShoppingCart(creditCardPayment);
cart.checkout(100); // 输出: "100 paid using Credit Card."
// 切换到使用PayPal支付
PaymentStrategy payPalPayment = new PayPalPayment("john@example.com");
cart.setPaymentStrategy(payPalPayment);
cart.checkout(200); // 输出: "200 paid using PayPal."
}
}
5. 运行输出
100 paid using Credit Card. 200 paid using PayPal.
解释
- 策略接口(
PaymentStrategy
):定义了一个支付策略的接口。 - 具体策略(
CreditCardPayment
和PayPalPayment
):实现了策略接口,提供了不同的支付算法。 - 上下文类(
ShoppingCart
):使用策略接口,客户端可以动态地更改上下文类的策略。
这种设计模式的优点是可以轻松地扩展新的策略而不改变现有代码,这非常符合“开闭原则”(Open/Closed Principle)。