为什么面向对象编程逐渐取代了面向过程编程了呢
那么就可能需要我们去了解什么是面向过程编程,不用去深入的学习,只是了解其概念
很多人很多时候,使用面向对象编程语言,却写出了面向过程的代码,没法发挥面向对象编程的优势,
那么为了搞懂面向对象和面向过程的区别,可以进行如下的对比
1.什么是面向过程编程和面向过程编程语言
2.面向对象相比于面向过程,优势何在
3.为什么说面向对象编程语言比面向过程高级
4.怎么区分面向对象和面向过程语言
5.在面向对象编程中,为什么容易写出面向过程的代码
6.面向过程的未来何在
那么我们一一讲解
1.什么是面向过程编程,或者面向过程编程语言
实际上,我们可以拿面向对象编程和面向对象编程语言来进行类比
面向对象编程是一种编程范式或者编程风格,以类或者对象为基本单元,并且将封装 继承 多态 四个特性,作为代码设计的基石
面向对象编程语言,则是在其基础上,支持了类和对象,并且提供了语法机制来实现
那么面向过程可以说是
面向过程编程也是一种编程范式或者编程风格,以过程作为组织代码的基本单元,以数据和方法相分离为特点,通过拼接一组顺序执行的代码来完成一项功能
面向过程编程语言是一种语言,不支持类或对象的概念,不支持面向对象的编程特性
常见的对比有
struct User {
char name[64]; int age; char gender[16]; }; struct User parse_to_user(char* text) { // 将text(“小王&28&男”)解析成结构体struct User } char* format_to_text(struct User user) { // 将结构体struct User格式化成文本(”小王\t28\t男”) } void sort_users_by_age(struct User users[]) { // 按照年龄从小到大排序users } void format_user_file(char* origin_file_path, char* new_file_path) { // open files… struct User users[1024]; // 假设最大1024个用户 int count = 0; while(1) { // read until the file is empty struct User user = parse_to_user(line); users[count++] = user; } sort_users_by_age(users); for (int i = 0; i < count; ++i) { char* formatted_user_text = format_to_text(users[i]); // write to new file… } // close files… } int main(char** args, int argv) { format_user_file(“/home/zheng/user.txt”, “/home/zheng/formatted_users.txt”); } |
public class User {
private String name; private int age; private String gender; public User(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } public static User praseFrom(String userInfoText) { // 将text(“小王&28&男”)解析成类User } public String formatToText() { // 将类User格式化成文本(”小王\t28\t男”) } } public class UserFileFormatter { public void format(String userFile, String formattedUserFile) { // Open files… List users = new ArrayList<>(); while (1) { // read until file is empty // read from file into userText… User user = User.parseFrom(userText); users.add(user); } // sort users by age… for (int i = 0; i < users.size(); ++i) { String formattedUserText = user.formatToText(); // write to new file… } // close files… } } public class MainApplication { public static void main(Sring[] args) { UserFileFormatter userFileFormatter = new UserFileFormatter(); userFileFormatter.format(“/home/zheng/users.txt”, “/home/zheng/formatted_users.txt”); } } |
可以看出C和Java写出的区别,C中方法和数据定义是分开,Java中则是整合到了一起
2.面向对象编程和面向过程编程相比有什么优势
(1)面向对象的特性OOP,更能面对大规模复杂程序的开发
对于简单的,处理起来只有一条主线的业务来说,面向过程这种编程方式更加合适,但是对于网状的,复杂的,就不应该使用面向过程编程风格了
面向对象是以类为思考对象的,如何去聚合一个类,类和类的交互,是需要长期的思考的
而且更加合适去模块化划分某些功能,虽然C中也可以按照功能的不同,将函数和数据结构来划分到不同的文件中,但是,付出的代价比面向对象编程要多付出一些
(2)OOP的代码更容易维护
对于更加复杂的模块,一旦可以使用封装继承多态抽象这四个特性,就能体现到了面向对象编程的优势了
一旦一个类使用了封装来保护自己的可用性,那么在修改前后,都能加方便
而且,抽象这种面向过程不具有的特性,也占了非常大的一点,可以让我们在不改变原有实现的情况下,替换为新代码
继承则是面向过程编程所没有的特性之一,这个更不必说
多态,可以让我们需要重写的时候,利用接口替换,或者利用子类来替换父类,从而升级代码
所以说,这四大特性,让我们更加轻松的书写出好的代码
(3)OOP更加容易阅读
面向对象编程更加容易阅读
一开始的面向过程语言,是一条线的,更加适合机器去理解,而现在的面向对象编程,更加适合于人类去理解编码
这也是大势所趋
4.什么样的代码看起来是面向对象,实际是面向过程的
虽然我们使用的是面向对象编程语言,但是实际上经常写出的是面向过程风格的代码,那么具体怎么区分是面向对象风格,什么是面向过程风格的呢?
(1)滥用 getter setter方法
在编写Java代码的时候,经常使用Idea或者Lombok插件,直接去生成getter,setter方法,虽然看起来方便,但是实际上违反了封装这个特性,将对象的编程风格退化为了面向过程编程风格
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(); } // …省略其他方法… } |
如上,是一个简化的购物车类,其中包含了itemsCount totalPrice items
对于 itemsCount,totalPrice两个属性,定义了其的getter,setter方法,对于items属性,定义了getter和addItem方法
如果简单的像上面一样,暴露出来,那么可以随意的设置总价这个属性,导致数据容易被篡改
面向对象的封装定义为,通过访问控制权限,来隐藏了内部数据,外部只能通过类提供的有限接口来访问,修改内部数据,对于不应该暴露的setter方法,明显违反了面向对象的封装
所以不应该书写,加上了,就成为面向过程的编程风格了
而且对于items这种集合类的对象,定义一个getter也是不应该的,因为对于一个集合类,是可以直接修改其内部属性的,如果需要暴露一个getter方法,可以考虑让getter返回一个copy对象,或者是返回一个UnmodifiableList集合,让其修改的时候直接报错,但是哈,即使是上面的getter,获取集合 中的一个对象还是可以的,于是仍然存在这篡改的风险
(2)滥用全局变量和方法
对于Java开发中,全局的变量或者方法不多见,但是有的,比如:单例类对象,静态成员变量,常量,常见的全局方法有静态方法
或者说,常见的我们会将一些参数,设置为常量,放到一个Constants类中,
将一些操作静态变量或者外部数据的方法,定义为一个Utils类中,在不创建对象的情况下,直接拿来用
但是会出现一些问题,如果一个项目将所有的常量定义到一个文件中去,比如
public class Constants {
public static final String MYSQL_ADDR_KEY = “mysql_addr”;
public static final String MYSQL_DB_NAME_KEY = “db_name”;
public static final String MYSQL_USERNAME_KEY = “mysql_username”;
public static final String MYSQL_PASSWORD_KEY = “mysql_password”;
public static final String REDIS_DEFAULT_ADDR = “192.168.7.2:7234”;
public static final int REDIS_DEFAULT_MAX_TOTAL = 50;
public static final int REDIS_DEFAULT_MAX_IDLE = 50;
public static final int REDIS_DEFAULT_MIN_IDLE = 20;
public static final String REDIS_DEFAULT_KEY_PREFIX = “rt:”;
// …省略更多的常量定义…
}
会导致降低项目的可维护性,每一次修改或者新增常量就要去翻这个类,太过于麻烦,而且再往这个类里面修改时候,会涉及到代码的编译,因为所有引用的类,都需要重新编译一次,耗时过长
所以降低了复用性和可维护性,建议将不同模块的Constants拆分成不同的类,比如MySQL的常量就应该放在MySQLConstants常量类中
对于Utils,同样需要避免去滥用,只不过在定义之前,先去想一下,是否真的需要去单独定义一个Utils类,不能将其放在其他类中吗
(3)定义数据和方法分离的类
简单来说,就是讲数据定义在一个类中,方法定义在另一个类中,如果使用的是常见的MVC三层结构来做,后端开发,很可能是书写的这样的面向过程编程风格
比如说,我们会把层次分为Controller Service Repository层,在每一层,都会定义相对应的VO BO Entity,然后将实际的方法写在Controller Service Repository类中
这种模式是一种面向过程的开发风格,也被叫做基于贫血模型的开发模式
(4)为什么会写出面向过程风格的代码呢?
因为人的思维模式导致的,习惯一步步的执行某个顺序,而不是将任务分成多个小块,设计类之间的交互,最后进行组织,
而且,面向对象编程会更加需要技巧,更加难一些,所以很容易就将代码设计为了面向过程风格的了
(5)面向过程编程就没有前途了吗
当然并非如此,如果是微小的程序,或者是单纯的数据处理的代码,那么可以使用面向过程编程
总结
1.什么是面向过程编程和面向过程编程语言
实际上,面向过程编程和面向过程编程语言,是数据和方法相分离,相比较于面向对象编程语言,不支持封装继承多态抽象等编程特性
2.面向对象编程和面向过程编程的优势是什么
拥有类和对象的特性,更加适合去大规模复杂程序的开发
写出来的代码更加容易维护,复用
面向对象的代码更容易阅读
3.常见的面向过程的代码特性
滥用getter setter方法
Constants Uitls类的设计
贫血模型的开发设计