面向复用的设计模式
结构型模式 Structural patterns
适配器模式 Adapter
用途:将某个类/接口转换为client期望的其他形式 例:客户端想要调用LegacyRectangle的display方法,但是接口不匹配,后两个参数客户端提供的是x2, y2,而不是w和h。
class LegacyRectangle {
void display(int x1, int y1, int w, int h) {... }
}
class Client {
public display() {
new LegacyRectangle().display(x1, y1, x2, y2);
}
}
可以新增适配器类解决这个问题,客户端调用这个适配器类的方法即可。
interface Shape {
void display(int x1, int y1, int x2, int y2);
}
将其实现,方法中将x2, y2转换成了w, h
class Rectangle implements Shape {
void display(int x1, int y1, int x2, int y2) {
new LegacyRectangle().display(x1, y1, x2-x1, y2-y1);
}
}
这是原本要调用的类
class LegacyRectangle {
void display(int x1, int y1, int w, int h) {...}
}
客户端中,调用适配器类的方法即可适配接口
class Client {
Shape shape = new Rectangle();
public display() {
shape.display(x1, y1, x2, y2);
}
}
装饰器模式 Decorator
当类需要有不同特性的实现,又需要将这些特性组合起来使用时,可以使用装饰器模式实现
- 为对象增加不同侧面的特性
- 对每一个特性构造子类,通过委派机制增加到对 象上
缺点:
- 不满足LSP
- 若需要实现不同特性的任意组合,父类接口中需包含所有子类的方法
例:先实现一个最基础的堆
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 abstract 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();
}
...
}
若要实现堆的撤销功能,则新建一个类,继承装饰器,实现Stack,并在其中添加新的undo方法。同样,基础的push, pop操作通过委托实现,不过要在push前将其记录下来。
public class UndoStack
extends StackDecorator
implements Stack {
private final UndoLog log = new UndoLog();
public UndoStack(Stack stack) {
super(stack);
}
public void push(Item e) {
log.append(UndoLog.PUSH, e);
super.push(e);
}
public void undo() {
//implement decorator behaviors on stack
}
...
}
其他类型的修饰同理。
在使用时,将不同类型的特性一层一层套在一起
Stack t = new SecureStack(
new SynchronizedStack(
new UndoStack(s))
$\color{red}{注:使用这种方法需要在Stack接口中包含Undo, Secure, Synchronized中的所有方法,否则无法调用特性方法,也无法类型转换再调用}$
外观模式 Facade
Facade模式提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用 例如在lab3中,要判断资源/位置是否冲突以及寻找前序项。若这些功能都由客户端实现的话,则会造成大量方法的调用,不美观也不好修改。可以通过facade模式,新建一个类来将这些操作封装在一起,统一接受客户端的请求执行操作。
行为类模式 Behavioral patterns
策略模式 Strategy
若有多种不同的算法实现同一个任务,则可以使用strategy模式使客户端动态切换算法。 为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例
例:购物时的付款方式,可以选则多种付款方式
public interface PaymentStrategy {
public void pay(int amount);
}
可以选择信用卡付款
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");
}
}
也可以选择Paypal付款
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.");
}
}
在购物车结账时,我们就可以选择二者之一了。
例如传入的参数是PaypalStrategy类的,使用的就是Paypal付款。
public class ShoppingCart {
...
public void pay(PaymentStrategy paymentMethod){
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
模板模式 Template Method
- 做事情的步骤一样,但具体方法不同
- 共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现
- 使用继承和重写实现模板模式
例:每天早上都要起床,晚上都要睡觉,中午都要恰饭,而每天的工作内容不同。
设计一天的模板如下
public abstract class OneDay{
public LocalDate date;
public final void getUp(){
System.out.println("Get Up.");
}
public final void eat(){
System.out.println("要恰饭的嘛。");
}
public abstract void work();
public final void sleep(){
System.out.println("Sleep.");
}
}
在不同子类中重写work方法:
public class Study extends OneDay{
@Override
public void work(){
System.out.println("Study.");
}
}
public class TouchFish extends OneDay{
@Override
public void work(){
System.out.println("摸了。");
}
}
即可在模板上添加不同的工作内容。
迭代器模式 Iterator
客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型
也就是说,不管对象被放进哪里,都应该提供同样的遍历方式
需要实现Iterable接口与其中的iterator方法。
public interface Iterable<T> {
...
Iterator<T> iterator();
}
并在类中内嵌一个类实现Iterator类,在其中实现hasNext, next, remove方法。
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
Comments