2015/02/18

継承は必ずしもis-a関係ではない(OOP) - Inheritance is not subtyping問題

Inheritance is not subtyping」は, Cookらによる1989年の論文で, そのままですが, オブジェクト指向における継承≠派生型(is-aの関係)ということのようです.

 派生型(Subtyping)については, is-a - Wikipediaにその説明が載っていますが, 確かにここでも, 継承が必ずis-a関係を構築するとは書かれてはいません.

 数理科学的バグ撲滅方法論のすすめ - 第4回 関数型言語とオブジェクト指向,およびOCamlの"O"について - ITpro に, その反例を示したソースコードがありますが, OCamlなのであまり実感わきません. オブジェクト指向言語は, JavaかPythonくらいしか使ったことがないので, Pythonでとりあえず書き直してみました.

 Pointクラスを継承したColoredPointクラス.
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def equals(self, another):
        return self.x == another.x and self.y == another.y

class ColoredPoint(Point):
    def __init__(self, x, y, c):
        self.x = x
        self.y = y
        self.c = c
    def equals(self, another):
        xeq = self.x == another.x
        yeq = self.y == another.y
        ceq = self.c == another.c
        return xeq and yeq and ceq
 この例では, 確かにPointクラスが継承されていますが, equalsがオーバーライドされています. 一方で, Pointクラス間ではequals関数で比較できるように実装されています.

 PointクラスとColoredPointクラスのインスタンスがis-a関係ならば, 当然, Pointクラスのインスタンス⇔ColoredPointクラスのインスタンス間で, インスタンスのequalityの比較が可能なはずです.
>>> ColoredPoint(1,2,3).equals(ColoredPoint(1,2,3))
True
>>> ColoredPoint(1,2,3).equals(Point(1,2))
Traceback (most recent call last):
  File "", line 1, in 
  File "notisa.py", line 16, in equals
    ceq = self.c == another.c
AttributeError: Point instance has no attribute 'c'
 で, 比較してみると, エラーが発生する. という現象が反例としてあげられるため, 継承≠is-aということのようです.

 面白いのは, 継承しなくても, is-aの関係が成立してしまうケースが存在することで, 次のFruitとOarngeクラスの例が考えられます.
class Fruit:
    def __init__(self, price):
        self.price = price
    def equals(self, another):
        return self.price == another.price

class Orange:
    def __init__(self, price):
        self.price = price
    def equals(self, another):
        return self.price == another.price
厳密な意味(?)でis-a関係が成立しているわけではないですが, equalsによる比較ができてしまい, 一種の部分型(is-a関係)のような振る舞いが可能になります. OCamlなどでは, この辺を型推論により自動的に型付けしてくれるようで, このような型を構造的部分型(structual subtyping)と呼ぶようです(前述のITProの記事より).

0 件のコメント :