学习设计模式《二》——外观模式

发布于:2025-04-19 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、基础概念

 1.1、外观模式的简介

        外观模式的本质是【封装交互、简化调用】

        外观模式的说明:就是通过引入一个外观类,在这个类里面定义客户端想要的简单方法,然后在这些方法里面实现;由外观类再去分别调用内部的多个模块来实现功能,从而让客户端变得简单;这样一来,客户端就只需要和外观类交互就可以了

        外观模式的实现:对一个子系统来说,外观类不需要很多,可以实现为一个单例; 或直接将外观类中的方法实现为静态方法,将外观类作为一个辅助工具类,方便外部直接调用 。

        外观模式目的:为了让外部减少与子系统内部多个对象或模块的交互,松散耦合,封装了内部细节, 从而让外部能够更简单地使用子系统。

        使用外观模式的注意点:因为外观模式是当做子系统对外的通道,虽然也可以在这里定义一些子系统没有的功能,但不建议这样做【外观应该是包装已有功能,它主要负责组合已有功能来实现客户需要,本身并不进行功能的处理,而不是添加新的实现】。

何时选用外观模式?

1、希望为一个复杂的子系统提供简单的对外通道,简化外部调用;

2、想要让外部程序和抽象类的实现部分松散耦合(使用外观类将子系统与外部客户端分离开,提供子系统的独立性与可移植性);
3、构建多层结构的系统。

外观模式的优缺点
序号 外观模式的优点 外观模式的缺点
1

松散耦合

(松散了客户端与子系统的耦合关系,让子系统内部模块更容易扩展与维护)

过多或不太合理的外观类容易让人迷惑(即是调用外观类好,还是直接调用模块好)
2

简单易用

(外观类让子系统更加易用,客户端不再需要了解子系统内部实现,也不需要与子系统内部模块交互)

3

 更好的划分访问层次

(合理使用外观类,可以帮助我们更好地划分访问层次【外观类把需暴露给外部的功能都集中提供,而子系统内部更多的方法是内部使用】)

 1.2、现实中的一些外观模式例子

比如我们现实生活中的需要购买台式电脑:可以有两种方式:

        方法一:自己列出所需的各种配件,然后买来各种配件,自己组装(这个方案是好,但是需要对各种电脑配件熟悉,这样才能选择到合适的配件,且需要考虑好各个配件间的兼容性)

        方案二:直接找到专业的装机公司或团体,把自己的需求提出来,然后有装机公司根据要求购买好对于的配件组装调试好给你(这个方案是省心,但是价格会贵一些,综合起来还是比较划算的,是大多数人的选择)【该方案中的专业装机公司或团体就可以看作是外观模式的外观类了】

二、外观模式示例

        在软件开发公司中会有一个为了减少大家复制粘贴工作的代码生成工具,用来生成基础的代码(比如数据库的增删查改【一般会生成三层内容(表现层、业务层、数据层)】),在项目开发中可以直接使用该工具生成基础的代码,然后可以留出更多的时间去实现项目中的业务内容;我们这里仅写一些示意代码,主要是用来展现外观模式:

 2.1、常规实现

1、编写配置模型类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    /// <summary>
    /// 配置模型示意
    /// </summary>
    internal class ConfigModel
    {
        //是否需要生产表现层
        private bool needGenPresentation = true;

        //是否需要生成逻辑层
        private bool needGenBusiness=true;

        //是否需要生成数据层(DAO表示Data Access Object)
        private bool needGenDAO = true;

        public bool NeedGenPresentation { get => needGenPresentation; set => needGenPresentation = value; }
        public bool NeedGenBusiness { get => needGenBusiness; set => needGenBusiness = value; }
        public bool NeedGenDAO { get => needGenDAO; set => needGenDAO = value; }

    }//Class_end
}

2、编写配置管理类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    internal class ConfigManager
    {
        private static ConfigManager configManager = null;
        private static ConfigModel configModel = null;

        public ConfigManager()
        {

        }

        public static ConfigManager GetInstance() 
        {
            if (configManager==null)
            {
                configManager = new ConfigManager();
                configModel = new ConfigModel();
                //读取配置文件,把值设置到ConfigModel中
            }
            return configManager;
        }

        //获取配置的数据
        public ConfigModel GetConfigData()
        {
            return configModel;
        }


    }//Class_end
}

3、编写表现层类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    /// <summary>
    /// 生成表现层模块示意
    /// </summary>
    internal class Presentation
    {
        public void Generate()
        {
            //1、从配置管理里面获取相应的配置信息
            ConfigModel configModel=ConfigManager.GetInstance().GetConfigData();

            if (configModel.NeedGenPresentation)
            {
                //2、按照要求生成对应的表现层代码,并保存为文件
                Console.WriteLine("正在生成表现层代码文件并保存");
            }
        }


    }//Class_end
}

4、编写业务层类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    /// <summary>
    /// 生产逻辑层模块示意
    /// </summary>
    internal class Business
    {
        public void Generate()
        {
            ConfigModel configModel = ConfigManager.GetInstance().GetConfigData();
            if (configModel.NeedGenBusiness)
            {
                Console.WriteLine("正在生成逻辑层的代码文件并保存");
            }
        }

    }//Class_end
}

5、编写数据层类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    /// <summary>
    /// 生成数据层模块示意
    /// </summary>
    internal class DAO
    {
        public void Generate()
        {
            ConfigModel configModel=ConfigManager.GetInstance().GetConfigData();
            if (configModel.NeedGenDAO)
            {
                Console.WriteLine("正在生成数据层的代码文件并保存");
            }
        }

    }//Class_end
}

6、编写客户端实现,需要客户端对接各个类进行了解调用组合

        客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块进行交互【对客户端来说,这是比较麻烦的,且如果其中的某个模块发生了变化,还可能引起客户端要随着变化】

using FacadePattern.CodeGenerationV1;

namespace FacadePattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("客户端要实现功能需自己管理不同的子模块与逻辑");
            /***
             * 目前没有配置文件,直接使用默认配置(生成三层模块)
             * 也就是说客户端必须要对这三个模块都有所了解,才能够正确使用;
             * 且客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块进行交互
             * 【对客户端来说,这是比较麻烦的,且如果其中的某个模块发生了变化,还可能引起客户端要随着变化】
             * (如何实现?才能让子系统外部的客户端在使用子系统的时候简单的使用子系统内部模块功能,而又不用与子系统内部的多个模块交互?)
             * ***/
            new Presentation().Generate();
            new Business().Generate();
            new DAO().Generate();

        }//Class_end
    }
}

 运行后的结果如下:

 2.2、外观模式

        如何实现?才能让子系统外部的客户端在使用子系统的时候简单的使用子系统内部模块功能,而又不用与子系统内部的多个模块交互?

1、单独创建一个外观类用来调用组合子系统的功能

/***
*	Title:"设计模式" 项目
*		主题:外观模式
*	Description:
*	    基础概念:本质是【封装交互、简化调用】
*	        外观模式:就是通过引入一个外观类,在这个类里面定义客户端想要的简单方法,
*	                  然后在这些方法里面实现;由外观类再去分别调用内部的多个模块来实现功能,
*	                  从而让客户端变得简单;这样一来,客户端就只需要和外观类交互就可以了。
*	                  
*	        外观模式目的:为了让外部减少与子系统内部多个对象或模块的交互,松散耦合,封装了内部细节,
*	                      从而让外部能够更简单地使用子系统
*	                      
*	        使用外观模式的注意点:因为外观模式是当做子系统对外的通道,虽然也可以在这里定义一些子系统
*	                              没有的功能,但不建议这样做【外观应该是包装已有功能,它主要负责组合已有
*	                              功能来实现客户需要,本身并不进行功能的处理,而不是添加新的实现】
*	                              
*	        外观模式的实现:对一个子系统来说,外观类不需要很多,可以实现为一个单例
*	                        或直接将外观类中的方法实现为静态方法,将外观类作为一个辅助工具类,方便外部直接调用                      
*	                              
*	        有外观模式,但可以不使用:虽然有外观类,但如果有需要,外部还是可以绕开外观类,而直接调用
*	                                  某个具体模块功能,这样就能实现兼顾组合功能和细节功能
*	                                  
*		    外观模式优点:
*		                松散耦合(松散了客户端与子系统的耦合关系,让子系统内部模块更容易扩展与维护)
*		                简单易用(外观类让子系统更加易用,客户端不再需要了解子系统内部实现,也不需要与子系统内部模块交互)
*		                更好的划分访问层次(合理使用外观类,可以帮助我们更好地划分访问层次【外观类把需暴露给外部的功能都集中提供,而子系统内部更多的方法是内部使用】)
*		    外观模式缺点:过多或不太合理的外观类容易让人迷惑(即是调用外观类好,还是直接调用模块好)
*		    
*		    何时选用外观模式?
*		                1、希望为一个复杂的子系统提供简单的对外通道,简化外部调用;
*		                2、想要让外部程序和抽象类的实现部分松散耦合(使用外观类将子系统与外部客户端分离开,提供子系统的独立性与可移植性)
*		                3、构建多层结构的系统
*		    
*	Date:2025
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
 ***/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FacadePattern.CodeGenerationV1
{
    /// <summary>
    /// 定义一个子系统的外观对象,统一对外提供方法,外部不用关心与管理内部模块的实现
    /// </summary>
    internal class Facade
    {
        private Facade()
        {
            
        }

        /// <summary>
        /// 直接把客户端需要的功能单独定义一个方法提供出来
        /// </summary>
        public static void Generate()
        {
            //该方法实现的时候,可能会调用到内部的多个模块
            new Presentation().Generate();
            new Business().Generate();
            new DAO().Generate();
        }

    }//Class_end
}

2、客户端可以直接调用外观类的方法(简单明了,客户端不用了解子系统的各个模块与细节)

using FacadePattern.CodeGenerationV1;

namespace FacadePattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("外观模式");
            //客户端直接调用外观对象的方法(简单便捷)
            Facade.Generate();

            Console.ReadLine();


        }//Class_end
    }
}

运行结果如下:

三、项目源码工程

kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern


网站公告

今日签到

点亮在社区的每一天
去签到