专栏首页志学Python灵活的设计组合

灵活的设计组合

组合比继承更灵活,因为它可以建模松散耦合的关系。对组件类的更改对复合类影响很小或没有影响。基于组成的设计更适合更改

在本部分中,您将使用合成来实现仍然符合PayrollSystemProductivitySystem要求的更好的设计

# In productivity.py

class ProductivitySystem:
    def __init__(self):
        self._roles = {
            'manager': ManagerRole,
            'secretary': SecretaryRole,
            'sales': SalesRole,
            'factory': FactoryRole,
        }

    def get_role(self, role_id):
        role_type = self._roles.get(role_id)
        if not role_type:
            raise ValueError('role_id')
        return role_type()

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

ProductivitySystem类使用映射到实现角色的角色类的字符串标识符来定义一些角色。它公开一个.get_role()方法,该方法在给定角色标识符的情况下,返回角色类型对象。如果没有找到该角色,则会引发ValueError异常

# In productivity.py

class ManagerRole:
    def perform_duties(self, hours):
        return f'screams and yells for {hours} hours.'

class SecretaryRole:
    def perform_duties(self, hours):
        return f'does paperwork for {hours} hours.'

class SalesRole:
    def perform_duties(self, hours):
        return f'expends {hours} hours on the phone.'

class FactoryRole:
    def perform_duties(self, hours):
        return f'manufactures gadgets for {hours} hours.'

您实现的每个角色都公开了一个.perform_duties(),它占用了工作的小时数。这些方法返回一个表示职责的字符串

角色类彼此独立,但它们公开相同的接口,因此它们是可互换的。稍后您将看到如何在应用程序中使用它们

# In hr.py

class PayrollSystem:
    def __init__(self):
        self._employee_policies = {
            1: SalaryPolicy(3000),
            2: SalaryPolicy(1500),
            3: CommissionPolicy(1000, 100),
            4: HourlyPolicy(15),
            5: HourlyPolicy(9)
        }

    def get_policy(self, employee_id):
        policy = self._employee_policies.get(employee_id)
        if not policy:
            return ValueError(employee_id)
        return policy

    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()}')
            if employee.address:
                print('- Sent to:')
                print(employee.address)
            print('')

PayrollSystem为每个员工保留一个工资政策的内部数据库。它公开一个.get_policy(),给定一个员工id,返回其工资单策略。如果系统中不存在指定的id,则该方法将引发ValueError异常

calculate_payroll()的实现与以前的工作方式相同。它获取一个雇员列表,计算工资单,并打印结果

# In hr.py

class PayrollPolicy:
    def __init__(self):
        self.hours_worked = 0

    def track_work(self, hours):
        self.hours_worked += hours

class SalaryPolicy(PayrollPolicy):
    def __init__(self, weekly_salary):
        super().__init__()
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary

class HourlyPolicy(PayrollPolicy):
    def __init__(self, hour_rate):
        super().__init__()
        self.hour_rate = hour_rate

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

class CommissionPolicy(SalaryPolicy):
    def __init__(self, weekly_salary, commission_per_sale):
        super().__init__(weekly_salary)
        self.commission_per_sale = commission_per_sale

    @property
    def commission(self):
        sales = self.hours_worked / 5
        return sales * self.commission_per_sale

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

首先,您要实现一个PayrollPolicy类,该类充当所有薪资策略的基类。此类跟踪工作小时数,这是所有工资单政策所共有的

其他策略类源自PayrollPolicy。我们在这里使用继承是因为我们想利用PayrollPolicy的实现。此外,SalaryPolicyHourlyPolicyCommissionPolicy也是PayrollPolicy

SalaryPolicy使用weekly_salary值初始化,然后在.calculate_payroll()中使用该值。HourlyPolicy使用hour_rate初始化,并通过利用基本类hours_working实现.calculate_payroll()

CommissionPolicy类派生自SalaryPolicy,因为它希望继承其实现。它是用weekly_salary参数初始化的,但是它还需要一个common_per_sale参数

使用common_per_sale来计算.commission,它被实现为一个属性,因此在请求时计算它。在这个例子中,我们假设每5小时工作一次,而.commission是销售的数量乘以commission_per_sale

首先利用SalaryPolicy中的实现,然后添加计算佣金,从而实现.calculate_payroll()方法。

# In contacts.py

class AddressBook:
    def __init__(self):
        self._employee_addresses = {
            1: Address('121 Admin Rd.', 'Concord', 'NH', '03301'),
            2: Address('67 Paperwork Ave', 'Manchester', 'NH', '03101'),
            3: Address('15 Rose St', 'Concord', 'NH', '03301', 'Apt. B-1'),
            4: Address('39 Sole St.', 'Concord', 'NH', '03301'),
            5: Address('99 Mountain Rd.', 'Concord', 'NH', '03301'),
        }

    def get_employee_address(self, employee_id):
        address = self._employee_addresses.get(employee_id)
        if not address:
            raise ValueError(employee_id)
        return address

AddressBook类为每个员工保留一个Address对象的内部数据库。它公开一个get_employee_address()方法,该方法返回指定员工id的地址。如果员工id不存在,则会引发一个ValueError错误

# In contacts.py

class Address:
    def __init__(self, street, city, state, zipcode, street2=''):
        self.street = street
        self.street2 = street2
        self.city = city
        self.state = state
        self.zipcode = zipcode

    def __str__(self):
        lines = [self.street]
        if self.street2:
            lines.append(self.street2)
        lines.append(f'{self.city}, {self.state} {self.zipcode}')
        return '\n'.join(lines)

该类管理地址组件并提供地址的漂亮表示形式

到目前为止,已经扩展了新类以支持更多功能,但是对以前的设计没有重大更改。这将随着员工模块及其类的设计而改变

本文分享自微信公众号 - 志学Python(lijinwen1996329ken),作者:志学Python

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 图解算法系列(五): 堆栈

    下压栈(或简称栈)是一种基于后进后出的(LIFO)策咯的集合类型. 其中添加移除新项总发生在同一端。这一端通常称为“顶部”。与顶部对应的端称为“底部”。栈的例子...

    公众号---志学Python
  • python算法队列

    3、练习:用上述的代码,完成67,45,34节点顺序放入队列,之后从队列的头部开始访问队列里的每一个元素。

    公众号---志学Python
  • 一起来探讨 python 类爆炸问题

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

    公众号---志学Python
  • Python与ZooKeeper集群连接

    由于项目的需要,需要学习Python客户端连接ZooKeeper集群,并实现创建临时节点、获得指定的路径下的信息、监听子节点变化的功能。

    py3study
  • 用python炒股?python除了生孩子还有什么不能的!

    不用深厚的数学功底也不用深厚的金融知识, 本文中也不会引用各种高深的投资模型或数学模型。这不用,那不用的,到底怎么用python炒股?往下看

    一墨编程学习
  • wxPython_04_实现Frame之间的跳转/更新的一种方法

    wxPython是Python中重要的GUI框架,下面通过自己的方法实现模拟类似PC版微信登录,并跳转到主界面(朋友圈)的流程。

    码农帮派
  • python练习题-day23

    郭耀华
  • 用Python炒股,你不可以我能行!网友:略牛

    由于小编并无深厚的数学功底也无深厚的金融知识, 所以不会在本文中引用各种高深的投资模型或数学模型。

    诸葛青云
  • python实现最大熵模型

    py3study
  • Python《植物大战僵尸》代码实现:植物卡片选择和种植

    最近一直在给这个植物大战僵尸游戏添加新的植物和僵尸, 因为网上的图片资源有限,能加的植物和僵尸比较少, 目前进展如下。

    AI科技大本营

扫码关注云+社区

领取腾讯云代金券