返回介绍

OOP和组合:有一个关系

发布于 2024-01-29 22:24:15 字数 3766 浏览 0 评论 0 收藏 0

在第25章中介绍过组合的概念。从程序员的角度来看,组合涉及把其他对象嵌入容器对象内,并使其实现容器方法。对设计师来说,组合是另一种表示问题领域中关系的方式。但是,组合不是集合的成员关系,而是组件,也就是整体的组成部分。

组合也反映了各组成部分之间的关系,通常称为“有一个”(has-a)关系。有些OOP设计书籍把组合称为聚合(aggregation),或者使用聚合描述容器和所含物之间较弱的依赖关系来区分这两个术语。本书中,“组合”就是指内嵌对象集合体。组合类一般都提供自己的接口,并通过内嵌的对象来实现接口。

既然我们已经有了员工,就把他们放到比萨店,开始忙吧。我们的比萨店是一个组合对象,有个烤炉,也有服务生和主厨这些员工。当顾客来店下单时,店里的组件就会开始行动:服务生接下订单,主厨制作比萨,等等。下面的例子(文件pizzashop.py)模拟了这个场景中所有的对象和关系。

PizzaShop类是容器和控制器,其构造函数会创建上一节所编写的员工类实例并将其嵌入。此外,Oven类也是在这里定义的。当此模块的自我测试程序代码调用PizzaShoporder方法时,内嵌对象会按照顺序进行工作。注意:每份订单创建了新的Customer对象,而且把内嵌的Server对象传给Customer方法。顾客是流动的,但是,服务生是比萨店的组合成分。另外,员工也涉及了继承关系,组合和继承是互补的工具。当执行这个模块时,我们的比萨店处理了两份订单:一份来自Homer,另一份来自Shaggy。

同样地,这只是一个用来模拟的例子,但是,对象和交互足以代表组合的工作。其简明的原则就是,类可以表示任何用一句话表达的对象和关系。只要用类取代名词,用方法取代动词,就有第一手的设计方案了。

重访流处理器

就更为现实的组合范例而言,可以回忆第22章介绍OOP时,写的通用数据流处理器函数的部分代码。

在这里,不是使用简单函数,而是编写类,使用组合机制工作,来提供更强大的结构并支持继承。下面的文件streams.py示范了一种编写类的方式。

这个类定义了一个转换器方法,它期待子类来填充。这是我们在第28章中介绍的抽象超类模式的一个例子(第7部分更多地介绍断言)。以这种方式编写代码,读取器和写入器对象会内嵌在类实例当中(组合),我们是在子类内提供转换器的逻辑,而不是传入一个转换器函数(继承)。文件converters.py显示了这种方法。

在这里,Uppercase类继承了类处理的循环逻辑(以及其超类内所写的其他任何事情)。它只需定义其所特有的事件:数据转换逻辑。当这个文件执行时,会创建并执行实例,而该实例再从文件spam.txt中读取,把该文件对应的大写版本输出到stdout流。

要处理不同种类的流,可以把不同种类的对象传入类的构造调用中。在这里,我们使用了输出文件,而不是流。

但是,就像之前所建议的,我们可以传入包装在类中的任何对象(该对象定义了所需要的输入和输出方法接口)。以下是简单例子,传入写入器类(把文字嵌HTML标签中)。

即使原始的Processor超类内的核心处理逻辑什么也不知道,如果跟随这个例子的控制流程,就会发现得到了大写转换(通过继承)以及HTML格式(通过组合)。处理代码只在意写入器的write方法,而且又定义一个名为convert的方法,并不在意这些调用在做什么。这种逻辑的多态和封装远超过类的威力。

Processor超类只提供文件扫描循环。在更实际的工作中,我们可能会对它进行扩充,使其子类能支持其他程序设计工具,而且在这个流程中,将其变为成熟的软件框架。在超类中编写一次这种工具,就可以在所有程序中重复使用。即使是这个简单的例子,因为类打包了不少东西并能继承,我们所需做的代码编写就是HTML格式这一步。其余都是免费的。

看看组合的另一个例子,可以参考第31章结尾的练习题9以及其附录B的解法。这个例子类似于比萨店的例子。本书中把焦点放在继承上,因为这是Python语言本身提供的OOP的主要工具。但是,在实际中,组合和继承用的一样多,都是组织类结构的方式,尤其是在较大型系统中。正如我们所见的,继承和组合通常是互补的(偶尔是互换的)技术。不过,因为组合是设计的话题,不在Python语言和本书的范围内,这个话题的其他内容请参考其他资源。

为什么要在意:类和持续性

本书这一部分内容中几次提到了pickle机制,因为它和类实例合起来使用效果很好。实际上,这些工具往往足够吸引人,可以促进类的通用用法——通过pickle或shelve一个类实例,我们得到了包含数据和逻辑的组合的数据存储。

例如,除了可以模拟真实世界的交互外,在这里开发的比萨店类,也可以作为持续保存餐馆数据库的基础。类实例可以利用Python的pickle或shelve模块,通过单个步骤储存到磁盘上。在第27章中的OOP教程中,我们使用shelve来存储类的实例,而对象的pickle接口很容易使用:

pickle机制把内存中的对象转换成序列化的字节流,可以保存在文件中,也可通过网络发送出去。解除pickle状态则是从字节流转换回同一个内存中的对象,Shelve也类似。但是它会自动把对象pickle生成按键读取的数据库,而此数据库会导出类似于字典的接口。

上例中,使用类来模拟员工意味着我们只需做一点工作,就可以得到员工和商店的简单数据库:把这种实例对象pickle至文件,使其在Python程序执行时都能够永续保存。

这一次性地把整个复合的shop对象保存到一个文件中。为了在另一个会话或程序中再次找回它,只需要一个步骤就够了。实际上,以这种方式存储的对象保存了状态和行为:

参考标准链接库手册和之后的范例,进一步了解关于pickle的内容。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文