【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 处理流程

相关文件可参考『官网文件』说明。


<<:  Extra04 - Git - 程序码版本控制

>>:  从盲人摸象到看见大象全貌

追求JS小姊姊系列 Day4 -- 我知道很怪,但你不好奇字串姐变身会怎样吗(下)

前情提要 倒在路边的我,醒来发现人早已不见,只好回家过节。 (时间来到了,回到家中的午餐後) **我...

Swift纯Code之旅 Day2. 「谁是主画面?」

昨天已经将Storyboard相关档案都删除了,那这时候Xcode会不知道Project的起始面画面...

Golang - Redis基本介绍

工作上没机会用到Redis 自己就搞一个来玩,以後工作说不定也会用到 Redis是什麽 官网:htt...

Constructor

当我们今天要储存个人的信息会使用到object,但仔细思考若有100位的话,是否太麻烦了 let p...

Day 29 - 用Mixins来共用方法

相信经过了前面二十几天的洗礼,小夥伴们应该都对Vue有了一定的掌握度了吧~ 今天要来提到的是Vue的...