设计模式⑦ :简单化

news2024/11/17 9:48:48

文章目录

  • 一、前言
  • 二、Facade 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 三、Mediator 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结

一、前言

有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著)。该系列文章可随意转载。

二、Facade 模式

Facade 模式 :简单窗口

1. 介绍

一般来说,随着时间推移,程序会变得越来越复杂,这使得程序结构也变得越来越复杂,在使用这些功能时,我们需要先整理清楚他们之间的关系,注意正确的调用顺序。与其如此,不如为这个大型程序准备一个“窗口“,这样就无需关注每个类,只需要简单的对窗口提出请求即可。

使用 Facade 模式可以为互相关联在一起的错综复杂的类整理出高层接口。其中 Facade 角色可以让系统对外只有一个简单的接口。而且 Facade 角色还会考虑到系统内部各个类之间的责任关系和依赖关系,按照正确的顺序调用各个类。


Facade 模式 中出场的角色

  • Facade(窗口): 该角色是代表构成系统的许多其他角色的简单窗口。Facade 角色向系统外部提供高层接口
  • 构成系统的许多其他角色 :这些角色各自完成自己的工作,他们并不知道Facade 角色。Facade 角色调用其他角色进行工作,但是其他角色并不会调用 Facade角色。
  • Client(请求者):该角色负责调用 Facade 角色。

类图如下:

在这里插入图片描述


Demo如下:

// 基础数据获取
public class Database {

    /**
     * 防止外部创建
     */
    private Database() {

    }

    /**
     * 获取基础数据
     * @return
     */
    @SneakyThrows
    public static Properties getProperties() {
        Properties prop = new Properties();
        prop.setProperty("123456789@qq.com", "张三");
        prop.setProperty("987654321@qq.com", "李四");
        return prop;
    }
}

// html 工具类
public class HtmlWriter {

    /**
     * 生成Html内容
     *
     * @param title
     * @throws IOException
     */
    public static String createContent(String title, String userName, String email) throws IOException {
        return "<html>" +
                "<head>" +
                "<title>" + title + "</title>" +

                "</head>" +
                "<body>" +
                "<h1>" + title + "</h1>" +
                "<div> 欢迎: " + userName + "</div>" +
                "<a href=\"" + email + "\"> " + email + "</a>" +
                "</html>";
    }

    /**
     * 生成html文件
     *
     * @param fileName
     * @param content
     */
    public static void writeHtml(String fileName, String content) {
        FileUtil.writeBytes(content.getBytes(StandardCharsets.UTF_8), fileName);
    }

}

// 页面生成类,
public class PageMaker {
    private PageMaker(){}

    /**
     * 构建欢迎页面
     */
    public static void markWelcomePage(String mailaddr) throws IOException {
        final Properties prop = Database.getProperties();
        final String userName = prop.getProperty(mailaddr);
        final String content = HtmlWriter.createContent(userName, userName, mailaddr);
        HtmlWriter.writeHtml("D://welcome.html", content);
    }
}

public class FacadeDemoMain {
    public static void main(String[] args) throws IOException {
    	//调用 PageMaker#markWelcomePage 生成 html 页面
        PageMaker.markWelcomePage("123456789@qq.com");
    }
}

结果如下:生成welcome.html 文件,打开内容如图
在这里插入图片描述

对用户来说,只需要调用 PageMaker#markWelcomePage,传入指定邮箱便可以生成对应的 HTML页面,而其内部实现的HtmlWriter和Database对用户来说是无法感知的,即 PageMaker 对于 生成 Html 这个功能来说就是一个 Facade。

2. 应用

  • Dubbo 的分层模式个人认为也是Facade 模式的应用:Dubbo提供各个SPI 接口,如果用户需要扩展或改变功能只需要重新对应的SPI 即可。而无需关心 SPI 的上下游的关联和调用关系。如下图:
    在这里插入图片描述

  • Facade 模式在项目中几乎无时无刻不在用到,这里以 Spring 中随便找的一个方法为例:DispatcherServlet#doDispatch 中 会调用 HandlerExecutionChain#applyPreHandle 方法执行拦截器方法,那么这里就可以认为 HandlerExecutionChain 对 HandlerInterceptor 进行了分装。DispatcherServlet 只需要调用HandlerExecutionChain#applyPreHandle 便可以执行拦截器方法而无需关注具体实现。

在这里插入图片描述



个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 个人理解在微服务级别,网关也起到类似 Facade 的作用:网关对外部暴露的接口是有限且可控的,外部无需关注网关内部接口的调用关系。

  • 在项目A 中,对同一数据的获取由于各个地区不同获取的途径或调用的第三方接口都不相同,此时需要在项目中对该数据的获取封装出一个 Facade ,当需要获取数据时只需要调用 Facade 暴露出的接口即可。Demo 如下:

    public interface DataSource {
        /**
         * 获取数据
         * @return
         */
        String getData();
    }
    
    public class HttpDataSource implements DataSource {
    
        @Override
        public String getData() {
            return "这是从 Http 调用获取的数据";
        }
    }
    
    public class DataBaseDataSource implements DataSource {
    
        @Override
        public String getData() {
            return "这是从数据库获取的数据";
        }
    }
    
    public class DataFacade {
    
        private DataBaseDataSource dataBaseDataSource = new DataBaseDataSource();
    
        private HttpDataSource httpDataSource = new HttpDataSource();
    
        /**
         * 获取数据,nb 从数据库获取,sh通过http调用获取
         * @param region
         * @return
         */
        public String getData(String region) {
            if ("nb".equals(region)) {
                return dataBaseDataSource.getData();
            } else if ("sh".equals(region)) {
                return httpDataSource.getData();
            } else {
                throw new RuntimeException("不支持");
            }
        }
    
    }
    
    public class DemoMain {
        public static void main(String[] args) {
            DataFacade dataFacade = new DataFacade();
            System.out.println(dataFacade.getData("nb"));
            System.out.println(dataFacade.getData("sh"));
        }
    }
    
    

    输出如下:

    在这里插入图片描述

3. 总结

扩展思路:

  • Facade 角色的工作:Facade 模式可以让复杂的东西看起来简单,使用 Facade 模式可以让我们不再在意后台工作的类和他们之间的关系。这里的重点是接口的变少了,这意味着程序和外部的关联关系弱化了,这使得我们的包作为组件更方便被复用。
  • 递归的使用Facade模式:在超大系统中往往包含非常多的包和类,如果我们在每个关键的地方都使用Facade 模式,系统的维护就会变的简单。

相关设计模式:

  • Abstract Factory 模式:可以将 Abstract Factory 模式看做是生成复杂实例时的 Facade 模式。因为它提供了“要想生成这个实例只需要调用这个方法就ok了”的简单接口。
  • Singleton 模式:有时会使用 Singleton 模式创建 Facade 角色。
  • Mediator 模式:在 Mediator 模式汇总, Mediator 角色与 Colleague 角色进行交互。而在 Facade 模式中,Facade 角色单方面地使用其他角色来对外提供高层接口。因此可以说 Mediator 是双向的,而 Facade 是单向的。

一时的小想法,仅仅个人理解,无需在意 :

  • Facade 模式不仅仅可以用于项目的代码中,在微服务搭建时,网关或者交换中心也起到类似的作用。对于外部访问,暴露出有限且可控的接口。
  • 个人认为最为简单的应用就是 MVC 中 Service 层暴露出的接口,每个 Service 接口内部需要调用多个 Dao 层的接口才能实现,那么就可以认为 Service 层是 Dao 层的封装。区别就是这里的 Service 层的“窗口”粒度更细。

三、Mediator 模式

Mediator 模式 : 只有一个仲裁者。

1. 介绍

当麻烦事发生时通知仲裁者,当发生涉及全体组员的事情,也通知仲裁者,当仲裁者下达指示时,组员会立即执行。组员之间不再相互沟通作出决定,而是发生任何事都向仲裁者报,一方面仲裁者站在整个团队的角度上对组员上报的事情做出决定。最后,整个团队的交流过程就变为了组员向仲裁者报告,仲裁者向组员下达指示。


Mediator 中出场的角色

  • Mediator(仲裁者、中介者):该角色负责定义与 Colleague 角色进行通信和做出决定的接口(API)
  • ConcreteMediator(具体的仲裁者、中介者):该角色负责实现 Mediator 角色的接口,负责作出实际决定。
  • Colleague(同事):该角色负责定义与 Mediator 角色进行通信的接口。
  • ConcreteColleague(具体的同事):该角色负责实现 Colleague 角色的接口。

类图如下:

在这里插入图片描述


2. 应用

  • Mediator 模式非常典型的应用则是在集群服务异常下线时的选举,如Redis的哨兵模式的监视和选举。
    以下面为例(内容来自于书籍 《Redis设计与实现》):

    1. 如下图展示了一个哨兵系统,server1 为主服务器,server2,server3, server4 为从服务器,sentinel 系统监视着这四个服务器
      在这里插入图片描述
    2. 假设这时server1 进入下线状态,那么从服务器 server2,server3, server4 对主服务器的复制将会被终中止,并且 Sentinel 系统会查询到 server1下线

    在这里插入图片描述
    3. 当 server1 的下线时长超过用户设定的下线时长上限时,Sentinel 系统就会对 server1 执行故障转移操作:

    1. Sentinel 系统会挑选 server1 的从服务器中的一个升级为主服务器
    2. 之后Sentinel 系统会想 server1 的所有从服务器发送新的复制指令,让他们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕。
      另外,Sentinel 还会监视已经下线的 server1,当其重新上线时会将其设置为新的主服务器的从服务器。
      在这里插入图片描述


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • Mediator 模式在目前的经验中似乎并没有用到,不过临时想到了一个应用场景:类如支付宝登录这种需要极度安全的操作肯定不能直接通过用户名密码验证,需要根据当前使用的手机是否新手机、当前账号多久没有登录、截止目前登录了多少次等种种因素决定当前登录是否有效,如此便可以使用仲裁者模式。Demo如下:

    // 判断接口
    public interface Colleague {
    
        /**
         * 信息判决
         * @param userInfo
         * @return
         */
        int judgment(String userInfo);
    }
    
    
    public class LoginMadiator {
    	// 登录次数验证
        private NumberColleague numberColleague = new NumberColleague();
    	// 手机号验证
        private PhoneColleague phoneColleague = new PhoneColleague();
    
        /**
         * 登录判决
         *
         * @return
         */
        public boolean loginJudgment(String userInfo) {
            // 根据用户信息获取登录次数的判定
            final int numberScore = numberColleague.judgment(userInfo);
            // 根据用户信息获取是否新手机的的判定
            final int phoneScore = phoneColleague.judgment(userInfo);
            // 如果登录次数或是否新手机的判定有一个小于80分,本次登录验证不通过。
            return numberScore < 80 || phoneScore < 80;
        }
    }
    
    public class DemoMain {
        public static void main(String[] args) {
            String userName = "夏义春";
            String password = "123456789";
            LoginService loginService = new LoginService ();
            // 用户登录, 返回用户登录信息
            final String userInfo = loginService.login(userName, password);
            LoginMadiator loginMadiator = new LoginMadiator();
            // 如果本次登录裁决失败,则进行人脸或短信验证
            if (!loginMadiator.loginJudgment(userInfo)){
                // TODO : 进行人脸验证 或短信验证
            }
        }
    }
    

3. 总结

相关的设计模式:

  • Facade 模式:在 Mediator 模式汇总, Mediator 角色与 Colleague 角色进行交互。而在 Facade 模式中,Facade 角色单方面地使用其他角色来对外提供高层接口。因此可以说 Mediator 是双向的,而 Facade 是单向的
  • Observer 模式:有时会使用Observer 模式来实现 Mediator 角色与 Colleague 角色之间的通信。

一时的小想法,仅仅个人理解,无需在意 :

  • 一个仲裁类控制其他类来完成任务,也可以存在变种:即仲裁者也可以变成组员,类似 主从模式,可以自由选举。总的来说,需要抉择多重的任务级别适用于仲裁者模式。

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

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

相关文章

c# 自定义 滑块TrackBar

辛苦半天做出来的&#xff0c;如果觉得好用&#xff0c;记得点赞 效果图如下&#xff1a; 具体操作&#xff1a; 1 、添加代码&#xff08;代码在下面&#xff09;&#xff0c;重新生成下整个工程&#xff0c;在工具栏中就出现控件&#xff0c;将控件拖到窗体中 2、只需要调整…

1434. 数池塘(四方向)-深度优先搜索-DFS

代码&#xff1a; #include<iostream> using namespace std; char a[200][200]; int fx[4]{0,1,0,-1}; int fy[4]{1,0,-1,0}; int k0; int n,m; void dfs(int x,int y){a[x][y].;int tx,ty;for(int i0;i<4;i){txxfx[i];tyyfy[i];if(tx>1&&tx<n&&am…

python24.1.19引入模块

引入方法 可引入第三方模块 安装模块&#xff1a;在终端输入pip install模块名字 eg&#xff1a;

kali下-MSF-ftp_login模块破解FTP账号及密码

一、环境准备 两台设备在同一个网络内 一台kali系统&#xff1a;192.168.10.128 一台winserver2016&#xff1a;192.168.10.132 二、MSF介绍 metasploit 全称是The Metasploit Framework&#xff0c;又称MSF&#xff0c;是Kali 内置的一款渗透测试框架&#xff0c;也是全球…

Docker(二)安装指南:主要介绍在 Linux 、Windows 10 和 macOS 上的安装

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 安装 Docker Docker 分为 stable test 和 nightly 三个更新频道。 官方网站上有各种环境下的 安装指南&#xff0c;这里主要介绍 Docker 在…

【网络技术】【traceroute】【Linux虚拟机(Ubuntu)】无法traceroute至谷歌【及解决方法】

一、问题描述 问题描述如下&#xff1a; Ubuntu虚拟机可以ping通谷歌&#xff08;www.google.com&#xff09;&#xff0c;但是却无法通过traceroute命令找到路由路线&#xff0c;如下图所示&#xff1a; 二、解决方法 从traceroute命令的返回内容可以看出&#xff0c;路由寻…

使用IEEE754标准转换过程

IEEE 754标准是一种用于浮点数表示和计算的标准。对于给定的浮点数&#xff0c;IEEE 754标准定义了如何表示它以及如何执行基本的算术运算。 例如&#xff0c;现在要将十进制浮点数3.25f转换为IEEE 754标准的二进制表示&#xff0c;可以按照以下步骤进行&#xff1a; 将3.25f…

网卡:初始化,frame组装,网卡处理电信号过程

ip模块中存储的是一堆数字信号&#xff0c;网卡内部会把数字信号转换成电信号或者光信号在网线中传输。 网卡只是一个硬件&#xff0c;需要驱动程序去操作他&#xff0c;计算机中已经把主流网卡的驱动程序&#xff08;不只是网卡的&#xff0c;还有鼠标&#xff0c;键盘这些硬件…

性能测试知多少---性能测试流程

看到好多新手&#xff0c;在性能需求模糊的情况下&#xff0c;随便找一个性能测试工具&#xff0c;然后就开始进行性能测试了&#xff0c;在这种情况下得到的性能测试结果很难体现系统真实的能力&#xff0c;或者可能与系统真实的性能相距甚远。 与功能测试相比&#xff0c;性能…

R.swift SwiftGen 资源使用指南

R.swift 和 SwiftGen 资源转换使用指南 R.swift &#xff08;原始代码会打包到项目&#xff1f;&#xff09; Pod platform :ios, 12.0 target LBtest do# Comment the next line if you dont want to use dynamic frameworksuse_frameworks!pod R.swift # pod SwiftGen, ~&g…

虚幻UE 特效-Niagara特效实战-雨天

回顾Niagara特效基础知识&#xff1a;虚幻UE 特效-Niagara特效初识 其他两篇实战&#xff1a;虚幻UE 特效-Niagara特效实战-火焰、烛火、虚幻UE 特效-Niagara特效实战-烟雾、喷泉 本篇笔记我们再来实战雨天&#xff0c;雨天主要用到了特效中的事件。 文章目录 一、雨天1、创建雨…

智汇云舟创始人兼总裁周舟:视频孪生赋能智慧城市多元场景建设

1月18日&#xff0c;由知名科技媒体和产业智库泰伯网主办的WIF2023创新先行者论坛暨企业家会员年会于北京成功举办。共有百余位科技公司创始人、管理者、投资人齐聚&#xff0c;研判产业经济趋势&#xff0c;寻找新形势下企业未来发展的新方向、新机会。智汇云舟创始人兼总裁周…

毅速ESU:金属3D打印技术助力模具开发降本增效

模具是工业之母&#xff0c;在高效批产方面极具优势&#xff0c;但随着企业对模具精度、结构复杂性、生产周期和成本的要求日益严格&#xff0c;传统模具制造已经不能完全满足市场需求。在众多的模具加工工艺新技术中&#xff0c;3D打印技术把增材制造理念引入模具行业&#xf…

精品量化公式——“筹码动态”,筹码动态改进版,增加了三个买点信号标识

不多说&#xff0c;直接上效果如图&#xff1a; ► 日线表现 代码评估 技术指标代码评估&#xff1a; 散筹估算: 使用EMA&#xff08;指数移动平均&#xff09;方法计算(WINNER(C*1.1)-WINNER(C*0.9))*70的3日均线&#xff0c;用黄色粗线表示。这是用于估算市场中散户投资者的…

在Linux中安装和配置Node.js与Express.js创建HTTP服务器

在Linux环境中&#xff0c;安装和配置Node.js与Express.js来创建一个HTTP服务器需要一系列的步骤。下面是一个详细的指南&#xff0c;帮助你在Linux上设置这个环境。 步骤1&#xff1a;安装Node.js 首先&#xff0c;你需要确保你的Linux系统已经安装了Node.js。你可以通过以下…

Vulnhub靶机:driftingblues 7

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;driftingblues7&#xff08;10.0.2.23&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entr…

限时免费参加游戏开发训练营!!!

限时免费 本周有效 游戏行业作为当下最火热的行业之一&#xff0c;受到了很多人的喜爱&#xff0c;也有很多同学想要进入游戏开发行业。 基于大家的需求&#xff0c;我们为大家提供了不同类型的游戏开发训练营&#xff0c;让你既能通过这些游戏Demo熟练开发技能&#xff0c;也…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-热门帖子推荐显示实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

【android】 android 里写jni

目录 &#xff08;1&#xff09; 环境准备 (2) 关联c文件到gradle文件 &#xff08;3&#xff09; 生成了 (4) 书写 &#xff08;5&#xff09; 使用 &#xff08;6&#xff09;业务调用 参考文档 &#xff08;1&#xff09; 环境准备 ndk, cmake (2) 关联c文件到gr…