装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。
装饰模式在不改变原类文件和继承关系的情况下,用被装饰对象的父类新派生一个装饰对象,用装饰对象来包裹被装饰对象(真实对象)以达到装饰(添加新功能/职责)的效果。
所谓“装饰”,就是动态地为真实对象添加新的功能。当系统需要新功能的时候,传统的做法是向旧的类中添加的新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,而新加的代码往往只是为了满足一些只在某种特定环境下才会执行的特殊行为的需求。应用装饰模式,把每个要装饰的功能放在单独的类中,并用这种类来包裹这些代码所要装饰的对象。因此,在需要执行特殊行为的时候,客户端代码就可以在运行时根据情况有选择、按顺序地使用装饰对象去包装真实对象。
装饰模式的优点是:一,把类中的装饰功能从类中去除,简化的类;二,有效地区分了类的核心职责和装饰功能,避免了重复的装饰逻辑。
装饰模式的实现特点有:
1. 装饰对象和真实对象拥有相同的接口或父类;
2. 装饰对象中包含真实对象的引用,真实对象包装在装饰对象中;
3. 客户端中不再操作真实对象,而是通过装饰对象把请求/参数传递给真实对象,通过装饰对象来操作真实对象;
4. 装饰对象在操作真实对象之前或之后可以进行一些额外的操作以满足特定的需求;
5. 如果包裹多个装饰对象,那么装饰对象的操作是有序的:越在外层的装饰对象越先执行。
下面基于装饰模式的Java代码的核心逻辑是一个平均值计算程序,在它的基础上装饰了两个额外的计算功能:
共同抽象父类:
import java.util.List;
public abstract class Calculator {
public abstract double getResult(List<Double> list);
}
真实对象,平均值计算:
import java.util.List;
public class AverageCalculator extends Calculator {
@Override
public double getResult(List<Double> list) {
double sum = 0;
for (int i = 0; i < list.size(); i++) {
sum += list.get(i);
}
return sum / (list.size() == 0 ? 1 : list.size());
}
}
装饰对象的共同父类:
import java.util.List;
public abstract class OptionalCalculator extends Calculator {
protected Calculator calculator;
public void setCalculator(Calculator calculator) {
this.calculator = calculator;
}
public abstract double getResult(List<Double> list);
}
计算超过平均值的数字个数的装饰对象:
import java.util.List;
public class OverAverageCalculator extends OptionalCalculator {
@Override
public double getResult(List<Double> list) {
double result = calculator.getResult(list);
int count = 0;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) >= result) {
count++;
}
}
System.out.println(count + "个高于平均值");
return result;
}
}
计算方差的装饰对象,只在数字个数超过20个的情况下才真正执行:
import java.util.List;
public class VarianceCalculator extends OptionalCalculator {
@Override
public double getResult(List<Double> list) {
double result = calculator.getResult(list);
if (list.size() > 20) {
double num = 0;
for (int i = 0; i < list.size(); i++) {
num += (list.get(i) - result) * (list.get(i) - result);
}
System.out.println(list.size() + "个数的方差是 " + (num / list.size()));
} else {
System.out.println("样本数量不足20个不计算方差");
}
return result;
}
}
客户端:
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
ArrayList<Double> list = new ArrayList<Double>();
double d;
do {
d = in.nextDouble();
if (d >= 0){
list.add(d);
}
} while (d >= 0);
Calculator ave = new AverageCalculator();
OptionalCalculator over = new OverAverageCalculator();
OptionalCalculator var = new VarianceCalculator();
over.setCalculator(ave);
var.setCalculator(over);
System.out.println("平均值:" + var.getResult(list));
}
}