【问题标题】:How can I encapsulate a private mutable object of a class such that it can expose its attributes publicly through an inmutable object?我如何封装类的私有可变对象,以便它可以通过不可变对象公开其属性?
【发布时间】:2023-01-27 09:02:46
【问题描述】:

更具体地说,我正在设计一个下载器类,该类的成员变量是对一组 DownloadItem 对象的引用,该对象表示用户希望通过网络下载的资源。这个想法是 Downloader 对象将处理有关连接到托管资源的服务器、获取文件数据/元数据和写入磁盘的详细信息,同时还会在查询时公开 DownloadItems 的状态。

DownloadItem 存储有关文件的信息,例如文件名、URL、文件大小等,以及其他元数据,例如状态、进度、下载开始时间等。其中一些信息在实例化之前是未知的,因此类本身应该是可变以允许 Downloader 对象修改它,但仅限于创建它的 Downloader 对象。

简而言之,我希望 DownloadItem 的属性可以通过 Downloader 对象访问,如下所示:

> DownloaderObj = Downloader()
> unique_id = DownloaderObj.download(url='https://path/to/resource', start_inmediate=False)
> print(DownloaderObj._download_queue)
  [<Class: DownloadItem url: https://path/to/resource filesize: -1>]
> DownloaderObj.start_download(unique_id)  # Handler thread updates metadata on the background
> print(DownloaderObj.get_download_by_id(unique_id).filesize)
  1024
> DowloaderObj.get_download_by_id(unique_id).filesize = 1024 # Should raise NotAllowed exception

在公开这些属性的 Downloader 类中可能有很多样板代码,但如果我想稍后扩展 DownloadItem 类以支持其他字段,这会增加两个类之间的耦合并使该类的可维护性降低。关于如何实现这一目标的任何想法或想法?

旁注:这个问题主要是为了我自己对 OOP 模式和设计选择的学习,所以请随意批评并添加尽可能多的上下文。

我尝试做类似的事情:

class InmutableWrapper:
    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, val):
        return self._obj.__getattr__(val)

然后在调用 Downloader.get_download_by_id() 时返回 InmutableDownloadItemObj = InmutableWrapper(DownloadItemObj) 但我仍然可以进行分配,这将在查询属性时反映出来:

> print(Downloader.get_download_by_id(unique_id).filesize)
  1024
> Downloader.get_download_by_id(unique_id).filesize = 2    # Assigment goes through
> print(Downloader.get_download_by_id(unique_id)._obj.filesize) # Underlying object remains unchanged
  1024 
> print(Downloader.get_download_by_id(unique_id).filesize)
  2

【问题讨论】:

    标签: python oop design-patterns


    【解决方案1】:

    实现此目的的一种方法是使用代理模式。代理模式是一种结构设计模式,它为另一个对象提供代理或占位符对象并控制对其的访问。

    在这种情况下,您可以创建一个代理类来包装 DownloadItem 对象并公开其属性。代理类应具有获取和设置 DownloadItem 对象属性的方法,但不应允许对 DownloadItem 对象本身进行任何直接赋值。

    例如:

    class DownloadItemProxy:
        def __init__(self, download_item):
            self._download_item = download_item
    
        def get_filesize(self):
            return self._download_item.filesize
    
        def set_filesize(self, filesize):
            self._download_item.filesize = filesize
    

    然后,在 Downloader 类中,您可以返回 DownloadItemProxy 类的实例而不是 DownloadItem 对象本身。这样,Downloader 类仍然可以访问和修改 DownloadItem 对象,但是 Downloader 类的用户将只能通过代理访问和修改 DownloadItem 对象的属性。 希望这有所帮助。

    【讨论】:

      猜你喜欢
      • 2014-03-21
      • 2020-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-20
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      相关资源
      最近更新 更多