Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用组合自定义行为

使用组合自定义行为

作者头像
公众号---人生代码
发布于 2020-05-18 08:14:47
发布于 2020-05-18 08:14:47
49600
代码可运行
举报
文章被收录于专栏:人生代码人生代码
运行总次数:0
代码可运行

如果您的设计依赖于继承,则需要找到一种方法来更改对象的类型以更改其行为。对于组合,您只需要更改对象使用的策略

想象一下,我们的经理突然变成了按小时计酬的临时雇员。您可以通过以下方式在程序执行期间修改对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In program.py

from hr import PayrollSystem, HourlyPolicy
from productivity import ProductivitySystem
from employees import EmployeeDatabase

productivity_system = ProductivitySystem()
payroll_system = PayrollSystem()
employee_database = EmployeeDatabase()
employees = employee_database.employees
manager = employees[0]
manager.payroll = HourlyPolicy(55)

productivity_system.track(employees, 40)
payroll_system.calculate_payroll(employees)

该程序从EmployeeDatabase获取员工列表,并检索第一个员工,即我们想要的经理。然后,它会创建一个新的HourlyPolicy,初始化为每小时$55,并将其分配给manager对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ python program.py

Tracking Employee Productivity
==============================
Employee 1 - Mary Poppins:
- screams and yells for 40 hours.

Employee 2 - John Smith:
- does paperwork for 40 hours.

Employee 3 - Kevin Bacon:
- expends 40 hours on the phone.

Employee 4 - Jane Doe:
- manufactures gadgets for 40 hours.

Employee 5 - Robin Williams:
- does paperwork for 40 hours.


Calculating Payroll
===================
Payroll for: 1 - Mary Poppins
- Check amount: 2200
- Sent to:
121 Admin Rd.
Concord, NH 03301

Payroll for: 2 - John Smith
- Check amount: 1500
- Sent to:
67 Paperwork Ave
Manchester, NH 03101

Payroll for: 3 - Kevin Bacon
- Check amount: 1800.0
- Sent to:
15 Rose St
Apt. B-1
Concord, NH 03301

Payroll for: 4 - Jane Doe
- Check amount: 600
- Sent to:
39 Sole St.
Concord, NH 03301

Payroll for: 5 - Robin Williams
- Check amount: 360
- Sent to:
99 Mountain Rd.
Concord, NH 03301

在Python中选择继承和组合

到目前为止,您已经了解了在Python中继承和组合是如何工作的。您已经看到派生类继承了它们的基类的接口和实现。您还看到了组合允许您重用另一个类的实现

对于同一个问题,您已经实现了两个解决方案。第一个解决方案使用多重继承,第二个使用复合

您还看到Python的duck类型化允许您通过实现所需的接口来重用具有程序现有部分的对象。在Python中,没有必要从基类派生出要重用的类

此时,您可能会问什么时候在Python中使用继承与组合。它们都支持代码重用。继承和组合可以解决Python程序中的类似问题

一般的建议是使用在两个类之间创建较少依赖关系的关系。这种关系就是组成。不过,有时继承会更有意义。

以下部分提供了一些指导原则,帮助您在Python中的继承和组合之间做出正确的选择

继承到模型“a”关系

继承仅应用于为一个关系建模。Liskov的替换原理说,继承自Base的Derived类型的对象可以替换Base类型的对象,而无需更改程序的所需属性

Liskov的替代原则是决定继承是否是合适的设计解决方案的最重要的指导原则。不过,答案可能并非在所有情况下都是直截了当的。幸运的是,您可以使用一个简单的测试来确定您的设计是否遵循Liskov的替换原则

假设您有一个类a,它提供了一个您希望在另一个类B中重用的实现和接口。您最初的想法是可以从a派生出B,并继承接口和实现。为了确保这是正确的设计,您需要遵循以下步骤:

您有一个类矩形,它公开一个.area属性。您需要一个类Square,它也有一个.area。似乎正方形是一种特殊类型的矩形,所以您可以从它派生并利用接口和实现。

正方形是长方形,因为它的面积是由它的高乘以它的长计算出来的。约束条件是这个平方。高度和广场。长度必须相等。

它是有意义的。你可以证明这种关系,并解释为什么正方形是长方形。让我们来颠倒一下这种关系,看看它是否有意义

长方形是正方形,因为它的面积是由它的高乘以它的长计算出来的。差值就是这个矩形。高度和矩形。宽度可以独立变化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

class Rectangle:
    def __init__(self, length, height):
        self._length = length
        self._height = height

    @property
    def area(self):
        return self._length * self._height

使用长度和高度初始化Rectangle类,它提供一个.area属性来返回该区域。长度和高度被封装,以避免直接改变它们。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

class Square(Rectangle):
    def __init__(self, side_size):
        super().__init__(side_size, side_size)

Square类使用side_size初始化,该side_size用于初始化基类的两个组件。现在,您编写一个小程序来测试行为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

rectangle = Rectangle(2, 4)
assert rectangle.area == 8

square = Square(2)
assert square.area == 4

print('OK!')

运行程序

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ python rectangle_square_demo.py

OK!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

class Rectangle:
    def __init__(self, length, height):
        self._length = length
        self._height = height

    @property
    def area(self):
        return self._length * self._height

    def resize(self, new_length, new_height):
        self._length = new_length
        self._height = new_height

.resize()采用对象的new_lengthnew_width。您可以将以下代码添加到程序中,以验证其是否正常运行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

rectangle.resize(3, 5)
assert rectangle.area == 15

print('OK!')

您调整矩形对象的大小,并断言新区域正确。您可以运行该程序以验证行为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ python rectangle_square_demo.py

OK!

那么,如果调整正方形大小会怎样?修改程序,然后尝试修改正方形对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# In rectangle_square_demo.py

square.resize(3, 5)
print(f'Square area: {square.area}')

将与矩形相同的参数传递给square.resize(),然后打印该区域。当你运行程序时,你会看到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ python rectangle_square_demo.py

Square area: 15
OK!

程序显示,新的区域是15像矩形对象。现在的问题是,square对象不再满足其长度和高度必须相等的square类约束

你怎么解决这个问题?你可以尝试几种方法,但所有的方法都会很尴尬。您可以在square中覆盖.resize()并忽略height参数,但是这对于查看程序的其他部分的人来说会很混乱,因为这些部分的矩形正在被调整大小,而其中一些矩形并没有得到预期的区域,因为它们实际上是正方形。

在一个像这样的小程序中,可能很容易发现奇怪行为的原因,但是在一个更复杂的程序中,问题就更难找到了

事实是,如果能够以两种方式证明两个类之间的继承关系,就不应该从另一个类派生出另一个类

在本例中,SquareRectangle继承.resize()的接口和实现是没有意义的。这并不意味着方形对象不能调整大小。这意味着接口是不同的,因为它只需要一个side_size参数

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CryptoCode 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一起来探讨 python 类爆炸问题
由于您不必从特定的类派生对象就可以被程序重用,因此您可能会问为什么应该使用继承而不是仅实现所需的接口。以下规则可能对您有帮助
公众号---人生代码
2020/05/17
6560
灵活的设计组合
组合比继承更灵活,因为它可以建模松散耦合的关系。对组件类的更改对复合类影响很小或没有影响。基于组成的设计更适合更改
公众号---人生代码
2020/05/18
4830
聊聊面向对象设计中的Is-A
面向对象编程范式得到了广大开发者的青睐,在做面向对象软件设计的同仁也或多或少曾经心存困惑过。比如,怎么样才是正确的封装?如何恰当的继承?何时应该抽象? 对于设计,我们很难说对与错,通常只有好与不好的区分,而所谓的最佳实践也只是 -- 在当前约束下,人们所能找到的最佳解决方案。
袁慎建@ThoughtWorks
2020/04/16
6330
Java面向对象编程的基本概念和原则(一)
类是一种抽象的概念,它描述了一类对象所共有的属性和行为。在Java中,类是通过class关键字来定义的。例如,下面是一个定义矩形类的例子:
玖叁叁
2023/05/08
1690
网络工程师学Python-13-继承
在 Python 中,继承是一种重要的面向对象编程概念。通过继承,我们可以定义一个新的类,它继承了现有类的属性和方法。这种代码重用可以使我们更高效地编写程序,并提高代码的可读性和可维护性。
网络技术联盟站
2023/04/20
2070
网络工程师学Python-13-继承
python 实现多继承
python是少数支持多重继承的现代编程语言之一。多重继承是同时从多个基类派生一个类的能力
公众号---人生代码
2020/05/18
7640
阿里大佬告诉我,想学习设计模式,得先学好这些硬核技能
可能你不是科班出生,甚至大学都没念,没背景没关系。我们只要每天进步一点点,一个月、两个月、半年、一年......。
田维常
2021/04/22
6380
阿里大佬告诉我,想学习设计模式,得先学好这些硬核技能
开源图书《Python完全自学教程》8.5.2多继承
子类 C 继承了两个父类 P1 和 P2 ,它也就具有了两个父类中所定义的类属性 p1, p2 。
老齐
2022/07/06
2510
开源图书《Python完全自学教程》8.5.2多继承
java基础语法-继承
在Java中,继承是一种面向对象的编程技术,它允许我们创建一个新的类,该类从现有的类中继承了所有的成员变量和成员函数。子类可以添加新的成员变量和成员函数,或者覆盖父类的成员函数。这种技术能够提高代码的重用性和可维护性,使得我们可以更加高效地编写Java程序。
玖叁叁
2023/05/07
2380
软件工程设计原理里氏替换原则举例
里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计的基本原则之一,由Barbara Liskov提出。这个原则指出,如果类 S 是类 T 的子类型,则程序中使用 T 的对象的地方都可以不经修改地使用 S 的对象。换句话说,子类的对象应该能够替换掉它们的父类对象,而不影响程序的正确性。这个原则强调了继承关系中的行为兼容性,保证了基类和派生类之间的正确抽象和继承关系。
小马哥学JAVA
2024/03/27
1870
设计模式:面向对象的设计原则上(SRP、OCP、LSP)
面向对象的设计原则,我们最熟悉的就是 SOLID 原则,SOLID 原则是五个常用原则的首字母缩写,当然除了 SOLID 原则,还有一些其他的原则,所以后面就分为 SOLID 原则和其他原则两大块来介绍。
oec2003
2021/12/08
5990
设计模式:面向对象的设计原则上(SRP、OCP、LSP)
100天精通Golang(基础入门篇)——第19天:深入剖析Go语言中方法(Method)的妙用与实践
掌握Go语言的方法(Method)🛠️不仅能让你的代码更加有序✅,也能实现更多功能🚀和更高的可维护性🔧。本篇博客是我们"100天精通Golang"系列📚的第19篇,将深入解释Go中方法的语法📖、用法🤔、作用范围🌐以及与函数的区别🔄等。
猫头虎
2024/04/09
1680
100天精通Golang(基础入门篇)——第19天:深入剖析Go语言中方法(Method)的妙用与实践
python 中的 组合
组合是一个面向对象的设计概念,模型a是有关系的。在composition中,一个称为composite的类包含另一个称为component的类的对象。换句话说,一个复合类有另一个类的组件
公众号---人生代码
2020/05/18
8770
聊聊 Go 语言中的面向对象
在 Go 语言中可以使用结构体[2](Structs)对属性进行封装,结构体就像是类的一种简化形式。
江不知
2019/12/12
5680
软件架构设计原则之里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)是指如果对每一个类型为T1的对象o1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
Tom弹架构
2021/10/24
4800
将特性与Mixin类混合
python中多重继承的用途之一是通过mixins扩展类功能。mixin是提供其他类方法的类,但不被视为基类
公众号---人生代码
2020/05/19
6680
设计模式-里氏替换原则
在上面的三块代码中,当调用SmartTest类的resize方法的时候,如果传入的是父类,那么将会可以的,如果传入的是子类,正方形,那么将会不可以的。
mySoul
2018/11/19
5260
软件架构设计原则--里氏替换原则
  里氏替换原则(Liskov Substitution Principle,LSP)是指,如果对每一个类型为T1的对象t1,都替换为类型为T2的对象t2,使得以T1定义的所有程序P在所有的对象t1都替换成t2时,程序P的行为没有发生变化,那么T2就是T1的子类型。   这个定义看上去比较抽象,我们重新理解一下。可以理解为一个软件实体如果适用于一个父类,那么一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类的对象能替换父类的对象,而程序逻辑不变。   根据这个理解,隐身含义为:子类可以扩展父类的功能,但不能改变父类原有的功能。
向着百万年薪努力的小赵
2022/12/02
3690
软件架构设计原则--里氏替换原则
manim入门学习2--爱,死,机器人动画制作
下面的这个就是代码里面涉及到的这个属性吧:我们的这个fade_in,fade_out和我们饿这个替换的函数replacementTransform表示的就是我们的这个animation对象;
阑梦清川
2025/02/24
1030
manim入门学习2--爱,死,机器人动画制作
Python 中的 SOLID 原则
SOLID 是一组面向对象的设计原则,旨在使代码更易于维护和灵活。它们是由 Robert “Uncle Bob” Martin 于 2000 年在他的论文 设计原则和设计模式中创造的。SOLID 原则适用于任何面向对象的语言,但在本文中我将重点关注它们在 Python 应用程序中的含义。
海拥
2023/02/26
4320
相关推荐
一起来探讨 python 类爆炸问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验