程序员老鬼

为什么go和rust语言都舍弃了继承?

今天我们来聊聊Go和Rust语言为什么放弃了继承。这个话题看似枯燥,但其实背后有很多有趣的技术思考,甚至还有些程序员的“老梗”可以分享。

话不多说,咱们直接进入正题。🐯

Image

首先,得承认一点,面向对象编程(OOP)并不是啥新鲜玩意儿了。大家耳熟能详的Java、C++这些语言,基本上都是依靠继承来组织代码的。

但是你有没有发现,随着我们代码量的增长,继承反而经常给我们添了不少麻烦?有时候,你为了修改一个功能,得从父类一路追溯到曾祖类,搞得自己像福尔摩斯一样查案。😂

Go和Rust的选择:告别继承,拥抱组合

Go和Rust这两门语言为什么果断抛弃了继承?其实不是因为它们“鄙视”面向对象,而是它们选择了更灵活的方式来解决问题——组合。你可能会问,啥叫组合?不急,咱们一步步来说。

继承的问题在于,虽然一开始感觉挺爽,复用代码轻松愉快,但它容易引发很多潜在的麻烦,尤其是在系统变得复杂时。继承就像是开一家连锁餐馆,虽然有统一标准,但一旦某一家要加点独特的菜品,你可能得去整合菜单,结果越改越乱。🐔🦆

来个经典的例子吧:我们有一只鸭子和一只鸡,发现它们都能飞,也都有两只脚、两只翅膀。于是我们抽象出了“鸟”这个父类,鸡鸭都继承了鸟。乍一看挺完美啊,代码复用了,还逻辑清晰。等我们继续扩展业务——企鹅来了。哎呀,问题大了,企鹅不会飞,它只是个会游泳的鸟。你总不能让企鹅继承会飞的鸟吧?🐧

于是乎,你只好继续拆解,给鸟类来个“会飞的鸟”和“不会飞的鸟”子类。再后来,橡皮鸭子又来了,它不光不会飞,甚至也不是真正的鸟类。到这个时候,你的设计开始扭曲,大家开始争论,啥到底是鸟?鸟到底该能做什么?

组合优于继承:灵活而强大

这时,Go和Rust的设计者们看不下去了。他们觉得,与其搞复杂的继承体系,不如简单点,用组合的方式来解决问题。鸭子有翅膀、会飞、会叫这些特性,直接组合进鸭子对象里,不用去继承一整个“鸟类家族”的结构。而企鹅?咱就不给它加上“飞行”特性,企鹅只需要“会游泳”这个能力就够了。

在Go和Rust中,这种组合的方式让代码更加灵活,也更容易维护。举个例子:

Go中的组合

package main

import "fmt"

// 定义飞行能力接口
type Flyable interface {
    Fly()
}

// 鸡类
type Chicken struct {}

func (c Chicken) Fly() {
    fmt.Println("鸡在飞!")
}

// 企鹅类
type Penguin struct {}

func (p Penguin) Swim() {
    fmt.Println("企鹅在游泳!")
}

func main() {
    var chicken Chicken
    var penguin Penguin

        chicken.Fly()    // 鸡在飞
    penguin.Swim()   // 企鹅在游泳
}

在上面的Go代码中,我们用了组合模式,鸡有飞行的能力,而企鹅没有。每个对象只包含它自己需要的行为,而不需要被迫继承一堆不相关的东西。这样,你的代码就更加灵活,可以根据实际需求来“组合”功能。

Rust中的组合

Rust也采用类似的思路,它用“traits”来实现组合。来看看代码:

trait Flyable {
    fn fly(&self);
}

struct Chicken;

impl Flyable for Chicken {
    fn fly(&self) {
        println!("鸡在飞!");
    }
}

struct Penguin;

impl Penguin {
    fn swim(&self) {
        println!("企鹅在游泳!");
    }
}

fn main() {
    let chicken = Chicken;
    let penguin = Penguin;

    chicken.fly();   // 鸡在飞
    penguin.swim();  // 企鹅在游泳
}

Rust里,鸡实现了Flyable这个trait,企鹅则只需要实现它自己的“游泳”功能。每个结构体都只“组合”了它需要的功能,这让Rust代码同样灵活而强大。

不仅仅是灵活性,Go和Rust避免继承还带来了其他几个好处:

1.避免多重继承的复杂性:多重继承往往会导致“钻石问题”,即两个父类之间有相同的祖先类,继承链条变得混乱。这在C++里简直是一场灾难。而使用组合,你只要给对象赋予它需要的功能,避免了这些复杂的继承链。

2.增强代码可维护性:继承体系的修改往往牵一发而动全身,一个父类的变更可能影响整个继承链。而组合则可以在不影响其他部分的前提下,单独调整某个对象的行为。

3.更自然的建模方式:继承是一种从“是什么”出发的建模方式,比如“鸡是什么?是鸟”。但现实生活中,我们往往更关注“它能做什么”。Go和Rust通过组合让我们可以专注于对象的行为,而不是强行分类。
Go和Rust的设计者们不是不喜欢OOP,他们只是觉得继承有时候过于复杂且僵化。在这些语言中,组合让我们能更加自然、灵活地表达对象的行为和特性,避免了继承的复杂性和各种头疼的问题。
而且,它们的代码更容易维护,也能更好地适应未来的变化。

所以,继承虽然有它的价值,但有时候我们得停下来问问自己:“咱到底是为了实现需求,还是为了设计出一棵完美的继承树?”🧐

最后,欢迎大家一起探讨,看看你们在开发过程中更喜欢继承还是组合

-END-

ok,今天先说到这,老规矩,看完文章记得右下角给何老师点赞。

最后送给大家一个福利,我这里有一份搞副业的教程,这份教程里有100+个搞钱小项目:

网盘拉新核心玩法、公众号运营变现、小红书虚拟资料引流等,现在扫码加我微信,即可领取这份副业教程。

添加时备注:副业

Image