引言:
哪些代码设计看似是面向对象, 实际是面向过程的?
只运用面向对象语言是不够的, 更重要的是运用面向对象的思想. 否则写出来的代码还是面向过程编程风格的.
一. 哪些代码看似是面向对象, 实际是面向过程的
下面举3个最典型的例子来学习
1.1 滥用getter, setter方法
现如今, 公司开发中有很多不规范, 如:Lombok插件, 它违背了面向对象的封装特性, 相当于将面向对象编程风格退化成了面向过程风格.
//购物车类
public class ShoppingCart {
private int itemsCount;
private double totalPrice;
private List<ShoppingCartItem> items = new ArrayList<>();
public int getItemsCount() {
return this.itemsCount;
}
public void setItemsCount(int itemsCount) {
this.itemsCount = itemsCount;
}
public double getTotalPrice() {
return this.totalPrice;
}
public void setTotalPrice(double totalPrice) {
this.totalPrice = totalPrice;
}
public List<ShoppingCartItem> getItems() {
return this.items;
}
public void addItem(ShoppingCartItem item) {
items.add(item);
itemsCount++;
totalPrice += item.getPrice();
}
// ...省略其他方法...
}
首先, 2个属性: itemsCount和totalPrice. 虽然它们都定义了private, 但是提供了public的getter和setter方法, 这跟直接订单2个属性为public没什么区别.外部都可以随意通过setter方法修改属性的值.
其次, 再看一下items, 虽然没有提供setter方法, 但是提供了getter方法, 外部获取这个list集合后, 可以对集合中的数据进行修改, 如: cart.getItems().clear();, 直接就会清空购物车.
针对第二点, 有其他的解决办法吗? Collections.unmodifiableList(), 我们可以返回一个不可变的集合给外部, 当外部调用add, clear方法时, 就会抛异常.
即便这样, 调用者也可以通过getItems获取items后, 遍历修改每个对象中的属性.
那么有啥更好的解决方法不? 可不可以clone一个新的集合返回给调用者呢?
1.2 滥用全局变量和全局方法
滥用常量类的弊端:
- 常量多会变的不好维护(一般都是拆分出多个, 单一职责, 如: RedisConstants, MysqlConstants)
- 编译耗时(由于一个常量依赖的代码较多, 导致每次修改这个常量时, 都会导致依赖它的类文件重新编译, 浪费了不必要的时间)
- 代码不复用(有时候为了一个常量, 引入了一个巨大的常量类)
1.2.1 Utils类存在的意义
问题背景:
如果我们有2个类, 分别是:A和B, 他们用到了一块相同的逻辑, 为了避免重复, 我们应该怎么办?
我们一般会想到面向对象特性: 继承, 但是如果A和B不一定是继承关系, 也不是兄弟关系, 这时候你抽象出一个无关的父类, 肯定会影响代码的可读性.
所以这时候我们可以定义一个Utils类, 用来处理那一块公共的代码.
1.3 定义数据和方法分离的类
数据和方法逻辑分离, 这是典型的面向过程编程模式
传统的MVC三层架构, controller:负责暴露接口给前端, service:负责核心业务逻辑, repository:负责数据读写. 这其实就是典型的面向过程编程风格
这种开发模式叫作: 贫血模型的开发模式. 后面会慢慢讲解.
二. 在面向对象编程中, 为什么容易写出面向过程风格的代码?
因为面向过程编程, 是一种流程化的, 更符合人类的思维. 我们一般思考问题都是: 第一步做什么, 第二步做什么, 最后再做什么.
而面向对象编程, 它是一种自底向上的思考方式, 不按照流程去拆解, 而是按照流程将类组装起来, 完成任务.
三. 面向过程编程及面向过程编程语言真的就无用武之地了吗?
面向过程不一定坏, 面向对象不一定好. 只是面向对象更加能解决复杂的逻辑.
我们最终的目的, 就是要写出易维护, 易读, 易扩展的代码.
四. 课堂讨论
4.1 题1
问: 看似面向对象实际面向过程的编程风格的代码有很多, 除了今天讲的三个, 你还遇到过哪些?
答: 没有抽象和继承, 一个功能复制多份