设计模式之美——看似面向对象,实则面向过程

news2024/11/18 9:46:45

常见的编程范式或者说编程风格有三种,面向过程编程、面向对象编程、函数式编程,而面向对象编程又是这其中最主流的编程范式。现如今,大部分编程语言都是面向对象编程语言,大部分软件都是基于面向对象编程这种编程范式来开发的。

总以为把所有代码都塞到类里,自然就是在进行面向对象编程了。实际上,这样的认识是不正确的。有时候,从表面上看似是面向对象编程风格的代码,从本质上看却是面向过程编程风格的。

1. 滥用 getter、setter 方法

定义完类的属性之后,就顺手把这些属性的 getter、setter 方法都定义上。它违反了面向对象编程的封装特性。

面向对象封装的定义是:通过访问权限控制,隐藏内部数据,外部仅能通过类提供的有限的接口访问、修改内部数据。所以,暴露不应该暴露的 setter 方法,明显违反了面向对象的封装特性。数据没有访问权限控制,任何代码都可以随意修改它,代码就退化成了面向过程编程风格的了。

总结:在设计实现类的时候,除非真的需要,否则,尽量不要给属性定义 setter 方法。除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器(比如例子中的 List 容器),也要防范集合内部数据被修改的危险。

我们可以通过 Java 提供的 Collections.unmodifiableList() 方法,让 getter方法返回一个不可被修改的 UnmodifiableList 集合容器,而这个容器类重写了 List 容器中跟修改数据相关的方法,比如add()、clear() 等方法。一旦我们调用这些修改数据的方法,代码就会抛出 UnsupportedOperationException异常,这样就避免了容器中的数据被修改。

2. 滥用全局变量和全局方法

在面向对象编程中,常见的全局变量有单例类对象、静态成员变量、常量等,常见的全局方法有静态方法。单例类对象在全局代码中只有一份,所以,它相当于一个全局变量。静态成员变量归属于类上的数据,被所有的实例化对象所共享,也相当于一定程度上的全局变量。而常量是一种非常常见的全局变量,比如一些代码中的配置参数,一般都设置为常量,放到一个 Constants 类中。静态方法一般用来操作静态变量或者外部数据。你可以联想一下我们常用的各种 Utils 类,里面的方法一般都会定义成静态方法,可以在不用创建对象的情况下,直接拿来使用。静态方法将方法与数据分离,破坏了封装特性,是典型的面向过程风格。

我们把程序中所有用到的常量,都集中地放到 Constants 类中。定义一个如此大而全的 Constants 类,并不是一种很好的设计思路。原因主要有以下几点。在这里插入图片描述
我们再来讨论一下 Utils 类。Utils 类存在的意义是什么?

实际上,Utils 类的出现是基于这样一个问题背景:如果我们有两个类 A 和 B,它们要用到一块相同的功能逻辑,为了避免代码重复,我们不应该在两个类中,将这个相同的功能逻辑,重复地实现两遍。这个时候我们该怎么办呢?

面向对象特性强调继承可以实现代码复用。利用继承特性,我们把相同的属性和方法,抽取出来,定义到父类中。子类复用父类中的属性和方法,达到代码复用的目的。但是,有的时候,从业务含义上,A 类和 B 类并不一定具有继承关系,比如 Crawler 类和 PageAnalyzer 类,它们都用到了 URL 拼接和分割的功能,但并不具有继承关系(既不是父子关系,也不是兄弟关系)。仅仅为了代码复用,生硬地抽象出一个父类出来,会影响到代码的可读性。

既然继承不能解决这个问题,我们可以定义一个新的类,实现 URL 拼接和分割的方法。而拼接和分割两个方法,不需要共享任何数据,所以新的类不需要定义任何属性,这个时候,我们就可以把它定义为只包含静态方法的 Utils 类了。

实际上,只包含静态方法不包含任何属性的 Utils 类,是彻彻底底的面向过程的编程风格。 但这并不是说,我们就要杜绝使用 Utils 类了。实际上,从刚刚讲的 Utils 类存在的目的来看,它在软件开发中还是挺有用的,能解决代码复用问题。所以,这里并不是说完全不能用 Utils 类,而是说,要尽量避免滥用,不要不加思考地随意去定义 Utils 类。

在定义 Utils 类之前,你要问一下自己,你真的需要单独定义这样一个 Utils 类吗?是否可以把 Utils 类中的某些方法定义到其他类中呢?如果在回答完这些问题之后,你还是觉得确实有必要去定义这样一个 Utils 类,那就大胆地去定义它吧。因为即便在面向对象编程中,我们也并不是完全排斥面向过程风格的代码。只要它能为我们写出好的代码贡献力量,我们就可以适度地去使用。

除此之外,类比 Constants 类的设计,我们设计 Utils 类的时候,最好也能细化一下,针对不同的功能,设计不同的 Utils 类,比如 FileUtils、IOUtils、StringUtils、UrlUtils 等,不要设计一个过于大而全的 Utils 类。

总结:对于这两种类的设计,我们尽量能做到职责单一,定义一些细化的小类,比如 RedisConstants、FileUtils,而不是定义一个大而全的 Constants 类、Utils 类。除此之外,如果能将这些类中的属性和方法,划分归并到其他业务类中,那是最好不过的了,能极大地提高类的内聚性和代码的可复用性。

3. 定义数据和方法分离的类

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

《Redis设计与实现》笔记

第二章:简单动态字符串 1.Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串( simple dynamic string,SDS)的抽象类型,并将SDS用作 Redis的默认字符串表示。 Redis里面,C字符串只会作为字符串…

操作系统第三章习题及答案(汤子瀛第四版)

第三章 1.高级调度与低级调度的主要任务是什么?为什么要引入中级调度? 答:高级调度的主要任务是根据某种算法,把外存上处于后备队列中的那些作业调入内存。低级调度是保存处理机的现场信息,按某种算法先取…

【UE4】打包失败 Failed to build UATTempProj.proj

我抄我自己之 https://zhuanlan.zhihu.com/p/586117443 前两天编 UE4.27 源码内存和CPU直接全是100%爆满,甚至还报错:c1060, the compiler is out of heap,设置虚拟内存也不不好使,发现16GB内存不配编 UE,火速换了64G之…

Kotlin或将超越Go?

知名软件行业分析公司 RedMonk 发布了 2022 年 6 月(第三季度)编程语言排行榜。 RedMonk 编程语言排行榜通过追踪编程语言在 GitHub 和 Stack Overflow 上的代码使用情况与讨论数量,统计分析后进行排序,其旨在深入了解潜在的语言…

你真的知道Spring Security安全框架吗?

1、什么是安全管理框架? 解决系统安全问题的框架。如果没有安全框架,我们需要手动处理每个资源的访问控制,非常麻烦。使 用安全框架,我们可以通过配置的方式实现对资源的访问限制。 安全框架,简单说是对访问权限进行控…

Android Material Design之MaterialButton(一)

按规矩先上效果图 资源引入 implementation com.google.android.material:material:1.4.0关键属性 属性描述app:backgroundTint背景着色app:backgroundTintMode着色模式app:strokeColor描边颜色app:strokeWidth描边宽度app:cornerRadius圆角大小app:rippleColor按压水波纹颜色…

【Java八股文总结】之计算机网络

文章目录计算机网络一、基础1、网络体系结构2、HTTP协议、TCP协议、UDP协议比较3、网络协议4、WebSocket和Socket的区别?5、常见的端口及其对应的服务?6、从浏览器输入URL到页面展示发生了什么?(★★★★★)1、DNS域名…

MySQL纯代码复习(下)

前言 本文章的语言描述会比上篇多一些 数据库的创建修改与删除 标识符命名规则 数据库名、表名不得超过30个字符,变量限制为29个必须只能包含A-Z,a-z,0-9,_等63个字符数据库名、表名、字段名等对象名中间不要包含空格同一个My…

Twitter引流如何开发客户

要想在twitter平台上取得效果,你需要先了解twitter的算法规则,去迎合平台,推特群推王给出以下5条建议,让你发布的帖子更容易被推荐。 这里Twitter群推王可以给大家讲一下,关于推特平台的算法排名: Twitter…

nginx降权+安装php

nginx降权 使用普通用户启动Nginx 为什么要让nginx服务使用普通用户 默认情况下,nginx的master进程使用的是root用户,worker进程使用的是nginx指定的普通用户,使用root用户跑nginx的master进程有两个大问题: (1&#x…

课程设计-天天象棋作弊软件判别

目录 1.作弊开挂可能迹象 2.设计作弊检测系统灵感 3.设计作弊检测系统思路 3.1反作弊系统应对策略框架 4.感想体悟 1.作弊开挂可能迹象 1.非实名认证; 2.头像:美女; 3.名称: (1)一串英文字母; (2)非正常中文名…

【深度分解网络:显著性分析:IVIF】

Infrared and Visible Image Fusion Based on Deep Decomposition Network and Saliency Analysis (基于深度分解网络和显著性分析的红外与可见光图像融合) 传统的图像融合侧重于选择一种有效的分解方法从源图像中提取代表性特征,并试图找到…

2022年海运行业研究报告

第一章 行业概况 海洋运输又称“国际海洋运输”,提供海上客运或者货运服务的行业。是国际物流中最主要的运输方式。它是指使用船舶通过海上航道在不同国家和地区的港口之间运送货物的一种方式,在国际货物运输中使用最广泛。国际贸易总运量中的2/3以上&a…

功率放大器和电压放大器的区别是什么意思

很多人经常会在后台咨询小编功率放大器和电压放大器的区别有哪些?今天就来为大家科普一下功率放大器和电压放大器的知识内容,希望大家下次能够区分,并且可以正常地选择和使用功率放大器。 图:功率放大电路与电压放大电路对比 功率…

Opengl ES之YUV数据渲染

YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: 《音视频基础知识-YUV图像》 YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用于显示,需要转换为RGB格式才能显…

简单的股票行情演示(一) - 实时标的数据

一、概述二、效果展示三、实现代码 1、行情数据中心2、数据拉取模块3、基础服务模块4、UI展示四、相关文章原文链接:简单的股票行情演示(一) - 实时标的数据 一、概述 很长一段时间都有一个想法,使用QCP去做一个行情展示小事例&…

TiDB Cloud

TiDB Cloud 为什么选择TiDB 分布式数据库-多租户混合工作负载-在同一个数据库中 事务型:基于行的数据分析型:基于列的数据 弹性比例: 缩小-减少节点横向扩展-添加节点 基于“RAFT”的高可用性 每个数据段的3个可用区进行复制 多租户 什么…

DataGridXL 2.0 for JavaScript Crack

你的web开发好了,客户说我习惯用excel这样的表格,你们是否能开发像电子表格一样的功能? Web 应用程序的类似 Excel 的体验---DataGridXL 2.0 for JavaScript Crack 你已经构建了一个 Web 应用程序,但你的用户坚持使用 Excel。 类似…

一、ROS2简介

ros2相关简介 ROS2的前身是ROS,ROS即机器人操作系统(Robot Operating System)。但是ROS本身并不是一个操作系统,而是一个软件库和工具集。 Ros的出现解决了机器人各个组件的通信问题,后来越来越多的机器人算法也集成到…

漫谈信息模型(1)

简单地表达复杂的世界,这是各类思想家近千年来的追求。如何将人类在世界上观察到的结果进行概念化表达?又如何描述人造的复杂工具?这种探索成为人类文明进步的一个重要的驱动力。计算机的出现,推动了人类对现实事物进行概念化的描…