我曾羡慕犬夜叉,我曾以为我会成为他,哈哈
废话少说,还是接着昨天的问题,书写吧
连载 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个小时。然后计算并显示每个员工的工资单
程序按预期工作,但是您必须添加四个新类来支持更改。随着新需求的出现,您的类层次结构将不可避免地增长,从而导致类爆炸问题,您的层次结构将变得非常大,以至于难以理解和维护
下图显示了新的类层次结构
欢迎读者留言互动,让我知道你喜欢什么样的文章