装饰器模式 Decorator
1、什么是装饰器模式
装饰器模式允许通过将对象放入特殊的包装对象中来为原始对象添加新的行为。这种模式是一种结构型模式,因为它通过改变结构来改变被装饰对象的行为。它涉及到一组装饰器类,这些类用来包装具体组件。
2、为什么使用装饰器模式
- 灵活性:装饰器模式允许在运行时动态地为对象添加新的行为,而无需修改其代码,提供了一种灵活的方式来扩展对象的功能。
- 避免子类爆炸:通过使用装饰器模式,可以避免创建大量子类来扩展对象的功能,从而避免了子类爆炸的问题。
- 组合功能:可以通过组合多个装饰器来实现复杂的功能组合,无需使用大量的继承关系。
3、如何使用装饰器模式
设计实现咖啡订单系统,包含基本咖啡和不同的配料作为装饰器
// 抽象组件 - 咖啡
interface Coffee {
String getDescription();
double cost();
}
// 具体组件 - 基本咖啡
class BasicCoffee implements Coffee {
@Override
public String getDescription() {
return "Basic Coffee";
}
@Override
public double cost() {
return 3.0;
}
}
// 抽象装饰器
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
}
// 具体装饰器 - 牛奶
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", Milk";
}
@Override
public double cost() {
return super.cost() + 1.0;
}
}
// 具体装饰器 - 糖
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", Sugar";
}
@Override
public double cost() {
return super.cost() + 0.5;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建基本咖啡
Coffee basicCoffee = new BasicCoffee();
System.out.println("Description: " + basicCoffee.getDescription());
System.out.println("Cost: $" + basicCoffee.cost());
// 添加牛奶装饰器
Coffee milkCoffee = new MilkDecorator(basicCoffee);
System.out.println("Description: " + milkCoffee.getDescription());
System.out.println("Cost: $" + milkCoffee.cost());
// 添加糖装饰器
Coffee sugarMilkCoffee = new SugarDecorator(milkCoffee);
System.out.println("Description: " + sugarMilkCoffee.getDescription());
System.out.println("Cost: $" + sugarMilkCoffee.cost());
}
}
4、是否存在缺陷和不足
- 可能导致类爆炸:有大量具体组件和装饰器时,可能导致类的数量急剧增加,增加了系统的复杂性。
- 破坏封装性:装饰器模式将具体组件暴露给装饰器类,可能破坏了封装性。
5、如何缓解缺陷和不足
- 使用抽象工厂:结合抽象工厂模式,通过工厂来创建组件和装饰器,降低类的数量。
- 使用组合模式:将具体组件和装饰器组织成树形结构,使用组合模式来管理它们的关系。
- 慎用过多装饰器:在设计时慎用过多的装饰器,确保仅在需要时使用,以避免类爆炸问题。
适配器模式 Adapter
1、什么是适配器模式
适配器模式允许原本由于接口不匹配而无法在一起工作的类能够协同工作。它通过引入一个包装类,即适配器,来转换原有类的接口为客户端期望的接口。
2、为什么使用适配器模式
- 解耦性:适配器模式允许客户端与目标类的实现细节解耦,使得客户端不需要知道目标类的内部实现。
- 复用性:适配器模式可以使得已有的类在新的系统中复用,而无需修改其代码。
- 灵活性:适配器模式允许在不改变现有代码的情况下引入新的类,提高系统的灵活性。
3、如何使用适配器模式
// 目标接口
interface Target {
void request();
}
// 不兼容的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specificRequest");
}
}
// 适配器类
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request();
}
}
4、是否存在缺陷和不足
- 可能导致过多的适配类:如果系统中有多个不同的类需要适配,可能会导致大量的适配器类,使系统变得复杂。
- 不支持多继承的语言的限制:在一些不支持多继承的语言中,适配器模式可能会受到限制。
5、如何缓解缺陷和不足
- 使用对象适配器而非类适配器:对象适配器通过组合的方式引入被适配对象,避免了类适配器的多继承问题。
- 考虑使用接口适配器:如果目标接口中定义的方法较多,可以考虑使用接口适配器模式,只需实现感兴趣的方法。