设计模式篇---工厂方法(可通过lambda实现)

news2024/11/27 7:29:30

文章目录

    • 概念
    • 结构
    • 实例
    • 通过lambda实现
    • 总结

概念

工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
这个模式还是比较好理解,且使用场景比较频繁。简单工厂是只有一个工厂类,通过if else 根据不同的参数返回对应的对象;而工厂方法去除了if else 的判断,更加的灵活,它把返回对象的功能交给了子类(而简单工厂是将返回对象的功能交给了一个固定的工厂类),通过扩展子类来实现不同的功能。

结构

工厂方法的类图如下:
在这里插入图片描述
Product(抽象产品):定义产品的接口,具体产品对象的父类。
ConcreateProduct(具体产品):实现了抽象产品类的方法,它与具体的工厂一一对应。
Factory(抽象工厂):它声明了抽象的方法,用来返回一个产品,所有具体的工厂都要实现该接口。
ConcreateFactory(具体工厂):它是抽象工厂的子类,返回具体的产品,与具体产品类一一对应。

实例

我们可以实现一个简单的计算器,拥有加减乘除四个算法,每个算法对应一个工厂类。

产品的抽象类,即抽象的操作类

public abstract class Operation {

    private Integer a;

    private Integer b;

    abstract Integer operation(Integer a, Integer b);

}

具体的加减乘除类四个类,继承父类Operation。

public class AddOperation extends Operation {

    @Override
    Integer operation(Integer a, Integer b) {
        return a + b;
    }
}
public class SubOperation extends Operation {

    @Override
    Integer operation(Integer a, Integer b) {
        return a - b;
    }
}
public class MultiOperation extends Operation {

    @Override
    Integer operation(Integer a, Integer b) {
        return a * b;
    }
}

public class DivOperation extends Operation {

    @Override
    Integer operation(Integer a, Integer b) {
        return a / b;
    }
}

抽象的工厂,四个工厂的接口,定义了抽象的计算方法。

public interface OperationFactory {

    //返回抽象的产品,子类返回具体的产品---里氏替换
    Operation createOperation();
}

四个具体的工厂类

public class AddFactory implements OperationFactory {

    @Override
    public Operation createOperation() {
        return new AddOperation();
    }
}
public class SubFactory implements OperationFactory {
    @Override
    public Operation createOperation() {
        return new SubOperation();
    }
}
public class MultiFactory implements OperationFactory {
    @Override
    public Operation createOperation() {
        return new MultiOperation();
    }
}
public class DivFactory implements OperationFactory {

    @Override
    public Operation createOperation() {
        return new DivOperation();
    }
}

客户端

public class Client {

    public static void main(String[] args) {

        //处理加法
        OperationFactory addFactory= new AddFactory();
        Operation operation = addFactory.createOperation();
        System.out.println(operation.operation(1, 2));


        //处理减法
        OperationFactory subFactory= new SubFactory();
        Operation operation1 = subFactory.createOperation();
        System.out.println(operation1.operation(1, 2));

        // 乘法、除法同上
    }
}

通过lambda实现

如果继续扩展的话,我们不用改动原来的代码,只需要再增加一个产品对象和对应的产品工厂就好了,非常符合开闭原则。但是如果有100个算法,我们就要创建100个具体产品类和100个具体产品工厂,维护起来太难了。如果我们用jdk8或者以上版本的话,可以通过lamda进行简化,不需要创建具体的类了,直接通过参数行为化来搞。

思路是这样:因为加减乘除这四个算法的入参和返回值的类型都是一样的(入参都是两个数,返回值都是一个数),这样就可以搞一个函数式接口,里面的抽象方法入参是两个数,返回值是一个数。然后将加减乘除四个算法的具体实现写在外面进行参数行为化。实现如下:

计算的函数式接口,入参数是T类型,返回值是V类型,如果只用来进行数的计算,可以直接写成Integer类型。

public interface Operate<T, V> {
    V operation(T a, T b);
}

客户端直接搞。

public class Client {

    static Map<String, Operate<Integer, Integer>> map = new HashMap<>();

    static {
        map.put("add", (a, b) -> {
            return (a + b);
        });

        map.put("sub", (a, b) -> {
            return (a - b);
        });
    }
    public static void main(String[] args) {

        //执行加法
        Integer add = map.get("add").operation(1, 2);
        System.out.println(add);


        //执行减法
        Integer sub = map.get("sub").operation(1, 2);
        System.out.println(sub);

    }
}

通过lambda,可以将计算的实现写在map的value里面。可以将map单独放在一个类里进行维护,当有新的计算操作时,就只维护这一个map的类就可以了(只需要执行put方法,但从一定程度上不符合开闭原则)。

总结

先说两个特别重要的概念:
里氏替换:子类型必须能够替换掉他们的基类型。
依赖倒置:高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象

工厂方法比简单工厂更符合开闭原则,当有新的需求时,不用修改之前代码。
客户端关注的是抽象的产品,具体工厂里返回的是具体的产品,这就必须让子产品永远可以替代父产品,我们维护每一个工厂类都是在管理具体的子产品。具体工厂对应抽象工厂也是同理。这就是里氏替换的应用。

客户端没有直接面向具体产品和具体工厂,而是依赖抽象产品和抽象工厂,这是高层依赖了抽象,具体产品和具体工厂实现了各自的抽象类,这是低层依赖了抽象。这是依赖倒置的应用。

最后肯定是符合开闭原则的。

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

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

相关文章

使用 Vue 创建一个简单的 Loading 动画

使用 Vue 创建一个简单的 Loading 动画 1. 开始之前 确保 正确安装了 Vue 3知道如何启动一个新的 Vue 项目&#xff08;或在项目中使用Vue&#xff09;了解 Vue 3 的 Composition API&#xff08;本文将使用&#xff09; 2. 设计组件 该组件应该包含三个部分 控制逻辑旋转…

Centos7:Flask-Apache部署

系列文章目录 RHCE第0章&#xff1a;RHCE开始前的准备 RHCE第1章&#xff1a;Web服务器&#xff08;上&#xff09; RHCE第1章&#xff1a;Web服务器&#xff08;下&#xff09; RHCE第2章&#xff1a;DNS服务 RHCE第3章&#xff1a;DHCP服务器 RHCE第4章&#xff1a;Firewall…

Tensorflow无人车使用移动端的SSD(单发多框检测)来识别物体及Graph的认识

环境是树莓派3B&#xff0c;当然这里安装tensorflow并不是一定要在树莓派环境&#xff0c;只需要是ARM架构就行&#xff0c;也就是目前市场上绝大部分的嵌入式系统都是用这套精简指令集。 在电脑端的检测&#xff0c;有兴趣的可以查阅SSD(Single Shot MultiBox Detector)系列&a…

SpringCloudAlibaba微服务实战系列(四)Sentinel熔断降级、异常fallback、block细致处理

SpringCloudAlibaba Sentinel降级和熔断 接着上篇文章的内容&#xff0c;在Sentinel中如何进行降级和熔断呢&#xff1f; 熔断降级规则 降级规则 在Sentinel中降级主要有三个策略&#xff1a;RT、异常比例、异常数&#xff0c;也是针对某个资源的设置。而在1.8.0版本后RT改为…

python post请求编码问题

在日常的python使用中&#xff0c;必不可少的就是爬虫相关的知识。 爬虫也常会遇到一个问题&#xff0c;即就是编码的问题。 如下&#xff1a; 通过抓包可以看到额&#xff0c;实际python提交的参数为&#xff0c;如下格式&#xff1a; 那这种的签名必不可能通过&#xff0…

FPGA实现串口回环

文章目录 前言一、串行通信1、分类1、同步串行通信2、异步串行通信 2、UART串口通信1、UART通信原理2、串口通信时序图 二、系统设计1、系统框图2.RTL视图 三、源码1、串口发送模块2、接收模块3、串口回环模块4、顶层模块 四、测试效果五、总结六、参考资料 前言 环境&#xff…

【框架篇】Spring Boot核心介绍及项目创建(详细教程)

Spring Boot介绍及项目创建 一&#xff0c;Spring Boot 核心介绍 Spring Boot 是基于 Spring 开发的一种轻量级的全新框架&#xff0c;不仅继承了 Spring 框架原有的优秀特性&#xff0c;而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。通过Spring Boot&…

NO.453 最小操作次数使数组元素相等

题目 给你一个长度为 n 的整数数组&#xff0c;每次操作将会使 n - 1 个元素增加 1 。返回让数组所有元素相等的最小操作次数。 思路 本题要求&#xff0c;获取最小操作次数&#xff0c;即在满足所有元素均相等的情况下&#xff0c;操作次数最少。 由于本题无法确定最终元素…

火车头采集器伪原创插件【php源码】

火车头采集是一款基于Python语言开发的网络爬虫工具&#xff0c;用于快速高效地从互联网上采集数据并存储到本地或远程数据库。它简单易用且功能强大&#xff0c;在各行各业广泛应用。 火车头采集器AI伪原创PHP源码&#xff1a; <?php header("Content-type: text/h…

(四)springboot实战——springboot的事件与监听器原理

前言 本节内容是关于springboot的一些核心原理的总结&#xff0c;包括springboot的事件原理、生命周期流程、事件触发流程等核心内容的介绍&#xff0c;从而帮助我们更好的理解与使用springboot&#xff0c;这里只做概念性的内容总结&#xff0c;实战的部分请关注作者后续博客…

数学建模-分类模型 Fisher线性判别分析

论文中1. 判别分析系数 2. 分类结果 多分类问题 勾选内容和上面一样

06微分方程模型练习

用Matlab求解微分方程 y ′ − 2 y 2 x 2 2 x , y ( 0 ) 1 y-2y2x^{2}2x,y\left( 0\right) 1 y′−2y2x22x,y(0)1 y1 dsolve(Dy-2*y2*x.^22*x) y2 dsolve(Dy-2*y2*x.^22*x,y(0)1,x)用Matlab分别求解微分方程 y ′ − 2 y 2 x 2 2 x , y ( 0 ) 1 y-2y2x^{2}2x,y\left…

C语言动态内存管理(二)经典笔试题

第二篇内容为大家详细剖析关于动态内存管理的几个经典笔试题 目录 四、笔试题1.请问运行Test函数会有什么样的结果&#xff1f;结果&#xff1a;结果运行出错的原因&#xff1a;本题目注意点&#xff1a;改正该题目的错误&#xff1a;正确修改1&#xff1a;&#xff08;利用传值…

Electron入门学习_使用预加载脚本

学习网址&#xff1a; https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-preload 1.什么是预加载脚本 Electorn 的主进程是一个拥有完全操作系统访问权限的Node.js ,除了Electron 模组&#xff0c;之外&#xff0c;您也可以访问Node.js 内置模块和所有通过npm安装…

质效两全:媒体服务的创新“顶设”

做媒体服务&#xff0c;一定要有刻入骨髓的抽象思维。 视频化浪潮汹涌、生成式人工智能AIGC极速迭代、体验需求和应用场景愈发多样......面对“视频生产力”的变革&#xff0c;我们能否透过纷繁复杂的表象&#xff0c;洞察音视频行业的“真正需求”&#xff1f; 是否存在一套…

【mysql数据库】MySQL7在Centos7的环境安装

说明&#xff1a; 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c;⼀旦安装&#xff0c;普通用户就能使用。初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使⽤root进⾏&#xff0c;尽快适应mysql语句&#xff0c;后⾯学了用户管理&#xff0c;在考虑新…

matlab dot()函数求矩阵内积,三维 ,多维 详解

matlab dot()函数求矩阵内积&#xff0c;三维 &#xff0c;多维 详解 Cdot(A,b,X)&#xff0c;这个参数X 只能取1,或者2。1 表示按列&#xff0c;2表示按行&#xff0c;如果没有参数。默认按列。 1&#xff09;按列优先计算 Cdot(A,B)dot(A,B,1)[a1*b1a4*b4 ,a2*b2a5*b5 ,a…

html,css初学

安装VSCODE ,插件&#xff1a;live server &#xff0c;html support html 然后为了更好地理解&#xff0c;请逐步输入&#xff0c;并及时查看效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>D…

A--自动收小麦机--2023河南萌新联赛第(二)场:河南工业大学

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 示例1 输入 4 1 2 1 1 4 5 2 2 2 3 4 输出 10 说明 在第4格放出水流后&#xff0c;水流会流向第3格&#xff0c;由于第3格高度比第4格低&#xff0c;所以水流继续向左流向第2格&#xff0…

爬虫的编解码方式

get请求的quote方法 我们在对爬取一个网页的时候&#xff0c;我们复制了这个网页的地址&#xff0c;但我们发现在将他粘贴下来以后不会是汉字&#xff0c;而是一串字符&#xff0c;这时候&#xff0c;我们需要去对字符进行编码&#xff0c;以便于我们能够继续去爬取网页。 例…