繁体   English   中英

如何构造类以使我可以轻松地从使继承对象腌制的“ Persistor”类继承?

[英]How to structure classes such that I can easily inherit from a “Persistor” class which pickles the inherting object?

我正在写一个新的库,我想保留一些对象。 我想使用mixin或某种适配器,所以我不必立即实现数据库。 我现在正在使用泡菜来存储对象。

假设我有一个User类。 如果泡菜存在,我想从文件夹中加载用户。 我编写了一个Persistor类,该类接受一个对象并将其写入指定的位置。 我可以使User类继承自Persistor类吗? 如果是这样,当实例化User类时,如果出现泡菜,该如何用已加载的对象替换该对象? 还是创建UserPersistor类? 我只是想从User类中抽象出状态的加载和保存。

class User(Persistor???):
    """
    Central class to hold all user attributes and relationships.
    """

    def __init__(
        self,
        first_name: str,
        username: str,
        date_of_birth: datetime.date
    ):
        self.first_name = first_name
        self.username = username
        self.date_of_birth = date_of_birth
import pickle
import os


class Persistor:
    """
    Class whose job is to save and load state until we need a database.
    """

    def __init__(
        self,
        persistence_key
    ):
        self.persistence_key = persistence_key
        self.persistence_path = "data/" + persistence_key

    @property
    def save_exists(self):
        return os.path.exists(self.persistence_path)

    def save(self):
        outfile = open(self.persistence_path, 'wb')
        pickle.dump(self, outfile)
        outfile.close()

    def load(self):
        if self.save_exists:
            infile = open(self.persistence_path, 'rb')
            db = pickle.load(infile)
            infile.close()
            return db

    def delete(self):
        if self.save_exists:
            os.remove(self.persistence_path)

简单的答案(这与Python特定的FWIW无关,这只是普通的OO设计):从语义上讲,继承表示“是”关系-因此,如果B继承自A,那么B也是A(参见Liskov替代原理) )。 因此,真正的问题是:您是否认为UserPersistor User

另一个常见,合理且被广泛接受的设计原则是单一责任原则 ,该原则指出每个组件都应具有一个明确定义的责任。 因此,问问自己您是否真的希望持久性实现详细信息成为您的User类职责的一部分...

至于最后一对面向对象设计指南,在GoF的“设计模式”一书的(长期)介绍中反复进行(实际上是关于面向对象设计FWIW的最佳文字之一-即使模式目录是同样也是您感兴趣的):您应该更喜欢使用组合/委托而不是继承(从技术上讲,继承是一种受限的组合/委托形式),并且应该始终编程为接口(Java术语中的“接口”-在大多数语言中,表示抽象基类),而不是实现(具体类)。 通过将两者结合在一起,您将获得易于测试,维护和发展的良好分离的代码。 例如,如果使User类继承自Persistor (这是一个具体类),则无法轻松切换到其他Persistor实现,而如果使用组合/委托和仅定义Persistor API的抽象基类,则可以(理论上...)使用您的User类的任何Persistor实现。

现在是理论上的问题,现实是某些抽象趋向于泄漏-例如,要使用pickle协议进行持久化,您的User类必须是可腌制的,因此这不是完全透明的,因此请遵循上述原理带有一粒盐-比起黄金法则,它更是值得思考的食物 就我而言,我可能会在这里使用组合/委托作为最安全的选择,并最终在实施过程中重新考虑整个设计,因为实施通常会掩盖设计缺陷(如果至少可以发现它们)。

编辑

我记得,由于多重继承,您不需要在Python中使用接口。 这会改变仅适用于Python的“是”规则吗?

实际上,继承有两个目的:子类型化(如上所述),还有代码重用-这就是GoF将继承描述为“组合/委托的受限形式”的原因(子类对父类的引用) )并自动委派其部分操作)。

子类型化是OO中非常重要的一部分,因为它构成了通用代码和多态调度的基础-因此给定的代码段可以与来自不同具体类型(实现)的对象集合相同地工作,只要它们实现相同的接口。

语义上 ,“接口”是一组定义的(公共)功能(方法,属性,属性等)。 现在,如何在技术上定义接口取决于语言。

静态类型语言需要类实现的接口的正式定义,以便它可以执行编译时检查并最终进行优化。 请注意,我写了“ interfaceS”-一个类可以实现多个接口,这实际上是一种很常见的情况。

对于支持多重继承的(静态)语言,显而易见的解决方案是将抽象基类用于接口定义。 Java不支持多重具体继承(这是一种设计选择),因此它有一个独特的名为“ interfaces”的构造-但这实际上是纯抽象基类的另一个名称(“ pure”:仅定义,完全没有实现)

在动态语言中,实际上并不需要(技术上的)正式接口定义(因为没有正式的类型检查),因此“接口” 通常是非正式的 -在动态语言中,您经常会发现诸如“类似列表”,“文件状”等(表示行为类似于列表或文件的对象)。

现在,尽管这在实践中行之有效(至少在大多数情况下是这样),但是对于形式化接口定义而言,仍然有很多要说的东西,即使仅用于文档(以及仅用于简单多态分派还不够,并且您必须检查您的对象以了解您应该如何处理它们-考虑对任意嵌套的字典和各种对象列表的结构进行递归操作)-因此Python 具有抽象基类 ,实际上,建议将它们用于任何不重要的项目。

在所有情况下,“子关系”仍然保持一定程度(即使子类不一定按照Liskov的定义是正确的子类型),因为子类确实继承了其父类的隐式接口( S)。

注意:Python也有“协议”,它们是类可以实现的特殊方法的正式定义集,它们将为该类的实例提供一些特殊行为(参见描述符迭代器 )。

我可以让我的User从object和Persistor继承,这将是python表示获得该功能的方式吗?

当然,是的,您可以-没有技术限制,这只是一个设计问题:您的User类也应该是Persistor,还是“更好”(取决于“更好”的定义),使两个明确不同的类-一种用于域模型(用户),另一种用于持久性(例如UserPersistor)。

如果我将持久性逻辑委托给Persistor类,则仍需要从User类代码中调用它

不一定...如果您确定持久性不是用户的责任,那么您可以反过来查看:持久性调用您的User对象-在这种情况下,您实际上甚至不需要组合/授权。

如果您自己拥有GoF,我强烈建议您在继续进行设计决策之前花一些时间阅读它,并注意第一部分。 不过只是警告:完成后,避免使用项目中所有和所有模式的意图,请始终问问自己对于当前的具体问题是否有意义以及是否会产生额外的复杂性(如果有)对您代码的这一特定部分没有任何用处;-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM