16. 应用案例:工资结算系统

某公司有三种类型的员工:部门经理月薪固定 15000 元;程序员按工作时间支付月薪,每小时 200 元;销售员月薪由 1800 元底薪加上销售额 5% 的提成构成。设计一个工资结算系统。

定义抽象类 Employee

部门经理、程序员、销售员都是员工,有相同的属性和行为。我们先设计一个名为 Employee 的父类,再通过继承派生子类。由于不会直接创建 Employee 类的对象,我们可以将其设计成专门用于继承的抽象类。通过 abc 模块中名为 ABCMeta 的元类来定义抽象类。

python
from abc import ABCMeta, abstractmethod


class Employee(metaclass=ABCMeta):
    """员工"""

    def __init__(self, name):
        self.name = name

    @abstractmethod
    def get_salary(self):
        """结算月薪"""
        pass

使用 @abstractmethod 装饰器将 get_salary 声明为抽象方法:抽象方法就是只有声明没有实现的方法,声明这个方法是为了让子类去重写这个方法。

派生三个子类

python
class Manager(Employee):
    """部门经理"""

    def get_salary(self):
        return 15000.0


class Programmer(Employee):
    """程序员"""

    def __init__(self, name, working_hour=0):
        super().__init__(name)
        self.working_hour = working_hour

    def get_salary(self):
        return 200 * self.working_hour


class Salesman(Employee):
    """销售员"""

    def __init__(self, name, sales=0):
        super().__init__(name)
        self.sales = sales

    def get_salary(self):
        return 1800 + self.sales * 0.05

三个类分别重写了 get_salary 方法,各实现不同——这就是多态:调用相同的方法,不同的子类对象做不同的事情。

使用 isinstance 判断类型并结算

python
emps = [Manager('刘备'), Programmer('诸葛亮'), Manager('曹操'),
        Programmer('荀彧'), Salesman('张辽')]
for emp in emps:
    if isinstance(emp, Programmer):
        emp.working_hour = int(input(f'请输入{emp.name}本月工作时间: '))
    elif isinstance(emp, Salesman):
        emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
    print(f'{emp.name}本月工资为: ¥{emp.get_salary():.2f}元')

isinstance 函数可以判断出一个对象是不是某个继承结构下的子类型,比 type 函数更强大(type 是精准匹配,isinstance 是模糊匹配)。


本章小结

面向对象编程是一种非常流行的编程范式,除此之外还有指令式编程函数式编程等。由于现实世界是由对象构成的,而对象是可以接收消息的实体,所以面向对象编程更符合人类正常的思维习惯

类是抽象的,对象是具体的,有了类就能创建对象,有了对象就可以接收消息,这就是面向对象编程的基础。定义类的过程是一个抽象的过程:找到对象公共的属性属于数据抽象,找到对象公共的方法属于行为抽象

面向对象的四大核心概念:抽象封装继承多态。要想灵活运用需要长时间的积累和沉淀,大量的编程练习和阅读优质的代码是最好的方法。