面向工具语言概论(四)
副标题#e#
四,彻底划清边界(继承疏散Subclassing和Subtyping)
在第二节我们接头了部门疏散Subclassing和subtyping的要领,即subclassing-implies-subtyping. 现今的很多面向工具语言,如Java, C#都是回收了这种技能。除此之外,尚有一种进一步疏散Subclassing和subtyping的要领。这种被称作inheritance-is-not-subtyping的要领通过完全盘据subclassing和subtyping之间的接洽而在更洪流平上利便了代码的重用。
它的发生很洪流平上是由于人们想要利用在反协变位置上的Self范例 (如Self范例的参数)。虽然,增大担任的本领的价钱是subsumption的机动性低落了。当Self范例呈此刻反协变的位置上时,subclass不再意味着subtype, 因此,subsumption也就不存在了。
下面请思量这样两个范例:
ObjectType Max is
var n: Integer;
method max(other:Max): Max;
end;
ObjectType MinMax is
var n: Integer;
method max(other:MinMax): MinMax;
method min(other:MinMax): MinMax;
end;
再思量两个类:
class MaxClass is
var n:Integer :=0;
method max(other: Self): Self is
if self.n > other.n then return self else return other end;
end;
end;
subclass MinMaxClass of MaxClass is
method min(other: Self): Self is
if self.n < other.n then return self else return other end;
end;
end;
#p#副标题#e#
要领min和max是二元的,因为它操纵两个工具:self和other. other的范例是一个呈此刻反协变位置上的Self范例。
留意,要领max有一个反协变的参数范例Self, 而且它被从类MaxClass担任到了MinMaxClass.
很直观地,类MaxClass对应着范例Max;类MinMaxClass对应着范例MinMax. 为了准确地暗示这种对应干系,我们必需针对包括利用Self范例的成员的类从头界说ObjectTypeOf,以便获得ObjectTypeOf(MaxClass) = Max, ObjectTypeOf(MinMaxClass) = MinMax。
为了使以上的等式创立,我们把类中的Self范例映射到ObjectType中的范例名称自己。我们同时让Self范例在担任的时候特化。
在本例中,当我们映射MinMaxClass的范例时,我们把担任来的max要领中的Self范例映射到MinMax范例。而对MaxClass中max要领的Self范例,我们利用Max范例。
如此,我们可以获得,任何MaxClass生成的工具,都具备Max范例。而任何MinMaxClass生成的工具都具备MinMax范例。
固然MinMaxClass是MaxClass的子类,但这里MinMax却不是Max的子范例(subtype).
举个例子,假如我们假设subtype在这种环境下创立,那么,对以下的这个类:
subclass MinMaxClass’ of MinMaxClass is
override max(other: Self): Self is
if other.min(self) = other then return self else return other end;
end;
end;
按照我们对Self范例的映射法则和基于布局的subtype法则,我们知道,ObjectTypeOf(MinMaxClass’) = MinMax, 所以,对任何MinMaxClass’生成的工具mm’ ,我们可以知道mm’ : MinMax.
而假如MinMax <: Max创立,按照subsumption, 我们就能推出mm’ : Max.
于是当我们挪用mm’.max(m)的时候,m可以是任何Max范例的工具。可是,当max的要领体挪用other.min(self)的时候,假如这个other不具有min要领,这个要领就会失败。
由此可见,MinMax <: Max并不创立。
子类(subclass) 在利用反协变的Self范例时就不再具有subtype的性质了。
五,工具协议 (Object Protocol)
从上一节的接头,我们看到对利用反协变Self范例的类,subclass不再是subtype了。这是一个令人失望的功效,究竟许多冲感人心的面向工具的利益是通过subtype, subsumption来实现的。
不外,幸运的是,固然失去了subtype, 我们照旧可以从中挖掘出来一些可以作为赔偿的有用的对象的。只不外,不象subtype, 我们不能享受subsumption了。
下面就让我们来研究这种新的干系。
在第四节的MinMax的例子中,subtype不再创立;简朴地利用泛型,引入
ObjectOperator P[M <: Max] is … end; 也好像没有什么用。P[Max]固然创立,但P[MinMax]却是不正当的,因为MinMax <: Max不创立。
可是,直观上看,任何支持MinMax这种协议的工具,也支持Max协议的 (固然我们还不知道这个“协议”到底是个什么对象)。于是,好像隐隐约约地又一个叫做“子协议”(subprotocol)的家伙在向我们招手了。
为了发明这个子协议的干系,让我们先界说两个type operator (还记得吗?就是浸染在范例上的函数):
#p#分页标题#e#
ObjectOperator MaxProtocol[X] is
var n: Integer;
method max(other: X) :X;
end;
ObjectOperator MinMaxProtocol[X] is
var n:Integer;
method max(other: X):X;
method min(other: X):X;
end;
这样,Max = MaxProtocol[Max], MinMax = MinMaxProtocol[MinMax]
更一般地说,我们可以界说:
什么 = 什么-Protocol[什么]
还记得lamda-calculus里的fixpoint吗?给定一个函数F, F(fixpoint(F)) = fixpoint(F)
而在我们这个子协议的type operator里,假如我们认为type operator是浸染于范例的函数的话, 那么这个“什么”,就是“什么-Protocol”函数的fixpoint啊!
也就是说:
什么= fixpoint (什么-Protocol).
除了以上的fixpoint的性质,我们还发明白存在于Max和MinMax之间的干系。
首先,MinMax是MaxProtocol的一个post-fixpoint,即:
MinMax <: MaxProtocol[MinMax]
其次,我们可以看出:
MinMaxProtocol[Max] <: MaxProtocol[Max]
MinMaxProtocol[MinMax] <: MaxProtocol[MinMax]
最后,假如我们用<::来暗示一个更高阶的子范例干系:
P <:: P’ 当且仅当 P[T] <: P’[T]
那么,MinMaxProtocol <:: MaxProtocol.
对付子协议的界说,我们可以采纳上面的<::的界说,即:
假如S-Protocol<::T-Protocol, 那么我们称范例S和范例T之间是子协议干系。 (1)
我们也可以不消这个高阶的干系,仍然利用<:这个subtype的干系:
假如S<:T-Protocol[S], 那么我们称范例S和范例T之间是子协议干系。 (2)
其实,第一个界说好像更直观一点,它更明晰地显示出子协议干系是浸染于范例上的函数(type operator)之间的干系,而不是范例之间的干系。
利用泛型技能,假如我们的某一个范例需要一个实现MaxProtocol的范例来实例化的话,我们可以回收下面两种要领中的一种:
ObjectOperator P1[X <: MaxProtocol[X]] is … end; (1)
ObjectOperator P2[P <:: MaxProtocol] is … end; (2)
这两种要领在表达本领上是沟通的。第一种要领叫做F-bounded parameterization. (译者按,Generic Java听说就回收了这个要领);第二种要领叫做 higher-order bounded parameterization.
对付详细语言的实现,为了利便,我们可以埋没这个type operator. 语法上可以直接对范例支持subprotocol的干系(用<#来暗示)。
对我们的MinMax的例子来说,我们就有:
MinMax <# Max.
(译者按,绕了一大圈,什么fixpoint啊,post-fixpoint啊,什么高阶干系啦,但愿没把你绕晕。其实,你只需要记着MinMax <# Max, 就八九不离十了,呵呵)
<#这个干系并不具有subsumption的性质,所以,你不能指望从它身上获得传统OO内里的多态。可是,与泛型相团结,它却长短常有用的。
好比说,我们可以对我们的所有你用来看成参数传给我的基于GP的快速排序模板函数给出这样的约束:用来实例化这个模板函数的的范例必需支持Comparable协议。