週末!プログラミング部

ソフトウェア開発ネタを中心に自分でいろいろ調べた内容を自分の勝手な解釈で思うがままに書いくためのブログ。サンプルソースコード、API、プラットフォーム、プログラミング言語、開発環境などを調査、分析して追求いく予定です。

Pythonでデザインパターン 陸ノ型「Prototype」

今回はPythonの勉強しながらデザインパターンのPrototypeをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Prototypeとは

直訳すると「原型」や「模範」という意味になります。
wikiには「生成されるオブジェクトの種別がプロトタイプ(典型)的なインスタンスであるときに使用され、このプロトタイプを複製して新しいオブジェクトを生成する」とあります。
Prototypeパターンとは「オブジェクトからオブジェクト(複製)を作成するパターン」です。
javaC#とかでいうとclone()のことですね(・`ω´・)b

試行錯誤の時間

Pythonにおけるclone()は標準ライブラリである「copy」を使うことで簡単に実現することができます。

ここで「copy」には浅いコピーであるcopy()と深いコピーであるdeepcopy()があるようです。

copy.copy(x)
x の浅い (shallow) コピーを返します。

copy.deepcopy(x[, memo])
x の深い (deep) コピーを返します。

・・・な ん だ そ れ は ?(゚Д゚≡゚Д゚)?

説明には以下のように書かれています。

浅いコピー (shallow copy) は新たな複合オブジェクトを作成し、
その後 (可能な限り) 元のオブジェクト中に見つかったオブジェクトに
対する 参照 を挿入します。

深いコピー (deep copy) は新たな複合オブジェクトを作成し、
その後元のオブジェクト中に見つかったオブジェクトの
コピー を挿入します。

これによるとオブジェクトの中に別のオブジェクトが含まれていた場合は、

  • 浅いコピーだと中にあるオブジェクトは「参照型」(Cとかでいうポインタ型)になる
  • 深いコピーだと中にあるオブジェクトは「値型」になる

・・・ということかな?(;^ν^)
説明だとよくわからないのでいつもどおりコードを書いて試してみます\(^o^)/

まず、以下のようなクラスを用意しました。
この例ではOuterというアウタークラスの中にInnerというインナークラスがあります。
アウタークラスの中でインナークラスのインスタンスを作成して、get/setを介してアウタークラスからインナークラスにアクセスできます。

class Outer(object) :
    def __init__(self) :
        self.__value = 0
        self.__inner = self.Inner()

    def getOuterValue(self):
        return self.__value

    def setOuterValue(self, value):
        self.__value = value

    def getInnerValue(self):
        return self.__inner.getValue()

    def setInnerValue(self, value):
        self.__inner.setValue(value)

    class Inner(object) :
        def __init__(self) :
            pass

        def getValue(self):
            return self.__value

        def setValue(self, value):
            self.__value = value

このクラスを使ったテストコードは以下のとおりです。

if __name__ == "__main__" :
    
    origin = Outer()
    origin.setOuterValue(1)
    origin.setInnerValue(2)
    
    clone = copy.copy(origin)       # 浅いコピー
    #clone = copy.deepcopy(origin)   # 深いコピー

    print("現在のオブジェクト値")
    print("origin - Outer:", origin.getOuterValue())
    print("origin - Inner:", origin.getInnerValue())
    print("clone  - Outer:", clone.getOuterValue())
    print("clone  - Inner:", clone.getInnerValue())
    
    print("クローンで書き換え後のオブジェクト値")
    clone.setOuterValue(3)
    clone.setInnerValue(4)
    print("origin - Outer:", origin.getOuterValue())
    print("origin - Inner:", origin.getInnerValue())
    print("clone  - Outer:", clone.getOuterValue())
    print("clone  - Inner:", clone.getInnerValue())

まずは浅いコピーcopy.copy(origin)の実行結果です。
クローンで書き換え後のオブジェクト値を見ると、「origin - Inner」の値が「clone - Inner」と同じになっています。 「origin - Outer」は書き換え前のままです。

現在のオブジェクト値
origin - Outer: 1
origin - Inner: 2
clone  - Outer: 1
clone  - Inner: 2
クローンで書き換え後のオブジェクト値
origin - Outer: 1
origin - Inner: 4
clone  - Outer: 3
clone  - Inner: 4

続いて深いコピーcopy.deepcopy(origin)の実行結果です。
これによると「clone - Inner」は書き換わっていますが、「origin - Inner」は書き換わっていません。

現在のオブジェクト値
origin - Outer: 1
origin - Inner: 2
clone  - Outer: 1
clone  - Inner: 2
クローンで書き換え後のオブジェクト値
origin - Outer: 1
origin - Inner: 2
clone  - Outer: 3
clone  - Inner: 4

これらの結果から浅いコピーだとやはり「参照型」となっているようです。
そのため、cloneを使ってInnerの値を書きかえると、originのInnerも書き換わってしまいます。
したがって、Prototypeパターンを実現するには中のオブジェクトすべてをコピーすべきなので、深いコピー (deep copy)を採用すべきでしょうかね?

サンプルコード

いつものごとくサンプルコードです!

# coding: utf-8

import copy

from abc import ABC, ABCMeta, abstractmethod

class Cloneable(metaclass=ABCMeta) :
    
    @abstractmethod
    def createClone(self) :
        pass

class Prototype(Cloneable) :

    def __init__(self, name: str) :
        self.__name = name

    def createClone(self) :
        return copy.deepcopy(self)

    def getName(self) :
        return self.__name

if __name__ == "__main__" :
    origin = Prototype("AAA")
    clone = origin.createClone()

    print("origin:", origin.getName())
    print("clone :",  clone.getName())

以下、実行結果。
originに付けた名前がcloneにも引き継がれていますね(・`ω´・)

python Prototype.py
origin: AAA
clone : AAA

Pythonでデザインパターン 伍ノ型「Singleton」

今回はPythonの勉強しながらデザインパターンのSingletonをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Singletonとは

直訳すると「単一要素」みたいな意味になります。
wikiには「そのクラスのインスタンスが1つしか生成されないことを保証するデザインパターンのこと」とあります。
Singletonパターンはwikiにも説明があるとおり、クラスのインスタンスが複数作られないようにするためのメカニズムです。
アプリケーション全体で統一しなければならない仕様を実装しなければならないときに役に立ちます。
私はデザインパターンの中でこのパターンが一番お気に入りで、オブジェクト指向言語でなにか作るときにを頻繁に利用しています。
実際の開発現場でも使っている人をチラホラ見かけたので習得しておいても損はないパターンかもしれないですね(・`ω´・)b

Singletonパターンが欲しくなるとき

それではいつものごとく、いろいろコードを書きながら考えてみます\(^ω^)/
まず以下のようなConfigクラスがあったとします。

class Config(object) :
    def __init__(self) :
        self.ParameterA = 1
        self.ParameterB = 2
        self.ParameterC = 3

このクラスを普通に使おうとするとこんな感じでしょうか?

def moduleA():
    conf = Config()
    pramA = conf.ParameterA

ひとつのモジュールで使うだけならこれでもいいのですが、 複数のモジュールでこのクラスのパラメータを共有しなければならなくなったとします。
そこでモジュール毎にインスタンスを作ってみます。
こうしてしまうとインスタンスがそれぞれ独立しているので、あるインスタンスのパラメータを書き換えても別のインスタンスには反映されません。

def moduleA():
    conf = Config()
    pramA = conf.ParameterA
    conf.ParameterA = 5

def moduleB():
    conf = Config()
    pramA = conf.ParameterA
    conf.ParameterA = 6

今度はConfigをグローバル変数にしてみます。
こうすればmoduleAとmoduleBでインスタンスを共有することができます。

conf = Config()

def moduleA():
    pramA = conf.ParameterA
    conf.ParameterA = 5

def moduleB():
    pramA = conf.ParameterA
    conf.ParameterA = 6

しかし、プログラムの規模が大きくなって複数人で開発するようになってきたりすると、 誰かがどこかでまたConfigのインスタンスを作ってしまったり、confに新しいインスタンスで上書きしてしまうかもしれません。 そうするとまた独立したインスタンスが複数できてしまうことになります。

conf = Config()
・
・
・
config = Config() # confの存在に気がつかずに新しいインスタンスを作成

def moduleA():
    pramA = conf.ParameterA
    conf.ParameterA = 5

def moduleB():
    pramA = conf.ParameterA
    conf = Config() # 誤ってインスタンスの上書き
    conf.ParameterA = 6
    
def moduleC():
    pramA = conf.ParameterA
    config.ParameterA = 7 # confではないインスタンスを使用

このようなことを回避するには、一度インスタンスを作成したら、それ以降は新しいインスタンスを作成できないようにしなければなりません(;゚Д゚)

そこでSingletonパターンの登場です。イメージとしては以下のような感じです。
ConfigにクラスメソッドgetInstance()を実装しておきます。
getInstance()は、クラス内にインスタンスがなければクラス変数に自身のインスタンスを作成してそれを返します。
クラス変数に自身のインスタンスがすでに存在すれば、それをそのまま返します。
そしてConfig()でインスタンスを作成しようとしたときエラーを出力するようにします。
このようにしておけばConfigクラス内だけに存在する単一のインスタンスを作ることができます。

#conf = Config()   # 新しいインスタンス生成はエラー

def moduleA():
    conf = Config.getInstance() # インスタンス取得
    pramA = conf.ParameterA
    conf.ParameterA = 5

def moduleB():
    #conf = Config()   # 新しいインスタンス生成はエラー
    conf = Config.getInstance() # インスタンス取得
    pramA = conf.ParameterA
    conf.ParameterA = 6

試行錯誤の時間

まずConfig()でインスタンスを作成できないようにしてみます。
Pythonではraise Exceptionで例外をスローできます。
そこで以下のようにしてコンストラクタが呼び出されたときにエラーを出力できるようにしてみました。

class Config(object) :
    def __init__(self) :
        raise Exception("Singletonクラス")

これでインスタンスを作成しようとするとエラーになります。

conf = Config() # エラーになる

つぎにインスタンスを取得するためのクラスメソッドgetInstance()を実装してみます。
この例ではクラス変数__mInstanceが空っぽだった場合、Config() で自身のインスタンスを作成するようにしています。
もうお気づきかもしれませんが、これでは困ったことがおきます。
先の実装で、Config()でインスタンスを作成しようとするとエラーを発生させるようにしてしまったからです(・´ω`・;)

class Config(object) :
    __mInstance = None

    def __init__(self) :
        raise Exception("Singletonクラス")

    @classmethod
    def getInstance(cls) :
        if cls.__mInstance == None :
            cls.__mInstance = Config()  # エラー
        return cls.__mInstance

そこで__new__というものを利用します。
参考)__new__ ってなに?
def __new__(cls)は、インスタンスオブジェクトが作成される前に呼び出さる特殊なメソッドです。
以下、動作確認用のコードを書いてみたのでこれで動きを見てみます。

# coding: utf-8

class parent(object) :
    def __new__(cls) :
        print("new")
        return super().__new__(cls)

    def __init__(self) :
        print("init")

    def __del__(self) :
        print("del")

if __name__ == '__main__':
    obj = parent()

これの実行結果は以下のようになります。
先に__new__が呼び出されて、__init__が呼び出されているのがわかります。
引数であるclsはクラスオブジェクトを示しており、super().__new__(cls)でクラスオブジェクトに対するインスタンスオブジェクトを作成することができます。
__new__(cls)のあとに__init__(self)が呼び出さるわけですが、selfはのsuper().__new__(cls)で作ったインスタンスオブジェクトだったわけですね。 (ちなみにreturn super().__new__(cls)を消せば、__init__(self)が呼び出されなくなります。当然ながら)

new
init
del

__new__を応用して以下のようにしてみました(・`ω´・)v
この例では、def __new__(cls)が呼び出されたときにraise Exceptionで例外をスローするようにしました。
そしてdef getInstance(cls)が呼び出されたとき、クラス変数__mInstanceが空っぽだった場合、 クラスオブジェクトを示すclsを使って、super().__new__(cls)でインスタンスを作成するようにしています。

class Config(object) :

    __mInstance = None

    def __new__(cls) :
        print("new")
        raise Exception("Singletonクラス")

    def __init__(self) :
        print("init")

    def __del__(self) :
        pass

    @classmethod
    def getInstance(cls) :
        if cls.__mInstance == None :
            cls.__mInstance = super().__new__(cls)
        return cls.__mInstance

これでConfig()でインスタンスを作成しようとすると以下のようなエラーを出力するようになります。

  File "Singleton.py", line 7, in __new__
    raise Exception("Singletonクラス")
Exception: Singletonクラス

インスタンスの取得(未作成の場合は作成も)するには、以下のようにしてやらなければならなくなるはずです。

conf = Config.getInstance()

サンプルプログラム

以下、サンプルプログラムです。

# coding: utf-8

class _Singleton(object) :

    __mInstance = None

    def __new__(cls) :
        print("new  parent")
        raise Exception("Singletonクラス")

    def __init__(self) :
        print("init parent")

    @classmethod
    def getInstance(cls) :
        if cls.__mInstance == None :
            cls.__mInstance = super().__new__(cls)
        return cls.__mInstance

class myClass(_Singleton) :

    def __new__(cls) :
        print("new  child")
        return super().__new__(cls)

    def __init__(self) :
        print("init child")

    def setValue(self, value) :
        self.__mValue = value
    
    def getValue(self) :
        return self.__mValue

if __name__ == '__main__':
    
    #obj = Singleton()              # エラー
    #obj = Singleton.getInstance()

    #obj1 = myClass()               # エラー

    obj1 = myClass.getInstance()
    obj1.setValue(100)
    print(obj1.getValue())

    obj2 = myClass.getInstance()
    print(obj2.getValue())

    obj2.setValue(50)
    print(obj1.getValue())

実行結果はこのようになります!
myClass.getInstance()でobj1とobj2で同じインスタンを取得しています。
その後、それぞれでgetValue/setValueを行っていますが、printすればobj1とobj2で値が共有できていることがわかります。

$ python Singleton.py
100
100
50

Pythonでデザインパターン 肆ノ型「FactoryMethod」

今回はPythonの勉強しながらデザインパターンFactoryMethodをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

FactoryMethodとは

直訳すると「工場手続き」みたいな意味になります。
wikiには「他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高める」とあります。

FactoryMethodについて考えてみる

wikiの文言だと私にはよくわからなかったので、 いつものごとく色々コードを書いて自分なりの理解を書いてみたいと思います(;^ω^)

まず以下のようなProductインタフェースとそれを実装したProductA クラスとProductB クラスがあったとします。

class Product(metaclass=ABCMeta) :
    @abstractmethod
    def functionA(self) :
        pass

class ProductA(Product) :
    def functionA(self) :
        # 機能Aを実装(処理内容は割愛)

class ProductB(Product) :
    def functionA(self) :
        # 機能Aを実装(処理内容は割愛)


これを使ったmainは以下のようなイメージになります。
このときProductAもProductBもインスタンスを作成後に、それぞれの専用初期化メソッドInitializeA、B、Cを順番に呼び出さなければならないものとします。
そうするとmainにProductA、ProductBに対するそれぞれの初期化メソッドを実装しなければならなくなります。 またProductのインスタンスを作成するたびに、これらのメソッドを選択して順番に呼ばなければならなくなります。
これではProductCとかProductDとかできるたびにmainがどんどん肥大化して、複雑になっていきますね。。。(´ε`;)

def ProductAInitializeA(product : Product) :
    # ProductAの初期化処理A

def ProductAInitializeB(product : Product) :
    # ProductAの初期化処理B

def ProductAInitializeC(product : Product) :
    # ProductAの初期化処理C

def ProductBInitializeA(product : Product) :
    # ProductBの初期化処理A

def ProductBInitializeB(product : Product) :
    # ProductBの初期化処理B

def ProductBInitializeC(product : Product) :
    # ProductBの初期化処理C

if __name__ == '__main__' :
    product = ProductA()
    ProductAInitializeA(product)
    ProductAInitializeB(product)
    ProductAInitializeC(product)
    product.functionA()  #これでProductAが使えるようになる

    product = ProductB()
    ProductBInitializeA(product)
    ProductBInitializeB(product)
    ProductBInitializeC(product)
    product.functionA()  #これでProductBが使えるようになる


これを回避するもっとも単純な方法は、ProductAクラス、ProductBクラスそれぞれに専用の初期化メソッドを実装し、コンストラクタなどで初期化処理を呼び出すことが考えられます。
こうするとmainはとてもシンプルになりますね(・`ω´・)

しかし、この方法ではProductAクラス、ProductBクラスに直接手を加えなければなりません。
もし「InitializeA、B、Cがこのアプリケーションのみで有効な初期化処理だった場合」 かつ 「ProductAクラスやProductBクラスを他のシステムで使いまわしたい場合」を考えると InitializeA、B、Cを他のシステムに合わせて変更しなければならなくなるかもしれません。
そうするとProductAクラス、ProductBクラスの再利用性は低下してしまいますね。。(;゚Д゚)

class ProductA(Product) :
    def functionA(self) :    
        # 処理A

    def __InitializeA(self) :
        # 初期化処理A

    def __InitializeB(self) :
        # 初期化処理B

    def __InitializeC(self) :
        # 初期化処理C

    def __init__(self) :
        self.__InitializeA()
        self.__InitializeB()
        self.__InitializeC()

class ProductB(Product) :
    def functionA(self) :    
        # 処理A

    def __InitializeA(self) :
        # 初期化処理A

    def __InitializeB(self) :
        # 初期化処理B

    def __InitializeC(self) :
        # 初期化処理C

    def __init__(self) :
        self.__InitializeA()
        self.__InitializeB()
        self.__InitializeC()

if __name__ == '__main__' :
    product = ProductA()
    product.functionA()

    product = ProductB()
    product.functionA()


そこで以下のようにしてみました(・`ω´・)b
この例では、まずFactoryという抽象クラスを用意しています。
このクラスには以下のものがあります。

  • 初期化処理を実装するためのInitializeA、B、Cインタフェース
  • Productのインスタンスを生成するためのcreateインスタンス
  • 初期化手順を実装したcreateProduct()メソッド

そしてFactoryAとFactoryBは、Factoryを継承して、それぞれ以下の実装が作れらています。

  • InitializeA、B、Cインタフェースを使ってProduct固有の初期化処理を実装
  • createインタフェースを使ってProductのインスタンスを生成・返却するように実装

これによりFactoryのcreateProductメソッドは、createでインスタンスを取得し、InitializeA、B、Cで初期化したインスタンスを返すことができるようになります。

これで、wikiにあるように「mainからProductの生成と初期化をFactoryクラスに追い出し」、そして「Productに手を加えないため再利用性を高めることができる」 ということが実現できそうな気がします。

class Factory(metaclass=ABCMeta) :
    @abstractmethod
    def _create(self) -> Product :
        pass
    
    @abstractmethod
    def _InitializeA(self, product : Product) :
        pass

    @abstractmethod
    def _InitializeB(self, product : Product) :
        pass

    @abstractmethod
    def _InitializeC(self, product : Product) :
        pass

    def createProduct(self) :
        product = self._create()
        self._InitializeA(product)
        self._InitializeB(product)
        self._InitializeC(product)
        return product

class FactoryA(Factory) :
    @abstractmethod
    def _create(self) -> Product :
        return FactoryA()
    
    @abstractmethod
    def _InitializeA(self, product : Product) :
        # 初期化処理A

    @abstractmethod
    def _InitializeB(self, product : Product) :
        # 初期化処理B

    @abstractmethod
    def _InitializeC(self, product : Product) :
        # 初期化処理C

class FactoryB(Factory) :
    @abstractmethod
    def _create(self) -> Product :
        return FactoryB()
    
    @abstractmethod
    def _InitializeA(self, product : Product) :
        # 初期化処理A

    @abstractmethod
    def _InitializeB(self, product : Product) :
        # 初期化処理B

    @abstractmethod
    def _InitializeC(self, product : Product) :
        # 初期化処理C

if __name__ == '__main__' :
    factory = FactoryA()
    product = factory.createProduct()
    product.functionA()

    factory = FactoryB()
    product = factory.createProduct()
    product.functionA()

こんな感じなのがFactoryMethodパターンかなと思っています\(^ω^)/

サンプルプログラム

以下、PythonFactoryMethodパターンのサンプルプログラムです。

# coding: utf-8

from abc import ABC, ABCMeta, abstractmethod

class Product(metaclass=ABCMeta) :
    @abstractmethod
    def use(self) -> None:
        pass

class Account(Product) :
    def __init__(self, owner: str) -> None :
        print("Create account: ", owner)
        self.__owner = owner

    def use(self) -> None:
        print("Use account: ", self.__owner)

    def getOwner(self) -> str :
        return self.__owner

class Factory(metaclass=ABCMeta) :
    def create(self, owner: str) -> Account :
        product = self.createProduct(owner)
        self.registerProduct(product)
        return product

    @abstractmethod
    def createProduct(self, owner: str) -> Account :
        pass

    @abstractmethod
    def registerProduct(self, product: Account) -> None :
        pass

class AccountFactory(Factory) :
    def __init__(self) :
        self.__owners: list[str] = []

    def createProduct(self, owner) -> Account :
        return Account(owner)

    def registerProduct(self, product: Account) -> None :
        self.__owners.append(product.getOwner())
    
    def getOwners(self) -> list :
        return self.__owners

if __name__ == '__main__' :
    factory = AccountFactory()

    account1 = factory.create("AAA")
    account2 = factory.create("BBB")
    account3 = factory.create("CCC")
    account4 = factory.create("DDD")

    account1.use()
    account2.use()
    account3.use()
    account4.use()

以下は実行結果です!

$ python FactoryMethod.py
Create account:  AAA
Create account:  BBB
Create account:  CCC
Create account:  DDD
Use account:  AAA
Use account:  BBB
Use account:  CCC
Use account:  DDD

Pythonでデザインパターン 参ノ型「TemplateMethod」

今回はPythonの勉強しながらデザインパターンTemplateMethodをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

TemplateMethodとは

直訳すると「型板手続き」みたいな意味になります。
wikiには「ある処理のおおまかなアルゴリズムをあらかじめ決めておいて、そのアルゴリズムの具体的な設計をサブクラスに任せること」とあります。

TemplateMethodについて考えてみる

TemplateMethodパターンは抽象クラスを使用することで実現することができます。
いつもどおり、ざっくりした例で説明してみます。

以下はテンプレートクラス:Templateです。
このクラスではinterfaceA()、interfaceB()、interfaceC()というインタフェースがあります。
また、methodA()というメソッドがあり、このメソッドではinterfaceA()、interfaceB()、interfaceC()を順番に呼び出しています。 これがテンプレートメソッドとなります。

class Template(metaclass=ABCMeta) :

    @abstractmethod
    def _interfaceA(self) :
        pass

    @abstractmethod
    def _interfaceB(self) :
        pass

    @abstractmethod
    def _interfaceC(self) :
        pass

    def methodA(self) :
        self._interfaceA()
        self._interfaceB()
        self._interfaceC()

そして以下がTemplateクラスを継承した実体クラス:Hogeです。
このクラスではTemplateクラスのinterfaceA()、interfaceB()、interfaceC()をオーバライドして実装しています。 処理内容は割愛で(;^ω^)

class Hoge(Template) :

    def _interfaceA(self) :
        # 処理A

    def _interfaceB(self) :
        # 処理B

    def _interfaceC(self) :
        # 処理C

最後にHogeクラスを使用するmainの例です。
この例ではHogeクラスのインスタンスを作成して、methodA()を呼び出しています。

if __name__ == '__main__' :
    hogo = Hogo()
    hogo.methodA() # interfaceA, B, Cが順番に呼び出される

HogeクラスはTemplateクラスを継承しているので、TemplateクラスのmethodA()が呼び出されます。
したがってHogeクラスで実装したinterfaceA()、interfaceB()、interfaceC()が順番に呼び出されます。

この例をwikiの言葉を借りてまとめると、
methodA()のように、「ある処理のおおまかなアルゴリズムをあらかじめ決めておいて」、
interfaceA()、interfaceB()、interfaceC()のように、別クラスで実装することで「そのアルゴリズムの具体的な設計をサブクラスに任せる」
という感じがTemplateMethodパターンとなります(・`ω´・)

サンプルプログラム

以下、サンプルプログラムです。
AbstractDisplayクラスがテンプレートクラス、displey()がテンプレートメソッドになります。
そしてCharDisplayとStringDisplayクラスがAbstractDisplayクラスを継承した実体クラスです。
CharDisplayは1文字、StringDisplayは文字列をdispley()で指定したように表示するようになっています。
そのために、それぞれAbstractDisplayクラスのインタフェースであるmyOpen()、myPrint()、myClose()をオーバライドして実装しています。
CharDisplayクラスのmyOpen()、myClose()は"***"、myPrint()ではコンストラクタで指定された文字を表示するようになっています。
またStringDisplayクラスではmyClose()は"---"、myPrint()ではコンストラクタで指定された文字列を表示するようになっています。

# coding: utf-8

from abc import ABC, ABCMeta, abstractmethod

class AbstractDisplay(metaclass=ABCMeta) :

    @abstractmethod
    def myOpen(self) :
        pass

    @abstractmethod
    def myPrint(self) :
        pass

    @abstractmethod
    def myClose(self) :
        pass

    def displey(self) :
        self.myOpen()
        for i in range(3) :
            self.myPrint()
        self.myClose()

class CharDisplay(AbstractDisplay) :

    def __init__(self, ch) :
        self.__ch = ch

    def myOpen(self) :
        print("***")

    def myPrint(self) :
        print(self.__ch)

    def myClose(self) :
        print("***")

class StringDisplay(AbstractDisplay) :
    
    def __init__(self, str) :
        self.__str = str
    
    def myOpen(self) :
        print("---")

    def myPrint(self) :
        print(self.__str)

    def myClose(self) :
        print("---")

if __name__ == '__main__' :

    cd = CharDisplay('T')
    sd = StringDisplay("Design Pattern")
    sd2 = StringDisplay("Template Method")

    cd.displey()
    sd.displey()
    sd2.displey()

実行結果は以下になります!
CharDisplayのdispley()とStringDisplayで表示結果は異なるけれど(myOpen()、myPrint()、myClose()の実装は異なるけれど)、アルゴリズムは同じ(displey()でmyOpen()、myPrint()、myClose()を使用する手順は同じ)であるとわかります(・`ω´・)v

$ python TemplateMethod.py
***
T
T
T
***
---
Design Pattern
Design Pattern
Design Pattern
---
---
Template Method
Template Method
Template Method
---

Pythonでデザインパターン 弐ノ型「Adapter」

今回はPythonの勉強しながらデザインパターンのAdapterをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Adapterとは

直訳すると「適合させるもの」という意味になりますが、
直訳だと少しわかりにくいですね(;´Д`)
wikiには「既存のクラスに対して修正を加えることなく、インタフェースを変更することができる」とあります。

Adapterについて考えてみる

アダプタと言われると、身近なものではACアダプタが思いつきます。
ACアダプタはコンセントからとったAC(交流)をDC(直流)に変換し、電子機器に電源を供給するための装置です。
このことからアダプタとは、何かからの入力を、変換して、別の何かで使えるようにするための仲介役であると捉えることができますね。

デザインパターンにおけるAdapterパターンもこの考え方を当てはめると、
クラスAがクラスBを使用したい場合、AとBの仲介役として、Bからの入出力を吸収(変換)して、AにBの機能を提供するためのパターンという感じです。

サンプルプログラム

AdapterパターンをPythonで実装してみました。
この例ではHumanクラスのStudentインタフェースでラップしたAdapterを作っています。
Adapterパターンの実現方法には継承を利用するパターンと委譲を利用するパターンがあります。

HumanAdapterクラスは継承を利用するパターンです。
こちらはHumanクラスを継承しています。
そしてStudentインタフェースshowName()とshowAge()を実装するために、親クラスであるHumanクラスのprintName()とprintAge()を呼び出しています。

一方、HumanAdapter2クラスは委譲を利用するパターンです。
こちらはコンストラクタ内でHumanクラスのインスタンスを作成しています。
そしてStudentインタフェースshowName()とshowAge()を実装するために、HumanクラスのインスタンスのprintName()とprintAge()を呼び出しています。

# coding: utf-8

from abc import ABC, ABCMeta, abstractmethod

class Human(object) :
    
    def __init__(self, name, age) :
        self.__name = name
        self.__age = age

    def printName(self) :
        print(self.__name)

    def printAge(self) :
        print(self.__age)

class Student(metaclass=ABCMeta) :

    @abstractmethod
    def showName() :
        pass

    @abstractmethod
    def showAge() :
        pass

class HumanAdapter(Human, Student) :

    def __init__(self, name, age) :
        Human.__init__(self, name, age)

    def showName(self):
        self.printName()

    def showAge(self) :
        self.printAge()

class HumanAdapter2(Student) :

    def __init__(self, name, age) :
        self.__human = Human(name, age)

    def showName(self):
        self.__human.printName()

    def showAge(self) :
        self.__human.printAge()

if __name__ == "__main__" :

    obj = HumanAdapter("AAA", 123)
    obj.showName()
    obj.showAge()
    
    obj2 = HumanAdapter2("BBB", 987)
    obj2.showName()
    obj2.showAge()

実行結果は以下のとおりです!
継承でも委譲でもAdapterの使い方は同じです。

$ python Adapter.py
AAA
123
BBB
987

Pythonでデザインパターン 壱ノ型「Iterator」

今回はPythonの勉強目的でデザインパターンIteratorをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Iteratorとは

直訳だと「繰り返す」や「反復」という意味になります。
wikiには「コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供する」とあります。

Iteratorについて考えてみる

プログラミング言語には配列、リスト、ハッシュテーブルなどのデータを1つのオブジェクト(集合体)にまとめる仕組みがあります。
しかし、データへアクセスする方法はオブジェクト毎に異なるので、オブジェクトを利用するユーザは「オブジェクト毎にデータにアクセスする処理」を書かなければいけなくなります。
例えば、以下のような感じです。

添え字を数値としてアクセスする一般的な配列
= arry[N]

添え字をキーワードとしてアクセスする連想配列
= arry["keyword"]

アクセッサーをもつオブジェクト
= arry.get(N)

などなど

そこで「共通の方法でオブジェクト(集合体)にアクセスしてデータを取り出す仕組みを提供するよ!」といっているのがIteratorパターンかなと思っています。
イメージとしては以下のような感じです。

if __name__ == '__main__':
    dataA = list(range(5))    # 0, 1, 2, 3, 4
    dataB = {"AAA" : 1, "BBB" : 2, "CCC" : 3, "DDD" : 4}

    # dataAに対するIteratorを作成
    itr  = Iterator(dataA)
    while itr.hasNext() :    # 次のデータがなくなるまで繰り返し
        print(itr.next())    # データを取り出して次へ

   # dataBに対するIteratorを作成
    itr  = Iterator(dataB)
    while itr.hasNext() :    # 次のデータがなくなるまで繰り返し
        print(itr.next())    # データを取り出して次へ

整数型の配列dataAと文字列型をキーとする連想配列集dataBがあったとします。
このイメージだと、まずdataAに対するIteratorというオブジェクトを作成しています。
dataAへのアクセスはIteratorオブジェクトが持つhasNext()とnext()というメソッドを使用して行います。
この例では、hasNext()は集合体から読み出せるデータがあるかどうかを判定する機能、 next()は集合体から次のデータを取り出す機能を提供しています。
hasNext()を使ってデータが無くなるまで繰り返しnext()を呼び出しています。
その後、集合体dataBに対するIteratorオブジェクトを作成して同様のことを行います。
これによりユーザは、dataAでもdataBでも共通の方法で集合体にアクセスできるようになります。

サンプルプログラム

以下、サンプルプログラムです。
StudentListが集合体クラスで、Studentが集合体で管理するデータクラスです。

MyStudentListはStudentListを継承しているため集合体としても機能するようにもなっており、 iterator()メソッドで自分自身を渡してMyStudentListIteratorを生成するようになっています。

MyStudentListIteratorは生成されたタイミングでMyStudentListのインスタンスを保持します。
このインスタンスを使って、next()やhasNext()で集合体を操作する機能を実現しています。

# coding: utf-8

from abc import ABC, ABCMeta, abstractmethod

# Iteratorインタフェース
class Iterator(metaclass=ABCMeta) :

    @abstractmethod
    def hasNext() :
        pass

    @abstractmethod
    def next() :
        pass

# Aggregateインタフェース
class Aggregate(metaclass=ABCMeta) :

    @abstractmethod
    def iterator() :
        pass

# データクラス
class Student(object) :

    def __init__(self, name, sex) :
        self.__name = name
        self.__sex  = sex

    def getName(self) :
        return self.__name

    def getSex(self) :
        return self.__sex

# データリストクラス(集合体)
class StudentList(object) :

    def __init__(self) :
        self.__students = []

    def add(self, student) :
        self.__students.append(student)

    def getStudentAt(self, index) :
        return self.__students[index]

    def getLastNum(self) :
        return len(self.__students)

# Aggregateインタフェースを実装したクラス
class MyStudentList(StudentList, Aggregate) :

    def __init__(self) :
        StudentList.__init__(self)

    def iterator(self) :
        return MyStudentListIterator(self)

# Iteratorインタフェースを実装したクラス
class MyStudentListIterator(Iterator) :

    def __init__(self, list) :
        self.__myStudentList = list
        self.__index = 0

    def hasNext(self) :
        if self.__myStudentList.getLastNum() > self.__index :
            return True
        else :
            return False

    def next(self) :
        student = self.__myStudentList.getStudentAt(self.__index)
        self.__index += 1
        return student

# ユースケース
if __name__ == '__main__':

    list = MyStudentList()
    list.add(Student("AAA", 1))
    list.add(Student("BBB", 0))
    list.add(Student("CCC", 1))

    itr = list.iterator()

    while itr.hasNext() :
        print(itr.next().getName())

実行結果は以下のとおりです。

$ python Iterator.py
AAA
BBB
CCC

Pythonを勉強しながらデザインパターンを勉強してみる

最近、Pythonの勉強を始めてみました。
私は新しい言語を覚えるとき、とにかく真似てもいいから色んなコードを書いて、その言語の雰囲気を味わうようにしています。
そんな勉強方法が良いかはさておき(・`ω´・;)

今回も色んなコードを書いてみるところから始めたいと思いますが、基本的な文法や考え方は他の言語を使っているからなんとなくわかる気がします。
なので、今回は題材としてデザインパターンPythonで書いていってみようかと思います。
実は、長らくプログラマーをやっていたのにデザインパターンも真面目に勉強したことがなかったのでこの機会に一緒にお勉強してみたいと思います(;´Д`)
使うかどうかはさておき(・`ω´・;)

デザインパターンとは

かつて、プログラミングの達人たちが自らの経験のもとに、「こういうときはこういう作り方をしたほうがいいよね!」といった実装のパターンを集めたノウハウ集です。
23の基本パターンから成ります。
その全てのパターンが、状況によっては必ずしも最善ではないかもしれませんが、プログラムを齧っている人ならば全パターンを知っておきたいところです。
使うかどうか、役に立つかどうか、はさておき(・`ω´・;)

Pythonデザインパターン一覧

※他の記事を追加したときはここにリンクを貼っていきたいと思います。
今、書けているのは、以下のとおり。
ゆっくり増やしていきたいと思うので温かい目で見守っていただけると助かります

参考書籍

以下を参考にさせてきただきました。