前言

封装、继承、多态是面向对象编程三大基石,前面我们学习了封装隐藏数据、继承复用代码,而多态是继承体系的最终落地,也是让代码具备高扩展性的关键特性。很多初学者分不清静态多态与动态多态,也不理解多态到底解决了什么开发痛点,今天结合通俗易懂的比喻 + 完整可运行案例,从零拆解多态全部知识点。

一、什么是多态

1. 通俗理解:同一个类型,不同行为

多态字面含义:多种形态。核心定义:同一个父类引用,指向不同子类实例,调用同名方法时执行各自子类独有的逻辑

举个生活化例子:所有酒水都属于 “酒”(父类),剑南春、五粮液、酒鬼酒都是酒的子类。

plaintext

酒 a = 剑南春
酒 b = 五粮液
酒 c = 酒鬼酒

编译时只知道变量是 “酒”,只有运行时(喝下去)才能分辨具体是哪一款酒,每一种酒口感不同,对应代码里就是同名方法执行不同逻辑,这就是多态。

2. 专业定义

程序中父类引用变量指向的具体对象、方法调用逻辑,编译阶段无法确定,运行时才判定。无需修改原有业务代码,就能切换不同子类实现,一套引用适配多种实体,让程序拥有多种运行状态。

二、为什么要使用多态?从反面案例看懂痛点

我们用 QQ 宠物场景举例,直观感受没有多态时代码的缺陷。

1. 初始版本:无继承、无多态,代码极度冗余

分别创建猫、狗两个独立类,各自拥有Eat()进食方法;主人类需要单独写喂猫、喂狗两个方法。

public class Cat
{
    public string Name { get; set; }
    public int Health { get; set; }
    public void Eat()
    {
        Health += 10;
        Console.WriteLine("喵喵喵~鱼很好吃,健康值+10");
    }
}

public class Dog
{
    public string Name { get; set; }
    public int Health { get; set; }
    public void Eat()
    {
        Health += 15;
        Console.WriteLine("汪汪汪~骨头不错哦,健康值+15");
    }
}

// 主人类
public class Lady
{
    public string Name { get; set; }
    public Lady(string name) => Name = name;
    // 单独喂猫
    public void FeedCat(Cat cat) => cat.Eat();
    // 单独喂狗
    public void FeedDog(Dog dog) => dog.Eat();
}

调用测试:

static void Main(string[] args)
{
    Lady lily = new Lady("lily");
    Dog dog = new Dog() { Name = "大黄", Health = 60 };
    Cat cat = new Cat() { Name = "蓝猫", Health = 50 };
    lily.FeedDog(dog);
    lily.FeedCat(cat);
}
代码存在两大致命问题
  1. 重复代码过多:猫、狗都有NameHealth属性,重复定义;
  2. 扩展性极差:后续新增鸟类、兔子宠物,必须修改Lady主人类,新增FeedBirdFeedRabbit方法,违背开闭原则。

2. 第一次优化:抽取父类抽象类,复用重复代码

把宠物共有属性、抽象进食方法抽成父类Pet,猫狗继承并重写Eat()

public abstract class Pet
{
    public string Name { get; set; }
    public int Health { get; set; }
    // 抽象进食方法,强制子类重写
    public abstract void Eat();
}

public class Cat : Pet
{
    public override void Eat()
    {
        Health += 10;
        Console.WriteLine("喵喵喵~鱼很好吃,健康值+10");
    }
}

public class Dog : Pet
{
    public override void Eat()
    {
        Health += 15;
        Console.WriteLine("汪汪汪~骨头不错哦,健康值+15");
    }
}

此时解决了代码重复问题,但主人类FeedCatFeedDog两个方法依然存在,新增宠物仍要修改主人类,扩展性问题没有根治。

3. 终极优化:引入多态,一套方法适配所有宠物

利用父类引用接收所有子类对象,主人只保留一个通用喂养方法:

public class Lady
{
    public string Name { get; set; }
    public Lady(string name) => Name = name;
    // 父类Pet作为参数,兼容所有Pet子类
    public void Feed(Pet pet)
    {
        pet.Eat();
    }
}

测试调用:


static void Main(string[] args)
{
    Lady lily = new Lady("lily");
    Pet dog = new Dog() { Name = "大黄", Health = 60 };
    Pet cat = new Cat() { Name = "蓝猫", Health = 50 };
    // 同一个Feed方法,分别调用Dog、Cat的Eat实现
    lily.Feed(dog);
    lily.Feed(cat);
}
多态带来的核心优势
  1. 高扩展性:新增鸟类只需要新建Bird : Pet并重写Eat(),主人类代码完全不用改动;
  2. 代码精简:统一入口方法,消除大量重复业务函数;
  3. 解耦:上层主人类只依赖抽象父类,不依赖具体猫狗子类,降低代码耦合。

更多推荐