什么是mixin,为什么它有用?

2022-09-05 00:59:48

Programming Python 中,Mark Lutz 提到了术语 mixin。我来自C / C++ / C#背景,我以前没有听说过这个词。什么是混杂剂?

在此示例的行之间阅读(我已链接到该示例,因为它很长),我假设它是使用多重继承来扩展类的情况,而不是适当的子类化。这是对的吗?

为什么我要这样做,而不是将新功能放入子类中?就此而言,为什么混合/多重继承方法比使用组合更好?

混合素与多重继承的区别是什么?这只是一个语义问题吗?


答案 1

混血儿是一种特殊的多重继承。在两种主要情况下使用混金:

  1. 您希望为类提供许多可选功能。
  2. 您希望在许多不同的类中使用一个特定功能。

举个例子,考虑werkzeug的请求和响应系统。我可以通过说以下方式制作一个普通的旧请求对象:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

如果我想添加接受标头支持,我会这样做

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

如果我想创建一个支持接受标头、etag、身份验证和用户代理支持的请求对象,我可以这样做:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

差异是微妙的,但在上面的例子中,mixin类并不是为了独立存在而生的。在更传统的多重继承中,(例如)可能更像。也就是说,该类可能被设计为独立存在。AuthenticationMixinAuthenticator


答案 2

首先,您应该注意 mixins 仅存在于多重继承语言中。你不能在Java或C#中做mixin。

基本上,mixin 是一种独立的基本类型,它为子类提供有限的功能和多态共振。如果你在C#中思考,想想一个你不必实际实现的接口,因为它已经实现了;您只是从它继承并从其功能中受益。

Mixins通常范围狭窄,不打算扩展。

[编辑 -- 至于为什么:]

我想我应该解释为什么,因为你问了。最大的好处是你不必一遍又一遍地自己做。在 C# 中,mixin 可能受益于处置模式的最大位置。每当您实现 IDisposable 时,您几乎总是希望遵循相同的模式,但最终您编写和重写相同的基本代码,并进行了微小的更改。如果有一个可扩展的处置混音器,你可以节省很多额外的打字。

[编辑2 -- 回答您的其他问题]

混合素与多重继承的区别是什么?这只是一个语义问题吗?

是的。mixin和标准多重继承之间的区别只是语义问题;具有多重继承的类可能会利用 mixin 作为多重继承的一部分。

mixin的要点是创建一个可以通过继承“混合”到任何其他类型的类型,而不会影响继承类型,同时仍然为该类型提供一些有用的功能。

同样,想想一个已经实现的接口。

我个人不使用mixins,因为我主要用一种不支持它们的语言开发,所以我很难想出一个像样的例子来为你提供“啊啊!”的时刻。但我会再试一次。我将使用一个人为的例子 - 大多数语言已经以某种方式提供了该功能 - 但希望这将解释应该如何创建和使用mixins。这里是:

假设您有一个类型,您希望能够序列化到 XML 或从 XML 序列化。您希望该类型提供一个“ToXML”方法,该方法返回一个字符串,该字符串包含具有该类型的数据值的 XML 片段,以及一个“FromXML”,该方法允许该类型从字符串中的 XML 片段重新构造其数据值。同样,这是一个人为的示例,因此您可能使用文件流或语言运行时库中的 XML Writer 类...无论什么。关键是您希望将对象序列化为 XML 并从 XML 获取新对象。

此示例中的另一个重要点是,您希望以通用方式执行此操作。您不希望必须为要序列化的每个类型实现“ToXML”和“FromXML”方法,而是需要一些通用方法来确保您的类型将执行此操作并且它正常工作。您希望代码重用。

如果您的语言支持它,您可以创建 XmlSerializable mixin 来为您完成工作。此类型将实现 ToXML 和 FromXML 方法。它将使用某种对示例不重要的机制,能够从与它混合在一起的任何类型中收集所有必要的数据,以构建ToXML返回的XML片段,并且当调用 FromXML 时,它同样能够恢复该数据。

和。。就是这样。若要使用它,您将具有需要序列化为 XML 的任何类型,该类型都继承自 XmlSerializable。每当需要序列化或反序列化该类型时,只需调用 ToXML 或 FromXML 即可。实际上,由于 XmlSerializable 是一种完全成熟的类型和多态性,因此可以想象,您可以构建一个对原始类型一无所知的文档序列化程序,只接受 XmlSerializable 类型的数组。

现在想象一下,将此方案用于其他事情,例如创建一个 mixin,以确保在中混合它的每个类都记录每个方法调用,或者一个 mixin,它为混合它的类型提供事务性。这个清单可以继续下去。

如果您只是将mixin视为一种小的基本类型,旨在为类型添加少量功能而不会以其他方式影响该类型,那么您就是金色的。

希望。:)