一.Structural patterns 结构型模式
Adapter 适配器模式
概念
将某个类/接口转换为client期望的其他形式,从而使原本因接口不匹配而无法在一起使用的两个类可以一起工作
通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。
使用场景
主要应用场景为扩展应用。修改一个已经投产中的接口,比如系统扩展时,需要使用一个已有或新建立的类,但这个类又不符合系统的接口。
代码示例
//定义一个shape接口,四个参数为两个顶点坐标
interface Shape {
void display(int x1, int y1, int x2, int y2);
}
//利用适配器调整参数列表不匹配的问题
class Rectangle implements Shape {
void display(int x1, int y1, int x2, int y2) {
new LegacyRectangle().display(x1, y1, x2-x1, y2-y1); //adaptor
}
}
//长方形类包含四个参数为左下顶点坐标和长方形的长和宽
class LegacyRectangle {
void display(int x1, int y1, int w, int h) {...}
}
//客户端初始化时,任然按照已知的shape类建立,但内部实现经过adaptor调整为rectangle
//将客户端与内部实现隔离
class Client {
//初始化rectangle
Shape shape = new Rectangle();
public display() {
shape.display(x1, y1, x2, y2);
}
}
Decorator 装饰器模式
概念
动态地给一个对象添加一些额外的职责。就增加功能来讲,装饰模式比生成子类更加灵活。
使用场景
- 需要扩展一个类的功能,或给一个类增加附加功能(如下方代码实例中 UndoStack)
- 需要动态给一对象增加功能
- 需要为一批的兄弟类进行改装或加装功能
代码实例
//栈功能定义接口
interface Stack {
void push(Item e);
Item pop();
}
//最基础的接口实现类
public class ArrayStack implements Stack {
... //rep
public ArrayStack() {...}
public void push(Item e) {...}
public Item pop() {...}
}
//对栈进行装饰的类
public class StackDecorator implements Stack {
protected final Stack stack;
public StackDecorator(Stack stack) { //在构造时进行委派
this.stack = stack;
}
public void push(Item e) {
stack.push(e);
}
public Item pop() {
return stack.pop();
}
...
}
public class UndoStack
extends StackDecorator
implements Stack {//带有回退功能的栈结构
private final UndoLog log = new UndoLog();
public UndoStack(Stack stack) {
super(stack); //调用父类中的构造方法StackDecorator(stack)
}
public void push(Item e) {
log.append(UndoLog.PUSH, e); //增加了新特性
super.push(e); //基础功能通过委派实现
}
public void undo() {//新功能
//implement decorator behaviors on stack
}
...
}
客户端需要一个具有多种特性的object,通过一层一层的装饰来实现
就像一层层的穿衣服
可以十分灵活的进行功能组织,在运行时才将功能组合,而继承在编译阶段就已经确定
Facade 外观模式
概念
- 提供一个统一的接口来取代一系列小接口调用,客户端需要通过一个简化的接口来访问复杂系统内的功能,相当于对复杂系统做了一个封装,简化客户端使用。
- 便于客户端学习使用,解耦
使用场景
- 为一个复杂模块和子系统提供一个供外界访问的接口
- 子系统相对独立-----外界对子系统的访问只要黑箱操作即可
- 预防低水平人员带来的风险扩散
代码示例
//两种数据库的具体功能列表 但易用性不够强
public class MySqlHelper {
public static Connection getMySqlDBConnection() {…}
public void generateMySqlPDFReport (String tableName, Connection con){…}
public void generateMySqlHTMLReport (String tableName, Connection con){…}
}
public class OracleHelper {
public static Connection getOracleDBConnection() {…}
public void generateOraclePDFReport (String tableName, Connection con){…}
public void generateOracleHTMLReport (String tableName, Connection con){…}
}
//经过外观模式改造后呈现的方法实现
public class HelperFacade {
public static void generateReport (DBTypes dbType, ReportTypes reportType, String tableName){
Connection con = null;
switch (dbType){
case MYSQL:
con = MySqlHelper.getMySqlDBConnection();
MySqlHelper mySqlHelper = new MySqlHelper();
switch(reportType){
case HTML:
mySqlHelper.generateMySqlHTMLReport(tableName, con);
break;
case PDF:
mySqlHelper.generateMySqlPDFReport(tableName, con);
break;
}
break;
case ORACLE: …//与上述相同
}
public static enum DBTypes { MYSQL,ORACLE; }
public static enum ReportTypes { HTML,PDF;}
}
//客户端调用的区别
//不使用外观模式
String tableName="Employee";
Connection con = MySqlHelper.getMySqlDBConnection();
MySqlHelper mySqlHelper = new MySqlHelper();
mySqlHelper.generateMySqlHTMLReport(tableName, con);
Connection con1 = OracleHelper.getOracleDBConnection();
OracleHelper oracleHelper = new OracleHelper();
oracleHelper.generateOraclePDFReport(tableName, con1);
//使用外观模式
HelperFacade.generateReport(HelperFacade.DBTypes.MYSQL,
HelperFacade.ReportTypes.HTML, tableName);
HelperFacade.generateReport(HelperFacade.DBTypes.ORACLE,
HelperFacade.ReportTypes.PDF, tableName);
二.Behavioral patterns 行为类模式
Strategy 策略模式
概念
问题:有多种不同的算法来实现同一个任务,但需要client根据需要动态切换算法,而不是写死在代码里
解决方案:为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例
eg:选择支付方式
使用场景
- 多个类只有在算法和行为上稍有不同的场景。
- 算法需要自由切换的场景
- 需要屏蔽算法规则的场景
- 具体策略数量超过4个,需要考虑使用混合模式
代码示例
public interface PaymentStrategy {
public void pay(int amount);
}
//策略1
public class CreditCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate){
this.name=nm;
this.cardNumber=ccNum;
this.cvv=cvv;
this.dateOfExpiry=expiryDate;
}
@Override
public void pay(int amount) {
System.out.println(amount +" paid with credit card");
}
}
//策略2
public class PaypalStrategy implements PaymentStrategy {
private String emailId;
private String password;
public PaypalStrategy(String email, String pwd){
this.emailId=email;
this.password=pwd;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using Paypal.");
}
}
//策略选择
public class ShoppingCart {
...
public void pay(PaymentStrategy paymentMethod){ //委派机制
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
//client
public class ShoppingCartTest {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("1234",10);
Item item2 = new Item("5678",40);
cart.addItem(item1);
cart.addItem(item2);
//pay by paypal
cart.pay(new PaypalStrategy("myemail@exp.com", "mypwd"));
//pay by credit card
cart.pay(new CreditCardStrategy(“Alice", "1234", "786", "12/18"));
}
}
Template 模板模式
概念
问题:做事情的步骤一样,但具体方法不同
解决:共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现
使用继承和重写实现模板模式
使用场景
- 多个子类有公有方法,并且逻辑基本相同
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
- 【重构时常用】 将相同的代码抽取到父类中,使用钩子函数约束其行为
代码示例
//模板类
public abstract class OrderProcessTemplate {
public boolean isGift;
public abstract void doSelect(); //每个类中都有但实现细节不相同,需要分别在每个类中进行定义
public abstract void doPayment();
public final void giftWrap() { //共有的方法
System.out.println("Gift wrap done.");
}
public abstract void doDelivery();
public final void processOrder() {
doSelect();
doPayment();
if (isGift)
giftWrap();
doDelivery();
}
}
//对模板的实现类
public class NetOrder
extends OrderProcessTemplate {
@Override
public void doSelect() { … }
@Override
public void doPayment() { … }
@Override
public void doDelivery() { … }
}
Interator 迭代器模式
概念
问题:客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型
也就是说,不管对象被放进哪里,都应该提供同样的遍历方式
代码示例
让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove),允许客户端利用这个迭代器进行显式或隐式的迭代遍历。
- Iterable接口:实现该接口的集合对象是可迭代遍历的
public interface Iterable<T> {
...
Iterator<T> iterator();
}
- Iterator接口:迭代器
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}