可维护性的设计模式
Creational patterns
工厂方法模式(Factory Method pattern)
工厂方法模式也称为虚拟构造器,创建实例时不使用new
方法,而是通过使用接口的方式,让客户端与返回具体实例之间解耦合。它的具体描述是:当client不知道/不确定要创建哪个具体类的实例,或者想在client代码中指明要具体创建的实例时,用工厂方法。 定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。它有两种实现。
- 设计多个工厂方法
如果设计多个具体的实现类,都继承自同一个顶层接口,在这个子类中实现它的不一致的功能,将相同的功能放在顶层接口中实现。那么这样的实现方式使得客户端与具体的实现相关联,知道具体的方法。而采用工厂方法,可以在接口中定义get
方法获得实例,通过继承父类对子类进行实现,返回具体的实例。使用 “工厂方法”来创建实例,得到实例的类型是抽象接口不是具体类,这样在需要增加类型时更符合OCP原则。 - 设计一个工厂方法
在设计工厂方法时根据输入的类型来决定返回哪一种具体的实例。这种实现方式只需要设计一个工厂方法,将具体的类型都用一个case
或者if-else
语句进行判断。
谈到工厂方法时,有一个与之功能相似的方法,称为静态工厂方法。静态工厂方法能够在ADT的内部直接实现,也可以构造单独的工厂。静态工厂方法能够具有指定名称,而不是仅仅new
进行指定。并且静态工厂方法能够返回原来返回类型的任意子类型。
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式提供了接口用来创建一组相关联的对象,但是这个实现并未指明具体的实现类型。相当于将所有的功能进行了一个组合,每个不同的类需要完成的功能进行打包,而每个功能的具体的实现却并未包含,对象的实例创建放在了使用具体产品时。这个设计模式通过下面这个图形能够较为容易理解:
Abstract Factory 创建的不是一个完整产品,而是“产品族”(遵循固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的object,各产品创建过程对client可见,但“搭配”不能改变。直接用factory method,client可能不知道搭配而用错工厂。
Structural patterns
代理模式(Proxy pattern)
代理模式的使用主要是因为在具体的应用当中,某些对象的信息不希望直接被客户端所访问到,因此设置了代理,在具体的对象隐秘信息间和具体的客户端之间设置防火墙。代理分为3种不同的方式,根据不同的实现需要采用不同的方法。
- 远程代理
远程代理是指为一个对象在不同的地址空间提供局部代表,本质上就是缓存机制。如果这个访问过程中,信息不会经常变化,那么远程代理能够很好提升性能,提升访问的速度。 - 虚拟代理
虚拟代理是根据实际的需要创建开销很大的对象,如果这个对象不会被经常访问到的话,我们使用虚拟代理就能够延迟创建,很大程度上能够提升程序的性能和访问速度。 - 保护代理
保护代理顾名思义就是为对象提供一种保护的机制。通过创建代理,不直接对实际对象进行访问,代理通过设置委托的方式对实际的对象进行访问,达到保护的目的,就是保护代理。
Behavioral patterns
观察者模式(Observer pattern)
观察者模式是为了保护状态的一致性,即有很多个对象依赖于某一个对象,但这个对象的状态或者信息发生改变的时候,就需要通过观察者模式对其他的对象进行通知。观察者模式是一种“发布-订阅”的形式,发布方发生了变化,通知订阅方,不同的订阅方根据具体的操作判断自身应该做什么操作。订阅方需要在发布方进行注册,发布方与订阅方之间通过接口进行分离。
抽象的subject
需要维护一个订阅者列表,抽象的observer
需要定义update的协议,即在得知发布方发生变化之后,自身做什么处理。具体的subject
需要维护数据,修改之后通知订阅方,而具体的observer
得到通知之后,需要修改自身的状态。
下面是一个具体的例子:
public class Subject{
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
private void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update(){
System.out.println( "Binary String: " +
Integer.toBinaryString( subject.getState() ) );
}
}
观察者模式有3种变体,可以在发生变化时推送通知、推送通知和数据、拉取数据。观察者模式使得主体和观察者之间松耦合,主体不需要关注观察者,并且可以动态增加、删除观察者。同时观察者的行为也不受到主体的控制。实现过程中,主体需要存储观察者列表,同时可以在观察者中的update
函数中增加参数来观察多个主体。同时可以设计触发机制。
访问者模式(Visitor pattern)
访问者模式的本质就是将数据和作用在数据上的操作分离开来,可能某些数据可以根据不同的操作进行处理,因此可以为ADT预留一个将来可以拓展功能的接入点,外部可以通过委托的方式接入到ADT中。下面以一个例子来进行展示:
/* Abstract element interface (visitable) */
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
/* Concrete element */
public class Book implements ItemElement{
private double price;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
}
}
/* Second element */
public class Fruit implements ItemElement{
private double weight;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
}
}
/* Abstract visitor interface */
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
public int visit(Book book) {
int cost=0;
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else
cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
}
}
在具体的实现中将数据和操作在运行时动态绑定,操作可以灵活更改,不需要更改visit的类。
Summary
以上5种是常用的面向可维护性的构造过程中的设计模式,分为了创建层面、结构层面和行为层面。创建层面主要有Factory method
和Abstract factory
,在结构层面有Porxy
,在行为层面有Observer
和Visitor
。