Python.use(better)
《前の記事|記事一覧|次の記事》
Python.use(better)
実録:はじめてのプログラミング《15》クラス(1)継承 vs. 委譲
《関連記事》-
スタック課題(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("{0}:".format(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 など)をすべて無効にする必要があるからです。逆説的な表現をすると「手抜きをしたい(プロトタイプ)なら、まさに継承は打ってつけ」です。
それが深刻な問題となるのは、継承したクラスの仕様が変更されたときです。すると「西暦二千年問題」と同じ病巣を抱え、そのたびにコードの改変を余儀なくされ「メンテナンスの悪夢」に陥ります。その典型が「消費税問題」で、新たな付加価値を与えることなく、膨大なメンテナンス費用を投資せざるを得ない状況に追い込まれます。《ひよ子》