从面向过程(pop)到面向对象(oop)

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. 内容出处

实际叙述比上面的更有趣,只能说入股不亏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值