Python.use(better)

前の記事記事一覧次の記事
Python.use(better)


実録:はじめてのプログラミング《18》

逆ポーランド課題(2)eval

リファクタリングを継続する前に、途中経過をまとめます。


class Polish(object):                # ver.2
    def __init__(self):
        self.stack = Stack()
    def eval(self, expression):
        parser = Parser()
        for e in expression.split():
            parser.scan(e).eval(self)
        return self.stack.pop()            
    def eval_Value(self, value):
        self.stack.push(value)
    def eval_Op(self, value):
        op2 = self.stack.pop()
        op1 = self.stack.pop()
        self.stack.push(eval("{0} {1} {2}".format(op1, value, op2)))

《Note》始めは「このコードの断片は分かりにくい」と感じるかもしれません。やがて、オブジェクト指向プログラミング〔Object-Oriented Programming; OOP〕が理解できるようになると、むしろ「このほうが分かりやすい」と感じるようになり、それこそが「OOP を習得できた」何よりの証です。《ひよ子》

parser が抽出した各トークンは、それが演算子かどうかで個別の処理が必要です。ところが、

        for e in expression.split():
            parser.scan(e).eval(self)

このコードの断片には、具象クラス Value/Op が含まれていません。ここで注目して欲しいのは、具体的な記述を含まない「抽象的な表現」になっていることです。つまり、なにか仕様変更があっても、このコードの断片を更新する必要がないのです。

《Note》逆に、具体的な記述を含むと、仕様変更のたびに「モジュールを開かないと」コードを更新できなくなります。次の《Tips》を参照してください。《りす》

class Token(object):                # ver.2a
    ...
    def eval(self, client):
        eval("client.eval_{0}(self)".format(self.__class__.__name__))

ver.1a/ver.1b で示したコードの断片は「メソッドの名前」が違うだけで、それ以外は同じです。そこで、リファクタリングを実践して、その共通する箇所を抽象クラス Token に記述します。すると、

class Value(Token): pass
class Op   (Token): pass

クラス Value/Op は、その存在に意義があるだけなので、その本体は空 pass になります。

《Note》pass を記述する(何も書かない)ことに重要な意味があります。これは、構文として必要だから記述するのとは、意味が違います。《りす》

Tips

ver.1 で示したコードの断片は「具体的で分かりやすい」という長所はあっても、逆に「具体的…」であることが短所にもなり得ます。なぜなら、要求仕様の変更があるたびに更新する必要があり、このクラス Polish を含む .py モジュールを「永遠に」閉じることができないからです。つまり、開放閉鎖原則〔Open-Closed Principle; OCP〕にも背くことになります。《ひよ子》

Last updated♪2009/03/10