文章目录
- 1. 情境
- 2. 抛出问题
- 3. 给出解决方案
- 4. 方案存在的bug
- 5. 补救措施
- 6. 得出结论:该方案实际是不可行的
- 7. 总结上述代码思考方式 -- 基于过程
- ① 思考方式
- ② 上述思考方式存在的问题
- 基于过程的思维方式核心
- 基于过程的思维方式的缺点
- 8. 转变思维,引出面向对象
- ① 那我们应该怎么想(涉及到编程思想)?
- ② 回到我们这个程序:我们想达到什么目标?
- ③ 小结
- 9. 规划明确目标站在更高层面思考问题
- 10. 上代码,设计和体验面向对象编程
- ① 定义狗类
- ② 运行APP
- 小问题
- 解决方案:添加public特性
- 继续让张大爷试用
- 王阿姨试用
- 小结
- 该部分完整代码
- 快捷键
- 11. 面向过程与面向对象
- ① 针对当前案例
- ② 总结
- 12. 对象与实例
- 13. 内容出处
1. 情境
假设隔壁王阿姨知道你是程序员,想给你钱让你帮忙给她家的狗写一个程序,记录她家狗的情况
你回家就按王阿姨的要求用c语言写了一个养狗系统(utf-8编码输出中文字符会乱码, 要想正确显示可以改成gbk, 这个不是重点):
#include<stdio.h>void eat_food();
void sleep();int main(void)
{// 记录狗的基础信息, 姓名、年龄、品种等char *name = "Tom";int age = 2;char *variety = "藏獒";// 但是狗还会吃饭、睡觉, 这些我们用函数来实现// 假如现在狗狗在吃饭, 我们就记录一下eat_food();// 过了一会儿, 狗狗睡了, 再记录一下sleep();return 0;
}void eat_food()
{// 吃饭// 吃什么饭printf("狗狗在吃饭,吃了...");
}void sleep()
{// 睡觉//怎么睡:趴着、蜷缩着...// 睡眠质量怎么样printf("狗狗在睡觉, 睡得...");
}
由此,一个简单的养狗系统就写好了。你把系统给了王阿姨,王阿姨说很满意,于是把这个系统推荐给了张大爷。张大爷就来找你了,想给你钱让你给他家狗也写一个。
于是,你回家给张大爷的狗又写了一个:
#include<stdio.h>void eat_food();
void sleep();int main(void)
{// 记录狗的基础信息, 姓名、年龄、品种等char *name = "Jerry";int age = 2;char *variety = "拉布拉多";// 但是狗还会吃饭、睡觉, 这些我们用函数来实现// 假如现在狗狗在吃饭, 我们就记录一下eat_food();// 过了一会儿, 狗狗睡了, 再记录一下sleep();return 0;
}void eat_food()
{// 吃饭// 吃什么饭printf("狗狗在吃饭,吃了...");
}void sleep()
{// 睡觉//怎么睡:趴着、蜷缩着...// 睡眠质量怎么样printf("狗狗在睡觉, 睡得...");
}
2. 抛出问题
我们可以发现两个系统功能都差不多,甚至可以说是高度重合,像吃饭、睡觉这些举动每个狗狗都会有。难道每次有人来找,都要重新再写一个么?有没有什么方案可以偷点懒?
3. 给出解决方案
解决方案:
(自己想的:把姓名、年龄、品种这些可能会产生差异的信息改成从控制台输入,然后再想办法把这些信息写入一个文件)
课程提到的:也不分什么张大爷的养狗系统了还是王阿姨的养狗系统了,直接写一个养狗系统,将姓名、年龄等可能会产生差异的信息改成用函数传参,其余像吃饭、睡觉等功能不变
#include <stdio.h>char *name;
int age;
char *variety;void eat_food();
void sleep();
void message(char *name_input, int age_input, char *variety_input);int main(void)
{// 王阿姨家的狗message("Tom", 2, "藏獒");// 输出 Tom 的信息printf("名字: %s, 年龄: %d, 品种: %s\n", name, age, variety);// 张大爷家的狗message("Jerry", 2, "拉布拉多");// 输出 Jerry 的信息printf("名字: %s, 年龄: %d, 品种: %s\n", name, age, variety);//狗狗的行为eat_food();sleep();return 0;
}void eat_food()
{// 吃饭// 吃什么饭printf("%s在吃饭, 吃了...\n", name);
}void sleep()
{// 睡觉//怎么睡:趴着、蜷缩着...// 睡眠质量怎么样printf("%s在睡觉, 睡得...\n", name);
}void message(char *name_input, int age_input,char *variety_input)
{name = name_input;age = age_input;variety = variety_input;
}
4. 方案存在的bug
但是问题来了,我们发现没有办法控制当前指令针对谁(例如:谁在睡觉,谁在吃饭)
5. 补救措施
用结构体控制操作对象
#include <stdio.h>
#include <string.h>// 定义结构体 Dog
struct Dog {char name[20];int age;char variety[30];void (*eat)(struct Dog *dog);void (*sleep)(struct Dog *dog);
};// 函数声明
void eat_food(struct Dog *dog);
void sleep(struct Dog *dog);int main(void)
{// 定义王阿姨家的狗struct Dog tom = {"Tom", 2, "藏獒", eat_food, sleep};// 定义张大爷家的狗struct Dog jerry = {"Jerry", 2, "拉布拉多", eat_food, sleep};// 输出 Tom 的信息printf("名字: %s, 年龄: %d, 品种: %s\n", tom.name, tom.age, tom.variety);// 输出 Jerry 的信息printf("名字: %s, 年龄: %d, 品种: %s\n", jerry.name, jerry.age, jerry.variety);// 让 Tom 吃饭tom.eat(&tom);// 让 Jerry 睡觉jerry.sleep(&jerry);return 0;
}// 函数定义
void eat_food(struct Dog *dog)
{printf("%s在吃饭, 吃了...\n", dog->name);
}void sleep(struct Dog *dog)
{//怎么睡:趴着、蜷缩着...printf("%s在睡觉, 睡得...\n", dog->name);
}
6. 得出结论:该方案实际是不可行的
我们可以发现结构体操作是很繁琐的,现在只有两条狗还好,如果1000条呢,一个一个控制它们吃饭睡觉,也蛮累的,由此我们可以发现这种方案实际上是不可行的。即使用c语言的结构体可以实现,但也是十分复杂的,不具备可实施性。
7. 总结上述代码思考方式 – 基于过程
我们发现我们在c语言里学到的这些知识都是从main函数里进去然后从上到下一步一步执行到结尾就没了。如果main函数里没有调用sleep()这些函数,那么这些函数就是没用的。
① 思考方式
我们可以想一下上述程序是怎么写出来的。是不是先想这条狗叫什么名字,年龄是多少,什么品种,然后再想狗的行为(例如:这狗能吃饭、能睡觉),依次把它们写成函数的形式。写完之后再调用函数(例如:吃饭的时候调用eat_food(),睡觉的话就调用sleep())
有没有发现一个问题:我们思考问题的方式好像一条直线。讲究顺序,"过程"这个词贯穿整体。例如:你现在饿了,所以你先去拿碗筷,然后去锅里盛了饭,然后夹菜,然后拿起筷子把饭放嘴里…
我们先想这个狗可能叫什么名字以及这个变量该用哪种数据类型,然后再想这个狗年龄多少以及这个变量该用哪种数据类型,然后想这狗什么品种以及这个变量该用哪种数据类型。然后又一想,这狗好像能吃饭,于是定义了一个吃饭的函数;这狗好像也能睡觉,于是又定义了一个睡觉的函数。需要的时候再去调用相应的函数。
大家做事情的时候通常都会用到这个过程,并且只有想到的时候才会去用它。例如:当你想吃饭的时候才去厨房,当你想睡觉的时候才会走向卧室。你想去做什么的时候然后再去做了,这就是过程。
② 上述思考方式存在的问题
我们现在需要改变一下思维:我们不可能把张大爷家的狗每天吃饭的过程都写一遍,我们也不可能把王阿姨家的狗每天的生活经历走一遍。因为两条狗在相同的时间线可能会做出不同的事情,例如:张大爷家的狗今天早上9点还在睡觉,但是王阿姨家的狗这个点已经饿了需要吃饭。这个时候就麻烦了,所以我们不能按照过程去想。
基于过程的思维方式核心
说白了过程有另一种说法:走一步看一步,没有目标。今天想吃饭就吃饭,想上班就上班,没有什么计划和目标。
基于过程的思维方式的缺点
由此可见基于过程的思维方式的缺点:目标不明确;不适用于大众(例如:A走一步看一步,毕业之后可能回去继承家产了;B走一步看一步,毕业之后只能去要饭), 不能表示所有的大众
8. 转变思维,引出面向对象
① 那我们应该怎么想(涉及到编程思想)?
但是有目标就不一样了,例如:大家都想成为java工程师,那java基础、面向对象、多线程、spring框架就是必经之路。所有java工程师都必须学习这些东西,那java工程师是我们的目标,是不是就意味着我们也必须要学习这些东西,这就符合大众了。这就是有目标,有了目标之后就脱离过程了。(C同学读了计算机之后根本就不知道自己以后要干嘛,就走一步看一步,老师教啥就学啥,老师教到哪就学到哪,这种就是偏向于过程)这个时候我们就能发现,有了目标好像我们就能概括所有东西了(即大众化)。比如说大家都奔着java工程师的方向去,那该领域的up主就很好制作教学视频了,只需要教授java基础、面向对象等共性内容就可以了(如果都走一步看一步,今天学java基础,明天学美术,后天又搞音乐去了,那让人家怎么教,人家都不知道你究竟想学什么)。目标明确的时候,我们就不能强调过程(不惜一切代价也要达成目标)。例如,你要是想读研,不管是自己考还是靠保研,只要能上怎么都行。
② 回到我们这个程序:我们想达到什么目标?
① 该程序要大众化,例如:不能说张大爷家的狗能睡觉王阿姨家的狗不能睡觉,不能说你家狗有保险我家狗没保险(其实也存在这种情况,但是我们的程序要体现出来)
② 明确目标,例如:我们的目标就是给小区里的100多条狗写个程序(不管你用什么语言写,只要把这个目标达到,问题就解决了)
③ 不强调过程,例如:我们不需要知道谁家的狗在吃饭、谁家的狗在睡觉,只需要知道它们都可以吃饭和睡觉就好了,它们想什么时候睡就什么时候睡,也不强调它们究竟是什么时候睡的、怎么睡的。甚至都不需要强调它们究竟有没有睡觉。那我们强调什么呢?强调目的 – 记录某条狗所有的东西,例如:多大了、24小时内干了什么事 …
③ 小结
我们可以发现我们的思维转变了,从原来的走一步看一步、没有目标转换成了只看目标不看过程。而且设置的目标更加大众化了,让所有的狗都能用。
9. 规划明确目标站在更高层面思考问题
我们的目标不就是给这些狗开发一个系统么,这些狗做什么我们现在不关注,我们应该专注于狗本身。试想一下,这狗是不是应该会吃饭、会睡觉、分品种…
也就是我们要先把总的设计图设计出来,不能说狗现在饿了你才去写一个吃饭的函数。
假如你现在的目标是java工程师,那是不是应该制定一个详细的规划,即第一步做什么、第二步做什么。当你执行完所有计划的时候,就达到目标了。
所以我们在设计狗系统的时候,我们也要做好规划,不能再走一步看一步。也不需要一个一个去问张大爷和王阿姨等人他们家狗什么时候吃饭、什么时候睡觉了,只需要设计出一个能够满足所有狗需求的方案,让大爷和阿姨自己去记录自家狗的情况(不需要我们亲自去记录了,用户自己就可以帮我们记录。这个时候我们就解放了,只是设计一个程序让用户自己记录,至于用户想不想记录,那是人家自己的事情,我们不需要关心人家的狗吃没吃饭、睡没睡觉)。说白了,面向对象这个编程就是让我们站在一个更高的层面去看待事物。
简而言之,我们需要先思考一下狗究竟都有什么东西,然后再去规划和设计这个狗系统。最后想办法去完成计划、达成目标–大众化(所有的大爷大妈都愿意使用并且都会使用),以上就是面向对象的概念基础
10. 上代码,设计和体验面向对象编程
① 定义狗类
这个时候我们应该站在更高的层次去考虑问题,这个狗应该具有大众化的意义。目前使用设计的思维:① 先去考虑常见的共性:所有狗都会有的东西(例如:都有名字、都有种类、都有年龄、都会吃饭、都会睡觉);② 接下来考虑可能发生的事情(例如:生病)。
如此一来公共的特性(谁家的狗都会这样)就定义好了,100多条狗都满足上述目标。
package com.practice.bean;public class Dogs {// 狗名字String name;// 狗年龄int age;// 狗种类String variety;// 狗食物String food;void eat(){System.out.println("狗吃饭!");}void sleep(){System.out.println("狗睡觉!");}
}
设计完成,目标完成
② 运行APP
假如我们把这个app发布了,该怎么用呢?---- 不管谁家狗过来都先填一张表
小问题
我们可以发现zhangDog.name用不了
解决方案:添加public特性
也就是说,我们需要在姓名等字段前面都加一个public,把它们变成共有字段,从而张大爷王阿姨它们可以自由更改自己狗的名字、种类、年龄、食物、睡觉等信息
我们可以发现现在zhangDog.name可以用了
继续让张大爷试用
也就是说,张大爷注册了这个APP,填了一下基本信息,然后记录了一下狗睡了。
王阿姨试用
王阿姨看了以后也想要,于是王阿姨也注册了一个。
小结
我们可以发现,这样一来我们就可以清楚地区分张大爷家的狗和王阿姨家的狗了。
当前的思维方式:先明确操作对象,然后考虑这些对象的特征和行为,然后抽离出共性的部分,最后把程序给写出来就好了。我们不需要关注狗怎么吃饭、怎么睡觉,让用户自己操作这个实例(先注册一个账号,然后完善信息,开始记录),总之就是让用户自己捣鼓。而且后续用户一直操作的都是对象的行为,什么张大爷家的狗的名字、王阿姨家的狗的年龄等等。用户一直在操作对象,一直在操作实例,没有操作过Dogs类里面的东西啊。我们实际生活中用到的时候,也是操作实例,这就是OOP,这就是面向对象编程。从头到尾用户能体验到的就是一直在操作这个对象。你说用户想给狗改个姓名,代码上是怎么操作的,就是这种用借助对象(实例)的方式去操作的,例如:zhangDog.name = “?”、wangDog.age = ?
该部分完整代码
制定目标的好处就在这,想用谁用谁。
import com.practice.bean.Dogs;public class ApplicationRun {public static void main(String[] args) {// 张大爷!你可以试一下这个APP了!先注册Dogs zhangDog = new Dogs();Dogs wangDog = new Dogs();// 设置狗信息zhangDog.name = "Jerry";zhangDog.age = 2;zhangDog.variety = "拉布拉多";wangDog.name = "Tom";wangDog.age = 2;wangDog.variety = "藏獒";// 开始试用zhangDog.sleep();// 分别输出张大爷和王阿姨家狗的名字System.out.println("张大爷家的狗叫什么名字 = " + zhangDog.name);System.out.println("王阿姨家的狗叫什么名字 = " + wangDog.name);}
}
快捷键
soutv + Tab:可以快速生成输出语句
main + 回车:可以快速生成main语句
11. 面向过程与面向对象
① 针对当前案例
POP语言(c语言):面向过程语言(c语言不就是走一步看一步吗,进入main函数以后,我要么调用函数要么不调用函数,走一步看一步,直到最后return 0结束)
就是思考一些过程,目标不明确,不适合大众。例如:最开始我们分别创建了一个wang_dog.c和zhang_dog.c,它们狗的作息什么的可能都不一样。这时候我们发现追踪每条狗24小时的行为过程太复杂了,所以我们就不能追踪过程了,我们应该明确目标(即追踪某条狗所有信息,找出共性)。c语言就是面向过程的一个语言,如果想要做一个大众化的东西,强调目标但不强调过程,c语言就不行,它的强项不在这里。
OOP语言(例如:java):面向对象语言。什么是对象?对象就是目标。
我们不就是要记录张大爷家的狗和王阿姨家的狗么,那张大爷家的那条狗就是对象,王阿姨家的狗也是一个对象。这个目标做什么,就是这个对象做什么。至于这个对象具体真的做什么,跟我们也没关系,我们只知道它可能吃喝拉撒了,至于什么时候产生这些行为我们也不需要管
② 总结
12. 对象与实例
什么叫对象?我们只需要先考虑一下谁是目标?我们是不是要给小区中所有大爷大妈的狗写一个程序啊,那我们自然要围绕狗的公共特征和行为(例如:年龄、吃饭等)编写程序了,所以狗就是我们的目标,也就是我们说的对象。这就是面向对象编程。
狗本来就是一个抽象的事物,它是一个名词。怎么给狗下一个定义?看一下百度百科给的定义,太抽象了。
假如有个小孩问你:什么是狗啊?你跟他扯狗是是什么犬科生物什么什么的,人家可能就会很懵。但是如果你跟ta指一下,那不,张大爷家的狗,看好了那就是狗。张大爷家的狗就是一个实实在在的例子。
所以什么是实例?实例就是现实生活中的一个东西,对抽象的东西进行表示出来的产物。我们可以想一下,狗本来是抽象的一个概念,世界上有那么多条狗,你怎么知道那条是张大爷家的狗。因为它是一个活生生的存在的事物,而且有自己的名字、年龄、品种、吃饭情况、睡觉情况等等,它还住在张大爷家,所有因素的总和决定了它就是独一无二的个体。除此之外,我们只要看见它就知道它是一条狗,因为它具备了狗的共性特征。
对象和实例的区别:对象是大于实例的。我把狗当作一个对象,然后张大爷家的狗就是一个实例。
13. 内容出处
实际叙述比上面的更有趣,只能说入股不亏