6.1 什么是面向过程编程
- 面向过程编程也是一种编程范式或编程风格。它以过程(可以理解为方法、函数、操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相分离为最主要的特点。面向过程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能
6.2 面向对象比面向过程的优势
- oop 更加能够应对大规模复杂程序开发
- oop 风格的代码更易复用、易扩展、易维护
- oop 语言更加人性化、更加高级、更加智能(汇编语言,高级语言)
- 扩展
- 问题:面向对象比面向过程,更加容易应对大规模复杂程序的开发。但像 Unix、Linux 这些复杂的系统,也都是基于 C 语言这种面向过程的编程语言开发的,你怎么看待这个现象?
- 解答:使用任何一个编程语言编写的程序,最终执行上都要落实到 CPU 一条一条指令的执行(无论通过虚拟机解释执行,还是直接编译为机器码),CPU 看不到是使用何种语言编写的程序。对于所有编程语言最终目的是两种:
提高硬件的运行效率和提高程序员的开发效率
。然而这两种很难兼得。
C 语言在效率方面几乎做到了极致,它更适合挖掘硬件的价值,如:C 语言用数组 char a[8],经过编译以后变成了(基地址+偏移量)的方式。对于 CPU 来说,没有运算比加法更快,它的执行效率的算法复杂度是 O(1) 的。从执行效率这个方面看,开发操作系统和贴近硬件的底层程序,C 语言是极好的选择。
C 语言带来的问题是内存越界、野指针、内存泄露等。它只关心程序飞的高不高,不关心程序猿飞的累不累
。为了解脱程序员,提高开发效率,设计了 OOP 等更“智能”的编程语言,但是开发容易毕竟来源于对底层的一层一层又一层的包装。完成一个特定操作有了更多的中间环节, 占用了更大的内存空间, 占用了更多的 CPU 运算。从这个角度看,OOP 这种高级语言的流行是因为硬件越来越便宜了。
6.4 有哪些看似面向对象实际面向过程风格的代码
- 对面向对象编程有误解,总以为把所有代码都塞到类里,自然就是在进行面向对象编程了
- 三个例子来说明
-
- 滥用 getter、setter 方法,违反了面向对象封装的特性
- 正确思路:在设计实现类的时候,除非真的需要,否则
尽量不要给属性定义 setter 方法
。除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器,那也要防范集合内部数据被修改的风险。
- Demo 购物车
public class ShoppingCart {
private int itemsCount;// 商品数量
private double totalPrice;// 商品总价
private List<ShoppingCartItem> items = new ArrayList<>();// 商品列表
public int getItemsCount() {return this.itemsCount;}
// 不合理 1:itemsCount 是私有属性,但是设置了 public 的 setter 方法,外部可以通过 setter 方法随意修改值
public void setItemsCount(int itemsCount) {this.itemsCount = itemsCount;}
public double getTotalPrice() {return this.totalPrice;}
public void setTotalPrice(double totalPrice) {this.totalPrice = totalPrice;}
// 不合理 2:虽然属性可以设置 getter 方法,但是此处返回的是 list 集合容器,外部调用还是可以修改里面的数据
// 比如:ShoppingCart cart = new ShoppCart();cart.getItems.clear();// 清空了购物车
// 虽然说需要清空购物车的需求,但是不应该把清空购物车的业务逻辑暴露给上层代码,正确做法应该在类中定义 clear()方法,将清空逻辑封装在方法中
// 如果想要查看购物车内容,Java 提供的 Collections.unmodifiableList()方法,让 getter 方法返回一个不可被修改的 UnmodifiableList 集合容器
public List<ShoppingCartItem> getItems() {return this.items;}
public void addItem(ShoppingCartItem item) {items.add(item);
itemsCount++;
totalPrice += item.getPrice();}
// ... 省略其他方法...
}
-
静态成员变量
(它属于类上的数据,被所有的实例化对象所共有,所以也是全局变量)
-
常量
(比如 MySQL 配置参数等)
-
单例类对象
(因为单例类的对象全局代码只有一份,所以相当于全局变量)
- 全局方法:
静态方法
- 正确思路:对于全局变量和全局方法,我们尽量能做到
职责单一,定义一些细化的小类
,比如 redis、mysql 相关的,就单独定义,而不是定义一个大而全的。除此之外,如果能将这些类中的属性和方法,划分归并到其他业务类中(就是要拆分出去),那是最好不过的了,能极大地提高类的内聚性和代码的可复用性。
-
- 定义 数据和方法 分离的类
- 数据定义在一个类中,方法定义在另一个类中,典型的就是目前 web 开发使用的 MVC 模式,也是典型的面向过程式开发
- 正确思路:后续添加进来
6.5 在面向对象编程中,为什么容易写出面向过程的代码
- 在生活中,你去完成一个任务,你一般都会思考,应该先做什么、后做什么,如何一步一步地顺序执行一系列操作,最后完成整个任务。
面向过程编程风格恰恰符合人的这种流程化思维方式
。
- 而面向对象编程风格正好相反。它是一种
自底向上的思考方式
。它不是先去按照执行流程来分解任务,而是将任务翻译成一个一个的小的模块(也就是类),设计类之间的交互,最后按照流程将类组装起来,完成整个任务。