面向对象的思考

本文探讨了面向对象编程中的核心概念,包括封装、继承、多态及抽象等,并深入分析了它们的意义与应用,强调了如何通过这些概念提高代码的复用性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 封装

  •         1.1 概念 与 意义  

          到底是为了安全 还是为了对用户友好

  •         1.2 封装是指 功能的打包和接口的暴漏

 把实现的细节隐藏在接口之后并不代表必须有强制的私有性
            先保护好自己再让别人利用,这两者并不冲突 ,让能看到的看到,不能看到的就隐藏

private protected,package,public

  •         1.3 封装的层次和粒度

 平级封装 mvc , 层次封装(应该严格划分每层的责任和义务,不能违背这个原则)
            私有方法存在的意义, 类,jar,模块,项目,系统  

  •         1.4 封装后的交互: 通信与接口 

            方法调用,数据库,文件,缓存,协议接口

封装和抽象的联系:

抽象是一种思想,而封装只是实现抽象的一种手段,抽象 > 封装。 封装不等于抽象

2. 继承(extends)

  •     2.1 存在的意义: 复用

(为了做富二代,为了发展),extends 更多的意思是为了扩展

  •     2.2 行为规范的一致性

    父类的private成员不会被子类继承,子类不能访问。但是子类对象的确包含父类的私有成员。
    父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。
     父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。
     父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。

在 C 语言里面,去实现单继承也非常简单,只要把基类放到继承类的第一个数据成员的位置就行了。

C语言来实现继承 实际是 使用的组合来实现继承,注意这个C 结构体的layout 按顺序排列有很大关系

3.  多态:世界是缤纷多彩的,实现多态必须得有继承吗?
 


  运行时的多种形态,必须是运行时

  比如在运行是产生随机数数,或反射的方法  


  目的:遵守一致的约定 用统一的代码结构 比如 循环完成不同的行为
  比如加法:string 乘法,数字乘法,矩阵乘法
  导出数据: 导出的功能 到excel 到word
  直接好处可以减少if 判断(使用Map 隐射后获取 多态类),大大提高了代码的简洁性和可维护性 。


 {
  "excel": excel  
  ,"word":word
  }

  //如果没有父类或接口 能不能多态 见 HelloTest ,js的例子
  js的例子
  o1={ msg:"hello1    ", hello:function (name){console.log(this.msg +name); }   }
  o2={ msg:"hello2    ", hello:function (name){console.log(this.msg +name); }   }
  o=[o1,o2]
  
  for(i in o){ o[i].hello("xue"); }
  或者
  for(i in o){ o[i]["hello"]("xue"); }

4. 抽象

抽象的终级目的是为了减少复杂度。复杂度来源于细节,细节是魔鬼。所以说抽象也是隐藏魔鬼的艺术即隐藏细节的艺术。

对抗复杂性要有效的忽略细节。

复杂度来源于细节组合中的规则,规则(if) 越多,越复杂.

细节主要有2种:

  • 1. 重复的繁杂的细节
  • 2. 重要的关键的细节


 

抽象的力量:

抽象使生活更美好,稳定需求,隔绝变化,

对使用者友好

分割复杂度

细节的东西你无需知道,就可以使用,比如你无需知道计算机的基本原理

封装是打包细节,不代表必须隐藏细节,抽象通过封装来实现。

那么你的封装是否有负担?

抽象是分层次的

比如  在世界层次查看地图,

国家层次

在 省级层次

在不同的层次中看到的细节是不同的,低层次的细节对高层次是隐藏的,所以说抽象是隐藏细节的艺术。

在软件开发中通俗的说

抽象就是还实现的方法、类、模块或功能。

抽象就是你暂时不知道怎么实现  或你不想知道怎么实现的 方法、类、模块或功能。

这些还未实现的,你不知道怎么实现的,你不想知道怎么实现的  就是抽象,因为你不需要关心细节,你只关心功能是否满足需求。

一定善于利用抽象来安排软件开发功能。

抽象与具体: 地图和实景

每研究一个新的东西或者做一个新的项目时,我总喜欢看它的架构图。架构图的意义如同地图,拿着架构图做项目就像对着地图旅游,进可知细节,退可揽全局。

以鸟瞰视角瞧瞧 各种设计方案。既然是鸟瞰图,就不要在乎细节。多年搬砖、掉坑、爬坑的经验告诉我,无论是游历一个新地方还是学一个新东西,首要的是抓大放小。这倒不是说细节不重要,只是带着全局的思维研究一个东西时,不容易迷路也不容易掉入细节的深井。天平的左边是全局,右边是细节,在全局和细节之间,我们最终会找到一个平衡点,但现在让我们从把天平偏向左边开始。

抽象遵守的原则:

开闭原则:对扩展开放对修改关闭  顶层结构必须稳定,下层可以随意发挥 

怎么实现开闭原则:
4.1.  稳定接口,扩展实现
4.2.  把不稳定部分抽取出去,使用回调来实现
4.3.  模板方法
4.4.  对话框回调 管道过滤器, listener 监听,方法的复合参数context 使接口的入口的稳定性
   或者把修改的地方集中在一起(常量类  最小化修改的地方)
4.5. 生活中的例子:压面机怎么压出不同形状的面条


要有面向对象的思维而不是面向对象的语法: js,python,c

接口是顶层规范,抽象类为顶层实现

抽象类则不一样,抽象类作为系统中多个子类的共同父类,它体现的是一种模板式设计。抽象类作为多个子类的父类,它可以被当作系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能,但这个产品依然不能当作最终产品,必须要有更进一步的完善。这种完善可能有几种不同方式。

如何抽象:

写程序的抽象和写小说基本上是一样的。

关键是要 界定范围后取好名字。

如同 【西游记】是小说的总名称,这是顶级抽象

然后是界定各章范围,并取好个章节名称,取名称的过程就是思考归纳总结和抽象的过程。

抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些应有的灵活性。

将一个软件工程项目划分为多个子模块分别进行实现是一种被广泛应用的编程技巧,它有助于促进复用代码,并显著提升代码的可读性和可维护性。

一个常见的误区是,只有需要复用的时候才去拆分函数,这种看法显然过于片面了。你可以思考一下,自己是如何抽象一个函数的,你只会在代码需要复用的时候才抽出一个函数吗?显然不是。因为函数不仅有代码复用的功能,还具有一定的描述性质以及代码封闭性。这种特性使得我们看到一个函数的时候,不必关注代码细节,就能大概知道这部分代码是干啥的。

我们还可以再用函数将一部分函数组合起来,形成更高层级的抽象。

汽车是按组件组装的,但只有 四个轮子是复用的。所以复用不是拆分函数 全部出发点。

抽象和实现的关系:

抽象是装修前: 关注整体结构 忽略细节

实现是装修后: 更加关注细节


以下片段来自:

 这不是一个抽象,这只是一个间接层

https://fhur.me/posts/2024/thats-not-an-abstractionhttps://fhur.me/posts/2024/thats-not-an-abstractionhttps://fhur.me/posts/2024/thats-not-an-abstraction

抽象的好坏取决于它隐藏底层复杂性的能力。想想一个真正伟大的抽象,比如 TCP。 TCP 帮助我们假装我们有一个可靠的通信通道,即使它是建立在不可靠的协议 IP 之上的。它承担了纠错、重传和数据包排序的复杂性,因此我们不必这样做。它做得非常好,作为开发人员,我们很少需要深入了解它的内部工作原理。

抽象是性能的敌人。添加的层数越多,与底层机器的距离就越远。优化代码变成了一层又一层剥离的练习,直到您最终开始真正的工作。每一层都代表着精神和计算负担。理解正在发生的事情需要更长的时间,找到重要的代码需要更长的时间,机器执行实际业务逻辑也需要更长的时间。

抽象也是简单性的敌人。每个新的抽象都应该让事情变得更简单——这应该是目标,对吧?但现实情况是,每一层都添加了自己的规则、自己的接口以及自己的失败可能性。这些抽象不但没有简化,反而增加了复杂性,使系统更难理解、维护和扩展。

这并不是说抽象不好——远非如此。好的抽象是强大的。它们使我们能够构建复杂的系统,而不会迷失在复杂性中。但我们必须认识到抽象并不是免费的。它们在性能和复杂性方面都有实际成本。如果“抽象”没有隐藏复杂性而只是添加了一个间接层,那么它根本就不是抽象。

下次当您进行抽象时,问问自己:这真的简化了系统吗?或者它只是另一层间接?明智地使用抽象,并记住——如果你没有真正隐藏复杂性,那么你只是在增加它。

一下内容来自: 再谈软件设计中的抽象思维(上),从封装变化开始

如何抽象:

抓住变化、分析变化、明确差异点,找到新概念抹平差异,是我们进行抽象的一般思考路径。

我们可以通过一个简单的案例感受一下这个过程,我写了一个吃苹果的程序eat(Apple apple),有一天我苹果吃腻了,想吃香蕉,问题来了,原来的eat(Apple apple)并不能被重用。差异性体现在Apple和Banana的不同,针对这个变化,我们需要一个新的抽象去抹平差异,关于如何抽象,关键是要寻找共性。Apple和Banana向上抽象的共性是什么呢?这个简单,我们都知道是Fruit,这个Fruit就是我们通过抽象获得的“新知识”、“新概念”。

为了让原来的eat更通用,我们可以用eat(Fruit fruit)来代替eat(Apple apple)。如果有一天我又想吃肉了,那么Fruit的抽象层次也不够了,必须要eat(Food food)才行。如下图所示,最后我们不断演化的过程,就是抽象层次不断提升的过程。(关于抽象层次,推荐去看《程序员的底层思维》

抽象数据 从隔离和保护数据开始,抽象行为从角色职责开始。

因为Object的抽象层次太高了,万物皆对象,在抹平万物的差异的同时,也失去了可理解性,以及业务语义直观表达的能力。

5. 保持软件的活力

任何事物都是慢慢的变得复杂,然后安静的死掉。

为什么会这样?变得复杂的同时,会丢失保持持续变化的活力,对变化反应迟钝,最终落伍被丢弃。

6. 关于学习

基础知识如主食,开发技术和运维技能是零食

主食得天天吃,零食需要的时候饿了就吃

Rust与面向对象

Rust与面向对象(一) - Rust语言中文社区
Rust与面向对象(二) - Rust语言中文社区
Rust与面向对象(三) - Rust语言中文社区

Rust与面向对象(四) - Rust语言中文社区

C语言实现面向对象三大特性 : 封装、继承、多态

再谈软件设计中的抽象思维(上),从封装变化开始

https://www.51cto.com/article/815294.html 探秘C++虚函数表:从内存深处解析多态的奥秘

电子书存档页面 | SaltTigerhttps://salttiger.com/archives/https://salttiger.com/archives/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值