面向可维护性的设计模式
1.创建型模式
关于如何“创建类的新实例”的模式
1.1 工厂方法模式
当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。
定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
静态工厂方法:
优点:避免将代码与具体的实现类(因具体应用而不同)绑定。代码对接口编程,因而适用于所有的实现类。
缺点:为了创建需要的具体的对象,客户端必须知道Creator抽象类的子类。
1.2 抽象工厂模式
提供接口以创建一组相关/相互依赖的对象,但不需要指明其具体类。
创建的不是一个完整产品,而是“产品族”(遵循固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的object,各产品创建过程对client可见,但“搭配”不能改变。
使用factory method,不能保证固定搭配
抽象工厂与工厂方法:
抽象工厂 | 工厂方法 |
---|---|
创建一组有关联的对象 | 创建一个对象 |
一组factory方法 | 一个factory方法 |
使用组合/委派 | 使用继承/子类型 |
(抽象工厂把创建对象的过程委派给其他的类)
1.3 构造器模式
创建复杂对象,其中包含多个组成部分
例:
该Pizza产品的part是以三个属性的形式体现,其builder就相当于给三个属性赋值。
/*Product*/
class Pizza{
private String dough = "";
private String sauce = "";
private String topping = "";
public void setDough(String dough){
this.dough = dough;
}
public void setSauce(String sauce){
this.sauce = sauce;
}
public void setTopping(String topping){
this.topping = topping;
}
}
Builder抽象类有三个抽象方法,分别创建三个part
/*"Abstract Builder"*/
abstract class PizzaBuilder{
protected Pizza pizza;
public Pizza getPizza(){
return pizza;
}
public void createNewPizzaProduct(){
pizza = new Pizza();
}
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
}
具体的builder子类
/* "ConcreteBuilder 1" */
class SpicyPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("pan baked");
}
public void buildSauce() {
pizza.setSauce("hot");
}
public void buildTopping() {
pizza.setTopping("pepperoni+salami");
}
}
/* "ConcreteBuilder 2" */
class HawaiianPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("cross");
}
public void buildSauce() {
pizza.setSauce("mild");
}
public void buildTopping() {
pizza.setTopping("ham+pineapple");
}
}
/* "Director" */
class Waiter {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
} //定义要delegate的builder
public Pizza getPizza() {
return pizzaBuilder.getPizza();
} //得到最终builder的结果
public void constructPizza() {
pizzaBuilder.createNewPizzaProduct();
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildTopping();
} //具体构建各部分
}
/* A customer ordering a pizza. */
public class PizzaBuilderDemo {
public static void main(String[] args) {
Waiter waiter = new Waiter();
PizzaBuilder hawaiianPizzabuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
//client使用不同的builder子类创建不同的产品
waiter.setPizzaBuilder( hawaiianPizzabuilder );
//建立waiter和builder的delegation关系
waiter.constructPizza();
Pizza pizza = waiter.getPizza();
}
}
与抽象工厂模式的对比:
抽象工厂模式创建的不是一个完整产品,而是“产品族”。得到多个不同产品的实例object,各产品创建过程对client可见,但“搭配”不能改变。
构造器模式创建的是一个完整的产品,有多个部分组成,client不需了解每个部分是怎么创建、各个部分怎么组合,最终得到一个产品的完整object
与模板模式的对比:
模板模式:行为类模式。复用算法骨架,强调步骤的次序
构造器模式:创建型模式。将一个复杂对象的构造方法与对象内部的具体表示分离出来,灵活扩展,适应变化。
2.结构型模式
2.1 桥接模式
主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。
要素:
1.抽象类(Abstraction):维护对行为实现(implementation)的引用。
2.Refined Abstraction:Abstraction的子类
3.Implementor:行为实现类接口
4.ConcreteImplementor:Implementor的子类
例:
Implementor:
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
ConcreteImplementor:
public class DrawRedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println(“Color: red " + radius + x + y);
}
}
public class DrawGreenCircle implements DrawAPI {
@Override public void drawCircle(int radius, int x, int y) {
System.out.println(“Color: green " + radius + x + y);
}
}
Abstraction:
public abstract class Shape {
protected DrawAPI drawAPI; //永久保存delegation并在其他场合使用
protected Shape(DrawAPI drawAPI){ //动态传入,建立delegation link
this.drawAPI = drawAPI;
}
public abstract void draw();
}
Refined Abstraction
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
drawAPI.drawCircle(radius,x,y); //将draw这个责任delegate到drawAPI这个接口的具体实例去执行
}
}
与Strategy模式的对比:
Bridge:结构型模式,强调双方的run time delegation linking
一个类A的对象中有其他类B的对象作为其组成部分,A的对象具体绑定到B的哪个具体子类,在运行时通过delegation加以组合, 并永久保存这种delegation关系。
Strategy:行为类模式,强调一方run-time使用另一方的“算法”
“算法”通常实现为“类的某个方法”的形式, strategy的目的并非在“调用算法的类”与“被调用算法所在的类”之间建立起永久联系,而只是帮助前者临时使用后者中的“算法”,前者无需永久保存后者的实例。
2.2 代理模式
在client和不希望被直接访问(敏感/私密/访问代价高)的对象之间建立防火墙。
例:
public interface Image {
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {…}
private void loadFromDisk(String fileName){…}
//每次创建都要 从磁盘装载,代价高
}
public class ProxyImage implements Image {
private Image realImage;
private String fileName;
public ProxyImage(String fileName){
//不需要在构造的时候从文件装载
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
//如果display的时候发现没有装载,则再delegation
realImage = new RealImage(fileName);
//Delegate到原来的类来完成具体装载
}
realImage.display();
}
}
Client:
Image image = new ProxyImage(“pic.jpg");
image.display();
image.display();
与Adapter模式的对比:
Adapter::消除不兼容,目的是B以客户端期望的统一的方式与A建立起联系。
Proxy: 目的:隔离对复杂对象的访问,降低难度/代价,定位在“访问/使用行为”
2.3 组合模式
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。它创建了对象组的树形结构。
例:
public interface Employee {
public void add(Employee e);
public void remove(Employee e);
public List<Employee> getSubordinates();
}
public class ConcreteEmployee implements Employee{
private String name;
private String position;
private List<Employee> subordinates;
public Employee(String name) {
this.name = name; subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
}
public class CEO extends ConcreteEmployee {
//Special fields or methods
}
public class Clerk extends ConcreteEmployee {
//Special fields or methods
}
Employee CEO = new CEO ("John");
Employee headSales = new ConcreteEmployee("Robert");
Employee clerk1 = new Clerk("Laura");
Employee clerk2 = new Clerk("Bob");
CEO.add(headSales);
CEO.add(clerk1);
clerk1.add(clerk2);
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
与Decorator的区别:
Composite: 目的是在同类型的对象之间建立起树型层次结构,一个上层对象可包含多个下层对象
叶节点的实体与组合实体相同;组合结构中所有元素具有相同接口
Decorator: 强调的是同类型对象之间的“特性增加”问题
3.行为式模式
3.1 Observer
dependent的状态必须与master的状态一致
四种对象:
抽象主体:管理dependents的列表,master状态变化时通知他们
抽象观察者:定义更新dependents的方式
具体主体:管理dependents的信息,master状态变化时通知他们
具体观察者:收到更新状态的信息之后更新状态
代码:
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(); //callbck调用“粉丝”的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() ) );
}
}
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15"); s
ubject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);//没有调用observer行为的代码
}
}
构建了一到多的依赖关系,将被观察者的状态与观察者连接起来。
优点:
1.主体与观察者之间的低耦合
2.允许广播
3.观察者更新状态不受主体的控制
java提供了Observer(观察者)和Observable(被观察者/主体)接口
3.2 Visitor
将数据与操作分离。创建一个外部类(visitor),该类对其他类中的数据进行操作。当操作逻辑改变时,只需改变visitor类的实现。
/* 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); //将处理数据的功能delegate到外部传入的visitor
}
}
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;
}
}
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{ new Book(20, "1234"),new Book(100, "5678"), new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items); System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0; for(ItemElement item : items)
sum = sum + item.accept(visitor);
return sum;
}
}
Visitor vs Iterator
迭代器:以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象。
Visitor:在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT
Strategy vs visitor
:visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度,灵活变化对其内部功能的不同配置。