《15》クラス(1)継承 vs. 委譲〈Python 2.x 版〉

実録:はじめてのプログラミング記事一覧
《15》クラス(1)継承 vs. 委譲

《著》小粒ちゃん+α《監修》小泉ひよ子とタマゴ倶楽部
2009年2月3日(火)

今日の進捗

  • Tutorial: Interactive Input Editing and History Substitution
  • Python.use(better) -- セミナー研修テキスト
  • スタック課題を「続・ひよ子のきもち」で公開
Comment
本人:野中 まだクラス図を自分で書けるほどではないけど図を見ると理解できるようになりました。
担当:本間 作図をするのが目的ではなくて、より理解しやすくするための手段のひとつだから、少しずつ利用できるようになれたらいいからね。(^^)
参考文献 note
Object-Oriented Modeling and Design: International Edition

Object-Oriented Modeling and Design: International Edition


《邦訳》asin:4810185273
(スリーアミーゴの一人である)Rumbaugh さんが OMT を紹介しています。
その中で、悪しき継承の典型について紹介しています。
残念ながら、java.util には、同じ病巣を抱えた箇所が少なくありません。《ひよ子》
Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)

Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)


《邦訳》asin:4890527974
「継承」の隘路と、それに代わる「委譲」について紹介しています。《ひよ子》

スタック課題(1)継承 vs. 委譲

何が問題か:継承 vs. 委譲

ここまでは、何も問題がなさそうに見えます。しかし、

def ex():
    stack = Stack()         ; print stack
    stack.push("A")         ; print stack
    stack.push("B")         ; print stack
    stack.push("C")         ; print stack
    s = stack.remove("A")   ; print stack, s

>>> ex()
[]
['A']
['A', 'B']
['A', 'B', 'C']
['B', 'C'] None

メソッド list.remove を利用すると、スタック内の任意の要素を削除できます。これが可能になる理由は、クラス Stack を組み込み型 list から拡張したからで、ある意味では自然な結果とも言えます。しかし、スタックの立場から見ると、不自然かつ「深刻な」問題です。そこで次に「継承」に代わって「委譲」による方法を試みます。すると、

class Stack(object):
    def __init__(self):
        self.items = []
    def __str__(self):
        return str(self.items)
    def push(self, item):
        self.items.append(item)    
    def pop(self):
        try:
            return self.items.pop()
        except:
            print "%s: %s"%(self.__class__.__name__,
                "pop from empty stack")

メソッド pop の動作を規定するときに、組み込み型のメソッド list.pop に委譲しています。また、組み込み型 str を呼び出したときの動作を規定する、特殊メソッド __str__ を実現するのに、その str を利用しているのも興味深いところです。すると、

>>> ex()
[]
['A']
['A', 'B']
['A', 'B', 'C']

Traceback (most recent call last):
  ...
    s = stack.remove("A")   ; print stack, s
AttributeError: 'Stack' object has no attribute 'remove'

スタックに remove 操作を試みると、例外 AttributeError を生成して、エラーメッセージが出力されます。つまり、「継承」に代えて「委譲」にするだけで「不正アクセス」を防げるのです。

Tips

(継承と比べて)委譲によるスタックの実現では、より多くのコードが必要になるかのような錯覚に陥ります。ところが実際には、継承だけで「完全な」スタックを実現するには「膨大な」コードを記述します。なぜなら、リストにとっては自然でも、スタックにとっては不自然な動作(list.remove など)をすべて無効にする必要があるからです。逆説的な表現をすると「手抜きをしたい(プロトタイプ)なら、まさに継承は打ってつけ」です。
それが深刻な問題となるのは、継承したクラスの仕様が変更されたときです。すると「西暦二千年問題」と同じ病巣を抱え、そのたびにコードの改変を余儀なくされ「メンテナンスの悪夢」に陥ります。その典型が「消費税問題」で、新たな付加価値を与えることなく、膨大なメンテナンス費用を投資せざるを得ない状況に追い込まれます。《ひよ子》

Last updated♪09/03/09