一、为什么需要「类的组合」?

在面向对象中有两种最核心的代码复用方式:

  1. 继承(is-a):是什么关系

  2. 组合(has-a):有什么关系

继承适合:子类是父类的一种(猫是动物、学生是人)

组合适合:一个类包含另一个类的对象(手机有屏幕、人有大脑、电脑有CPU)

面试金句:优先组合,少用继承。 组合耦合度更低、代码更灵活,是实际项目主流写法。

二、什么是类的组合?

2.1 概念定义

类的组合:在一个类中,将另一个类的对象作为自己的属性

简单理解:一个类包含另一个类,实现功能复用。

2.2 核心关系

继承:is-a 学生 人类

组合:has-a 电脑 CPU 人 身份证 汽车 发动机

三、基础组合案例(通俗易懂)

场景:定义 CPU类电脑类,电脑包含CPU

# 子类:CPU类
class Cpu:
    def __init__(self, core):
        self.core = core  # 核心数

    def run(self):
        print(f"{self.core}核CPU正在高速运行!")

# 主类:电脑类(组合CPU)
class Computer:
    def __init__(self, brand, cpu_core):
        self.brand = brand
        # 将另一个类的对象,作为当前类的属性
        self.cpu = Cpu(cpu_core)

    def start(self):
        print(f"{self.brand} 电脑开机")
        # 调用组合对象的方法
        self.cpu.run()

# 测试
pc = Computer("联想", 8)
pc.start()

运行结果

联想 电脑开机
8核CPU正在高速运行!

代码解析

Computer 类中并没有写 CPU 相关代码,直接组合 Cpu 对象,实现功能复用。

优点:CPU类可以单独维护、单独修改、多次复用

四、进阶案例:多人组合场景

场景:每个人拥有一个身份证,多人可以复用身份证类

# 身份证类
class IdCard:
    def __init__(self, id_num, address):
        self.id_num = id_num
        self.address = address

    def show_id(self):
        print(f"身份证号:{self.id_num},地址:{self.address}")

# 人类(组合身份证)
class Person:
    def __init__(self, name, age, id_obj):
        self.name = name
        self.age = age
        # 组合:传入IdCard对象
        self.id_card = id_obj

    def show_info(self):
        print(f"姓名:{self.name},年龄:{self.age}")
        self.id_card.show_id()

# 测试:两个不同的人,组合各自的身份证
card1 = IdCard("110101200001011234", "北京市")
p1 = Person("张三", 20, card1)

card2 = IdCard("310101199902024567", "上海市")
p2 = Person("李四", 22, card2)

p1.show_info()
print("-" * 40)
p2.show_info()

五、组合的优势:低耦合、高复用

如果用继承实现上面的案例

人继承身份证 → 逻辑错误,人不是身份证,强行继承会造成语义混乱。

组合完美解决

语义正确:人 身份证

代码解耦:身份证类和人类互不干涉

灵活替换:可以随时更换 CPU、更换身份证对象

六、组合 vs 继承(超清晰对比)

对比项

继承(extends)

组合(composition)

关系

is-a 是什么

has-a 有什么

耦合度

高(父子强依赖)

低(独立模块)

灵活性

差,固定继承

高,可动态替换对象

使用场景

层级体系、从属关系

部件组装、功能复用七、动态组合(高阶写法)

七、动态组合(高阶写法)

不用在构造方法固定传入,可以动态绑定组件,灵活性拉满。

class Battery:
    def power(self):
        print("电池供电中...")

class Phone:
    def __init__(self, name):
        self.name = name
        self.battery = None  # 先空着

    # 动态组合
    def set_battery(self, bat_obj):
        self.battery = bat_obj

    def work(self):
        if self.battery:
            self.battery.power()
            print(f"{self.name} 正常工作")

# 动态装配电池
p = Phone("华为手机")
b = Battery()
p.set_battery(b)
p.work()

八、常见面试题总结

1. 什么是组合?

在一个类中包含另一个类的实例对象作为属性,用来实现代码复用,属于 has-a 关系。

2. 组合和继承哪个好?

组合更好,耦合更低、扩展性更强、符合设计模式思想。

3. 什么时候用组合?

当两个类是 包含、组件、附属 关系时,一律用组合。

九、全文总结

  1. 组合是 has-a 包含关系,继承是 is-a 从属关系;

  2. 组合:将一个类的对象作为另一个类的属性;

  3. 组合代码解耦、灵活、可单独维护组件;

  4. 项目开发优先使用组合,避免过度继承导致代码僵化。

✨ 点赞收藏,OOP三大核心:封装、继承、组合 全部学完!

更多推荐