(1)我们先来看一个快餐店的例子:快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。而使用继承的方式存在的问题:
FriedRice
和 FriedNoodles
分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。(2)装饰者模式 (Decorator Pattern) 是一种结构型设计模式,它能在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)。
装饰者模式中的角色如下:
我们使用装饰者模式对快餐店案例进行改进,体会装饰者模式的精髓。其类图如下:
FastFood.java
//快餐类
public abstract class FastFood {
private float price;
private String desc;
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public abstract float cost();
}
FriedRice.java
//炒饭
public class FriedRice extends FastFood{
public FriedRice(){
super(10, "炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
FriedNoodles.java
//炒面
public class FriedNoodles extends FastFood{
public FriedNoodles(){
super(12,"炒面");
}
@Override
public float cost() {
return getPrice();
}
}
Garnish.java
//装饰者类
public abstract class Garnish extends FastFood{
//声明快餐类的变量
private FastFood fastFood;
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
public Garnish(FastFood fastFood, float price, String desc) {
super(price, desc);
this.fastFood = fastFood;
}
}
Egg.java
//鸡蛋类
public class Egg extends Garnish{
public Egg(FastFood fastFood){
super(fastFood, 1, "鸡蛋");
}
@Override
public float cost() {
//计算价格,鸡蛋价格 + 快餐价格
return getPrice() + getFastFood().cost();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Bacon.java
//培根类
public class Bacon extends Garnish{
public Bacon(FastFood fastFood){
super(fastFood, 2, "培根");
}
@Override
public float cost() {
//计算价格,培根价格 + 快餐价格
return getPrice() + getFastFood().cost();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Client.java
public class Client {
public static void main(String[] args) {
//点一份炒饭
FastFood food = new FriedRice();
System.out.println(food.getDesc() + " " + food.cost() + " 元");
System.out.println("===============");
//在上面的炒饭中加一个鸡蛋
food = new Egg(food);
System.out.println(food.getDesc() + " " + food.cost() + " 元");
System.out.println("================");
//再加一个鸡蛋
food = new Egg(food);
System.out.println(food.getDesc() + " " + food.cost() + " 元");
System.out.println("================");
food = new Bacon(food);
System.out.println(food.getDesc() + " " + food.cost() + " 元");
}
}
输出结果如下:
炒饭 10.0 元
===============
鸡蛋炒饭 11.0 元
================
鸡蛋鸡蛋炒饭 12.0 元
================
培根鸡蛋鸡蛋炒饭 14.0 元
在上述例子中使用装饰者模式至少有以下优点:
(1)装饰者模式的优点和缺点如下所示:
(2)总体而言,装饰者模式提供了一种灵活的方式来扩展对象的功能,符合开闭原则和单一职责原则。然而,它也可能引入复杂性并要求对代码进行更多的设计和精心安排。在实际应用中,需要根据具体场景来评估使用装饰者模式的利弊,权衡灵活性和复杂性之间的关系。
(1)装饰者模式适用于以下场景:
(2)总的来说,装饰者模式适用于需要动态地为对象添加功能或对对象的功能进行扩展的场景。它提供了一种灵活的方式来组合各个功能模块,保持接口的一致性,同时符合开闭原则和单一职责原则。常见的应用场景包括:日志记录、缓存、权限验证、事务管理等。
(1)I/O 流中的包装类使用到了装饰者模式:BufferedInputStream
,BufferedOutputStream
,BufferedReader
,BufferedWriter
。
(2)现以 BufferedWriter
举例来说明,先看看如何使用 BufferedWriter
:
import java.io.BufferedWriter;
import java.io.FileWriter;
public class Demo {
public static void main(String[] args) throws Exception{
//创建 BufferedWriter 对象
//创建 FileWriter 对象
FileWriter fw = new FileWriter("E:\\a.txt");
BufferedWriter bw = new BufferedWriter(fw);
//写数据
bw.write("hello Buffered");
bw.close();
}
}
使用起来感觉确实像是装饰者模式,接下来看它们的结构:
小结:BufferedWriter 使用装饰者模式对 Writer 子实现类进行了增强,添加了缓冲区,提高了写数据的效率。
(1)装饰者模式和静态代理模式有一些相同点和不同点,可以总结如下:
(2)总体而言,装饰者模式和静态代理模式都是实现对象功能扩展的常见模式,它们通过中间层的方式来添加额外的功能,并控制对原始对象的访问。装饰者模式更加灵活,可以在运行时动态地组合功能装饰器,而静态代理模式在编译时确定代理类和被代理对象的关系。选用哪种模式应基于具体需求和场景来综合考虑。