iT邦幫忙

2021 iThome 鐵人賽

2
Software Development

淺談中台架構、DDD與Python實踐系列 第 9

【Day 09】配接器 設計模式(Python)

前言

上一篇我們用 Python 簡單的幾行程式,就可以實踐工廠方法設計模式,本篇繼續討論另一個設計模式 -- 配接器(Adapter)。

配接器(Adapter) 設計模式

配接器就像多規格轉接頭一樣,可以連接不同介面的類別或物件,使用者可以不知道每一種類別的規格,直接呼叫配接器即可,配接器會自動將要求送至合適的插座(Socket或稱Adaptee)處理,並將結果經由配接器傳回。
https://ithelp.ithome.com.tw/upload/images/20211019/20001976eVouLZr60X.png
圖一. 各種不同規格的插座(Socket)

https://ithelp.ithome.com.tw/upload/images/20211019/20001976R3PfIKip07.png
圖二. 可連接多種規格的配接器(轉接頭)

實踐

我們就以插頭為例,假設插座有2孔/3孔,電壓有110V/220V之分。

  1. 定義三種插座如下:
# 歐規插座,一律為3孔,省略 HoleNumber 方法
class European_Socket:
    def voltage(self):
      return 220

# 美規插座,有2孔/3孔
class USA_Socket1:
    def voltage(self):
      return 110

    def HoleNumber(self):
      return 2

class USA_Socket2:
    def voltage(self):
      return 110

    def HoleNumber(self):
      return 3
  1. 配接器程式碼如下,歐規沒有 HoleNumber 方法,配接器代為處理:
# 配接器
class Adapter(European_Socket):
    __socket = None
    def __init__(self, socket = None):
      self.__socket = socket
   
    def HoleNumber(self):
      # 是否為歐規
      if self.__socket is None or isinstance(self.__socket, European_Socket):
        return 3
        
      return self.__socket.HoleNumber()
  1. 測試
# 測試      
socket = USA_Socket1()
adapter = Adapter(socket)
print(adapter.HoleNumber())
      
adapter2 = Adapter()
print(adapter2.HoleNumber())

輸出結果分別為2、3。

從以上結果知道,使用者只要知道 Adapter 規格,配接器會幫我們連接至適合的插座。

應用

以上的範例只說明配接器的概念,但沒有彰顯設計模式的威力,以下舉聊天機器人的套件 ChatterBot為例,它是一個標準的配接器,除了內建的logic adapter,也可以自製adapter。

  1. ChatterBot 安裝:ChatterBot 並未依賴最新版的套件,安裝可能會發生問題,必須要特殊處理。
    使用 pip install chatterbot 不成功的話,必須執行 pip install chatterbot --no-dependencies,然後一一安裝依賴的套件。讀者不一定要安裝ChatterBot,如果只是要了解配接器的應用,看看下列程式即可。

  2. 首先載入套件及相關 adapter。

# 載入相關套件
from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer

# 載入內建的logic adapter
bot = ChatBot(
    'Built-in adapters',
    storage_adapter='chatterbot.storage.SQLStorageAdapter',
    logic_adapters=[
        'chatterbot.logic.MathematicalEvaluation',
        'chatterbot.logic.TimeLogicAdapter',
        'chatterbot.logic.BestMatch'
    ],
    database_uri='sqlite:///database.sqlite3'
)
  1. 測試。
# 時間測試:呼叫 bot.get_response。
response = bot.get_response("What time is it?")
print(f'回答:{response}')

由TimeLogicAdapter處理,輸出結果:The current time is 12:10 AM。

  1. 測試算術:一樣呼叫 bot.get_response。
# 7 + 7
response = bot.get_response("What is 7 plus 7?")
print(f'回答:{response}')

由MathematicalEvaluation處理,輸出結果:7 plus 7 = 14。

  1. 同樣呼叫 bot.get_response,可以由不同的Adapter處理,ChatterBot 還允許自訂Adapter,接收請求。

例如,加上 my_adapter.MyLogicAdapter 自訂配接器。

bot = ChatBot(
    'custom_adapter',
    storage_adapter='chatterbot.storage.SQLStorageAdapter',
    logic_adapters=[
        'my_adapter.MyLogicAdapter',
        'chatterbot.logic.MathematicalEvaluation',
        'chatterbot.logic.BestMatch',
    ],
    database_uri='sqlite:///database.sqlite3'
)

my_adapter.MyLogicAdapter 程式碼:

from chatterbot.logic import LogicAdapter

class MyLogicAdapter(LogicAdapter):

    def __init__(self, chatbot, **kwargs):
     super().__init__(chatbot, **kwargs)

    def can_process(self, statement):
        if statement.text.find('訂位') >= 0:
            return True
        return False

    def process(self, input_statement, additional_response_selection_parameters):
        import random
        from chatterbot.conversation import Statement
        
        # Randomly select a confidence between 0 and 1
        confidence = random.uniform(0, 1)
        
        # 
        answers = ['訂位日期、時間及人數?', '哪一天? 幾點? 人數呢?']
        selected_statement = Statement(text=random.choice(answers))
        selected_statement.confidence = confidence

        return selected_statement

測試:

# 測試自訂配接器
response = bot.get_response("我要訂位")
print(f'回答:{response}')

由MyLogicAdapter處理,輸出結果:訂位日期、時間及人數?

https://ithelp.ithome.com.tw/upload/images/20211020/20001976iClzZ4pIGx.png
圖三. ChatterBot 的 Adapter 處理流程

相關文件可參考『官網文件』說明。


上一篇
【Day 08】工廠方法設計模式(Python)
下一篇
【Day 10】Repository 設計模式(Python)
系列文
淺談中台架構、DDD與Python實踐10

尚未有邦友留言

立即登入留言