はじめに
この記事は個人的な勉強メモです。inputしたものはoutputしなくてはという強迫観念に駆られて記事を書いています。
あわよくば詳しい人に誤りの指摘やアドバイスを頂ければいいなという思いを込めてQiitaの記事にしています。
エンジニアとして社会人生活を送っていますが、デザインパターンについてちゃんと学んだことがなかったので勉強してみました。
ここに記載している内容は
https://github.com/ck-fm0211/notes_desigh_pattern
にuploadしています。
過去ログ
デザインパターンについて勉強してみた(個人的メモ)その1
デザインパターンについて勉強してみた(個人的メモ)その2
デザインパターンについて勉強してみた(個人的メモ)その3
デザインパターンについて勉強してみた(個人的メモ)その4
デザインパターンについて勉強してみた(個人的メモ)その5
Chain of Responsibilityパターン
- Chain of Respoisibility パターンとは、「責任」を負ったものが、「鎖」状につながれた状態をイメージさせるパターン
- 例:
- 何かの決済を「課長」にお願いすると、課長決裁で対応できるものであれば、課長が決裁する。
- 課長決済で対応できないものについては、「部長」に決裁をお願いすることになる。
- 当然、部長が決裁できない内容のものであれば、より上位の決裁責任者に決裁が委ねられる。
- 「責任者」を「鎖状」につないでおき、「いずれかの段階」で、「誰か」が処理をすることを表現するようなパターン。
実際に使ってみる
題材
-
学校での決裁を考えてみる
-
生徒A「遠足のおやつはいくらまでですか?」
- 新人先生「300円までです」 ← 新人先生の決裁
-
生徒B「バナナはおやつに入りますか?」
- 新人先生「判断できない」
- ベテラン先生「おやつに入りません」 ← ベテラン先生の決裁
-
生徒の親「携帯電話をもたせてもいいですか?」
- 新人先生・ベテラン先生「判断できない」
- 職員会議で決定 ← 更に上位の決裁
-
「責任を持つものが、自分の裁量で判断できるものに関しては自分で判断し、自分で判断できないものに関しては、次の責任者に判断を任せる」という連鎖
-
Chain of Responsibility パターンを利用するには、「一般的な責任者を表すクラス」を作成し、それを継承する形で、生徒、新人先生、ベテラン先生、職員会議などのクラスを作成する
-
「一般的な責任者を表すクラス」は、「判断」するメソッドと、自分で判断できなかった場合に、判断を仰ぐ、「次の責任者」を表すフィールド変数をもつ
-
各責任者を表すクラスは、この「一般的な責任者を表すクラス」を継承して作成する
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod
class Responsible(metaclass=ABCMeta):
def __init__(self, responsible_person: str):
self._responsible_person = responsible_person
self._next_level: Responsible = None
@abstractmethod
def set_next(self, next_level):
self._next_level = next_level
return self._next_level
@abstractmethod
def be_able_to_judge(self, question: Question) -> bool:
pass
@staticmethod
@abstractmethod
def judge(question: Question):
pass
@abstractmethod
def put_question(self, question: Question):
if self.be_able_to_judge(question):
self.judge(question)
elif self._next_level is not None:
self._next_level.put_question(question)
else:
print("誰にも判断できませんでした。やってみなさい。")
class Question:
"""
フィールドとして質問の内容を格納するString インスタンスと、
質問の難易度を表す Level インスタンスを持つ。
"""
def __init__(self, question: str, level: Level):
self._question = question
self._level = level
class Level:
"""
フィールドとして難易度を表すint型の値と、
自身の難易度と引数のLevelオブジェクトの難易度を比較するlessThan(Level level)メソッドを持つ
"""
def __init__(self, level: int):
self._level: int = level
def less_than(self, level: Level):
pass
class RookieTeachee(Responsible):
def __init__(self, responsible_person):
super().__init__(responsible_person)
self._level = Level(2)
def be_able_to_judge(self, question: Question):
if question._level.less_than(self._level):
return True
return False
@staticmethod
def judge(question: Question):
# ・・・
- 「責任者」それぞれを、クラスに分けてしまうことで、各責任者の役割を限定することができる。
- 責任者の役割を明確にし、責任者をクラスとして分割することで、より柔軟に、判断の流れである「鎖」を組み替えることができるようになる。
- 一方で、毎回鎖をたどっていくため、処理速度が遅くなることが考えられる。
- 速度重視のプログラムが必要なのか、柔軟性が求められるのか、状況によって判断する必要がある。
Chain of Responsibilityパターンのまとめ
Facadeパターン
- Facadeパターンは、既存のクラスを複数組み合わせて使う手順を、「窓口」となるクラスを作ってシンプルに利用できるようにするパターン
実際に使ってみる
題材
- 本を借りに図書館へ行ったがどこに何があるかわからない
- 所蔵本リストと、貸出帳があるが、使い方がわからない
# -*- coding:utf-8 -*-
class BookList:
"""所蔵本リスト"""
@staticmethod
def search_book(book_name):
location = None
# 本の名前から探す
# あればその場所を、なければnullを返す
return location
class LendingList:
"""貸出帳"""
@staticmethod
def check(book_name):
# 貸出帳をチェックする
# 貸出中ならtrue、そうでなければfalseを返す
return True
class Visitor:
@staticmethod
def main(args):
# 借りたい本はどこにあるんだろう?
# いつどこでどのメソッドを使うんだろう?
pass
- 司書さんに聞く
- これが「窓口」
class Visitor:
@staticmethod
def main(args):
# 窓口の司書さんに場所を聞く
shisho = Librarian()
location = shisho.search_book("昆虫図鑑")
if location == "貸出中です":
print("貸出中かよ!!")
elif location == "その本は所蔵していません":
print("無いんかい!!!")
else:
print("ありがとうございます")
class Librarian:
@staticmethod
def search_book(book_name):
# 本を探す
location = BookList.search_book(book_name)
# 本の場所がNoneではない(所蔵してる)とき
if location is not None:
# 貸出中かチェックする
if LendingList.check(book_name):
# 貸出中のとき
return "貸出中です"
else:
# 貸出中ではないとき
return location
else:
# 所蔵していないとき
return "その本は所蔵していません"
Facadeパターンのまとめ
Mediatorパターン
- 多数のオブジェクトの間の調整を行いながら処理をすすめる必要が ある場合に利用すると威力を発揮するパターン
- 例
- 信号が壊れた交差点で交通整備する警官
- 状況を判断して「止まれ」「進め」を指示する
- 複数のオブジェクト間の調整をするために、 各オブジェクトからの問い合わせを受け、 適宜判断を行い指示を出す「仲裁人」の役割を果たすクラスを利用するパターン
実際に使ってみる
題材
- マッチングアプリ
- 依頼に対して状況を見て回答する
# -*- coding:utf-8 -*-
class Concierge:
def __init__(self):
self._user_dict = {}
def add_user(self, user: User):
self._user_dict[user.get_name()] = user
@staticmethod
def consultation(colleagueInLove: User, secretLover: User):
possibility = 0
# 様々な状況を考慮してpossibilityを導出
return possibility
class User:
def __init__(self):
self._name = None
def get_name(self):
return self._name
class John(User):
def __init__(self):
super().__init__()
self._name = "John"
self._secret_lover = None
self._tension = None
self._mediator = Concierge()
def get_name(self):
return self._name
def set_secret_lover(self, user: User):
self._secret_lover = user
def needs_advice(self):
self._tension = self._mediator.consultation(self, self._secret_lover)
- Mediator パターンは、入力インタフェースなどに利用することができる。
- 例
『複雑に絡み合ったある条件を満たさなければ「有効」にならないボタン』を作成したいとき- 「ラジオボタン Aが選択されている状態で、テキストボックスに入力がある場合」もしくは、 「ラジオボタンB が選択されている状態で、チェックボックス C もしくは D にチェックが入っている場合」 に「有効」になるボタンは、「ラジオボタン A」がチェックされたときに、 「テキストボックスに入力があるか」を検証したり、ラジオボタン B が選択されたときに 「チェックボックス C もしくは D がチェックされているか」を検証するようなプログラムにしていると、チェックボックス C にチェックが入れられたときや、チェックボックス D のチェックがはずされたときなども検証する必要があるプログラムになる。
- 条件が複雑になるにつれ、どこで何をチェックしているのか管理できなくなってくるでしょう。
- Mediator パターンでは、各オブジェクトは、自分の状態が変わったときに、Mediator に連絡し、 Mediator は、状態が変わったオブジェクトだけでなく、各オブジェクトの状態を総合的に判断し、 「ボタン」を示すオブジェクトに、「有効」「無効」を伝えるような設計になり、管理が比較的楽になる。