面向对象特性

学习目标

通过本集的学习,你将能够:

  • 理解封装的好处
  • 掌握继承的利弊
  • 理解多态的实现
  • 学会使用接口与抽象类

1. 封装

1.1 什么是封装?

封装将数据和操作封装在一起,隐藏内部细节。

封装示例:
class BankAccount {
private:
    double balance;  // 私有,外部无法直接访问

public:
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        }
    }

    double get_balance() {
        return balance;
    }
};

1.2 封装的好处

封装的优点:
1. 数据保护:防止非法访问
2. 简化接口:只暴露必要的方法
3. 易于修改:内部实现可以改变
4. 可重用性:独立的模块

1.3 访问控制

访问修饰符(Java):
- public: 任何人都可访问
- protected: 子类和同包可访问
- private: 只有自己可访问
- (default): 同包可访问

访问修饰符(C++):
- public: 公开
- protected: 受保护
- private: 私有

2. 继承

2.1 什么是继承?

继承让一个类继承另一个类的属性和方法。

继承示例:
class Animal {
public:
    void eat() {
        cout << "Eating" << endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        cout << "Barking" << endl;
    }
};

Dog d;
d.eat();  // 继承自 Animal
d.bark(); // 自己的方法

2.2 继承的好处

继承的优点:
1. 代码复用:避免重复代码
2. 层次结构:组织类的关系
3. 多态基础:支持多态

2.3 继承的问题

继承的缺点:
1. 紧耦合:子类依赖父类实现
2. 脆弱基类:修改父类可能破坏子类
3. 继承膨胀:层次过深
4. 不够灵活:运行时无法改变

2.4 组合优于继承

优先使用组合:
class Engine {
public:
    void start() { ... }
};

class Car {
private:
    Engine engine;  // 组合,不是继承

public:
    void start() {
        engine.start();
    }
};

3. 多态

3.1 什么是多态?

多态让不同对象对同一消息有不同响应。

多态示例:
class Shape {
public:
    virtual double area() = 0;  // 纯虚函数
};

class Circle : public Shape {
private:
    double r;
public:
    Circle(double r) : r(r) {}
    double area() override {
        return 3.14 * r * r;
    }
};

class Rectangle : public Shape {
private:
    double w, h;
public:
    Rectangle(double w, double h) : w(w), h(h) {}
    double area() override {
        return w * h;
    }
};

// 使用
vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));

for (Shape *s : shapes) {
    cout << s->area() << endl;  // 多态调用
}

3.2 多态的实现

动态绑定(虚函数表):

每个有虚函数的类有一个 vtable
每个对象有一个 vptr 指向 vtable

调用时:
1. 通过 vptr 找到 vtable
2. 找到对应的函数指针
3. 调用函数

4. 接口与抽象类

4.1 抽象类

抽象类不能实例化,包含抽象方法。

抽象类(Java):
abstract class Shape {
    abstract double area();  // 抽象方法

    void print() {  // 普通方法
        System.out.println("Shape");
    }
}

4.2 接口

接口定义契约,只有方法签名。

接口(Java):
interface Drawable {
    void draw();
}

interface Resizable {
    void resize(double factor);
}

class Circle implements Drawable, Resizable {
    public void draw() { ... }
    public void resize(double factor) { ... }
}

4.3 抽象类 vs 接口

抽象类:
- 可以有构造函数
- 可以有非抽象方法
- 可以有字段
- 单继承

接口:
- 不能有构造函数
- 只有抽象方法(Java 8+ 可有默认方法)
- 只有常量
- 多实现

5. 实用案例

5.1 案例1:封装银行账户

class BankAccount {
    private double balance;

    public BankAccount(double initial) {
        balance = initial;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        }
    }

    public double getBalance() {
        return balance;
    }
}

5.2 案例2:多态形状

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, r):
        self.r = r

    def area(self):
        return 3.14 * self.r * self.r

class Rectangle(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

shapes = [Circle(5), Rectangle(4, 6)]
for s in shapes:
    print(s.area())

6. 自测问题

  1. 封装的好处是什么?
  2. 继承有什么优缺点?
  3. 什么是多态?
  4. 抽象类和接口的区别是什么?
  5. 为什么说组合优于继承?

7. 下集预告

下一集我们将学习异常处理机制!

参考资料

  • 《设计模式》(GoF)
  • 《程序设计语言:概念与构造》
« 上一篇 指针与引用 下一篇 » 异常处理机制