簡體   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