Python.use(better)
《前の記事|記事一覧|次の記事》
Python.use(better)
実録:はじめてのプログラミング《18》クラス(4)eval
《関連記事》-
逆ポーランド課題(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〕にも背くことになります。《ひよ子》