装饰模式详解 (附java语言源码)

装饰模式(Decorator Pattern)

动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator模式比生成子类的方式更为灵活。(Attach additional responsibilities to an object dynamically. Decorators provides a flexible alternative to subclasses for extending functionality.)

装饰模式(Decorator):也叫包装器模式(Wrapper),装饰模式是一种用于提代继承的技术,无需通过子类增加继承就能拓展对象的新功能;使用对象的关联关系提代继承关系,更加灵活,同时避免类型体系的快速膨胀。

开发中使用的场景

  1. 典型应用场景就是:IO中输入流和输出流的设计;
  2. Swing包中图形界面构件功能;
  3. Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能;
  4. Struts2中,request,response,session对象的处理

装饰模式的优缺点

优点:

  1. 扩展对象功能,比继承灵活,不会导致类个数急剧增加;
  2. 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象;
  3. 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加
    新的具体构件子类和具体装饰子类。

缺点:

  1. 产生很多小对象。大量小对象占据内存,一定程度上影响性能;
  2. 装饰模式易于出错,调试排查比较麻烦。

装饰模式和桥接模式的区别
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。

案例:
比如去吃小摊,主体有煎蛋,火腿片等等,每个配料的价格都不一样,不管你怎么配配料,最终价格是手抓饼基础价加上每一种所选配料价格的总和。小摊的价格单如下:
在这里插入图片描述
用装饰者模式即可解决此问题,主体是手抓饼和肉夹馍,而配料则是装饰者:类图如下:
在这里插入图片描述
功能的实现大概有以下5个步骤:
步骤1:定义Component(被装饰对象的基类)抽象组件:定义一个抽象接口,来规范准备附加功能的类

/**
 *  定义一个对象接口,可以给这些对象动态地添加职责
 */
public abstract class Pancake {

    public String desc = "我不是一个具体的煎饼";
    public String getDesc(){
        return desc;
    }
    public abstract double price();
}

步骤2:ConcreteComponent(具体被装饰对象)具体组件:将要被附加功能的类,实现抽象构件角色接口

  1. 手抓饼:
/**
 * 定义一个对象,可以给这个对象添加一些职责:手抓饼
 */
public class TornCake extends Pancake {
    public TornCake() {
        desc = "手抓饼";
    }

    @Override
    public double price() {
        return 4;
    }
}

  1. 肉夹馍:
/**
 * 定义一个对象,可以给这个对象添加一些职责:肉夹馍
 */
public class Roujiamo extends Pancake {
    public Roujiamo() {
        desc = "肉夹馍";
    }

    @Override
    public double price() {
        return 6;
    }
}

步骤3:Decorator(装饰者抽象类)抽象装饰者:维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。

/**
 * 持有对具体构件角色的引用并定义与抽象构件角色一致的接口
 */
public abstract class Condiment extends Pancake {
    public abstract String getDesc();
}

步骤4:ConcreteDecorator(具体装饰者)具体装饰: 实现抽象装饰者角色,负责对具体构件添加额外功能。

  1. 煎蛋:
/**
 *  具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
 *  煎蛋:FiredEgg
 */
public class FiredEgg extends Condiment {

    private Pancake pancake;

    @Override
    public String getDesc() {
        return pancake.getDesc()+",煎蛋";
    }

    @Override
    public double price() {
        return pancake.price()+2;
    }

    public FiredEgg(Pancake pancake) {//构造器
        this.pancake = pancake;
    }
}

  1. 火腿片
/**
 *  具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
 *  火腿片:Ham
 */
public class Ham extends Condiment {
    private Pancake pancake;

    @Override
    public String getDesc() {
        return pancake.getDesc() + ", 火腿片";
    }

    @Override
    public double price() {
        return pancake.price() + 1.5;
    }

    public Ham(Pancake pancake) {
        this.pancake = pancake;
    }
}

  1. 松肉
/**
 *  具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
 *  松肉:MeatFloss
 */
public class MeatFloss extends Condiment {

    private Pancake pancake;

    @Override
    public String getDesc() {
        return pancake.getDesc()+",松肉";
    }

    @Override
    public double price() {
        return pancake.price()+1;
    }

    public MeatFloss(Pancake pancake) {
        this.pancake = pancake;
    }
}

  1. 黄瓜丝
/**
 *  具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
 *  黄瓜丝:Cucumber
 */
public class Cucumber extends Condiment {

    private Pancake pancake;

    @Override
    public String getDesc() {
        return pancake.getDesc()+",黄瓜丝";
    }

    @Override
    public double price() {
        return pancake.price()+0.5;
    }

    public Cucumber(Pancake pancake) {
        this.pancake = pancake;
    }
}

步骤5:测试

/**
 * 测试类
 * author https://blog.csdn.net/cui_yonghua/article/details/90512943
 */
public class TestDecorator {
    public static void main(String[] args) {
    
        Pancake tornCake = new TornCake();
        tornCake = new FiredEgg(tornCake);
        tornCake = new MeatFloss(tornCake);
        //手抓饼+煎蛋+松肉 的价格
        System.out.println(String.format("%s ¥%s", tornCake.getDesc(), tornCake.price()));
        
        Pancake roujiamo = new Roujiamo();
        roujiamo = new FiredEgg(roujiamo);
        roujiamo = new FiredEgg(roujiamo);
        roujiamo = new Ham(roujiamo);
        roujiamo = new MeatFloss(roujiamo);
        roujiamo = new Cucumber(roujiamo);
        //肉夹馍+其它 的价格
        System.out.println(String.format("%s ¥%s", roujiamo.getDesc(), roujiamo.price()));
        
    }
}

控制台打印结果如下:
在这里插入图片描述
至此,装饰模式的介绍,应用场景,优缺点,以及一个完整案例已介绍完毕!

如果想了解更多设计模式,可点击:设计模式概述 以及 23种设计模式的介绍

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java语言中图形界面设计基础详解Java语言中的图形界面设计主要依赖于Java Swing库和Java AWT库。Swing库是一个基于Java AWT库的扩展,提供了更丰富的控件和更好的外观。 在Java中,图形界面设计主要分为以下几个步骤: 1. 创建窗口(JFrame):使用JFrame类创建窗口,可以设置窗口的大小、标题、关闭方式等属性。 2. 添加控件(JButton、JLabel、JTextField等):使用各种控件类创建需要的控件,然后使用窗口的add()方法将控件添加到窗口中。 3. 设置控件属性:可以设置控件的大小、位置、文本、字体、颜色等属性。 4. 添加事件处理器:为控件添加事件处理器,可以在用户操作控件时响应相应的事件。 5. 显示窗口:使用窗口的setVisible()方法将窗口显示出来。 下面是一个简单的Java图形界面程序示例: ```java import javax.swing.*; public class MyFrame extends JFrame { public MyFrame() { // 设置窗口标题 setTitle("My Frame"); // 设置窗口大小 setSize(300, 200); // 设置窗口关闭方式 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 创建一个按钮 JButton button = new JButton("Click me"); // 将按钮添加到窗口中 add(button); // 显示窗口 setVisible(true); } public static void main(String[] args) { // 创建窗口对象 MyFrame frame = new MyFrame(); } } ``` 这个程序创建了一个窗口,并在窗口中添加了一个按钮。当用户点击按钮时,可以响应相应的事件处理器,实现更复杂的交互逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据知道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值