Python デザインパターン サンプルコード Decorator
結城 浩「Java言語で学ぶデザインパターン入門」をPython化
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-18)Python3.11で動作確認済み
Display (getColumns, getRows, getRowText, show) ↑ ↑ StringDisplay Border (getColumns, getRows, getRowText) ↑ ↑ SideBorder FullBorder (getColumns, getRows, getRowText) (getColumns, getRows, getRowText, _makeLine)サンプルは文字列の周りに飾り枠を付けるものです。文字列を表す具象クラス StringDisplay,飾り枠を表す 具象クラス SideBorder,FullBorderとこれらの抽象クラス Displayには同じメソッドがあります。抽象クラス Border のことを Decorator と言うそうです。もちろんそのサブクラスも Decorator です。
b1 = StringDisplay(u"Hello, world") b2 = SideBorder(b1, '#') b3 = FullBorder(b2) b1.show() b2.show() b3.show()メインルーチンの4番目の表示の例は,引数をネスト(入れ子)にすることによりインスタンス化しています。判り易いように Java 風にインデントしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import unicodedata from abc import ABCMeta, abstractmethod class Display(metaclass=ABCMeta): @abstractmethod def getColumns(self): pass @abstractmethod def getRows(self): pass @abstractmethod def getRowText(self): pass def show(self): for i in range(self.getRows()): sys.stdout.write("{0}\n".format(self.getRowText(i))) class StringDisplay(Display): def __init__(self, string): self.string = string def getColumns(self): #self.width = len(self.string) # 全角を1文字に数える def str_len(str): length = 0 for ch in str: ch_width = unicodedata.east_asian_width(ch) if ch_width in "WFA": length += 2 #戻り値:WとFとAが全角 else: length += 1 #戻り値:NaとHが半角 return length self.width = str_len(self.string) # 全角を2文字に数える return self.width def getRows(self): return 1 def getRowText(self, row): if row == 0: return self.string else: return None class Border(Display): def __init__(self, display): self.display = display class SideBorder(Border): def __init__(self, display, ch): super(SideBorder, self).__init__(display) self.borderChar = ch def getColumns(self): return 1 + self.display.getColumns() + 1 def getRows(self): return self.display.getRows() def getRowText(self, row): return self.borderChar + \ self.display.getRowText(row) + \ self.borderChar class FullBorder(Border): def __init__(self, display): super(FullBorder, self).__init__(display) def getColumns(self): return 1 + self.display.getColumns() + 1 def getRows(self): return 1 + self.display.getRows() + 1 def getRowText(self, row): if row == 0: return '+' + self._makeLine('-', self.display.getColumns()) + '+' elif row == self.display.getRows() + 1: return '+' + self._makeLine('-', self.display.getColumns()) + '+' else: return '|' + self.display.getRowText(row - 1) + '|' def _makeLine(self, ch, count): buf = [] for i in range(0, count): buf.append(ch) return "".join(buf) def main(): # x = Display() # Test : Can't instantiate abstract class Display with abstract methods b1 = StringDisplay("Hello, world") b2 = SideBorder(b1, '#') b3 = FullBorder(b2) b1.show() b2.show() b3.show() b4 = SideBorder( FullBorder( FullBorder( SideBorder( FullBorder( StringDisplay("こんにちは。") ), '*' ) ) ), '/' ) b4.show() if __name__ == "__main__": main() """標準出力 Hello, world #Hello, world# +--------------+ |#Hello, world#| +--------------+ /+------------------+/ /|+----------------+|/ /||*+------------+*||/ /||*|こんにちは。|*||/ /||*+------------+*||/ /|+----------------+|/ /+------------------+/ """