首页 > 编程问答 >在Python中抽象出具有相同接口的真实硬件和模拟设备

在Python中抽象出具有相同接口的真实硬件和模拟设备

时间:2024-08-07 04:55:40浏览次数:11  
标签:python oop design-patterns state-pattern proxy-pattern

我正在寻找一种更惯用或更简洁的 OOP 模式来实现与以下内容等效的功能。

接口和实现

from abc import ABC, abstractmethod
from typing import override

class Device:
    """Main interface that hides whether the device is a real hardware device or
    a simulated device.
    """

    def __init__(self, ip_address: str, port: int, simulated: bool) -> None:
        if simulated:
            self.__session = DeviceSimulated()
        else:
            self.__session = DeviceHardware(ip_address, port)

    def initialize(self) -> None: self.__session.initialize()

    def close(self) -> None: self.__session.close()

    def turn_on(self) -> None: self.__session.turn_on()

    def turn_off(self) -> None: self.__session.turn_off()

class DeviceHardware(Device):
    def __init__(self, ip_address: str, port: int) -> None:
        self._ip_address = ip_address
        self._port = port

    @override
    def initialize(self) -> None:
        print(f"DeviceHardware initialized with IP: {self._ip_address} and Port: {self._port}")

    @override
    def close(self) -> None: print("DeviceHardware closed")

    @override
    def turn_on(self) -> None: print("DeviceHardware turned on")

    @override
    def turn_off(self) -> None: print("DeviceHardware turned off")


class DeviceSimulated(Device):
    def __init__(self) -> None:
        # Nothing to initialized for the simulated device
        pass

    @override
    def initialize(self) -> None: print(f"DeviceSimulated initialized")

    @override
    def close(self) -> None: print("DeviceSimulated closed")

    @override
    def turn_on(self) -> None: print("DeviceSimulated turned on")

    @override
    def turn_off(self) -> None: print("DeviceSimulated turned off")

客户端用法

这将由这样的客户端使用。

# Simulated case
device = Device(ip_address="192.168.1.10", port=123, simulated=True)
device.initalize()
device.turn_on()
device.turn_off()
device.close()

# Real hardware case
device = Device(ip_address="192.168.1.10", port=123, simulated=False)
device.initalize()
device.turn_on()
device.turn_off()
device.close()

问题

基本上,重点是我向用户提供了一个统一的界面,这样他们需要关心的就是切换 simulated 参数。同样,我为接口的模拟和真实硬件设备实现提供了一个共享的统一接口。

是否有更好的 Python 和/或 OOP 方法来处理这个问题以满足这两个约束?这很有效,很简单,也很直接,但可能有点冗长。因此,虽然可能有更优雅的解决方案,但我对那些需要更少代码和样板的解决方案特别感兴趣。

我用不同的语言完成了此操作,但是,这些对象是基于值的,而不是基于引用的,所以做法有所不同。正如 Python 中的情况,它有点像状态模式,因为 Device 根据 simulated 参数更改其“状态”,每个方法都推迟到特定的、实际的“状态”班级。


你当前使用抽象基类(ABC)和组合的方法是完全有效的,并且是 Python 中一种很好且常用的模式。但是,如果你的目标是减少样板代码并提高简洁性,你可以探索其他一些方法:

1. 使用依赖注入:

你可以在 Device 构造函数中注入一个模拟或真实的设备实现,而不是在 Device 类中处理创建逻辑。这可以提高灵活性,并允许更容易地测试代码的不同部分。

from abc import ABC, abstractmethod

class Device(ABC):
    @abstractmethod
    def initialize(self) -> None: pass

    @abstractmethod
    def close(self) -> None: pass

    @abstractmethod
    def turn_on(self) -> None: pass

    @abstractmethod
    def turn_off(self) -> None: pass

class DeviceHardware(Device):
    # ... (与之前相同的实现)

class DeviceSimulated(Device):
    # ... (与之前相同的实现)

# 客户端用法
if simulated:
    device = DeviceSimulated()
else:
    device = DeviceHardware("192.168.1.10", 123)

device.initialize()
# ...

2. 使用简单的类和条件逻辑:

对于这个特定的例子,如果你没有很多方法,你可以完全放弃 ABC 并使用带有条件逻辑的简单类来实现相同的功能。这会减少代码量,但会牺牲一些结构。

class Device:
    def __init__(self, ip_address: str = None, port: int = None, simulated: bool = False) -> None:
        self.ip_address = ip_address
        self.port = port
        self.simulated = simulated

    def initialize(self) -> None:
        if self.simulated:
            print("DeviceSimulated initialized")
        else:
            print(f"DeviceHardware initialized with IP: {self.ip_address} and Port: {self.port}")

    # ... (其他方法的类似实现)

# 客户端用法
if simulated:
    device = Device(simulated=True)
else:
    device = Device(ip_address="192.168.1.10", port=123)

device.initialize()
# ...

3. 使用模拟库进行测试:

如果你主要关心测试,那么可以使用像 unittest.mock 这样的模拟库。这允许你在测试期间模拟真实硬件设备的行为,而无需创建单独的模拟实现。

最终, 最佳方法 取决于你的具体需求和约束。如果你需要一个结构良好且可扩展的解决方案,那么使用 ABC 和组合或依赖注入是不错的选择。如果你需要一个更简单且更简洁的解决方案,并且不介意牺牲一些结构,那么带有条件逻辑的简单类可能是合适的。如果你的主要目标是测试,那么使用模拟库可能是最有效的方法。

标签:python,oop,design-patterns,state-pattern,proxy-pattern
From: 78840951

相关文章

  • Python Django,使用外部MSSQL数据库
    我正在尝试创建一个连接到外部MSSQL数据库以仅检索信息(只读)的django网站。这个数据库非常庞大,有数百个表。我目前可以通过在django应用程序中创建一个函数来使其工作,该函数使用connectionString并运行原始SQL查询并将其返回到pandas数据帧中。不知何故,我感觉......
  • 使用 Python 中的 Matplotlib 和时间序列索引生成奇怪的图
    我正在尝试使用Python中的Matplotlib绘制一些时间序列数据,但生成的图看起来很奇怪,我不明白为什么。这是我正在使用的代码:filtered_df=df.loc[(df.index>'2010-01-01')&(df.index<='2010-01-08')]#Plottingthedatafig,axs=plt.subplots(1,1,figsize=(12,......
  • Dash Python:通过 @callback 链接选项卡
    这个问题是下面链接的问题的扩展:DashPython:布局函数中的@Callback未被调用我有一个简单的数据框:importpandasaspddf=pd.DataFrame({'Class1':[1,2,3,4,5],'Class2':[6,7,8,9,10]})我创建了一个数据提取函数,该函数根......
  • 如何在 Python 中使用 Langchain 返回已使用的上下文以进行回答
    我已经构建了一个像这样的RAG系统:defformat_docs(docs):return"\n\n".join(doc.page_contentfordocindocs)response_schemas=[ResponseSchema(name="price",description="Price",type="float"),ResponseSchema(......
  • 如何从 python socket.sendmsg 获取套接字 Tx 时间戳
    在阅读此处、此处和此处时,我发现在Linux系统上,您可以通过设置套接字选项来请求接收和传输的数据包的时间戳。我目前可以使用SO_TIMESTAMPNS和SO_TIMESTAMPING来通过recvmsg获取Rx时间戳。使用sendmsg我不知道......
  • Python 类型注释中“|”两边是否“强制”使用空格?
    “Union运算符”|没有出现在PEP8的其他建议中的“始终被空格包围的运算符”列表中因此,应该可以将其样式设置为类似于算术运算符,并删除圆括号、方括号内的空格,或者如果该运算符比表达式中的其他运算符具有更高的优先级。在我看来,删除空格可以提高表达式......
  • ArcPro (3.2+) Python 脚本工具中从 .atbx Toolbox 相对导入本地模块
    我设置了一个库和关联的ArcGISToolbox,以便:/root├──Toolbox.atbx├──mylib│└──my_function.py├──my_tools│└──my_gp_script.py我将代码存储库的开发克隆保存在公司共享服务器上的一个位置,并在GitHub上托管一份副本。当我进行更新时,我会......
  • 部署伪分布式 Hadoop集群
    部署伪分布式Hadoop集群一、JDK安装配置1.1下载JDK1.2上传解压1.3java环境配置二、伪分布式Hadoop安装配置2.1Hadoop下载2.2上传解压2.3Hadoop文件目录介绍2.4Hadoop配置2.4.1修改core-site.xml配置文件2.4.2修改hdfs-site.xml配置文件2.4.3修改ha......
  • Python vs. R:揭秘机器学习领域的双璧
    一、引言1.1背景介绍随着大数据和人工智能技术的飞速发展,机器学习已经成为了一个热门领域。在机器学习领域,Python和R是两种广泛使用的编程语言。Python因其简洁易读的语法和强大的库支持,成为了最受欢迎的编程语言之一。而R则以其强大的统计分析和数据可视化能力,在统计学......
  • Windows10 安装编译后的 pysqlcipher3-1.2.1 基于 Python 3.8.10
    Windows10安装编译后的pysqlcipher3-1.2.1基于Python3.8.10本文主要是将直接安装编译后的文件,不一定的成功,但是可以尝试使用,若无法直接安装,请参考编译过程,自行编译安装,编译过程见这里安装pysqlcipher3这里用32位举例因为64位安装完全相同,只需要把对应的位数换成64......