Python 组合模式详解 —— 用“拥有”关系构建灵活的系统
·
前言
在面向对象编程中,代码复用的两大支柱是继承和组合。继承表达的是 “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 类非常灵活:
-
可以随时替换
car、card或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 面向对象编程中与继承并列的重要复用手段。掌握组合,你就能构建出更加灵活、可维护的系统。记住:优先使用组合,谨慎使用继承。
更多推荐



所有评论(0)