一、故事解说:星巴克如何制作花式咖啡
假设你去星巴克点咖啡:
-
基础咖啡:点一杯美式咖啡(基础饮品);
-
添加装饰:
- 加奶泡(装饰 1):美式变拿铁;
- 加糖浆(装饰 2):拿铁变香草拿铁;
- 加奶油(装饰 3):香草拿铁变顶级香草拿铁;
-
核心逻辑:每加一种料(装饰),就给咖啡增加一种新特性,且可以随时添加或移除,不影响基础咖啡的制作流程。
装饰器模式核心:动态地给对象添加新功能,就像给咖啡添加配料一样,通过包装(装饰器)包裹原始对象,在不修改原始代码的情况下扩展功能。
二、装饰器模式核心结构(咖啡案例)
java
// 1. 抽象组件:咖啡
interface Coffee {
String getDescription(); // 获取描述
double getPrice(); // 获取价格
}
// 2. 具体组件:美式咖啡(基础咖啡)
class Americano implements Coffee {
@Override
public String getDescription() {
return "美式咖啡";
}
@Override
public double getPrice() {
return 25.0;
}
}
// 3. 抽象装饰器:咖啡装饰基类
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 getPrice() {
return decoratedCoffee.getPrice();
}
}
// 4. 具体装饰器:加奶泡
class MilkFoamDecorator extends CoffeeDecorator {
public MilkFoamDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + " + 奶泡";
}
@Override
public double getPrice() {
return decoratedCoffee.getPrice() + 5.0;
}
}
// 5. 具体装饰器:加糖浆
class SyrupDecorator extends CoffeeDecorator {
private String syrupType;
public SyrupDecorator(Coffee decoratedCoffee, String syrupType) {
super(decoratedCoffee);
this.syrupType = syrupType;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + " + " + syrupType + "糖浆";
}
@Override
public double getPrice() {
return decoratedCoffee.getPrice() + 8.0;
}
}
// 6. 客户端:组合装饰
public class CoffeeShop {
public static void main(String[] args) {
// 基础美式咖啡
Coffee coffee = new Americano();
System.out.println(coffee.getDescription() + " - 价格: ¥" + coffee.getPrice());
// 美式 + 奶泡
coffee = new MilkFoamDecorator(coffee);
System.out.println(coffee.getDescription() + " - 价格: ¥" + coffee.getPrice());
// 美式 + 奶泡 + 香草糖浆
coffee = new SyrupDecorator(coffee, "香草");
System.out.println(coffee.getDescription() + " - 价格: ¥" + coffee.getPrice());
}
}
三、Android 常用装饰器模式案例与实现
案例 1:TextView 样式装饰(动态添加样式)
java
// 1. 抽象组件:文本视图
interface TextView {
void setText(String text);
String getText();
void setTextSize(float size);
float getTextSize();
}
// 2. 具体组件:基础TextView
class BaseTextView implements TextView {
private String text;
private float textSize = 16f; // 默认16sp
@Override
public void setText(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public void setTextSize(float size) {
this.textSize = size;
}
@Override
public float getTextSize() {
return textSize;
}
}
// 3. 抽象装饰器:TextView装饰基类
abstract class TextViewDecorator implements TextView {
protected TextView decoratedTextView;
public TextViewDecorator(TextView decoratedTextView) {
this.decoratedTextView = decoratedTextView;
}
// 委托给被装饰的TextView
@Override
public void setText(String text) {
decoratedTextView.setText(text);
}
@Override
public String getText() {
return decoratedTextView.getText();
}
@Override
public void setTextSize(float size) {
decoratedTextView.setTextSize(size);
}
@Override
public float getTextSize() {
return decoratedTextView.getTextSize();
}
}
// 4. 具体装饰器:加粗装饰
class BoldDecorator extends TextViewDecorator {
public BoldDecorator(TextView decoratedTextView) {
super(decoratedTextView);
}
@Override
public void setText(String text) {
// 先调用原始方法设置文本
super.setText(text);
// 添加加粗装饰逻辑
applyBold();
}
private void applyBold() {
System.out.println("应用加粗样式");
// 实际中通过SpannableString设置加粗
}
}
// 5. 具体装饰器:斜体装饰
class ItalicDecorator extends TextViewDecorator {
public ItalicDecorator(TextView decoratedTextView) {
super(decoratedTextView);
}
@Override
public void setText(String text) {
super.setText(text);
applyItalic();
}
private void applyItalic() {
System.out.println("应用斜体样式");
// 实际中通过SpannableString设置斜体
}
}
// 6. 在Activity中使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建基础TextView
TextView textView = new BaseTextView();
textView.setText("装饰器模式示例");
textView.setTextSize(18);
// 添加加粗装饰
textView = new BoldDecorator(textView);
// 添加斜体装饰
textView = new ItalicDecorator(textView);
// 最终效果
System.out.println("文本: " + textView.getText());
System.out.println("字号: " + textView.getTextSize() + "sp");
// 输出:
// 应用加粗样式
// 应用斜体样式
// 文本: 装饰器模式示例
// 字号: 18.0sp
}
}
优点:
-
灵活扩展:可动态添加任意数量的样式装饰(加粗、斜体、下划线等),无需修改原始 TextView 代码;
-
功能解耦:每种样式(加粗、斜体)独立实现,代码更易维护;
-
符合开闭原则:新增样式装饰只需添加新类,不影响现有代码。
缺点:
- 多层包装影响性能:过多装饰器会导致方法调用栈加深,可能影响性能;
- 调试困难:多层装饰器包裹后,调试时难以追踪原始方法调用路径。
案例 2:网络请求装饰(添加请求头、日志)
java
// 1. 抽象组件:网络请求
interface NetworkRequest {
void send(String url);
String getRequestHeader(String key);
}
// 2. 具体组件:基础网络请求
class BaseNetworkRequest implements NetworkRequest {
private Map<String, String> headers = new HashMap<>();
public BaseNetworkRequest() {
// 设置基础请求头
headers.put("Content-Type", "application/json");
}
@Override
public void send(String url) {
System.out.println("发送请求到: " + url);
System.out.println("请求头: " + headers);
}
@Override
public String getRequestHeader(String key) {
return headers.get(key);
}
}
// 3. 抽象装饰器:网络请求装饰基类
abstract class NetworkRequestDecorator implements NetworkRequest {
protected NetworkRequest decoratedRequest;
public NetworkRequestDecorator(NetworkRequest decoratedRequest) {
this.decoratedRequest = decoratedRequest;
}
@Override
public void send(String url) {
decoratedRequest.send(url);
}
@Override
public String getRequestHeader(String key) {
return decoratedRequest.getRequestHeader(key);
}
}
// 4. 具体装饰器:添加Token请求头
class TokenDecorator extends NetworkRequestDecorator {
private String token;
public TokenDecorator(NetworkRequest decoratedRequest, String token) {
super(decoratedRequest);
this.token = token;
}
@Override
public void send(String url) {
// 添加Token到请求头
decoratedRequest.getRequestHeaderMap().put("Authorization", "Bearer " + token);
super.send(url);
}
// 扩展方法:获取请求头Map(实际中通过接口暴露)
protected Map<String, String> getRequestHeaderMap() {
// 实际中通过反射或接口暴露
return new HashMap<>();
}
}
// 5. 具体装饰器:添加日志记录
class LoggingDecorator extends NetworkRequestDecorator {
public LoggingDecorator(NetworkRequest decoratedRequest) {
super(decoratedRequest);
}
@Override
public void send(String url) {
// 记录请求开始
log("请求开始: " + url);
// 调用原始请求
super.send(url);
// 记录请求结束
log("请求结束");
}
private void log(String message) {
System.out.println("[网络日志] " + message);
}
}
// 6. 在Activity中使用
public class NetworkActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建基础请求
NetworkRequest request = new BaseNetworkRequest();
// 添加Token装饰
request = new TokenDecorator(request, "user_token_123");
// 添加日志装饰
request = new LoggingDecorator(request);
// 发送请求
request.send("https://api.example.com/data");
// 输出:
// [网络日志] 请求开始: https://api.example.com/data
// 发送请求到: https://api.example.com/data
// 请求头: {Content-Type=application/json, Authorization=Bearer user_token_123}
// [网络日志] 请求结束
}
}
优点:
-
请求功能模块化:Token 管理、日志记录等功能独立封装,便于维护;
-
动态添加拦截逻辑:可在运行时决定是否添加 Token、日志等装饰;
-
与 OkHttp 拦截器兼容:本质与 OkHttp 的 Interceptor 原理一致,易于理解。
缺点:
- 装饰顺序影响结果:装饰器顺序不同可能影响请求(如先日志后 Token,或反之);
- 状态管理复杂:装饰器之间可能需要共享状态(如 Token 装饰器需要知道日志装饰器的记录结果)。
案例 3:Drawable 装饰(动态修改图片样式)
java
// 1. 抽象组件:Drawable
interface Drawable {
void draw(Canvas canvas);
int getWidth();
int getHeight();
}
// 2. 具体组件:基础Drawable(简化示例)
class BaseDrawable implements Drawable {
private Bitmap bitmap;
public BaseDrawable(Bitmap bitmap) {
this.bitmap = bitmap;
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
@Override
public int getWidth() {
return bitmap.getWidth();
}
@Override
public int getHeight() {
return bitmap.getHeight();
}
}
// 3. 抽象装饰器:Drawable装饰基类
abstract class DrawableDecorator implements Drawable {
protected Drawable decoratedDrawable;
public DrawableDecorator(Drawable decoratedDrawable) {
this.decoratedDrawable = decoratedDrawable;
}
@Override
public void draw(Canvas canvas) {
decoratedDrawable.draw(canvas);
}
@Override
public int getWidth() {
return decoratedDrawable.getWidth();
}
@Override
public int getHeight() {
return decoratedDrawable.getHeight();
}
}
// 4. 具体装饰器:圆角装饰
class RoundedCornersDecorator extends DrawableDecorator {
private float cornerRadius;
public RoundedCornersDecorator(Drawable decoratedDrawable, float cornerRadius) {
super(decoratedDrawable);
this.cornerRadius = cornerRadius;
}
@Override
public void draw(Canvas canvas) {
// 创建圆角矩形路径
RectF rect = new RectF(0, 0, getWidth(), getHeight());
Path path = new Path();
path.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW);
// 裁剪画布
canvas.save();
canvas.clipPath(path);
// 绘制原始图片
super.draw(canvas);
canvas.restore();
}
}
// 5. 具体装饰器:边框装饰
class BorderDecorator extends DrawableDecorator {
private int borderWidth;
private int borderColor;
public BorderDecorator(Drawable decoratedDrawable, int borderWidth, int borderColor) {
super(decoratedDrawable);
this.borderWidth = borderWidth;
this.borderColor = borderColor;
}
@Override
public void draw(Canvas canvas) {
// 绘制原始图片
super.draw(canvas);
// 绘制边框
Paint paint = new Paint();
paint.setColor(borderColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(borderWidth);
Rect rect = new Rect(0, 0, getWidth(), getHeight());
canvas.drawRect(rect, paint);
}
}
// 6. 在Activity中使用
public class ImageActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 加载原始图片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.avatar);
Drawable drawable = new BaseDrawable(bitmap);
// 添加圆角装饰(半径10dp)
drawable = new RoundedCornersDecorator(drawable, dp2px(10));
// 添加边框装饰(宽度2dp,颜色红色)
drawable = new BorderDecorator(drawable, dp2px(2), Color.RED);
// 设置到ImageView
ImageView imageView = findViewById(R.id.avatarImageView);
imageView.setImageDrawable(new AndroidDrawableWrapper(drawable));
}
private int dp2px(float dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
// 包装自定义Drawable到Android的Drawable
private class AndroidDrawableWrapper extends Drawable {
private Drawable wrappedDrawable;
public AndroidDrawableWrapper(Drawable wrappedDrawable) {
this.wrappedDrawable = wrappedDrawable;
}
@Override
public void draw(Canvas canvas) {
wrappedDrawable.draw(canvas);
}
@Override
public void setAlpha(int alpha) {
// 简化实现
}
@Override
public void setColorFilter(ColorFilter cf) {
// 简化实现
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public int getWidth() {
return wrappedDrawable.getWidth();
}
@Override
public int getHeight() {
return wrappedDrawable.getHeight();
}
}
}
优点:
-
图片样式灵活扩展:可动态添加圆角、边框、滤镜等效果,无需修改图片加载逻辑;
-
保持原始功能:装饰器不修改原始 Drawable 的绘制逻辑,只添加额外功能;
-
与 Android Drawable 体系兼容:通过包装类可集成到 Android 原生 Drawable 体系。
缺点:
- 性能开销:每次绘制都需要经过装饰器链,复杂装饰可能影响渲染性能;
- 状态同步问题:多个装饰器可能需要共享状态(如圆角装饰和边框装饰的尺寸同步)。
四、装饰器模式的适用场景与总结
适用场景:
- 动态功能添加:需要在运行时给对象添加功能,且不修改其原始代码(如 TextView 样式、网络请求拦截);
- 功能组合灵活:功能可自由组合(如同时添加加粗和斜体,或只添加其中一种);
- 避免继承膨胀:当子类数量爆炸时(如 TextView 有 10 种样式组合,子类数 2^10=1024),装饰器模式更高效;
- 与现有框架兼容:如 OkHttp 的 Interceptor、Android 的 DrawableWrapper 等框架已使用装饰器模式。
核心优点:
- 灵活扩展:通过组合而非继承扩展功能,避免子类爆炸;
- 解耦功能:每个装饰器专注单一功能,代码更易维护;
- 动态组合:运行时自由组合装饰器,满足不同场景需求;
- 符合开闭原则:新增功能只需添加新装饰器,不修改现有代码。
核心缺点:
- 多层包装复杂度:过多装饰器会导致代码难以理解(如装饰链过长);
- 性能损耗:方法调用经过多层装饰器,可能有微小性能损失;
- 调试困难:多层包装后,堆栈跟踪和调试变得困难。
Android 中的最佳实践:
-
利用系统装饰器:如 Android 的
DrawableWrapper、OkHttp 的 Interceptor,避免重复造轮子; -
控制装饰链长度:避免过度使用装饰器,一般不超过 3-4 层;
-
装饰器职责单一:每个装饰器只负责一个功能(如 Token、日志、缓存);
-
结合工厂模式:用工厂创建装饰器链,避免客户端直接操作装饰器组合。
装饰器模式是 Android 开发中扩展对象功能的强大工具,尤其适合需要动态添加功能或组合多种功能的场景,理解它有助于更好地使用 OkHttp、自定义 View 等核心模块。