你能否通过一个银行账户案例理解封装、继承和多态的实际应用

wen java案例 50

本文目录导读:

你能否通过一个银行账户案例理解封装、继承和多态的实际应用

  1. 封装 —— 保护你的“小金库”
  2. 继承 —— 复用“银行模板”
  3. 多态 —— 同一个接口,不同表现
  4. 对应关系

这是一个非常经典的面向对象编程(OOP)教学场景,用一个银行账户系统来解释封装、继承和多态,既直观又贴近现实。

下面我们一步步构建这个系统,并解释每个概念是如何体现的。


封装 —— 保护你的“小金库”

核心思想:把数据(属性)和操作数据的方法(行为)捆绑在一起,并隐藏内部细节,对外只暴露必要的接口,就像你不需要知道银行后台如何计算利息,只需要知道“查询余额”和“取款”按钮怎么按。

案例分析

假设我们有一个 BankAccount 类。

class BankAccount:
    def __init__(self, owner, initial_balance):
        self.__owner = owner          # 私有属性: 两个下划线开头
        self.__balance = initial_balance  # 私有属性: 余额不能直接修改
    # 公共方法: 获取余额 (对外暴露的接口)
    def get_balance(self):
        return self.__balance
    # 公共方法: 存款
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"存款成功,当前余额: {self.__balance}")
        else:
            print("存款金额必须大于0")
    # 公共方法: 取款 (内部校验逻辑隐藏)
    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"取款成功,当前余额: {self.__balance}")
        else:
            print("余额不足或金额无效")

这个例子中的封装体现在

  • __balance 是私有属性,外部代码不能直接 account.__balance = 1000
  • 必须通过 get_balance()deposit()withdraw() 这些公开接口来操作。
  • 存款和取款时的校验逻辑(如金额必须大于0、不能透支)被封装在方法内部,使用者无需关心。

继承 —— 复用“银行模板”

核心思想:子类可以继承父类的属性和方法,并在此基础上添加自己特有的行为,就像“储蓄卡”和“信用卡”都是“银行卡”,有共同的基础功能(存、取、查余额),但也有各自的特有功能(信用卡可以透支)。

案例分析

我们从 BankAccount 派生出两个子类:SavingsAccount(储蓄账户)和 CheckingAccount(支票账户/活期账户)。

# 继承自 BankAccount
class SavingsAccount(BankAccount):
    def __init__(self, owner, initial_balance, interest_rate):
        # 调用父类的构造方法
        super().__init__(owner, initial_balance)
        self.__interest_rate = interest_rate   # 新增属性: 利率
    # 新增方法: 计算利息
    def apply_interest(self):
        interest = self._BankAccount__balance * self.__interest_rate / 100  # 注意访问私有属性的方式
        # 更推荐的方式: 通过父类的存款方法
        self.deposit(interest)
        print(f"应用利息 {interest:.2f} 元")
class CheckingAccount(BankAccount):
    def __init__(self, owner, initial_balance, overdraft_limit):
        super().__init__(owner, initial_balance)
        self.__overdraft_limit = overdraft_limit  # 新增属性: 透支额度
    # 重写父类方法: 允许透支
    def withdraw(self, amount):
        # 特有逻辑: 余额 + 透支额度 >= 取款金额
        if 0 < amount <= (self._BankAccount__balance + self.__overdraft_limit):
            # 调用父类的取款方法? 不行, 因为父类有余额限制。
            # 需要直接操作余额(为了演示, 这里直接操作私有属性, 实际应使用 protected)
            self._BankAccount__balance -= amount
            print(f"取款成功 (可使用透支),当前余额: {self._BankAccount__balance}")
        else:
            print("超过透支额度")

这个例子中的继承体现在

  • SavingsAccountCheckingAccount继承了 BankAccountget_balance()deposit() 方法,不需要重写。
  • 它们各自增加了新属性(利率、透支额度)和新方法(apply_interest)。
  • 它们可以选择重用或重写父类的方法(CheckingAccount 重写了 withdraw)。

多态 —— 同一个接口,不同表现

核心思想:不同类的对象,对同一个方法调用,会做出不同的响应,前提是它们共享一个共同的基类或接口,所有类型的账户都有“取款”这个功能,但储蓄卡不能透支,信用卡可以。

案例分析

我们编写一个通用的函数,来处理不同类型的账户。

def process_account(account: BankAccount):
    """模拟月末处理: 对不同类型的账户进行不同操作"""
    print(f"处理账户: {account.__class__.__name__}")
    print(f"当前余额: {account.get_balance()}")
    # 重点: 相同的调用, 不同的行为!
    account.deposit(100)  # 所有账户都能存款, 行为一致
    # 取款: 不同类型的账户, 取款逻辑可能不同
    account.withdraw(200)  # 储蓄卡可能失败(余额不足), 支票卡可能成功(允许透支)
    print("--- 处理完成 ---\n")
# 创建实例
savings = SavingsAccount("张三", 500, 2.5)
checking = CheckingAccount("李四", 100, 500)
# 调用同一个函数, 传入不同类型的账户
process_account(savings)   # 输出: 存款100, 取款200失败(余额600, 但取200够, 实际是没重写withdraw)
                         # 更正: SavingsAccount 没有重写 withdraw, 所以用父类的, 余额600>200, 成功。
                         # 我们修改一下: 让 SavingsAccount 的余额初始为50, 这样取200会失败。
# 为了演示更清晰, 我们重新初始化:
savings2 = SavingsAccount("王五", 50, 3.0)
checking2 = CheckingAccount("赵六", 100, 500)
def process_account_polymorphic(account):
    print(f"处理 {account.__class__.__name__}")
    print(f"余额: {account.get_balance()}")
    account.withdraw(200)   # 这里就是多态的核心!
    print(f"取款后余额: {account.get_balance()}")
process_account_polymorphic(savings2)    # 输出: 余额不足或金额无效 (因为savings2余额只有50)
process_account_polymorphic(checking2)   # 输出: 取款成功 (因为checking2能透支, 余额-100)

这个例子中的多态体现在

  • process_account_polymorphic 函数接受一个 BankAccount 类型的参数。
  • 在函数内部,调用了 account.withdraw(200)
  • 当传入 SavingsAccount 对象时,withdraw 行为是“余额不足则拒绝”。
  • 当传入 CheckingAccount 对象时,withdraw 行为是“允许透支”。
  • 相同的代码(account.withdraw(200)),因对象类型不同,产生不同的结果

对应关系

概念 银行业的现实类比 代码中的体现
封装 银行的数据库(余额、密码)只有内部系统能访问,你只能通过ATM或柜台(接口)操作。 私有属性 __balance + 公共方法 get_balance()deposit()withdraw()
继承 储蓄卡、信用卡都是“银行卡”的扩展,它们共享卡号、密码、基本存取功能,但各有特点(利率、透支)。 SavingsAccountCheckingAccount 继承 BankAccount,重用并扩展了功能。
多态 你喊一声“取200块”,储蓄卡会检查余额;信用卡会检查信用额度,同一个指令“取款”,不同卡的处理方式不同。 调用 account.withdraw(200),实际执行的是对象所属子类的 withdraw 方法。

通过这个银行账户案例,你应该能清晰地看到 封装保护了数据和实现细节,继承实现了代码的复用和扩展,多态让代码更灵活、可扩展性更强。 这三者共同构成了面向对象编程的基石。

抱歉,评论功能暂时关闭!