设计模式-简单例子理解适配器模式、装饰器模式

news2024/10/7 14:32:03

文章目录

  • 一、适配器模式
    • 1. 要点
    • 2. Demo
  • 二、装饰器模式
    • 1. 要点
    • 2. Demo
  • 三、区别

本文参考:

基本原理:装饰器模式 | 菜鸟教程 (runoob.com)

基本原理:适配器模式 | 菜鸟教程 (runoob.com)

优缺点和区别,装饰模式:适配器模式和装饰模式 - 掘金 (juejin.cn)

装饰模式和责任链模式区别:【设计模式】——装饰模式VS职责链模式_Mandy_i的博客-CSDN博客

装饰者模式demo:设计模式-装饰者模式 (Decorator Pattern)-保姆级攻略,实战项目_哔哩哔哩_bilibili

这两种模式经常被拿出来进行考察分别,在这里我就尝试从一个初学者的角度做一个学习和记录。

一、适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

1. 要点

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

对象

  1. 被适配器基础类:想被转换的类,通过适配器类包装
  2. 适配器类:包装被适配的基础类,转换成目标类
  3. 目标类:适配器类要去适配的终极目标

主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

如何解决继承或依赖(在构造函数中处理或者 getset方法处理)

关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

2. Demo

我们要实现一个聋哑人手语翻译的场景,这里聋哑人的手语是被适配的类,翻译器是适配器类,目标对象是通过翻译器掌握聋哑人的手语表达的一个对象。

实现要点:这里选择继承的解决方法(还可以通过依赖),适配器类继承被适配的类,从而能够使用到被继承类的方法,同时实现目标类的接口,达成向目标类接口的转换。后面的调用只需要使用适配器类,就可以完成被适配基础类的行为。

  1. 定义被适配基础类接口

    /**
     * @Author jiangxuzhao
     * @Description 被适配类的接口
     * @Date 2023/5/22
     */
    public interface Adaptee {
        void handLanguage();
    }
    
  2. 实现具体的被适配基础类

    /**
     * @Author jiangxuzhao
     * @Description 具体的被适配基础类
     * @Date 2023/5/22
     */
    public class ConcreteAdaptee implements Adaptee{
        @Override
        public void handLanguage() {
            System.out.println("打手语...");
        }
    }
    
  3. 定义目标类接口

    /**
     * @Author jiangxuzhao
     * @Description 目标对象的接口
     * @Date 2023/5/22
     */
    public interface Target {
        void targetTranslate();
    }
    
  4. 实现具体的目标类

    /**
     * @Author jiangxuzhao
     * @Description 具体的目标对象
     * @Date 2023/5/22
     */
    public class ConcreteTarget implements Target{
        @Override
        public void targetTranslate() {
            System.out.println("人能搞懂的东西...");
        }
    }
    
  5. 实现适配器类

    /**
     * @Author jiangxuzhao
     * @Description 适配器类,继承实现
     * @Date 2023/5/22
     */
    public class Adapter1 extends ConcreteAdaptee implements Target{
        @Override
        public void targetTranslate() {
            super.handLanguage();
            System.out.println("Adapter1 把手语翻译成人能搞懂的东西...");
        }
    }
    
    /**
     * @Author jiangxuzhao
     * @Description 适配器类,构造函数依赖实现
     * @Date 2023/5/22
     */
    public class Adapter2 extends ConcreteAdaptee implements Target{
        Adaptee adaptee;
    
        // 构造函数依赖实现
        public Adapter2(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public void targetTranslate() {
            adaptee.handLanguage();
            System.out.println("Adapter2 把手语翻译成人能搞懂的东西...");
        }
    }
    
  6. 测试

    import org.junit.Test;
    
    /**
     * @Author jiangxuzhao
     * @Description
     * @Date 2023/5/22
     */
    public class testAdapter {
        @Test
        public void testAdapter1(){
            Target target = new Adapter1();
            target.targetTranslate();
        }
    
        @Test
        public void testAdapter2(){
            Target target = new Adapter2(new ConcreteAdaptee());
            target.targetTranslate();
        }
    }
    

    输出:
    在这里插入图片描述

二、装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

1. 要点

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

对象:被装饰基础类和装饰类。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,继承或者依赖(关联使用)。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

2. Demo

我们要模拟一个制作饮料的场景,在基础的茶底中可以加入不同的配料进行装饰,使其成为一款新的子饮料,计算价格。

实现要点在于:基础类和装饰类都要继承于同一个父类,装饰类中可以通过构造方法传入前一次制作出的半成品(因为都是继承于同一个父类的),这也称之为关联使用,是一种很常见的复用技巧。

  1. 定义饮料抽象类,这是基础类和装饰类的共同类,这里我采用了抽象类定义,理论上来说接口定义也行

    /**
     * @Author jiangxuzhao
     * @Description
     * @Date 2023/5/22
     */
    public abstract class Beverage {
        public abstract void cost();
    }
    
  2. 实现具体的饮料基础类作为茶底

    /**
     * @Author jiangxuzhao
     * @Description 奶茶茶底类
     * @Date 2023/5/22
     */
    public class MilkTea extends Beverage{
        private static final double COST=5.5;
        @Override
        public double cost() {
            System.out.println("奶茶茶底售价:"+COST);
            return COST;
        }
    }
    
    /**
     * @Author jiangxuzhao
     * @Description 果茶茶底类
     * @Date 2023/5/22
     */
    public class FruitTea extends Beverage{
        private static final double COST=5.5;
        @Override
        public double cost() {
            System.out.println("果茶茶底售价:"+COST);
            return COST;
        }
    }
    
  3. 定义装饰者抽象类,继承于饮料父类

    /**
     * @Author jiangxuzhao
     * @Description
     * @Date 2023/5/22
     */
    public abstract class BeverageDecorator extends Beverage{
        public abstract double cost();
    }
    
  4. 实现具体的装饰者作为配料

    /**
     * @Author jiangxuzhao
     * @Description 珍珠配料类
     * @Date 2023/5/22
     */
    public class Boba extends BeverageDecorator{
        // 珍珠的价格
        private static final double COST=1.0;
        private final Beverage beverage;
    
        public Boba(Beverage beverage) {
            this.beverage=beverage;
        }
    
        @Override
        public double cost() {
            System.out.println("加入珍珠的单价为:"+COST);
            return beverage.cost()+COST;
        }
    }
    
    /**
     * @Author jiangxuzhao
     * @Description
     * @Date 2023/5/22
     */
    public class Pudding extends BeverageDecorator{
        private static final double COST=1.0;
        private final Beverage beverage;
    
        public Pudding(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public double cost() {
            System.out.println("加入布丁的单价为:"+COST);
            return beverage.cost()+COST;
        }
    }
    
  5. 测试

    import org.junit.Test;
    
    /**
     * @Author jiangxuzhao
     * @Description
     * @Date 2023/5/22
     */
    public class testDecorator {
        @Test
        public void testBeverage(){
            // 奶茶底
            Beverage myTea= new MilkTea();
            
            // 加入两粒珍珠,一杯布丁
            myTea = new Boba(myTea);
            myTea = new Boba(myTea);
            myTea = new Pudding(myTea);
    
            // 输出最后的价格
            double cost = myTea.cost();
            System.out.println(cost);
        }
    }
    

    输出:

    加入布丁的单价为:1.0
    加入珍珠的单价为:1.0
    加入珍珠的单价为:1.0
    奶茶茶底售价:5.5
    8.5
    

三、区别

适配器模式和装饰者模式都属于结构型模式,功能都类似,都是包装作用。

装饰模式强调的是动态扩展原有接口功能,所有装饰类都有一个共同的父类,而适配器模式侧重于转换接口,通过继承和依赖的方式,将源角色转换成目标角色。

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

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

相关文章

微服务流量控制组件Sentinel

1 简介 Sentinel是阿里开源的项目,是一款面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保障服务的稳定性。 核心思想是:根据对应资源配置的规则来为资源执行相…

西米支付:“中止”支付牌照,汇卡支付机构“失联”

近日,又一家支付公司因“失联”被列入了经营异常名录。 工商信息显示,目前被“中止”中的持牌支付机构广东汇卡商务服务有限公司(简称“汇卡支付”)因“通过登记的住所或者经营场所无法联系” 被广州市市场监督管理局列入经营异常…

网络进阶学习:单臂路由(灵魂五问)

单臂路由(灵魂五问) 一问:什么是单臂路由?二问:单臂路由这一概念怎么出现的?三问:单臂路由解决什么问题?能不能用其他方式取代单臂路由?四问:单臂路由最合适的应用场景&…

电脑E盘被不小心格式化了?别急,介绍三种数据恢复方法

电脑E盘格式化后如何恢复数据?意外的电脑E盘格式化或许是每个人都遇到过的问题。然而,当您发现您的重要数据已经丢失时,您可能会感到沮丧甚至绝望。但请不要担心。在本文中,我们将介绍一些有助于您找回数据的方法,希望…

2023年5月DAMA-CDGA/CDGP数据治理认证咋样

DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…

uvc驱动ioctl分析下

uvc驱动ioctl分析下 文章目录 uvc驱动ioctl分析下uvc_ioctl_enum_input枚举输入uvc_query_ctrl__uvc_query_ctrluvc_ioctl_g_input 获取输入uvc_ioctl_s_input 设置输入uvc_query_v4l2_ctrluvc_ioctl_queryctrl查询控制器uvc_ioctl_query_ext_ctrl查询扩展控制器 uvc_ioctl_g_c…

系统分析师经典易错题,解题思路二

企业应用集成(Enterprise Application Integration EAI)技术企业应用集成技术可以消除信息孤岛,它将多个企业信息系统连接起来,实现无缝集成,使他们就像一个整体一样。EAI是伴随着企业信息系统的发展而产生和演变的,企业的价值取向是推动EAI技术发展的原动力,而EAI的实现…

煤矿电子封条系统 yolov7网络模型

煤矿电子封条系统通过yolov7网络模型算法,煤矿电子封条系统可以实现对煤矿井下人员的出入管理,提高对煤矿井下人员的监管效果。YOLOv7 的策略是使用组卷积来扩展计算块的通道和基数。研究者将对计算层的所有计算块应用相同的组参数和通道乘数。然后&…

从热爱到深耕,在开发路上的他们勇敢逐梦

2022年的程序员节, #大龄程序员去哪儿了#成为了社交媒体上最火的话题之一,程序员的职场成长问题在社会上引起了广泛关注。 有2位在技术领域摸爬滚打很多年的开发者,35岁后的他们,有70后,有80后,依然在编程…

【Java编程系列】Springcloud-gateway自带限流方案实践篇

1、前言 作为一个后端开发,对于后端服务的安全性方面,一定要有足够的考虑。近期的开发工作中,有一个实现分享外部链接的需求点,个人认为这一块会有安全隐患。比如,因为这个分享的外链会被用户无限制点开查看&#xff0…

常见分布函数。

一维常见分布函数 1.离散型 ① 0 - 1分布 记 X~B(1,p) 如果X的概率分布为 ( 1 0 p 1 − p ) \begin{pmatrix} 1 & 0 \\ p & 1-p \end{pmatrix} (1p​01−p​),则称X服从参数为P的0-1分布&#xff08;0<p<1&#xff09;。 注&#xff1a;0-1分布又称一次伯努利试…

iOS-Telegraph异步响应实现

背景 Telegraph该库只支持管理本地同步请求&#xff0c;为了长远打算&#xff0c;需要研究是否能使response异步回调的方法 参考gitHub-Telegraph文档 现象&#xff1a;根据文档说明和示例&#xff0c;以及查看源码实现确认该第三方库确实只支持管理本地同步的请求响应 它的…

【程序员日记】——从业务编排到低代码 | 京东云技术团队

之前总聊微服务&#xff0c;今天换一个话题—低代码。 低代码这个词也是最近这几年很火的概念&#xff0c;尤其是遇到大环境下行&#xff0c;很多大厂和互联网那个公司也在慢慢在低代码方向发力&#xff0c;当然&#xff0c;对于传统项目交付型的软件公司&#xff0c;低代码也…

LabVIEWCompactRIO 开发指南28 可重入和非重入子VI之间的权衡

LabVIEWCompactRIO 开发指南28 了解可重入和非重入子VI之间的权衡 重入是子VI执行属性中的设置。在LabVIEW FPGA中&#xff0c;子VI执行默认设置为可重入。重入在FPGA逻辑中创建子VI的多个副本。这能够并行执行子VI的多个副本&#xff0c;同时存储不同且独立的数据存储。 在…

计算GMAC和GFLOPS

GMAC 代表“Giga Multiply-Add Operations per Second”&#xff08;每秒千兆乘法累加运算&#xff09;&#xff0c;是用于衡量深度学习模型计算效率的指标。它表示每秒在模型中执行的乘法累加运算的数量&#xff0c;以每秒十亿 (giga) 表示。 乘法累加 (MAC) 运算是许多数学计…

opencv_c++学习(十七)

一、边缘检测 左侧上面的曲线表示的是像素从左到右的变化&#xff0c;下面的曲线是上面曲线求导而得。 Sobel边缘检测算子&#xff1a; Sobel(InputArray src, outputArray dst,int ddepth, int dx, int dy, int ksize 3, double scale 1, double delta 0, int borderType …

AI 图像编辑技术 DragGAN 问世,用户可以通过拖拽改变汽车大小或人物表情等

&#x1f680; AI 图像编辑技术 DragGAN 问世&#xff0c;用户可以通过拖拽改变汽车大小或人物表情等 近日&#xff0c;马克斯・普朗克计算机科学研究所研究者们推出了一种控制GAN的新方法DragGAN&#xff0c;用户可以通过拖拽改变汽车大小或人物表情等。 DragGAN类似于Photo…

顶层策划打开互联网市场大门

大家好&#xff01;我是小鱼。随着市面上越来越多的中小企业老板进入互联网市场&#xff0c;对软件开发的技术和要求也逐渐高了起来。单纯做一个商城已经远远不满足客户的需求了。那么客户他到底需要什么? 小编总结了一下&#xff0c;第一客户需要一个完整的系统体系&#xff…

类和对象 - 下(C++)

目录 构造函数补充 构造函数体赋值 初始化列表 explicit关键字 Static成员 概念 特性 友元 友元函数 友元类 内部类 匿名对象 编译器对拷贝对象的优化 理解类和对象 构造函数补充 构造函数体赋值 构造函数&#xff1a; 我们知道 构造函数本质就是在对象创建的同时对其进行初始…

【Android】配置不同的开发和生产环境

目录 前言 配置build.gradle&#xff08;Module级别&#xff09; 创建对应环境的目录 切换不同环境 ​编辑选择打包的环境 前言 在web开发中不同的环境对应的配置不一样&#xff0c;比如开发环境的url是这样&#xff0c;测试环境的url是那样的&#xff0c;在app中也会涉…