装饰者模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
使用场景:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
示例:
interface Coffee {
double getCost();
String getIngredients();
}
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1;
}
@Override
public String getIngredients() {
return "Coffee";
}
}
abstract class CoffeeDecorator implements Coffee {
private final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
@Override
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
class MilkCoffee extends CoffeeDecorator {
public MilkCoffee(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Milk";
}
}
class WhipCoffee extends CoffeeDecorator {
public WhipCoffee(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 0.7;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Whip";
}
}
public class DecoratorExample {
public static void main(String[] args) {
Coffee c = new SimpleCoffee();
System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());
c = new MilkCoffee(c);
System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());
c = new WhipCoffee(c);
System.out.println("Cost : " + c.getCost() + "; Ingredients : " + c.getIngredients());
}
}
该示例演示了一个简单的咖啡饮料店,其中定义了一个抽象接口Coffee,该接口有两个方法:getCost()和getIngredients()。
然后我们定义了一个简单的咖啡饮料:SimpleCoffee,它实现了Coffee接口,并且有默认的价格和成分。
优点
装饰者模式可以提供比继承更多的灵活性
可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
会产生很多的小对象,增加了系统的复杂性
这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
策略模式(Strategy Pattern)是一种常见的设计模式,它定义了一族算法,将每个算法都封装起来,让它们之间可以互相替换。策略模式可以使得算法独立于使用它的客户端而独立变化,从而可以让客户端在不修改源代码的情况下改变算法的使用方式。
这里是一个使用策略模式实现排序的示例代码。假设有一个Sorter类,它可以使用不同的排序算法来对一个整数数组进行排序,其中具体使用哪个算法由客户端代码指定。
首先,需要定义一个接口SortStrategy,表示一个排序算法。这个接口包含一个排序方法sort,它接受一个整数数组作为参数,并返回排序后的数组:
public interface SortStrategy {
int[] sort(int[] array);
}
接下来,可以定义具体的排序算法。这里,我们实现了两种排序算法:冒泡排序和快速排序。它们都实现了SortStrategy接口:
public class BubbleSort implements SortStrategy {
public int[] sort(int[] array) {
// 冒泡排序算法的具体实现
// ...
return sortedArray;
}
}
public class QuickSort implements SortStrategy {
public int[] sort(int[] array) {
// 快速排序算法的具体实现
// ...
return sortedArray;
}
}
现在,我们可以定义Sorter类了。它包含一个sort方法,它接受一个整数数组和一个排序策略作为参数。排序策略是一个实现了SortStrategy接口的类的实例。Sorter类的sort方法使用传入的排序策略来对数组进行排序
public class Sorter {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public int[] sort(int[] array) {
return this.strategy.sort(array);
}
}
最后,可以在客户端代码中使用Sorter类来对数组进行排序了。先创建一个整数数组,然后创建一个Sorter对象,并将排序策略设置为冒泡排序。然后,使用Sorter对象的sort方法对数组进行排序,并将结果打印出来。接着,将排序策略设置为快速排序,再次对数组进行排序,并将结果打印出来
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3};
Sorter sorter = new Sorter();
sorter.setStrategy(new BubbleSort());
int[] sortedArray = sorter.sort(array);
System.out.println(Arrays.toString(sortedArray)); // 输出 [1, 2, 3, 4, 5, 6]
sorter.setStrategy(new QuickSort());
sortedArray = sorter.sort(array);
System.out.println(Arrays.toString(sortedArray)); // 输出 [1, 2, 3, 4, 5, 6]
}
策略模式是一种行为型设计模式,它可以动态地更改一个类的行为。
在这个代码中,我们有两种排序算法:BubbleSort 和 QuickSort。
在 Sorter 类中,我们使用策略模式,以便动态地选择要使用的排序算法。
在主类 Main 中,我们创建了一个 Sorter 对象,并使用 BubbleSort 算法排序。
然后,我们可以通过使用 setSortStrategy() 方法更改要使用的排序算法。
其实策略模式和模板模式很相似,但又不相同,我们看下一章:详述java的设计模式(三)