前言

在面向对象编程中,代码复用的两大支柱是继承组合。继承表达的是 “is-a”(是一个)关系,而组合表达的是 “has-a”(有一个)关系。

核心原则:组合优于继承—— 当类与类之间是“拥有”关系时,优先使用组合而非继承。

本文将结合三段完整的代码示例,深入讲解 Python 中组合的实现方式、应用场景以及实战案例。

一、什么是组合?

组合(Composition) 是指:将一个类的实例作为另一个类的属性

简单来说,就是在一个类中“拥有”另一个类的对象,通过对象之间的协作来完成复杂功能。

class A:
    pass

class B:
    def __init__(self):
        self.a = A()  # B 拥有一个 A 的实例

二、基础示例:Point 与 Circle

2.1 定义 Point 类

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def get_x(self):
        return self.x

    def get_y(self):
        return self.y

    def set_x(self, x):
        self.x = x

    def set_y(self, y):
        self.y = y

    def __str__(self):
        return f'{self.x}, {self.y}'

2.2 通过组合定义 Circle 类

Circle 类“拥有”一个 Point 对象作为圆心:

class Circle:
    def __init__(self, center: Point, r: int):
        self.center = center  # 组合:Point 实例作为属性
        self.r = r

    def get_center(self):
        return self.center

    def get_r(self):
        return self.r

    def __repr__(self):
        return f"Circle(Point({self.center.x}, {self.center.y}), {self.r})"

    def __str__(self):
        return f"Circle({self.center}, {self.r})"

组合的优势在这里体现得淋漓尽致Circle 不需要关心 Point 的内部实现,只需要通过 self.center 访问即可。Point 的逻辑变化不会影响 Circle 的代码。

2.3 批量创建

circles = []
for i in range(1, 11):
    point = Point(i, i)
    circle = Circle(point, i)
    circles.append(circle)

for c in circles:
    print(c.get_center().get_x(), c.get_r())

三、进阶示例:人、车、卡、武器的组合

组合非常适合描述现实世界中复杂的“拥有”关系。来看一个更丰富的例子:

3.1 定义各个组件类

class Car:
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed

    def get_car_info(self):
        return f'color:{self.color}  speed:{self.speed}'

    def __str__(self):
        return self.get_car_info()

class Card:
    def __init__(self, level):
        self.level = level

    def __str__(self):
        return f'level:{self.level}'

class Weapon:
    def __init__(self, type, level):
        self.type = type
        self.level = level

    def add_level(self):
        self.level += 1

    def __str__(self):
        return f'type:{self.type}, level:{self.level}'

3.2 通过组合构建 Person 类

class Person:
    def __init__(self, name, car: Car, card: Card, weapon: Weapon):
        self.name = name
        self.car = car      # 组合:Car 实例
        self.card = card    # 组合:Card 实例
        self.weapon = weapon # 组合:Weapon 实例

    def __str__(self):
        return f"name:{self.name}  car info: {self.car} card info: {self.card} weapon info: {self.weapon}"

3.3 使用与操作

c1 = Car('red', 5)
cd1 = Card('C1')
w1 = Weapon("铁剑", 3)

p1 = Person("小白", c1, cd1, w1)

# 通过 Person 对象访问和操作组合进来的组件
print(p1.weapon.level)  # 3
p1.weapon.add_level()   # 武器升级
print(p1.weapon.level)  # 4

print(p1.card.level)    # C1
print(p1.car.speed)     # 5

这种设计使得 Person 类非常灵活:

  • 可以随时替换 carcard 或 weapon 实例

  • 每个组件都可以独立变化和扩展

  • Person 类本身保持简洁

四、实战案例:角色对战游戏

这是一个更完整的组合应用——角色管理类组合角色类,实现自动对战逻辑。

4.1 定义 Role 类

import random

class Role:
    def __init__(self, name, hp, attack):
        self.name = name
        self.hp = hp
        self.attack = attack

    def is_alive(self):
        return self.hp > 0

    def attack_action(self, other):
        other.hp -= self.attack

    def __str__(self):
        return f"name:{self.name}  hp:{self.hp} attack:{self.attack}"

    def __repr__(self):
        return f"Role({self.name}, {self.hp}, {self.attack})"

4.2 定义 RoleManage 类(组合 Role)

class RoleManage:
    def __init__(self):
        self.roles = []  # 组合:Role 实例列表

    def generate_roles(self, n=10):
        for i in range(1, n + 1):
            r = Role(f"role {i}", random.randint(3000, 5000), random.randint(100, 300))
            self.roles.append(r)

    def begin_game(self):
        while len(self.roles) > 1:
            # 依次攻击
            for i in range(len(self.roles) - 1):
                if self.roles[i].is_alive():
                    self.roles[i].attack_action(self.roles[i + 1])

            if self.roles[-1].is_alive():
                self.roles[-1].attack_action(self.roles[0])

            # 移除死亡角色(倒序遍历)
            for i in range(len(self.roles) - 1, -1, -1):
                if not self.roles[i].is_alive():
                    print(f"{self.roles[i].name} 剩余血量{self.roles[i].hp}死亡")
                    self.roles.pop(i)

            self.show_roles()

    def show_roles(self):
        print(self.roles)

4.3 运行游戏

rm = RoleManage()
rm.generate_roles()
rm.begin_game()

这个案例完美展示了组合的优势

  • RoleManage 不关心 Role 的内部实现细节

  • 角色列表的管理(生成、遍历、删除)与角色本身的战斗逻辑分离

  • 可以轻松扩展(如增加不同类型的角色、不同的攻击方式等)

五、继承 vs 组合:如何选择?

对比维度 继承 组合
关系类型 is-a(是一个) has-a(有一个)
耦合度 强耦合(父类变化影响子类) 弱耦合(组件独立变化)
灵活性 编译时确定,较僵化 运行时动态组合,灵活
代码复用 纵向复用(父子层级) 横向复用(平级组装)
适用场景 明确的类型层级关系 复杂功能组装、需要灵活替换

建议:当关系是“是一个”时用继承;当关系是“有一个”时用组合。在大多数场景下,组合比继承更灵活、更易维护

六、总结

知识点 要点
组合的定义 将一个类的实例作为另一个类的属性
组合 vs 继承 组合表达 has-a,继承表达 is-a
组合的优势 低耦合、高灵活、易扩展、可运行时替换
实战应用 批量对象管理、复杂系统组装、游戏开发

组合是 Python 面向对象编程中与继承并列的重要复用手段。掌握组合,你就能构建出更加灵活、可维护的系统。记住:优先使用组合,谨慎使用继承

更多推荐