面向对象的思考
1. 封装
- 1.1 概念 与 意义
到底是为了安全还是为了对用户友好?
- 1.2 封装是指功能的打包和接口的暴漏
把实现的细节隐藏在接口之后并不代表必须有强制的私有性
先保护好自己再让别人利用,这两者并不冲突 ,让能看到的看到,不能看到的就隐藏
private protected,package,public
封装在增加易用性的同时会丧失灵活性.易用性和灵活性需要分情况考虑.
- 1.3 封装的层次和粒度
平级封装 mvc , 层次封装(应该严格划分每层的责任和义务,不能违背这个原则)
私有方法存在的意义, 类,jar,模块,项目,系统
- 1.4 封装后的交互: 通信与接口
方法调用,数据库,文件,缓存,协议接口
封装和抽象的联系:
抽象是一种思想,而封装只是实现抽象的一种手段,抽象 > 封装。 封装不等于抽象.
封装要考虑数据封装和行为封装.结构体 提供了数据封装.
结构体和类 为一组相关数据提供了更精确的上下文语意.
如果应程序仅由原生类型组成,则会大大增加维护和重构的难度。相比之下,结构体可以将相数据聚合为一个内聚的单元,更易于管理。例如,RGB是表示颜色的一种标准,由红、绿蓝三种基本颜色分量构成。与单独定义三个整数相比,将它们组织在一个RGB结构体中能很好地体现数据的上下文语义。
2. 继承(extends)
- 2.1 存在的意义: 复用
(为了做富二代,为了发展),extends 更多的意思是为了扩展
- 2.2 行为规范的一致性
父类的private成员不会被子类继承,子类不能访问。但是子类对象的确包含父类的私有成员。
父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。
父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。
父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。
在 C 语言里面,去实现单继承也非常简单,只要把基类放到继承类的第一个数据成员的位置就行了。
C语言来实现继承 实际是使用的组合来实现继承,注意这个C 结构体的layout 按顺序排列有很大关系
Rust 超越面向对象,第三部分:继承
https://www.thecodedmessage.com/posts/oop-3-inheritance/https://www.thecodedmessage.com/posts/oop-3-inheritance/这篇文章讨论的传统继承的局限性
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. 关于学习
基础知识如主食,开发技术和运维技能是零食
主食得天天吃,零食需要的时候饿了就吃
软件的本质是什么?从不同的角度来看,会有不同的答案。有人认为是程
序加文档,有人认为是人机交互,有人认为是增删改查,有人认为是抽象模型,
而我认为是算法。
计算机科学的基石是图灵机抽象:一个输入集合,一个输出集合,一个内
部状态集合,一个计算规则集合。这个抽象十分强大,我们甚至可以认为一头
奶牛也是一个图灵机:吃的是草,挤的是奶。
一个表达式也是一个图灵机,其中的操作数是输入,求值的结果是输出。
一个函数也是图灵机,参数是输出,返回值是输出。编程或设计软件就是在通
用图灵机的基础上,设计一个具体的图灵机。我们设计软件接受怎样的输入,
设计软件内部的状态,设计表示计算规则的代码,设计软件的输出。
近年来逐渐流行的函数式编程,就是建立在这个抽象的基础上。而且函数
式编程的思想由来已久,可以追溯到最古老的高级语言之一:Lisp。这种思想
非常简单:给定一个输入集合,经过函数的处理,给出一个输出集合。由此也
导出了Map-Reduce等流行的架构设计模式:一个计算集群仍然是一个图灵机。
纯粹的函数有一点不足,它没有内部状态。可以说,它是简化了的图灵机。
但在有些时候,我们确实需要内部状态。根据内部状态的不同,对于同样的输
入,可能给出不一样的输出。于是便有了闭包的概念,它是一个函数加上相关
的上下文环境状态。这样,我们可以毫无困难地构建任何具体的图灵机(好吧,
正确实现一个算法还是比较困难的)。
闭包可以看成是拥有内部状态的函数,这就相当于一个简单的对象,它只
有一个方法。反过来,对象可以看成是几个闭包,它们共享了内部状态。所以
有人说:闭包是懒人的对象,对象是懒人的闭包。因此,函数式编程和面向对
象思想,在底层基础上是一致的。
面向对象思想的历史和函数式一样久远。实际上,它们都是我们在设计算
法时的一种抽象。只有利用抽象概念,才能实现人与人之间的沟通。“你想吃
苹果吗?”这里的“苹果”就是一个抽象概念,它隐藏了苹果实现的许多细节。
人的大脑喜欢工作在一组抽象概念上。名词是结构或存在的抽象,动词是行为
或过程的抽象。
我们在设计算法时,既需要函数抽象,也需要对象抽象。今天,面向对象
和函数式编程的思想在各种编程语言中融合,可以说是殊途同归。
抽象是强大的工具,但用得不好,也会产生不良的后果。最重要的问题,
就是创建太多不必要的抽象。毕竞,抽象只是我们脑中的概念,我们可以创造
出任何概念。比如上帝和各种鬼神,直到科学家说,在科学的系统里不需要假
设存在一个上帝。面向对象在这方面遇到的问题比较多。举例来说,一个Java
的Helloworld程序,就要涉及好几个概念,直接导致程序的代码比较长。而
在函数式编程中,这通常只是一次函数调用。又比如,在一个使用Struts、Spring、
Hibernate构建的JavaWeb应用程序中,处理一个Get请求的调用栈,可能是
长长的一串。数据在不同的概念抽象之间反复倒手,白白浪费了计算资源。
任何两种观点都是互补的。面向对象思想在过去的软件开发中取得了辉煌
的成绩。函数式编程让我们能从另一个角度审视面向对象,更进一步体会面向
对象抽象的强大,也发现面向对象中一些误用的地方。如无必要,勿增实体。
也许我们不需要假设以太的存在,就能解释光在真空中的传播。
Rust与面向对象
Rust与面向对象(一) - Rust语言中文社区
Rust与面向对象(二) - Rust语言中文社区
Rust与面向对象(三) - Rust语言中文社区
Rust与面向对象(四) - Rust语言中文社区
C语言实现面向对象三大特性 : 封装、继承、多态
Object-oriented design patterns on OSHub面向对象的设计模式
再谈软件设计中的抽象思维(上),从封装变化开始
https://www.51cto.com/article/815294.html 探秘C++虚函数表:从内存深处解析多态的奥秘
电子书存档页面 | SaltTigerhttps://salttiger.com/archives/https://salttiger.com/archives/
https://lwn.net/Articles/444910/https://lwn.net/Articles/444910/内核中的面向对象设计模式,第 1 部分
