设计模式第七讲-外观模式、适配器模式、模板方法模式详解

news2024/11/18 1:30:25

一. 外观模式

1. 背景

  在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了。

  软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。

2. 定义和特点

(1). 定义

  是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

(2). 优点

 A. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。

 B. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。

 C. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

(3). 缺点

 A. 不能很好地限制客户使用子系统类。

 B. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

3. 具体实现

(1). 模式结构

 A. 外观角色:为多个子系统对外提供一个共同的接口。

 B. 子系统角色:实现系统的部分功能,客户可以通过外观角色访问它。

 C. 客户端:通过一个外观角色访问各个子系统的功能。

结构图如下:

(2). 使用场景

见下面代码。

(3). 代码实操

子系统代码

    /// <summary>
    /// 子业务类1
    /// </summary>
    public class ChildService1
    {
        public void MyHandler1()
        {
            Console.WriteLine("我正在处理业务1");
        }
    }
    /// <summary>
    /// 子业务类2
    /// </summary>
    public class ChildService2
    {
        public void MyHandler2()
        {
            Console.WriteLine("我正在处理业务2");
        }
    }
    /// <summary>
    /// 子业务类3
    /// </summary>
    public class ChildService3
    {
        public void MyHandler3()
        {
            Console.WriteLine("我正在处理业务3");
        }
    }

外观角色

    /// <summary>
    /// 外观角色
    /// </summary>
    public class FacadeService
    {
        private ChildService1 s1 = new ChildService1();
        private ChildService2 s2 = new ChildService2();
        private ChildService3 s3 = new ChildService3();

        public void MyHandler()
        {
            s1.MyHandler1();
            s2.MyHandler2();
            s3.MyHandler3();
        }
    }

测试

            {
                Console.WriteLine("---------------下面是普通调用---------------");
                ChildService1 s1 = new ChildService1();
                ChildService2 s2 = new ChildService2();
                ChildService3 s3 = new ChildService3();
                s1.MyHandler1();
                s2.MyHandler2();
                s3.MyHandler3();

                Console.WriteLine("---------------下面是外观模式调用---------------");
                FacadeService f = new FacadeService();
                f.MyHandler();
            }

运行结果

4. 适用场景分析

 (1). 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。

 (2). 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。

 (3). 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击 C++后端学习资料 免费领取

二. 适配器模式

1. 背景

  在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等。

  在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在(且不能修改),但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

2. 定义和特点

(1). 定义:

  将一个类的方法转换成客户希望的另外一种要求的实现规范,使得原本由于接口不兼容而不能一起工作的那些类能一起工作,这就是适配器模式。适配器模式分为【类适配器模式】和【对象适配器模式】两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

(2). 优点

  A. 客户端通过适配器可以按照系统要求的编程规范透明地调用目标接口。

  B. 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。

  C. 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

(3). 缺点

  对类适配器来说,更换适配器的实现过程比较复杂。

3. 具体实现

(1). 模式结构

 A. 目标接口:当前系统业务所要求遵守编程规范的接口,它可以是抽象类或接口。

 B. 适配者类(被适配的类):第三方提供的新类或系统中已经存在的类,它与系统要求的编程规范的目标接口不兼容。

 C. 适配器类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

类适配器图:

对象适配器图:

(2). 使用场景

  系统要求所有的数据库帮助类必须实现ISqlHelp接口,面向该接口编程,如SQLServerHelp类。 此时第三方提供了一个新的MySql的帮助类(假设是dll,不能修改),它的编程规范和ISqlHelp不兼容,这个时候就需要引入适配器类,使二者能相互兼容。

(3). 代码实操

系统要求的标准开发规范代码

    /// <summary>
    /// 数据库连接抽象接口
    /// (代码中统一要求, 对数据库操作都要面向该接口编程)
    /// </summary>
    public interface ISqlHelp
    {
        public void Add();
        public void Del();
        public void Modify();

    }
    /// <summary>
    /// SQLServer数据库操作类
    /// (这是一个样例)
    /// </summary>
    public class SQLServerHelp : ISqlHelp
    {
        public void Add()
        {
            Console.WriteLine($"sqlserver数据库Add成功");
        }
        public void Del()
        {
            Console.WriteLine($"sqlserver数据库Del成功");
        }
        public void Modify()
        {
            Console.WriteLine($"sqlserver数据库Modify成功");
        }
    }

          //1. 项目要求的标准编程模式
                Console.WriteLine("----------------------------1. 项目要求的标准编程模式---------------------------------");
                ISqlHelp s1 = new SQLServerHelp();
                s1.Add();
                s1.Del();
                s1.Modify();

第三提供的新类, 不满足开发规范

 /// <summary>
    /// MySQL帮助类(第三方提供,不能修改)
    /// 没有实现ISqlHelp接口,有自己的一套逻辑.
    /// 但是项目有统一编程要求,要基于ISqlHelp接口编程,
    /// 我们不能修改MySqlHelp内部的逻辑,所以这个时候要通过适配器模式进行适配
    /// </summary>
    public class MySQLHelp
    {
        public void AddMySQL()
        {
            Console.WriteLine($"MySQL数据库Add成功");
        }

        public void DelMySQL()
        {
            Console.WriteLine($"MySQL数据库Del成功");
        }

        public void ModifyMySQL()
        {
            Console.WriteLine($"MySQL数据库Modify成功");
        }
    }

               //2. 第三方给的MySql帮助类,不符合标准模式
                Console.WriteLine("------------------2. 第三方给的MySql帮助类,不符合标准模式----------------------");
                MySQLHelp m = new MySQLHelp();
                m.AddMySQL();
                m.DelMySQL();
                m.ModifyMySQL();

通过适配器模式兼容新的开发规范

    /// <summary>
    /// MySQLHelp适配器类1
    /// (通过继承的方式实现)
    /// </summary>
    public class MySQLHelpAdapter1 : MySQLHelp, ISqlHelp 
    {
        public void Add()
        {
            AddMySQL();
        }

        public void Del()
        {
            DelMySQL();
        }

        public void Modify()
        {
            ModifyMySQL();
        }
    }

 /// <summary>
    /// MySQLHelp适配器类2
    /// (通过组合的方式实现,可以属性注入、构造函数注入、方法注入)
    /// </summary>
    public class MySQLHelpAdapter2 : ISqlHelp
    {
        public MySQLHelpAdapter2()
        {

        }


        //属性注入,直接写死
        private MySQLHelp _mySqlHelp = new MySQLHelp();

        /// <summary>
        /// 构造函数注入
        /// (可以改成抽象模式)
        /// </summary>
        /// <param name="mySqlHelp"></param>
        public MySQLHelpAdapter2(MySQLHelp mySqlHelp)
        {
            this._mySqlHelp = mySqlHelp;
        }

        /// <summary>
        /// 方法注入
        /// (可以改成抽象模式)
        /// </summary>
        /// <param name="mySqlHelp"></param>
        public void SetAdapter(MySQLHelp mySqlHelp)
        {
            this._mySqlHelp = mySqlHelp;
        }


        public void Add()
        {
            _mySqlHelp.AddMySQL();
        }

        public void Del()
        {
            _mySqlHelp.DelMySQL();
        }

        public void Modify()
        {
            _mySqlHelp.ModifyMySQL();
        }
    }

           //3.通过适配器模式使用MySQL帮助类,且满足标准编程模式
                Console.WriteLine("------------------3.1 通过继承模式实现适配器------------------");
                ISqlHelp s2 = new MySQLHelpAdapter1();
                s2.Add();
                s2.Del();
                s2.Modify();
                Console.WriteLine("------------------3.2 通过组合模式实现适配器------------------");
                ISqlHelp s3 = new MySQLHelpAdapter2();
                s3.Add();
                s3.Del();
                s3.Modify();

适配器运行结果:

4. 适用场景分析

(1). 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。

(2). 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。

三. 模板方法模式

1. 背景

  在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

  例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

  这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。

2. 定义和特点

(1). 定义

 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

(2). 优点

 A. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。

 B. 它在父类中提取了公共的部分代码,便于代码复用。

 C. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

(3). 缺点

 A. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。

 B. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

3. 具体实现

(1). 模式结构

 A. 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。

  ① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

  ② 基本方法:是整个算法中的一个步骤,包含以下几种类型。

    抽象方法:在抽象类中申明,由具体子类实现。

    具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。

 B. 具体子类:实现抽象类中所定义的抽象方法。

结构图如下:

(2). 使用场景

  处理银行业务:取号→排队→办业务→评分, 除了办业务因人而异外(在子类中实现), 其它三步都是固定的,在抽象类中实现。

(3). 代码实操

抽象类:

  /// <summary>
    /// 抽象类
    /// 处理银行业务:取号→排队→办业务→评分
    /// 除了办业务因人而异外, 其它三步都是固定的,在抽象类中实现
    /// </summary>
    public abstract class AbstractHandler
    {
        /// <summary>
        /// 模板方法
        /// 里面的步骤是按照固定顺序执行的
        /// </summary>
        public void TemplateMethod()
        {
            Handler1();
            Handler2();
            Handler3();
            Handler4();
        }

        /// <summary>
        /// 具体方法1
        /// </summary>
        public void Handler1()
        {
            Console.WriteLine("我是取号业务");
        }
        /// <summary>
        /// 具体方法2
        /// </summary>
        public void Handler2()
        {
            Console.WriteLine("我是排队业务");
        }

        /// <summary>
        /// 抽象方法3
        /// 个人业务,因人而异,需要去子类中实现
        /// </summary>
        public abstract void Handler3();

        /// <summary>
        /// 具体方法4
        /// </summary>
        public void Handler4()
        {
            Console.WriteLine("我是评分业务");
        }


    }

具体子类:

    /// <summary>
    /// 具体子类1
    /// </summary>
    public class ChildHandler1 : AbstractHandler
    {
        /// <summary>
        /// 重写办个人业务的方法
        /// </summary>
        public override void Handler3()
        {
            Console.WriteLine("我来进行理财业务");
        }
    }
    /// <summary>
    /// 具体子类2
    /// </summary>
    public class ChildHandler2 : AbstractHandler
    {
        /// <summary>
        /// 重写办个人业务的方法
        /// </summary>
        public override void Handler3()
        {
            Console.WriteLine("我来进行存款业务");
        }
    }

测试:

            {
                //流程1
                Console.WriteLine("---------------下面是流程1---------------------");
                AbstractHandler h1 = new ChildHandler1();
                h1.TemplateMethod();

                //流程2
                Console.WriteLine("---------------下面是流程2---------------------");
                AbstractHandler h2 = new ChildHandler2();
                h2.TemplateMethod();
            }

运行结果:

4. 适用场景分析

(1). 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。

(2). 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

原文https://www.cnblogs.com/yaopengfei/p/13496730.html

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

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

相关文章

最大权闭合子图(最小割模型)

1&#xff0c;定义&#xff1a; 1&#xff0c;最大权闭合子图是最小割的一个模型。即每一个子图中的每一个点&#xff0c;其出边的点也全应该在这个子图中。而所有子图中&#xff0c;其点的权值和最大就是最大权闭合子图。 2&#xff0c;构建该图&#xff0c;我们把所有正权值…

Docker镜像创建及管理(Hub官方仓库使用及私有注册中心搭建)

写在前面 系统环境&#xff1a;centos 7 一、Docker如何创建镜像 镜像的来源有两种&#xff1a; 从镜像仓库下载镜像&#xff1b;自己创建新的镜像。创建分为两种&#xff1a;&#xff08;1&#xff09;基于已有镜像创建&#xff1b;&#xff08;2&#xff09;使用Dockerfi…

【数据治理-03】无规矩不成方圆,聊聊如何建立数据标准

无规矩&#xff0c;不成方圆&#xff01;数据标准&#xff08;Data Standards&#xff09;是保障数据的内外部使用和交换的一致性和准确性的规范性约束&#xff0c;作为数据治理的基石&#xff0c;是绕不开的一项工作&#xff0c;如此重要的活如何干&#xff0c;咱们一起聊聊。…

【数据结构】排序算法

目录 1.理解排序 1.1 排序的概念 1.2 排序的运用场景 1.3 常见的排序算法 2.插入排序算法 2.1 直接插入排序 2.2 希尔排序 3.选择排序算法 3.1 直接选择排序 3.2 堆排序 4.交换排序算法 4.1 冒泡排序 4.2 快速排序 4.2.1 hoare 法 4.2.2 挖坑法 4.2.3 前…

前期软件项目评估偏差,如何有效处理?

1、重新评估制定延期计划 需要对项目进行重新评估&#xff0c;将新的评估方案提交项目干系人会议&#xff0c;开会协商一致后按照新的讨论结果制定计划&#xff0c;并实施执行。 软件项目评估偏差 怎么办&#xff1a;重新评估制定延期计划2、申请加资源 如果项目客户要求严格&a…

用股票交易量查询接口是怎么查询a股全天总成交量的?

用股票交易量查询接口是怎么查询a股全天总成交量的&#xff1f;今天下班就以通达信给大家讲解一下&#xff0c;通常是在K线图的底部状态栏&#xff0c;可以在日线进行查看a股成交量。在市场栏底部的子图中。 有当天成交的数量。成交量是表示一定的时间内已经成交的中的成交数量…

【数据挖掘】期末复习笔记(重点知识)

Data Mining 一、概述 1.1 数据挖掘 VS 机器学习 VS 深度学习 VS 知识发现 知识发现&#xff1a; 知识发现就是在数据中发掘知识&#xff0c;将低层次的原始数据转换为高层次的信息。 数据挖掘&#xff1a; 数据挖掘是用一系列的方法或算法从数据中挖掘有用的信息&#xf…

Android中的MVC、MVP、MVVM架构你清楚不?(附实现代码)

01 架构介绍 先来看一下MVC、MVP、MVVM的架构图。 从这些架构图中&#xff0c;可以看到每种架构都有3个模块以及数据流动方向箭头。 模块 在系统架构中&#xff0c;首先要做的就是把系统整体按照一定的原则划分成模块。 数据流动 模块划分之后&#xff0c;模块之间的通信&…

工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议

工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议 在 MODBUS 协议下&#xff0c;所有寄存器被定义为“保持寄存器” &#xff08;详见 MODBUS 通讯协议标准说明&#xff09;&#xff0c; 设备支持基于 MODBUS 协议的多个连续寄存器读取、单个寄存器写入两种指令码&#x…

电液伺服阀控制器YY-100

供电电源&#xff1a; 24V DC(18&#xff5e;36V)&#xff1b; 控制输入&#xff1a; -10V&#xff5e;10V DC&#xff1b;最大输出&#xff1a; 70mA &#xff1b;增益 &#xff1a; 调节范围——1&#xff5e;40 mA&#xff08;出厂设置——4 mA&#xff09;&#xff1b; 偏置…

C语言从0到1算法小白训练营——day2

我们学习不仅仅是要把难的学会&#xff0c;也要注重基础&#xff0c;注重内功。 接下来我们继续先从基础知识开始&#xff1a; 1. 字符串字符常量注释 1.1 字符串 如&#xff1a;“abc” ①定义&#xff1a;由双引号引起来的一串字符称为字符串。 ②C语言规定&#xff0c;…

【计算机网络】P1 - 物理层

物理层大纲物理层基本概念数据通信基础两种入网方式传输过程源系统、传输系统与目的系统数据与信号信源、信宿与信道三种通信方式两种传输方式大纲 物理层基本概念 物理层解决如何在传输媒体上&#xff08;同轴电缆&#xff0c;光纤等&#xff09;上传输数据比特流。主要任务为…

detach,主线程终止后子线程会结束吗

此前&#xff0c;我对detach的理解是&#xff0c;当主线程退出后&#xff0c;子线程能够继续存在。实际上&#xff0c;当主线程退出后&#xff0c;子线程也随之结束了。先看一个例子&#xff1a; #include <iostream> #include <thread> #include <unistd.h>…

交叉编译 zlib

交叉编译 zlib 概述 zlib 被设计为一个免费的、通用的、不受法律约束的、即不受任何专利保护的无损数据压缩库&#xff0c;可在几乎任何计算机硬件和操作系统上使用。zlib 数据格式本身可以跨平台移植。与Unix 压缩和 GIF 图像格式中使用的 LZW 压缩方法不同&#xff0c;zlib …

RocketMq使用规范(纯技术和实战建议)

概述&#xff1a; 使用规范主要从&#xff0c;生产、可靠性、和消费为轴线定义使用规范&#xff1b;kafka使用核心&#xff1a;削峰、解耦、向下游并行广播通知&#xff08;无可靠性保证&#xff09;和分布式事务&#xff0c;本规范仅从削峰、解耦、向下游并行广播通知论述&am…

OceanBase 4.0解读:兼顾高效与透明,我们对DDL的设计与思考

关于作者 谢振江&#xff0c;OceanBase 高级技术专家。 2015年加入 OceanBase, 从事存储引擎相关工作&#xff0c;目前在存储-索引与 DDL 组&#xff0c;负责索引&#xff0c;DDL 和 IO 资源调度相关工作。 回顾关系型数据库大规模应用以来的发展&#xff0c;从单机到分布式无…

什么是BOM?与焊盘不匹配,怎么办?

什么是BOM&#xff1f; 简单的理解就是&#xff1a;电子元器件的清单&#xff0c;一个产品由很多零部件组成&#xff0c;包括&#xff1a;电路板、电容、电阻、二三极管、晶振、电感、驱动芯片、单片机、电源芯片、升压降压芯片、LDO芯片、存储芯片、连接器座子、插针、排母、…

成为IT服务台经理需要什么技能

要给员工带来愉快的体验&#xff0c;就必须对你的服务台进行有效的管理。为此&#xff0c;了解为什么服务台经理的角色对于绘制企业组织良好的服务台至关重要。在本指南中&#xff0c;我们将深入探讨他们的角色、能力和贡献&#xff0c;以了解如何顺利处理服务台操作。 IT 服务…

【面试题】前端 移动端自适应?

移动端 h5 开发中有一个绕不开的话题&#xff1a;移动端自适应方案。移动端的设备尺寸不尽相同&#xff0c;要把 UI 设计图较好地展示在移动端上&#xff0c;需要让 h5 页面能自适应设备尺寸。接下来将对移动端自适应的相关概念、方案和其他一些常见问题做个介绍。概念简介大厂…

什么是 Web3?解读未来的去中心化网络:The Decentralized Internet of the Future Explained

目录 互联网的演化 什么是 Web 1.0? 什么是 Web 2.0? Web 2.0 变现与安全性 什么是 Web 3.0? 原生支付 创立公司的新方式 Web3 中的身份 如果你读到这篇文章,那么你已经是当代互联网世界的一员了。我们现在使用的网络和10年前大不相同。所以,互联网是怎么演化的,…