ホームページ  >  記事  >  バックエンド開発  >  Mixin是什么概念?

Mixin是什么概念?

WBOY
WBOYオリジナル
2016-06-06 16:23:095118ブラウズ

在浏览tornado的代码时,auth中的类都以Mixin命名,这个词好奇怪啊,查了一下资料,有人解释Mixin为mix in,混入的意思,类似于多重继承。auth模块实现OpenID和OAuth,为什么要用Mixin方式?Mixin的应用场景?与“接口”概念有什么区别?

回复内容:

Mixin 实质上是利用语言特性(比如 Ruby 的 include 语法、Python 的多重继承)来更简洁地实现组合模式

以如下 Java 伪码为例,实现一个可复用的“打标签”组件(Taggable),并且应用到帖子(Post)模型上:
<code class="language-java"><span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.ArrayList</span><span class="o">;</span>

<span class="kd">interface</span> <span class="nc">Entity</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">();</span>
    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getKind</span><span class="o">();</span>
<span class="o">}</span>

<span class="kd">interface</span> <span class="nc">Taggable</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">addTag</span><span class="o">(</span><span class="kt">int</span> <span class="n">tagId</span><span class="o">);</span>
    <span class="kd">public</span> <span class="n">List</span><span class="o"><span class="n">Integer</span><span class="o">></span> <span class="nf">getTags</span><span class="o">();</span>
<span class="o">}</span>

<span class="kd">class</span> <span class="nc">TaggableImpl</span> <span class="kd">implements</span> <span class="n">Taggable</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="n">Entity</span> <span class="n">target</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">TaggableImpl</span><span class="o">(</span><span class="n">Entity</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">target</span> <span class="o">=</span> <span class="n">target</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">addTag</span><span class="o">(</span><span class="kt">int</span> <span class="n">tagId</span><span class="o">)</span> <span class="o">{</span>
        <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="na">getId</span><span class="o">();</span>
        <span class="kt">int</span> <span class="n">kind</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="na">getKind</span><span class="o">();</span>
        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"insert into ... values "</span>
                <span class="o">+</span> <span class="n">id</span> <span class="o">+</span> <span class="s">", "</span>
                <span class="o">+</span> <span class="n">kind</span> <span class="o">+</span> <span class="s">", "</span>
                <span class="o">+</span> <span class="n">tagId</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">ArrayList</span><span class="o"><span class="n">Integer</span><span class="o">></span> <span class="nf">getTags</span><span class="o">()</span> <span class="o">{</span>
        <span class="c1">// query from database</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o"><span class="n">Integer</span><span class="o">>();</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">class</span> <span class="nc">Post</span> <span class="kd">implements</span> <span class="n">Entity</span><span class="o">,</span> <span class="n">Taggable</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">final</span> <span class="kd">static</span> <span class="kt">int</span> <span class="n">KIND</span> <span class="o">=</span> <span class="mi">1001</span><span class="o">;</span>

    <span class="kd">private</span> <span class="n">Taggable</span> <span class="n">taggable</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">id</span><span class="o">;</span>
    <span class="kd">private</span> <span class="n">String</span> <span class="n">title</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">Post</span><span class="o">(</span><span class="kt">int</span> <span class="n">id</span><span class="o">,</span> <span class="n">String</span> <span class="n">title</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">title</span> <span class="o">=</span> <span class="n">title</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">taggable</span> <span class="o">=</span> <span class="k">new</span> <span class="n">TaggableImpl</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getKind</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">KIND</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">addTag</span><span class="o">(</span><span class="kt">int</span> <span class="n">tagId</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">taggable</span><span class="o">.</span><span class="na">addTag</span><span class="o">(</span><span class="n">tagId</span><span class="o">);</span>  <span class="c1">// delegate</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">ArrayList</span><span class="o"><span class="n">Integer</span><span class="o">></span> <span class="nf">getTags</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">taggable</span><span class="o">.</span><span class="na">getTags</span><span class="o">();</span>  <span class="c1">// delegate</span>
    <span class="o">}</span>
<span class="o">}</span>
</span></span></span></span></code>
Mixin 就是混入的意思。

和多重继承类似(其实可以把 Mixin 看作多重继承的一种在特定场景下的应用),但通常混入 Mixin 的类和 Mixin 类本身不是 is-a 的关系,混入 Mixin 类是为了添加某些(可选的)功能。自由地混入 Mixin 类就可以灵活地为被混入的类添加不同的功能。

传统的「接口」概念中并不包含实现,而 Mixin 包含实现。实际上 Mixin 的作用和 Java 中的众多以「able」结尾的接口很相似。不同的是 Mixin 提供了(默认)实现,而 Java 中实现了 -able 接口的类需要类自身来实现这些混入的功能(Serializable 接口是个例外)。 趁着午休来答一个。

如楼上很多答主一样,谈到Mixin就不得不谈到多重继承,因为Mixin的出现就是为了解决多重继承的问题,那么多重继承有什么问题呢?

在《松本行弘的程序世界》一书中,作者列举了以下三点:

  1. 结构复杂化:如果是单一继承,一个类的父类是什么,父类的父类是什么,都很明确,因为只有单一的继承关系,然而如果是多重继承的话,一个类有多个父类,这些父类又有自己的父类,那么类之间的关系就很复杂了。
  2. 优先顺序模糊:假如我有A,C类同时继承了基类,B类继承了A类,然后D类又同时继承了B和C类,所以D类继承父类的方法的顺序应该是D、B、A、C还是D、B、C、A,或者是其他的顺序,很不明确。
  3. 功能冲突:因为多重继承有多个父类,所以当不同的父类中有相同的方法是就会产生冲突。如果B类和C类同时又有相同的方法时,D继承的是哪个方法就不明确了,因为存在两种可能性。
当然你可以说有些语言解决了这个问题,但是并不是所有语言都想要去纠结这个问题。

所以为能够利用多继承的优点又解决多继承的问题,提出了规格继承和实现继承这两样东西。

简单来讲,规格继承指的是一堆方法名的集合,而实现继承除了方法名还允许有方法的实现。

Java 选择了规格继承,在 Java 中叫 interface(不过Java8中已经有默认方法了),而 Ruby 选择了实现继承,也可以叫Mixin,在 Ruby 中叫 module。

从某种程度上来说,继承强调 I am,Mixin 强调 I can。当你 implement 了这个接口或者 include 这个 module 的时候,然后就你行你上。

所以这又可以扯到 duck typing 去了,不细说。要想了解具体的可以看一下《松本行弘的程序世界》这本书。 这叫迷信方法,你想知道好处,打开py源码,搜搜 mixin,试着不用迷信实现一个本来用了迷信的模块,就能切身感受一下了。 为了解决多重继承的问题,Java引入了接口 (interface)技术Lisp、Ruby引入了 Mix-in 技术。


以 Ruby 为例,Mix-in 有效地降低多重继承复杂性(谁是你爹,哪个爹的优先级高,你的把妹方法是继承自哪个爹的等)。 Ruby中 Mix-in 的单位是 模块 (module)

Mix-in 技术按一下规则来限制多重继承:
  1. 继承用但一继承;
  2. 第二个及两个以上的父类必须是 Mix-in 的抽象类。

Mix-in 类是具有以下特征的抽象类:
  1. 不能单独生成实例;
  2. 不能继承普通类。

按照以上的原则,类在层次上具有单一继承一样的树结构,同时又可以实现功能的共享(方法是:把共享的功能放在 Mix-in 类中,再把 Mix-in 类插入到树结构里)。

Java 用 接口 解决规格继承(类都有哪些方法)的问题,Mix-in 则解决了实现继承(类中都用了什么数据结构和什么算法)的问题。

逼逼了这么多,对于 Mix-in 的理解是,Mix-in 只不过是实现多重继承的一个技巧而已。 被约束的多重继承。 mixin不是多继承,mixin是duck type的糖,让你可以不用去把一坨坨Interface继承一遍然后才能彼此调用接口。 mixin 并没有特别权威的标准,非要让我下个定论的话:mixin 其实就是在语言不提供标准多重继承的情况下,变相实现多重继承的一个语法糖。 不同版本的 mixin 实现不太一样,但出发点都是在允许继承接口和继承实现的基础上,简化继承关系,避免多重继承的坑。


我认为 C# 的扩展方法其实就是一种 mixin,但又不是通过代码拷贝等动态特性实现的,而是在编译器层面很严格的帮你进行了封装。对其它静态语言来说,如果没有多重继承,要自己实现 mixin 是很困难的。动态语言有些就支持 mixin,不支持的话自己造一个也很容易。


我最近也利用 javascript 实现了一个版本的 mixin 机制,为了支持可视化编辑,添加了类似组件系统的能力,将整合进 0.5 的 Fireball-x,到时会进一步更新答案。


手机码字求赞,欢迎更多讨论。 就是编译的时候把一段代码复制到另一个地方的意思。 Mixin是一种特殊的多重继承,也就是多重继承的子集。
使用Mixin的好处是,同时享有单一继承的单纯性和多重继承的共有性。

作为Mixin类,需要满足以下条件:
  1. 不能单独生成实例对象,属于抽象类。
  2. 不能继承Mixin以外的类。
因为有以上限制,Mixin类通常作为功能模块使用,在需要该功能时“混入”,而且不会使类的关系变得复杂(比如,同名方法到底从哪个父类继承)。
Java的接口,只提供了“规格”的多重继承。Mixin类则同时提供了“规格”和“实现”的多重继承,使用上相比接口会更加简单。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。