面向对象范式
使用面向对象编程范式,就好像我们在代码中操作的是现实生活中的对象。我们为类和方法选择简单易懂的名字,因此只要懂英语的人,大致都能看懂代码在做什么。
有时方法之间只存在细微的差别,那我们是不是要为每种情况都创建一个类似 DO_SMTH_WITH_OBJECT_A
、DO_SMTH_WITH_OBJECT_B
的方法呢?当然不必!虽然我们确实要区分它们,但我们可以借助多态性(Polymorphism),以更加优雅的方式处理这些差异。
多态(Polymorphism)
多态指的是对象或其方法根据对象的类型或方法的参数表现出多种行为的能力。这一定义可能看起来有些抽象,我们通过实践来深入理解它的含义。
假设你正在编程控制一个智能仓库里的机器人,这些机器人负责搬运箱子。目前我们只有两种箱子类型:普通箱子(USUAL_BOX) 和 易碎箱子(FRAGILE_BOX)。但这并不意味着未来不会出现其他类型的箱子。
为了简化与机器人的交互,我们定义一个统一的方法 MOVE
:
Robot 类展示了重载(overloading)
不同语言对这种多态的支持情况不同。例如,Java、Kotlin 和 Scala 都支持方法重载,但 Python 和 JavaScript 并不支持。
现在,当我们命令机器人搬运物品时,它会检查箱子的类型,并据此调整动作:如果是易碎箱子,它会减速,并将其放置到专门区域。这种根据参数不同定义多个同名方法的机制,叫做方法重载(overloading)。
你可以看到,根据箱子的不同类型,机器人会执行不同的任务,我们称这类为多态方法(polymorphic method)。
子类型(Subtyping)
我们的仓库天花板较高,因此可以安装高层货架。普通机器人无法够到高处,这时我们可以创建一个新的子类:DRONE(无人机),用于搬运高处的箱子。
现实生活中的无人机
这些无人机和地面机器人本质上做同一件事:搬运箱子(MOVE)。但它们的动作方式不同——无人机是通过飞行来搬运的。
我们可以说:机器人这个对象具有多种形态,例如普通机器人和无人机。它们的执行方式根据其类型而异,这正是**多态对象(polymorphic object)**的体现。
在子类中重新定义父类的方法的技术,称为方法重写(overriding)。我们使用重写来扩展或改变方法的执行流程。在本例中,我们通过重写导航方法,使机器人能够正确控制无人机的移动方式。
鸭子类型(Duck Typing)
另一种实现多态的方法叫做鸭子类型。
“鸭子类型”这个术语来源于这样一句话:
“如果它像鸭子一样走路、像鸭子一样游泳、像鸭子一样叫,那么它很可能就是鸭子。”
在编程中,如果一个对象 A 拥有与对象 B 相同的方法或属性,那么我们就可以说 A 是 B 的一种形式。
在本例中,如果某个对象具有 MOVE
方法,那么我们就可以用它来搬运箱子。虽然这种多态形式较弱,但它的本质是:只要对象具有相同的方法名,就可以使用。
举例来说,假设我们有两个对象:CAR(汽车) 和 BUS(公交车)。它们都拥有 MOVE
、STOP
和 HONK
方法。也就是说,它们实现了相同的接口,具备相同的行为。
再比如,我们新建两个类型的机器人:HEAVYLIFTER(重型搬运机器人) 和 SCAVENGER(清理机器人),它们都实现了 MOVE
方法。我们可以像使用普通机器人那样使用 HEAVYLIFTER,但 SCAVENGER 的算法只会把箱子搬去垃圾桶——如果你用错了对象,你的箱子可能就被当成垃圾处理掉了。
因此,如果你选择使用鸭子类型,一定要配合测试来验证程序的行为,防止出现意外结果。
鸭子类型常见于 Python 和 JavaScript 这类动态语言,而 Java 则不支持这种方式。
总结
我们可以利用多态来定义对象在不同类型或参数下的行为:
-
重载(Overloading):定义同名但参数不同的方法;
-
重写(Overriding):在子类中修改父类的方法;
-
鸭子类型(Duck Typing):只要对象实现了某个方法,就可以被认为具备某种能力。