设计模式之装饰模式--优雅的增强

news2024/11/23 21:47:05

目录

  • 概述
    • 什么是装饰模式
    • 为什么使用装饰模式
    • 关键角色
    • 基本代码
    • 应用场景
  • 版本迭代
    • 版本一
    • 版本二
    • 版本三—装饰模式
  • 装饰模式中的巧妙之处
    • 1、被装饰对象和装饰对象共享相同的接口或父类
    • 2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法
    • 3、子类方法与父类方法共享相同的引用
    • 4、装饰模式与继承的对比
  • 总结

概述

什么是装饰模式

    装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象放入包含行为的特殊封装对象中来为原始对象添加新的行为。装饰模式在不改变原始对象接口的情况下,动态地将责任附加到对象上。就增加功能来看,装饰模式比生成子类更为灵活。

为什么使用装饰模式

    使用装饰模式有以下几个优点:

灵活性:装饰模式允许在运行时动态地给对象添加功能,而不需要修改其代码或使用继承。这使得系统更加灵活,易于扩展和修改。
单一职责原则:装饰模式能够将不同的功能划分到不同的类中,使每个类只负责单一的功能。这遵循了单一职责原则,使得代码更加清晰、可读性更高。
可组合性:由于装饰模式使用了对象组合而不是继承,因此可以通过不同的方式将装饰器组合起来,以获得不同的行为组合。
装饰模式的结构

关键角色

  • 抽象组件(Component):定义了原始对象和装饰器的公共接口,可以是接口或抽象类。
  • 具体组件(Concrete Component):实现了抽象组件接口,是被装饰的原始对象。
  • 装饰器(Decorator):实现了抽象组件接口,并持有一个抽象组件对象的引用,在装饰器中可以添加一些额外的行为。
    -具体装饰器(Concrete Decorator):继承自装饰器,具体实现了在装饰器中定义的额外行为。

基本代码

应用场景

1、在不改变现有对象结构的情况下,对对象的功能进行扩展或修改。
2、需要动态地给对象添加功能,以便根据需要增加或移除功能。
3、对象的职责应该能够被多次扩展,而不会导致类的数量急剧增加。

版本迭代

需求:给人搭配不同的服饰

版本一

//Person
public class Person {
    private String name;
    public Person(String name ){
        this.name=name;
    }
    public void wearTShirts(){
        System.out.println("大T恤");
    }
    public void wearBigTrouser(){
        System.out.println("跨裤");
    }
    public void wearSneakers(){
        System.out.println("球鞋");
    }
    public void show(){
        System.out.println("装扮的"+name);
    }
}
//客户端
public class Client {
    public static void main(String[] args) {
        Person xc=new Person("小蔡");

        System.out.println("第一种装扮");
        xc.wearTShirts();
        xc.wearBigTrouser();
        xc.wearSneakers();
        xc.show();

        System.out.println("--------------");
        System.out.println("第二种装扮");
        xc.wearTShirts();
        xc.wearSneakers();
        xc.show();

    }
}

    版本一非常简单,给人穿衣服就只有Person这个类,但是需求如果需要增加“超人”的装扮,就需要修改Person类(毕竟超人不是人,不能使用实例化Person,并且超人的衣服最起码是有个内裤和斗篷的~~)
    这个时候怎么办,把服饰拿出去作为单独的类是不是就可以了呢

版本二

//Person
public class Person {
    private String name;
    public Person(String name){
        this.name=name;
    }
    public void show(){
        System.out.println("装扮的"+name);
    }
}

//抽象服饰类
public abstract class Finery {
    public abstract void show();
}

//具体的服饰类
public class BigTrouser extends Finery{
    @Override
    public void show() {
        System.out.println("跨裤");
    }
}

public class TShirts extends Finery{
    @Override
    public void show() {
        System.out.println("大t恤");
    }
}

//客户端
public class client {
    public static void main(String[] args) {
        Person xc=new Person("小蔡");
        Finery tt=new TShirts();
        Finery kk=new BigTrouser();

        kk.show();
        tt.show();
        xc.show();

    }
}

    新的问题好像又来了,客户端看起来就是光着身子的小蔡一件件的穿上了衣服,也就是说穿衣服的过程不应该在客户端显示,而应该在内部完成,并且每个人的爱好不一样,穿衣服的顺序并不确定,这个建造者就不同了,而是需要把所有的功能按照正确顺序串联起来进行控制。装饰模式上场了

版本三—装饰模式

//服饰抽象类
public interface ICharacter {
    public void show();
}

//被装饰对象人
public class Person implements ICharacter{
    private String name;
    public Person(String name){
        this.name=name;
    }
    @Override
    public void show() {
        System.out.println("装扮的"+name);
    }
}
//装饰类
public class Finery implements ICharacter{
    protected ICharacter component;
    public void decorate(ICharacter component){
        this.component=component;
    }
    @Override
    public void show() {
        if(component !=null){
            component.show();
        }

    }
}

//具体装饰类
public class Sneakers extends Finery{
        public void show(){
            System.out.println("球鞋");
            super.show();
        }

}

public class BigTrouser extends Finery{
        public void show(){
            System.out.println("裤子");
            super.show();
        }
    }

public class Tshirts extends Finery{
    public void show(){
        System.out.println("T恤");
        super.show();
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        Person xc=new Person("小蔡");
        System.out.println("第一种装扮");
        Sneakers pqx=new Sneakers();
        pqx.decorate(xc);

        BigTrouser kk=new BigTrouser();
        kk.decorate(pqx);

        Tshirts tt=new Tshirts();
        tt.decorate(kk);

        tt.show();
    }
}

装饰模式中的巧妙之处

1、被装饰对象和装饰对象共享相同的接口或父类

    这样做的目的是是为了让它们可以互相替代。这种互相替代的能力是装饰模式的关键特点之一。通过共享相同的接口或父类,可以在不修改原始对象的情况下,动态地添加、删除或更改对象的行为。

    互相替换的好处在于灵活性和扩展性。当需要为对象添加额外的功能时,可以直接用装饰对象替换原始对象,而不需要修改原始对象的代码。可以将装饰器对象看作是被装饰对象的 “包装”。这样可以避免引入大量的条件语句或继承关系,使得代码更加清晰、可维护和可扩展。

    另外,互相替换还可以实现功能的组合和嵌套,比如上述给人装扮的例子,穿了靴子和裤子,就是把靴子和裤子的功能进行了组合。通过将装饰器对象嵌套在其他装饰器对象中,可以构建出复杂的功能组合,以满足不同的需求。

2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法

    例如上面装扮的例子中的show方法.装饰器类(服饰类)通过持有一个被装饰对象(component)的引用来添加额外的功能。当调用装饰器类的 show() 方法时,它会先调用被装饰对象的 show()方法,然后再执行自己的装饰逻辑(如下图所示)。这样的设计可以实现动态地扩展对象的功能,而无需改变原始对象的结构。通过嵌套和组合多个装饰器类灵活地添加、移除和组合各种功能,以满足不同的需求。
在这里插入图片描述

3、子类方法与父类方法共享相同的引用

    在具体装饰子类中都有super.show方法,这个是调取父类的show方法
在这里插入图片描述
    那么这样写的巧妙之处在哪呢,在这里是看不到2中说到的“”当调用装饰器类的 show() 方法时,它会先用被装饰对象的 show()方法,然后再执行自己的装饰逻辑。因为这个逻辑写到了父类中
在这里插入图片描述
    在这里有个疑问,在具体的子类中component为什么肯定是它的上一级被装扮对象呢,比如“穿了裤子的小蔡”接着要穿T恤。这就是因为每次调用装扮对象的decorate方法,传入的都是上一级被装扮对象(如下图所示),同时子类没有重写decorate方法,此时子类方法将与父类方法共享相同的引用,并且它们将具有相同的实现和行为,也就把component传给装备对象的属性,以供后面调用show方法时使用。
在这里插入图片描述

4、装饰模式与继承的对比

    装饰模式将功能添加到对象上的方式与继承不同。使用继承时,子类会继承父类的行为,而装饰模式可以动态地将额外的行为添加到对象上,而不需要继承。这使得装饰模式更加灵活,因为可以在运行时选择不同的行为组合。

    另外,装饰模式遵循了开放-关闭原则,即对扩展开放,对修改关闭。通过组合和委托的方式,可以动态地将功能添加到对象上,而不需要修改现有代码

总结

    装饰模式是一种灵活且可扩展的设计模式,它可以动态地给对象添加新的行为。使用装饰模式可以避免类爆炸问题,并能够在运行时选择不同的功能组合。它与继承相比更加灵活,符合单一职责原则,并遵循开放-关闭原则。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国人民大学与加拿大女王大学金融硕士—重要的是,你一直在努力

人虽然生下来就分三六九等,不同的人过着不同的生活,我的生活没办法选择,我只能尽我所能的让自己变得优秀。中国人民大学与加拿大女王大学金融硕士是我们无论怎样都可以变优秀的优质渠道。V13146152701 那么我们为什么要读研,读研…

串口通信代码整合1

本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步! > 发布人:日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

OLE DB 访问接口所需的(最大)数据长度为 18,但返回的数据长度为 6。

sqlserver查询oracle链接服务器视图,报错 给最终返回的字符串进行类型转换,字符串大小按返回值最大的那个oracle源本字段类型长度 aaaaaa AS yljgbmcast(aaaaaa AS varchar(10)) AS yljgbm

Proteus仿真--1602LCD显示仿手机键盘按键字符(仿真文件+程序)

本文主要介绍基于51单片机的1602LCD显示仿手机键盘按键字符(完整仿真源文件及代码见文末链接) 仿真图如下 其中左下角12个按键模拟仿真手机键盘,使用方法同手机键一样,长按自动跳动切换键值,松手后确认选择&#xff…

API接口加密,解决自动化中登录问题

一、加密方式 AES:对称加密,快RAS:非对称加密,慢AESRAS:安全高效 加密过程:字符串》字节流》加密的字节流(算法),解密有可能出现乱码,所以不能直接转成字符…

python+selenium自动化测试--鼠标悬停浮窗定位

页面上有些元素会隐藏起来,要鼠标放到某个位置才会显示出来,例如百度首页https://www.baidu.com/设置下面的隐藏按钮,如下图所示 定位鼠标悬停才显示的元素,要引入新模块,如下所示 from selenium.webdriver.common.ac…

12.JavaScript(WebAPI) - JS api文献精解

文章目录 1.WebAPI 背景知识1.1什么是 WebAPI1.2什么是 API1.3API 参考文档 2.DOM 基本概念2.1什么是 DOM2.2DOM 树 3.获取元素3.1querySelector3.2querySelectorAll 4.事件初识4.1基本概念4.2事件三要素4.3简单示例 5.操作元素5.1获取/修改元素内容5.1.1innerText5.1.2innerHT…

AD9371 官方例程裸机SW 和 HDL配置概述(二)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 : AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射: AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 : AD9371 官方…

javafaker测试数据生成实战

javafaker测试数据生成实战 1.背景2.介绍2.1 特点 3. 使用3.1 基础使用3.1.1 maven依赖3.1.1 使用示例 3.2 进阶使用3.1 生成中文信息3.2 根据姓名生成账号3.2.1 maven依赖3.2.2 中文转拼音工具类 3.3 高级使用3.3.1 中文性名重复处理方案1: 偷懒方式方案2: 较真模式 1.背景 最…

ChatGPT 被爆重大隐私泄露!在回答时突然蹦出陌生男子自拍照,你的数据都将被偷走训练模型!

ChatGPT 被爆重大隐私泄露 ! 一位用户在向 ChatGPT 询问 Python 中的代码格式化包 black 的用法时,没有一点点防备,ChatGPT 在回答中插入了一个陌生男子的自拍照(手动捂脸.jpg) 可以看到刚开始 ChatGPT 还相当正常&am…

智慧灯杆网关智能化选择(网关助力城市完整项目方案)

在当代城市发展中,智慧照明作为一项重要的技术创新,正逐渐改变着我们的城市生活。作为城市智慧照明的核心设备,智慧灯杆网关SG600凭借出色的性能和创新的解决方案,成为了引领城市智慧照明的完美选择。本文将详细介绍SG600的特点和…

linux centos7安装colmap

centos安装colmap 一、安装依赖 sudo yum install \gflags-devel \glog-devel \glew-devel \atlas \atlas-devel \lapack-devel \blas-devel \flann-devel \lz4-devel \sqlite-devel \metis-devel \qt5-qtbase-devel二、编译安装colmap git clone https://github.com/colmap/…

剑指JUC原理-10.并发编程大师的原子累加器底层优化原理(与人类的优秀灵魂对话)

👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring源码、JUC源码🔥如果感觉博主的文章还不错的话,请👍三连支持&…

服装手机壳抱枕diy来图定制小程序开发

服装手机壳抱枕diy来图定制小程序开发 一、我们的定位与特色 首先,我们是一个多元化商品定制商城。与其他商城不同的是,我们致力于提供全方位的定制服务,包括手机壳、抱枕、服装、水杯贴图等各类商品。 此外,我们还提供冲洗照片…

C语言中什么时候用“->”

使用说明 "->"是C语言中的一个运算符&#xff0c;它用于访问结构体或者联合体中的成员&#xff0c;其左边是一个指向结构体或者联合体的指针&#xff0c;右边是需要访问的成员名。 举例说明 定义结构体 # include <stdio.h> # include <stdlib.h>…

【算法练习Day37】零钱兑换 II组合总和 Ⅳ

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 零钱兑换 II组合总和 Ⅳ总结…

Redis入门04-消息通知

目录 Redis中的消息通知 命令行操作 Redis中的管道 Redis中的消息通知 Redis可以用作消息队列的中间件&#xff0c;它提供了一种轻量级、高性能的消息传递机制&#xff0c;适用于实时通信、任务队列、事件处理等各种应用。以下是有关如何使用Redis作为消息队列的一些重要信…

中国人民大学与加拿大女王大学金融硕士——人生是旷野,不是一条轨道

在这个瞬息万变的时代&#xff0c;我们每个人都像是一颗流星&#xff0c;在宇宙中独自燃烧。我们每个人都有自己的梦想&#xff0c;自己的追求&#xff0c;自己的道路。然而&#xff0c;很多时候&#xff0c;我们却发现自己被现实的轨道所束缚&#xff0c;无法自由地追求自己的…

记一次并发问题 Synchronized 失效

记一次并发问题 Synchronized 失效 场景&#xff1a;为避免信息提交重复&#xff0c;给事务方法增加了synchronized修饰符&#xff0c;实际场景中仍然无法完全避免重复&#xff0c;原因是因为在第一个线程执行完synchronized代码段后&#xff0c;此时spring还未完成事务提交&a…

macOS 安装brew

参考链接&#xff1a; https://mirrors4.tuna.tsinghua.edu.cn/help/homebrew/ https://www.yii666.com/blog/429332.html 安装中科大源的&#xff1a; https://zhuanlan.zhihu.com/p/470873649