深入解析面向过程与面向对象

文章探讨了代码编译过程中视角转换的重要性,比较了面向过程和面向对象编程模型,强调了面向对象的封装、继承和多态特性,同时讨论了继承带来的好处和潜在问题,以及如何通过权限控制来优化封装。
摘要由CSDN通过智能技术生成

代码是由我们写的,写完后就交给编译器去编译为电脑认识的东西,然后电脑再把它放到 CPU 里面去高速执行,最后再将结果展示给我们。这里面涉及到两次视角转换:一次是我们将写的转换为电脑认识的;一次是电脑执行完后转换为我们认识的。

换句话说就是:我们写的代码是面向我们的,编译为电脑执行的代码就是面向计算机的。

正如下图所示:

我们发现,人看的东西很直白,但是效率又很低;而电脑看的东西则根本无法读懂,但是效率又很高;那么怎么让人容易看明白,电脑执行起来又高效呢?

这就涉及到一个问题:面向对象和面向过程。

面向过程

面向过程是以当前发生的事情为目标进行编程,以过程为核心,不考虑将来可能发生的事情。

简单直白点说就是:我只管现在的,将来啥样我不管,将来好不好改我也不管,我重视的就是当前这个过程,这就是计算机的思维。比如 C 语言,就是这个原理,C 语言是没有对象的,有对象的 C 语言叫 OC,虽然被骂的自己都不认识自己了,但是起码人家有了对象。

比如,现在要做个车库管理软件,用面向过程的思维就是:只有一个类,你要干啥全部写在这里面,趁早把肚子里的需求直接梭哈,否则将来不好改我不改,或者你加钱。我不管好不好改,我只管现在写着爽不爽,快不快,刺激不刺激。

面向过程这么垃圾,为什么还存在呢?

因为有用啊!

面向过程本来就不是用来做客户软件的,是用来写计算机硬件的,谁家计算机硬件天天改?

而且,面向过程主要是针对计算机的,跟计算机谈扩展性?跟计算机谈可读性和可维护性?人家一秒一亿次的速度你有吗?就算可读性再差,也能用速度击败你。

其实,面向过程的速度并不差,反而更快!

为啥子呢?因为面向过程只注重当前,不注重未来的拓展性,所以就没有多余的逻辑,反而执行得更快。

考察如下代码:

int getPrice(int level){
    if(level>10) {
        return 100;
    }else if(level>9) {
        return 90;
    }else if(level>5) {
        return 50;
    }
    return 10;
}

这是面向过程的,如果将来加了其他 level,那就继续加 if-else 分支,拓展性真是差的一批,再去看面向对象的写法。

interface IGetPrice {
    int getPrice(int level);
}
class Level10 implements IGetPrice{
    int getPrice(int level) {
        return 100;
    }
}
class Level9 implements IGetPrice {
}
class ....

嗯,拓展了多了类,用反射根据 level 创建出对应的对象就可以了,将来增加需求只需增加一个类就行了,拓展性确实强。

但是,哪个效率高啊,明显是面向过程效率高啊,只需要调用函数,甚至连对象都不用创建,就可以了。

所以,你看,面向过程还是有用的,尤其是在开发需求修改不频繁,比如写一些工程算法或者写系统软件的时候,就应该采用面向过程,因为这时候要求的不是拓展性,而是速度,是性能!

那么,不要求速度的时候呢?那就应该考虑面向对象了。

面向对象

什么是面向对象呢?

面向对象就是将世间万物都视为对象,针对这些对象的具体行为进行编码。

面向对象有三大特点: 封装、继承和多态。

封装

封装就是把现实事物封装成抽象的类,并可以将类的权限加以修改,把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

简单来说就是:你只能看我给你看的,只能改我让你改的,别的你都不可以。

封装是面向对象的特征之一,是对象和类概念的主要特性,如果没有封装,那么继承就是无意义的,多态也就不存在了。

封装让对象针对不同的角色有不同的表现,让程序更有层级感。

有人说,封装不就是将一些属性改为私有的,一些属性改为公有的吗,也就是改个权限吗?

不是!

修改权限只不过是封装的一部分,而且是次要部分,封装的主要部分是:将具体事物抽象成类这个过程。

比如,现实中的一辆汽车,你把它抽象为一个类:

class Car {}

这才是封装的主要部分。

继承

继承是让类拥有另一个类的数据和函数,表达一种我也是你的关系。

继承的类叫做子类,也叫做派生类;被继承的类叫做父类,也叫基类。

就像人类只有一个亲爹一样,子类也只能有一个父类。C++ 这种就另当别论。

比如,上述的汽车,我们具体封装如下:

class Car {
    private int speed = 100;
    public int price = 100;
    public void run() {
        println("run");
    }
}

class AudiCar extends Car {
}

我们的AudiCar只需要继承一下Car,那么它就自己带有了Car里面的price和run()了,因为继承了就表示我也是你,那么奥迪车也是车,也有价格、也会跑。那么,里面有个speed是私有的属性,AudiCar有没有这个属性呢?答案是有的,其实也确实是继承了的,只不过不可见而已,不可见且不可修改,那也就等于没有继承。

那你这继承有啥用呢?我自己再写一份不就行了吗?

继承可以省略大量代码,并且容易拓展,子类的公有属性和行为都可以放在父类中,而子类只需要放自己特有的属性和行为就可以了,这样可以统一处理子类的共有属性,方便管理;而子类的特有属性又可以在子类内部自己处理。

如果,我不仅有一个奥迪车,还有宝马、奔驰、布加迪,那么我只要创建出它们的类型,直接继承Car就行了,就不用反复声明run()和price了,这就节省了代码。

有人会问,不对啊,它们的速度都不一样,价格也不一样,这直接继承了肯定有问题啊。

这就涉及到多态了,其实继承就是为了多态。

多态

多态指的是:一个类的子类可以有不同的表现。

比如,还是上述例子:

class Car {
    private int speed = 100;
    public int price = 100;
    public void run() {
        pritnln("run");
    }
}

class AudiCar extends Car {
    public void run() {
        println("audi run");
    }
}

class BenchiCar extends Car {
    public void run() {
        println("Benchi run");
    }
}

我们让奥迪和宝马分别实现了自己的run(),但是它们价格和速度都一样,这就是多态,可以拥有相同的属性,也可以拥有不同的行为。

上面我们说到,继承就是为了多态。

这就来证明。

假如继承不是为了多态,那么上述的AudiCar和BenchiCar的代码就一模一样,都跟Car一样,那么,为什么要写三个一模一样的类呢?所有地方直接使用Car不就行了吗。

有人说,复用啊,继承除了多态不就是复用吗?

但这不是复用,你这是 CV,你这代码一模一样,复用率都 100% 了,就不叫复用了。

所以,继承就是为了多态。

那么,多态又是为了啥呢?

多态是为了代码容易修改,容易拓展。换句话说,没有多态,就没有设计模式了。

我们考察如下代码,我要一辆汽车,上班开:

public User {
    public AudiCar car;
    
    public void goWork(){
        car.run();
    }
}

简单明了的代码,直接给你一辆奥迪车。如果有一天,我发现奥迪车被偷了,那就只能换个奔驰了?

嗯,那就修改下代码:

public User {
    public BenchiCar car;
    
    public void goWork(){
        car.run();
    }
}

如果后面再想换呢?那就再改?

这样不太好啊,我本来只说要个能跑的车就行,没有说要啥车,只要能跑就行啊,那所有的车都能跑啊,直接这样行吗:

public User {
    public Car car;
    
    public void goWork(){
        car.run();
    }
}

并不指定啥牌子的车,只要是车就行,换句话说,不指定具体的子类,而是指定它们共同的能跑的父类——车!

这样将来不管你啥车被偷都没事,只要不被偷光,我就不用改代码:

Car car = new AudiCar();
car = new BenchiCar();
car = new 还没被偷的Car();

都可以啊,这就是多态的好处,拓展性强,容易修改。

继承和实现的关系

我们上述说到了继承关系只能有一个,也就是只能有一个父类。那么,如果我需要多继承呢?

这时候你就可以用接口了。

接口表示我具有某种功能,继承表示我就是某种东西。

当然,你只能是一种东西,所以你只能单继承;但是你可以有多种功能,所以你可以实现多个接口。

interface CanFly {}
interface CanSwim {}
interface Can72Change {}
class NvWa {}

class Sunwukong extends NvWa implenents CanFly, CanSwim, Can72Change {
}

继承的坏处

上面我们通过实现接口解决了多继承的问题,那么继承有什么坏处呢?

嗯,继承破坏了封装。上面我们说了,封装的次要功能是:将某些属性保护起来,不被别人知道。但是,一旦你被继承了,你那些保护起来的东西,也都被子类继承到了,那么子类会不会滥用呢?

所以,我们就发明了权限关键词这个东西。

  • public:所有人可见。

  • protected:只有自己和子类可见。

  • private:只有自己可见。

  • final:用来修饰方法,避免被重写。

父类只要把自己的属性声明为 private 类型的,那么子类也就不可见了(通过反射修改的行为是可耻的)。

通过这个东西,可以让继承更加完美。

  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值