面向对象进阶 - 多态

7 阅读6分钟

专栏导航

在前几章中,我们学习了继承、抽象类和接口。这一周,我们将探索面向对象编程中最重要也是最难理解的概念——多态。多态让我们的代码更加灵活、可扩展。

一、什么是多态?

1.1 现实生活中的多态

想象一下:

一个"发出声音"的指令
  ├── 狗 → 汪汪
  ├── 猫 → 喵喵
  ├── 鸟 → 叽叽喳喳
  └── 牛 → 哞哞

同样的指令,不同的对象有不同的反应,这就是多态

1.2 编程中的多态

多态(Polymorphism)是面向对象编程的三大特性之一。它允许使用统一的接口处理不同类型的对象,这些对象会根据自身的类型做出不同的响应。

Animal animal = new Dog();  // 父类引用指向子类对象
animal.MakeSound();         // 调用 Dog 的方法

1.3 多态的好处

好处:

代码复用:同一套代码处理不同类型
灵活扩展:新增类型无需修改现有代码
解耦合:降低代码之间的依赖
易于维护:代码结构清晰

二、多态的实现方式

2.1 三种实现多态的方式

方式关键字说明
方法重载同类中同名不同参数编译时多态
方法重写virtual/override运行时多态
接口实现接口多态运行时多态

2.2 多态流程图

父类引用 → 指向子类对象
    ↓
调用方法
    ↓
运行时判断对象实际类型
    ↓
调用对应类型的实现
    ↓
多态效果

三、方法重载(编译时多态)

3.1 什么是方法重载?

方法重载是指在同一个类中,定义多个同名方法,但参数列表不同。

3.2 示例

using System;

namespace Week12Practice
{
    class Calculator
    {
        // 重载1:两个整数相加
        public int Add(int a, int b)
        {
            return a + b;
        }

        // 重载2:三个整数相加
        public int Add(int a, int b, int c)
        {
            return a + b + c;
        }

        // 重载3:两个 double 相加
        public double Add(double a, double b)
        {
            return a + b;
        }

        // 重载4:字符串连接
        public string Add(string a, string b)
        {
            return a + b;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 方法重载示例 =====\n");

            Calculator calc = new Calculator();

            Console.WriteLine($"10 + 20 = {calc.Add(10, 20)}");
            Console.WriteLine($"10 + 20 + 30 = {calc.Add(10, 20, 30)}");
            Console.WriteLine($"10.5 + 20.5 = {calc.Add(10.5, 20.5)}");
            Console.WriteLine($"Hello + World = {calc.Add("Hello", "World")}");
        }
    }
}

输出:

===== 方法重载示例 =====

10 + 20 = 30
10 + 20 + 30 = 60
10.5 + 20.5 = 31
Hello + World = HelloWorld

四、方法重写(运行时多态)

4.1 virtual 和 override

使用 virtualoverride 关键字实现方法重写。

class Parent
{
    public virtual void Method()
    {
        Console.WriteLine("父类方法");
    }
}

class Child : Parent
{
    public override void Method()
    {
        Console.WriteLine("子类方法");
    }
}

4.2 完整示例

using System;

namespace Week12Practice
{
    // 基类:动物
    class Animal
    {
        public string Name { get; set; }

        // 虚方法:可以被重写
        public virtual void MakeSound()
        {
            Console.WriteLine($"{Name} 发出声音");
        }

        public virtual void Eat()
        {
            Console.WriteLine($"{Name} 正在吃东西");
        }
    }

    // 派生类:狗
    class Dog : Animal
    {
        public Dog(string name)
        {
            Name = name;
        }

        // 重写方法
        public override void MakeSound()
        {
            Console.WriteLine($"{Name}: 汪汪汪!");
        }

        public override void Eat()
        {
            Console.WriteLine($"{Name} 正在啃骨头");
        }
    }

    // 派生类:猫
    class Cat : Animal
    {
        public Cat(string name)
        {
            Name = name;
        }

        public override void MakeSound()
        {
            Console.WriteLine($"{Name}: 喵喵喵!");
        }

        public override void Eat()
        {
            Console.WriteLine($"{Name} 正在吃鱼");
        }
    }

    // 派生类:牛
    class Cow : Animal
    {
        public Cow(string name)
        {
            Name = name;
        }

        public override void MakeSound()
        {
            Console.WriteLine($"{Name}: 哞哞哞!");
        }

        public override void Eat()
        {
            Console.WriteLine($"{Name} 正在吃草");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 方法重写示例 =====\n");

            // 使用父类引用指向子类对象
            Animal[] animals = new Animal[]
            {
                new Dog("旺财"),
                new Cat("咪咪"),
                new Cow("奶牛"),
                new Dog("大黄")
            };

            Console.WriteLine("--- 多态调用 ---");
            foreach (Animal animal in animals)
            {
                animal.MakeSound();  // 多态:调用各自的重写方法
                animal.Eat();
                Console.WriteLine();
            }
        }
    }
}

输出:

===== 方法重写示例 =====

--- 多态调用 ---
旺财: 汪汪汪!
旺财 正在啃骨头

咪咪: 喵喵喵!
咪咪 正在吃鱼

奶牛: 哞哞哞!
奶牛 正在吃草

大黄: 汪汪汪!
大黄 正在啃骨头

五、使用抽象类实现多态

5.1 抽象类多态

using System;

namespace Week12Practice
{
    // 抽象类:图形
    abstract class Shape
    {
        public string Name { get; set; }

        // 抽象方法:必须重写
        public abstract double CalculateArea();
        public abstract double CalculatePerimeter();

        // 普通方法:可以重写
        public virtual void Display()
        {
            Console.WriteLine($"图形: {Name}");
            Console.WriteLine($"面积: {CalculateArea():F2}");
            Console.WriteLine($"周长: {CalculatePerimeter():F2}");
        }
    }

    // 具体类:圆形
    class Circle : Shape
    {
        public double Radius { get; set; }

        public Circle(double radius)
        {
            Name = "圆形";
            Radius = radius;
        }

        public override double CalculateArea()
        {
            return Math.PI * Radius * Radius;
        }

        public override double CalculatePerimeter()
        {
            return 2 * Math.PI * Radius;
        }

        public override void Display()
        {
            base.Display();
            Console.WriteLine($"半径: {Radius:F2}");
        }
    }

    // 具体类:矩形
    class Rectangle : Shape
    {
        public double Width { get; set; }
        public double Height { get; set; }

        public Rectangle(double width, double height)
        {
            Name = "矩形";
            Width = width;
            Height = height;
        }

        public override double CalculateArea()
        {
            return Width * Height;
        }

        public override double CalculatePerimeter()
        {
            return 2 * (Width + Height);
        }

        public override void Display()
        {
            base.Display();
            Console.WriteLine($"宽: {Width:F2}, 高: {Height:F2}");
        }
    }

    // 具体类:三角形
    class Triangle : Shape
    {
        public double Base { get; set; }
        public double Height { get; set; }
        public double SideA { get; set; }
        public double SideB { get; set; }
        public double SideC { get; set; }

        public Triangle(double @base, double height, double sideA, double sideB, double sideC)
        {
            Name = "三角形";
            Base = @base;
            Height = height;
            SideA = sideA;
            SideB = sideB;
            SideC = sideC;
        }

        public override double CalculateArea()
        {
            return 0.5 * Base * Height;
        }

        public override double CalculatePerimeter()
        {
            return SideA + SideB + SideC;
        }

        public override void Display()
        {
            base.Display();
            Console.WriteLine($"底: {Base:F2}, 高: {Height:F2}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 抽象类多态示例 =====\n");

            Shape[] shapes = new Shape[]
            {
                new Circle(5),
                new Rectangle(4, 6),
                new Triangle(6, 4, 5, 5, 6)
            };

            double totalArea = 0;

            foreach (Shape shape in shapes)
            {
                shape.Display();  // 多态:调用各自的 Display 方法
                Console.WriteLine();
                totalArea += shape.CalculateArea();
            }

            Console.WriteLine($"总面积: {totalArea:F2}");
        }
    }
}

输出:

===== 抽象类多态示例 =====

图形: 圆形
面积: 78.54
周长: 31.42
半径: 5.00

图形: 矩形
面积: 24.00
周长: 20.00
宽: 4.00, 高: 6.00

图形: 三角形
面积: 12.00
周长: 16.00
底: 6.00, 高: 4.00

总面积: 114.54

六、使用接口实现多态

6.1 接口多态

using System;
using System.Collections.Generic;

namespace Week12Practice
{
    // 接口:可绘制
    interface IDrawable
    {
        void Draw();
    }

    // 接口:可移动
    interface IMovable
    {
        void Move(int x, int y);
    }

    // 实现 IDrawable 的类
    class Circle : IDrawable
    {
        public double Radius { get; set; }

        public Circle(double radius)
        {
            Radius = radius;
        }

        public void Draw()
        {
            Console.WriteLine($"绘制圆形,半径: {Radius}");
        }
    }

    // 实现 IDrawable 的类
    class Rectangle : IDrawable
    {
        public double Width { get; set; }
        public double Height { get; set; }

        public Rectangle(double width, double height)
        {
            Width = width;
            Height = height;
        }

        public void Draw()
        {
            Console.WriteLine($"绘制矩形,宽: {Width}, 高: {Height}");
        }
    }

    // 同时实现 IDrawable 和 IMovable 的类
    class Car : IDrawable, IMovable
    {
        public string Model { get; set; }

        public Car(string model)
        {
            Model = model;
        }

        public void Draw()
        {
            Console.WriteLine($"绘制汽车,型号: {Model}");
        }

        public void Move(int x, int y)
        {
            Console.WriteLine($"汽车 {Model} 移动到 ({x}, {y})");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 接口多态示例 =====\n");

            // IDrawable 多态
            Console.WriteLine("--- 使用 IDrawable 接口 ---");
            List<IDrawable> drawables = new List<IDrawable>
            {
                new Circle(5),
                new Rectangle(4, 6),
                new Car("宝马")
            };

            foreach (IDrawable drawable in drawables)
            {
                drawable.Draw();  // 多态:调用各自的 Draw 方法
            }

            // IMovable 多态
            Console.WriteLine("\n--- 使用 IMovable 接口 ---");
            List<IMovable> movables = new List<IMovable>
            {
                new Car("奔驰"),
                new Car("奥迪")
            };

            foreach (IMovable movable in movables)
            {
                movable.Move(100, 200);
            }
        }
    }
}

输出:

===== 接口多态示例 =====

--- 使用 IDrawable 接口 ---
绘制圆形,半径: 5
绘制矩形,宽: 4, 高: 6
绘制汽车,型号: 宝马

--- 使用 IMovable 接口 ---
汽车 奔驰 移动到 (100, 200)
汽车 奥迪 移动到 (100, 200)

七、实践示例:支付系统

using System;
using System.Collections.Generic;

namespace Week12Practice
{
    // 抽象类:支付方式
    abstract class PaymentMethod
    {
        public string Name { get; set; }
        public decimal FeeRate { get; set; }

        // 模板方法:定义支付流程
        public bool ProcessPayment(decimal amount)
        {
            Console.WriteLine($"=== {Name} 支付处理 ===");

            if (!Validate(amount))
            {
                Console.WriteLine("验证失败!");
                return false;
            }

            decimal fee = CalculateFee(amount);
            decimal total = amount + fee;

            Console.WriteLine($"支付金额: {amount:C}");
            Console.WriteLine($"手续费: {fee:C}");
            Console.WriteLine($"总计: {total:C}");

            if (ExecutePayment(total))
            {
                Console.WriteLine("✅ 支付成功!");
                return true;
            }
            else
            {
                Console.WriteLine("❌ 支付失败!");
                return false;
            }
        }

        // 抽象方法:派生类必须实现
        protected abstract bool Validate(decimal amount);
        protected abstract bool ExecutePayment(decimal amount);

        // 普通方法:默认实现
        protected virtual decimal CalculateFee(decimal amount)
        {
            return amount * FeeRate;
        }
    }

    // 具体类:信用卡支付
    class CreditCardPayment : PaymentMethod
    {
        public string CardNumber { get; set; }

        public CreditCardPayment(string cardNumber)
        {
            Name = "信用卡";
            CardNumber = cardNumber;
            FeeRate = 0.02m;  // 2% 手续费
        }

        protected override bool Validate(decimal amount)
        {
            if (CardNumber.Length != 16)
            {
                Console.WriteLine("卡号格式错误!");
                return false;
            }

            if (amount <= 0 || amount > 50000)
            {
                Console.WriteLine("金额必须在 0-50000 之间!");
                return false;
            }

            return true;
        }

        protected override bool ExecutePayment(decimal amount)
        {
            Console.WriteLine("扣款中...");
            // 模拟支付
            return true;
        }
    }

    // 具体类:支付宝支付
    class AlipayPayment : PaymentMethod
    {
        public string AccountNumber { get; set; }

        public AlipayPayment(string accountNumber)
        {
            Name = "支付宝";
            AccountNumber = accountNumber;
            FeeRate = 0.01m;  // 1% 手续费
        }

        protected override bool Validate(decimal amount)
        {
            if (amount <= 0 || amount > 20000)
            {
                Console.WriteLine("金额必须在 0-20000 之间!");
                return false;
            }

            return true;
        }

        protected override bool ExecutePayment(decimal amount)
        {
            Console.WriteLine("余额扣款中...");
            // 模拟支付
            return true;
        }
    }

    // 具体类:微信支付
    class WeChatPayment : PaymentMethod
    {
        public string OpenId { get; set; }

        public WeChatPayment(string openId)
        {
            Name = "微信支付";
            OpenId = openId;
            FeeRate = 0.005m;  // 0.5% 手续费
        }

        protected override bool Validate(decimal amount)
        {
            if (amount <= 0 || amount > 5000)
            {
                Console.WriteLine("金额必须在 0-5000 之间!");
                return false;
            }

            return true;
        }

        protected override bool ExecutePayment(decimal amount)
        {
            Console.WriteLine("零钱扣款中...");
            // 模拟支付
            return true;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 支付系统多态示例 =====\n");

            List<PaymentMethod> payments = new List<PaymentMethod>
            {
                new CreditCardPayment("1234567890123456"),
                new AlipayPayment("zhangsan"),
                new WeChatPayment("wx_zhangsan")
            };

            decimal[] amounts = { 100, 500, 299.99m };

            for (int i = 0; i < payments.Count; i++)
            {
                payments[i].ProcessPayment(amounts[i]);
                Console.WriteLine();
            }
        }
    }
}

输出:

===== 支付系统多态示例 =====

=== 信用卡 支付处理 ===
支付金额: ¥100.00
手续费: ¥2.00
总计: ¥102.00
扣款中...
✅ 支付成功!

=== 支付宝 支付处理 ===
支付金额: ¥500.00
手续费: ¥5.00
总计: ¥505.00
余额扣款中...
✅ 支付成功!

=== 微信支付 支付处理 ===
支付金额: ¥299.99
手续费: ¥1.50
总计: ¥301.49
零钱扣款中...
✅ 支付成功!

八、多态的最佳实践

8.1 ✅ 推荐做法

  1. 使用多态处理不同类型
List<Animal> animals = { new Dog(), new Cat(), new Bird() };
foreach (var animal in animals)
{
    animal.MakeSound();  // 多态调用
}
  1. 使用接口定义契约
interface ILogger
{
    void Log(string message);
}

// 多个实现
class ConsoleLogger : ILogger { }
class FileLogger : ILogger { }

8.2 ❌ 不推荐做法

  1. 不要滥用多态
// ❌ 简单场景不需要多态
class Simple
{
    public void Method() { }
}
  1. 不要忘记重写方法
// ❌ 忘记 override
class Child : Parent
{
    public void Method() { }  // 应该用 override
}

本章总结

  • ✅ 理解了多态的概念和作用
  • ✅ 学会了方法重载
  • ✅ 掌握了方法重写(virtual/override)
  • ✅ 学会了使用抽象类实现多态
  • ✅ 学会了使用接口实现多态
  • ✅ 实践了完整的支付系统