我曾羡慕犬夜叉,我曾以为我会成为他,哈哈

一起来探讨 python 类爆炸问题_python

废话少说,还是接着昨天的问题,书写吧

连载 Python OOP指南(1)

运行程序输出如下

$ python program.py

Calculating Payroll
===================
Payroll for: 1 - John Smith
- Check amount: 1500

Payroll for: 2 - Jane Doe
- Check amount: 600

Payroll for: 3 - Kevin Bacon
- Check amount: 1250

Payroll for: 20000 - Anonymous
- Check amount: 1000000

如您所见,​​PayrollSystem​​仍然可以处理新对象,因为它符合所需的接口

由于您不必从特定的类派生对象就可以被程序重用,因此您可能会问为什么应该使用继承而不是仅实现所需的接口。以下规则可能对您有帮助

使用继承来重用实现:您的派生类应该利用它们的大部分基类实现。他们还必须为is a relationship建模。客户类也可能有一个id和一个名称,但是客户不是雇员,所以您不应该使用继承。

实施要重用的接口:当您希望类被应用程序的特定部分重用时,您可以在类中实现所需的接口,但是无需提供基类或从另一个类继承

# In hr.py

class PayrollSystem:
def calculate_payroll(self, employees):
print('Calculating Payroll')
print('===================')
for employee in employees:
print(f'Payroll for: {employee.id} - {employee.name}')
print(f'- Check amount: {employee.calculate_payroll()}')
print('')

class Employee:
def __init__(self, id, name):
self.id = id
self.name = name

class SalaryEmployee(Employee):
def __init__(self, id, name, weekly_salary):
super().__init__(id, name)
self.weekly_salary = weekly_salary

def calculate_payroll(self):
return self.weekly_salary

class HourlyEmployee(Employee):
def __init__(self, id, name, hours_worked, hour_rate):
super().__init__(id, name)
self.hours_worked = hours_worked
self.hour_rate = hour_rate

def calculate_payroll(self):
return self.hours_worked * self.hour_rate

class CommissionEmployee(SalaryEmployee):
def __init__(self, id, name, weekly_salary, commission):
super().__init__(id, name, weekly_salary)
self.commission = commission

def calculate_payroll(self):
fixed = super().calculate_payroll()
return fixed + self.commission

您删除了​​abc​​​模块的导入,因为​​Employee​​​类不需要是抽象的。您还从其中删除了抽象的​​calculate_payroll()​​方法,因为它不提供任何实现

基本上,您继承了派生类中​​Employee​​​类的id和name属性的实现。因为​​.calculate_payroll()​​​只是​​PayrollSystem.calculate_payroll()​​​方法的一个接口,所以不需要在​​Employee​​基类中实现它

注意​​commission​​​类是如何从​​SalaryEmployee​​​派生出来的。这意味着​​commonemployee​​​继承了​​SalaryEmployee​​​的实现和接口。可以看到,​​calculate_payroll()​​​方法如何利用基类实现,因为它依赖于​​super().calculate_payroll()​​的结果来实现自己的版本

类爆炸问题

如果您不小心,继承会导致您进入难以理解和维护的巨大的类层次结构。这称为类爆炸问题

您开始构建薪资系统用于计算薪资的​​Employee​​​类型的类层次结构。现在,您需要向这些类添加一些功能,以便它们可以与新的​​ProductivitySystem​​一起使用

​ProductivitySystem​​根据员工角色跟踪生产力。有不同的员工角色

经理:他们四处走走,大喊大叫,告诉他们该怎么做。他们是受薪雇员,可赚更多钱

秘书:他们为经理完成所有书面工作,并确保一切按时计费和付款。他们也是受薪员工,但赚的钱少

销售员工:他们打很多电话来销售产品。他们有薪水,但他们也会得到销售佣金

工厂工人:他们为公司生产产品。他们按小时支付

有了这些要求,您开始发现​​Employee​​​及其派生类可能属于hr模块之外的其他位置,因为现在​​ProductivitySystem​​也使用了它们

您创建一个​​employees​​模块并将类移到那里

# In employees.py

class Employee:
def __init__(self, id, name):
self.id = id
self.name = name

class SalaryEmployee(Employee):
def __init__(self, id, name, weekly_salary):
super().__init__(id, name)
self.weekly_salary = weekly_salary

def calculate_payroll(self):
return self.weekly_salary

class HourlyEmployee(Employee):
def __init__(self, id, name, hours_worked, hour_rate):
super().__init__(id, name)
self.hours_worked = hours_worked
self.hour_rate = hour_rate

def calculate_payroll(self):
return self.hours_worked * self.hour_rate

class CommissionEmployee(SalaryEmployee):
def __init__(self, id, name, weekly_salary, commission):
super().__init__(id, name, weekly_salary)
self.commission = commission

def calculate_payroll(self):
fixed = super().calculate_payroll()
return fixed + self.commission

实现保持不变,但是将类移动到​​employee​​模块。现在,您更改您的程序以支持更改

# In program.py

import hr
import employees

salary_employee = employees.SalaryEmployee(1, 'John Smith', 1500)
hourly_employee = employees.HourlyEmployee(2, 'Jane Doe', 40, 15)
commission_employee = employees.CommissionEmployee(3, 'Kevin Bacon', 1000, 250)
payroll_system = hr.PayrollSystem()
payroll_system.calculate_payroll([
salary_employee,
hourly_employee,
commission_employee
])

您运行该程序并验证它仍然可以工作

$ python program.py

Calculating Payroll
===================
Payroll for: 1 - John Smith
- Check amount: 1500

Payroll for: 2 - Jane Doe
- Check amount: 600

Payroll for: 3 - Kevin Bacon
- Check amount: 1250

一切就绪后,您就可以开始添加新类了

# In employees.py

class Manager(SalaryEmployee):
def work(self, hours):
print(f'{self.name} screams and yells for {hours} hours.')

class Secretary(SalaryEmployee):
def work(self, hours):
print(f'{self.name} expends {hours} hours doing office paperwork.')

class SalesPerson(CommissionEmployee):
def work(self, hours):
print(f'{self.name} expends {hours} hours on the phone.')

class FactoryWorker(HourlyEmployee):
def work(self, hours):
print(f'{self.name} manufactures gadgets for {hours} hours.')

首先,添加一个从​​SalaryEmployee​​​派生的​​Manager​​​类。该类公开了将由生产力系统使用的​​work()​​方法。该方法占用员工工作时间

然后添加​​Secretary​​​,​​SalesPerson​​​和​​FactoryWorker​​​,然后实现​​work()​​接口,以便生产力系统可以使用它们。

现在,您可以添加​​ProductivitySytem​​类

# In productivity.py

class ProductivitySystem:
def track(self, employees, hours):
print('Tracking Employee Productivity')
print('==============================')
for employee in employees:
employee.work(hours)
print('')

该类使用​​track()​​方法跟踪员工,该方法获取员工列表和要跟踪的小时数。您现在可以将生产力系统添加到程序中

# In program.py

import hr
import employees
import productivity

manager = employees.Manager(1, 'Mary Poppins', 3000)
secretary = employees.Secretary(2, 'John Smith', 1500)
sales_guy = employees.SalesPerson(3, 'Kevin Bacon', 1000, 250)
factory_worker = employees.FactoryWorker(2, 'Jane Doe', 40, 15)
employees = [
manager,
secretary,
sales_guy,
factory_worker,
]
productivity_system = productivity.ProductivitySystem()
productivity_system.track(employees, 40)
payroll_system = hr.PayrollSystem()
payroll_system.calculate_payroll(employees)

该程序将创建不同类型的员工列表。员工名单被发送到生产力系统以跟踪他们40个小时的工作。然后,将相同的员工列表发送到薪资系统以计算其薪资

$ python program.py

Tracking Employee Productivity
==============================
Mary Poppins screams and yells for 40 hours.
John Smith expends 40 hours doing office paperwork.
Kevin Bacon expends 40 hours on the phone.
Jane Doe manufactures gadgets for 40 hours.

Calculating Payroll
===================
Payroll for: 1 - Mary Poppins
- Check amount: 3000

Payroll for: 2 - John Smith
- Check amount: 1500

Payroll for: 3 - Kevin Bacon
- Check amount: 1250

Payroll for: 4 - Jane Doe
- Check amount: 600

该程序显示员工通过生产力系统工作40个小时。然后计算并显示每个员工的工资单

程序按预期工作,但是您必须添加四个新类来支持更改。随着新需求的出现,您的类层次结构将不可避免地增长,从而导致类爆炸问题,您的层次结构将变得非常大,以至于难以理解和维护

下图显示了新的类层次结构

一起来探讨 python 类爆炸问题_派生类_02

欢迎读者留言互动,让我知道你喜欢什么样的文章

一起来探讨 python 类爆炸问题_python_03