【デザインパターン勉強】Proxyメソッド
ざっくり概要
ある処理を肩代わりする代わりに出来ない処理が着た場合、 処理を他のクラスに肩代わさせるメソッドです。
生徒と先生の関係を考えてみます。
先生といえども、生徒の質問に全て答えられるわけではないですね。 数学の先生が英語の質問をされたときは、英語の先生(クラス)を呼び出して回答する そんなパターンです。
コード
import abc class Teacher(metaclass=abc.ABCMeta): def __init__(self): self.subject = "Math" @abc.abstractmethod def question_1(self): pass @abc.abstractmethod def question_2(self): pass class MathTeacher(Teacher): def question_1(self,question): print(question,"two") def question_2(self,question): EnglishTeacher().question_2(question) class EnglishTeacher(Teacher): def question_1(self,question): MathTeacher().question_1(self,question) def question_2(self,question): print(question,"緑茶です。") if __name__ == '__main__': # 生徒役 MathTeacher().question_1("1+1 equal?") #1+1 equal ? two MathTeacher().question_2("what does mean green tea?") #what does mean green tea? 緑茶です。
ざっくり解説
今回の例では、生徒の質問(if name以下)はデフォルトで数学の先生(MathTeacherクラス) としておきます。question_2では、英語の質問なので、数学の先生は英語の先生に答えてもらってます。
状況としては数学の先生が同学年の英語の先生が病気のため代理で授業しているとシチューエーションをイメージして下さい。
mainにある処理のイメージを書き下すと、 question_1は数学の先生なんで余裕ですが、question_2は少しむずかしいかもしれません。そこで、1学年下の英語の先生に頼み込んで 回答している状況を示しています。
現実的な利用シーン(個人的感想)
代理で行なうメリットとしては、重いクラスの代理を軽いクラスで肩代わりすることが可能です。 例えば、
- (あまり実感はないですが)クラスの初期化に呼び出されるコンストラクタが重い
- DBをロードしなくてはいけない処理がある
といったケースでは代理を立てることで動きの遅いクラスを軽いクラスに肩代わりさせられます。
参考にしたもの
【デザインパターン勉強】Decoratorメソッド
ざっくり概要
decoratorとは 装飾 の意味です。 あるコアクラスを元にして機能をかぶせていく手法です。
今回は簡略化のために1つのコア、一つの機能をかぶせた例を用いて説明します。
コード
class PlainNumber(): def __init__(self,num): self._num = num class MultiplicationNumber(PlainNumber): def __init__(self,num): self._number = num def render(self,num): return self._number.render() * num if __name__ == '__main__': plain_num = PlainNumber(11) multiple_output = MultiplicationNumber(plain_num) print("11*2=",multiple_output.render(2)) # 11*2= 22
ざっくり解説
コードの流れは以下の通りです。
- 数字を設定(PlainNumberクラス)を設定
- 掛け算(MultiplicationNumber)機能を追加し
- 数字×掛け算を実行
MultiplicationNumberはPlainNumberを継承しているので掛け算が実行出来ますね。
現実的な利用シーン(個人的感想)
- 長所
開発のシーンでは、最初に決めた仕様し、実装し、あとから機能追加といったことがあると思います。 そういった際、設計を変えずに追加することが出来て、便利なんじゃないでしょうか。
- 短所
デコレートをしすぎると管理しづらい気がします。(意図せず重複した機能を持ったクラスを追加するなど)そこらへんは長所とトレードオフな気がしますが
参考文献
Javaで学ぶデザインパターン入門 https://github.com/faif/python-patterns/blob/master/structural/decorator.py
【デザインパターン勉強】Facadeメソッド
ざっくり概要
Facade(建物の正面)という意味を指しています。 このパターンは内部の処理を簡素化したクラスを一つにすることで、複雑な処理を見せないメソッドです。
自分なりの言葉で言うと、処理を一つのクラスに集約したら見通して良くなって、すぐわかるんじゃね? ということです。
コード
ココのコードを利用して説明します。
import time class TC1: def run(self): print("test1") print("settup") print("finish") time.sleep(0.1) class TC2: def run(self): print("test2") print("settup") print("finish") time.sleep(0.1) class TC3: def run(self): print("test3") print("settup") print("finish") time.sleep(0.1) class TestRunner: def __init__(self): self.tc1 = TC1() self.tc2 = TC2() self.tc3 = TC3() self.tests = [self.tc1,self.tc2,self.tc3] def runAll(self): [i.run() for i in self.tests] if __name__ == '__main__': testrunner = TestRunner() testrunner.runAll()
ざっくり解説
TestRunnerクラス
このクラスで処理をまとめています。 具体的にはTC1~3で標準出力するメッドをrunAllに集約しています。
今回はTC1~TC3という3つのクラスのメソッドを用いて集約してますが、 もっと複雑なケースを考えてみます。
- そのままAクラスはインスタンス化して、
- Aのメソッドを呼び出し
- Bクラスメソッドを使って
・・・C、D、E、F、Gの処理へ続く。
といったケースですと、複雑で何しているかわからなくなりますね。
一つのクラス(窓口)に集約化することで見通しが良くなります。 クラス名もわかりやすく(例えば、MakeCSV具体的な作業を明示) とか、わかりやすくすれば初見の人でも通りやすくなるんじゃないでしょうか。
利用方法
個人開発をしている人にはあまり実りのあるメソッドとはいえないと思います(処理の流れをわかっているため)。 しかしながら、何人も関わっている開発プロダクトの場合、は必須のパターンだと思います。
参考文献
【デザインパターン勉強】Singletonメソッド
ざっくり概要
このパターンの目的は 一つのクラスに複数のインスタンスを作らないようにすることです。
利用シーン... パスワード管理のメソッドなど管理が厳重にする必要があるメソッドを管理する時に使うんでしょうかね。。
コード
class Singleton: initial_class_name = None # クラスインスタンス化した際、__new__→__init__が呼び出される。 def __init__(self): print("init") def __new__(self): if Singleton.initial_class_name is None: print("new") Singleton.initial_class_name = super().__new__(self) return Singleton.initial_class_name if __name__ == '__main__': a = Singleton() #こんな感じで出力→<__main__.Singleton object at 0x106614438> b = Singleton() #↑と同じ。<__main__.Singleton object at 0x106614438> print(a) print(b) print(a is b)
ざっくり解説。
classをインスタンス化した際にクラス変数(initial_class_name)をつけておくことで フラグ(インスタンスを生成したかどうか)として機能させています。
今回は参考URLが非常に秀逸だったのであまり語ることはないです。。
参考URL
【デザインパターン勉強】adapterメソッド
ざっくり概要
adapterメソッドとはざっくり言うと既に 提供されているclassと既に必要な抽象classを結びつけてプログラムを作成する方法です。 ポイントは、仲介役classを設定(提供されているclassと必要な抽象class)することです。
登場メソッド
必要な抽象メソッド(抽象classに存在)
提供されているメソッド(提供されているclass)
本来であれば・・・
抽象classと提供されているclassのメソッドを同一化すれば問題ないです が、 今回は 所々の事情で 修正出来ないケースを想定しています。
コード
#python3.5.2 import abc import os import csv class Format: def __init__(self,content): self.__content = content def make_file_csv(self,file_name): f = open('{}.csv'.format(file_name),'w') f.write(self.__content) def make_file_txt(self,file_name): f = open('{}.txt'.format(file_name),'w') f.write(self.__content) class TextMake(metaclass=abc.ABCMeta): @abc.abstractmethod def make_csv(self): pass @abc.abstractmethod def make_txt(self): pass class TextMakeFormat(TextMake,Format): def __init__(self,content): super().__init__(content) def make_csv(self,file_name): self.make_file_csv(file_name) def make_txt(self,file_name): self.make_file_txt(file_name) if __name__ == '__main__': textobj = TextMakeFormat('おはようございます。\n今日も元気で頑張りましょう。') textobj.make_csv("csv_file") #TextMakeFormatを経由してcsvファイル作成 textobj.make_txt("txt_file") #TextMakeFormatを経由してtxtファイル作成
各クラスの概要
Format(提供されているclass)
具体的処理メソッド(csv txtファイル作成)が記述されています。
TextMake(必要な抽象クラス)
既に必要とされているケースであり、メソッド(≠Formatのメソッド)が記述されています。
TextMakeFormat(仲介役)
FormatとTextMakeクラスの仲介役をします。 具体的には以下の手順となります。
- TextMake(必要な抽象クラス)をオーバーライドしてメソッドを宣言
- オーバーライドしたメソッドにFormat(提供されているclass)のメソッドを記述 これにより、必要な抽象class→提供されているClassの仲介を果たします。
感想
理解不足もあるかもしれませんが、利用シーンがあまり思い浮かびませんでした。。
参考文献
【デザインパターン勉強】builderメソッド
ざっくり概要
builderメソッドを自分の言葉でいうと、
作成するメソッドの役割は同じだが、異なる結果を得るプログラムを何個も 作る際、有用なプログラム(パターン) といった感じです。
ここで重要な点は 作成するメソッドの役割は同じ ということです。
本記事で記載したコードは税計算を表示するプログラムです。
登場メソッド
- set_total(金額インプット)
- set_tax(税率インプット)
- message_tax(消費税を計算、出力)
コード
# python3.5.2 import abc class TaxCalcDirector(): def __init__(self, builder): self.__builder = builder def construct(self,total,tax): self.__builder.set_total(self,total) self.__builder.set_tax(self,tax) self.__builder.message_tax(self) class AbstractTaxCalcBuilder(metaclass=abc.ABCMeta): @abc.abstractmethod def set_total(self,total): pass @abc.abstractmethod def set_tax(self,rate): pass @abc.abstractmethod def message_tax(self,message): pass class ConsumptionTaxBuilder(AbstractTaxCalcBuilder): def __init__(self): self.rate = 0 self.total = 0 def set_total(self,total): self.total = total def set_tax(self,rate): self.rate = rate def message_tax(self): tax = self.total * self.rate message = " ".join(["あなたの払う税金は",str(tax),"円です"]) print(message) class DeductionTaxBuilder(AbstractTaxCalcBuilder): def __init__(self): self.rate = 0 self.total = 0 def set_total(self,total): self.total = total def set_tax(self,rate): self.rate = -1 * rate def message_tax(self): tax = self.total * self.rate message = " ".join(["あなたが還付される税金は",str(tax),"円です"]) print(message) def main(): # 支払う税金 tax = TaxCalcDirector(ConsumptionTaxBuilder) tax.construct(1000,0.3) #あなたの払う税金は 300.0 円です # 還付 tax = TaxCalcDirector(DeductionTaxBuilder) tax.construct(200,0.1) #あなたが還付される税金は -20.0 円です if __name__ == '__main__': main()
main()メソッドでは、行いたい作業(クラス名)をインスタンス化しています。
作業に応じて、
tax = TaxCalcDirector(クラス) #クラス→ ConsumptionTaxBuilderやDeductionTaxBuilder
とすることで、クラスの継承を変えて、税の還付金計算に変更することが出来ます。
つまり、具体的な作業を認識せずに処理を変えられます。
今回は消費税計算、税の還付金計算と処理を変えています。
各クラスの役割
AbstractTaxCalcBuilder(Builder)
インスタンスを作成する際に必要な作業(メソッド)を規定しています。 つまり、以下のメソッドとなります。
- 金額インプット set_total
- 税率インプット set_tax
- 消費税を計算、出力 message_tax
メソッドをオーバーライドし忘れを防ぐために抽象クラスとしています。 教科書では Builder と呼ばれますが、「現場監督」といったほうがイメージしやすい気がします。
ConsumptionTaxBuilder(ConcreteBuilder)
AbstractTaxCalcBuilderで定義されたメソッドを具体化しています。
教科書では ConcreteBuilder と呼ばれますが「現場作業員」といったほうがイメージしやすい気がします。
TaxCalcDirector(Director)
AbstractTaxCalcBuilderのメソッドを用いてインスタンスを生成しています。 また、インスタンス作成の際、ConsumptionTaxBuilder継承してインスタンス化しています。
必要な情報は builderのメソッドとConsumptionTaxBuilderというConcreteBuilder のクラス名のみです。
ここで、重要なのは以下の2点です。
- AbstractTaxCalcBuilder(Builder)のメソッドのみを使っていること
- 具体的なアウトプットを得る際はConcreteBuilderのクラス名を継承して実行
教科書では Directorと呼ばれて説明されています。監督する立場なのでイメージしやすいですね。
ここでわかることは アウトプットを変更する際、ConcreteBuilderクラス名を修正して実行すればいい
→つまり、クラス名だけで柔軟にアウトプットを変えることが出来るということです。