我正在寻找一种更惯用或更简洁的 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