【问题标题】:Polymorphism and database loading in PythonPython中的多态性和数据库加载
【发布时间】:2012-07-26 17:27:07
【问题描述】:

我在以多态方式将记录加载到数据库时遇到问题。

我有:

  • 由不同类型记录扩展的“Record”对象(RecordARecordB
  • 一个方法load_record() 不知道它正在加载什么类型的记录
  • 一个数据库接口,需要包含数据库的所有信息

据我了解,多态我可以这样做:

class RecordA(Record):
    def load_into_db(self, db_impl):
        db_impl.load_record_a(self)


class DbImpl(DbInt):
    def load_record_a(self, record):
        ... 
    def load_record_b(self, record):
        ... 

def load_record(record):
    record.load_into_db(db_impl)

class RecordA(Record):
    def get_record_data(self):
        ....
    def get_db_proc(self, db_impl):
        return db_impl.get_record_a_proc()


class DbImpl(DbInt):
    def get_record_a_proc(self):
        ...

def load_record(record):
    record_data = record.get_data()
    db_proc = record.get_db_proc(db_impl)
    db_impl.load_record(record_data, db_proc)

这两个看起来有点笨重。或者,我们可以有效地使用 switch 语句:

class DbImpl(DbInt):
    procs = { RecordA: ..., RecordB: ...}
    ...

def load_record(record):
    data = record.get_data()
    db_proc = procs[type(record)]
    db_impl.load_record(record_data, db_proc)

现在可能很明显,问题是数据库需要使用特定的存储过程(在它自己的代码中),但不询问记录本身,它不知道要使用哪一个。

对我来说,示例 1 看起来最具有多态性,但是每当我们添加新类型的记录时,它仍然需要编辑 DbImpl 代码,那么它是否比示例 3 更好?如果是这样,为什么?

干杯, 会

【问题讨论】:

    标签: python mysql database oop polymorphism


    【解决方案1】:

    对我来说最有意义的是拥有一个包含基本数据库功能的基类,例如将某些内容放入数据库和连接信息。然后,您将从这个基类中继承 RecordA、RecordB、RecordC、...,它们将保存信息/功能,例如存储过程信息。拥有一个包含一堆方法的基类感觉很笨拙,在我看来不适合 OOP 范式。

    因此,具有基本数据库功能的基类,那么每个 Record 子类都将包含该特定记录类型所需的信息。

    【讨论】:

    • 我不会想到这样做。我打算为不同类型的数据库(Oracle、MySQL)对数据库类进行子类化/实现。
    • 啊,在这种情况下,我会在您的不同组件之间提供一个通用接口,以使其对子类透明,尽管这可能无法完全适用于您的存储过程,但如果做得好将需要更少的代码开销。
    【解决方案2】:

    在这种情况下,如果 Record 类型与特定的存储过程相关,那么我会说记录本身需要了解需要运行的存储过程并将该数据传递给实现它的 DB 类。因此,不需要使用每种新记录类型来修改 DB 类。只需对 recordType 进行编码以了解需要使用的存储过程名称并将该名称公开给 DB 类。

    【讨论】:

    • 如果我们必须为数据库类的不同实现创建不同的存储过程(事实上,我们确实这样做了),这将引发一个问题。我不愿意将特定于数据库的信息放在记录类中。
    • 不管你仍然需要编写新功能,而不是在一个类中,你将它移到数据库类中,从而脱离 OOP 设计。
    • @sean 这是对 wilbo 还是我的回应?对我来说,您可能希望 DB 类保持原样,而不是为每种不同的记录类型添加案例。因此,您将使用哪个存储过程的知识与记录类一起使用,但实际实现它需要 DB 类。要么我们你有每个存储过程的许多 DB 子类,并且有一个工厂类,记录使用它来获取它需要的那个。
    • 致威尔博。您所说的是我将尝试实现的内容,因为拉出过程并将它们放入子类中比在 DB 类中实现一堆方法更有意义。 DB 类应该有底层的后端代码来通信和执行过程,这样子类就可以完成工作而无需开销或复制代码的多个副本。
    【解决方案3】:

    我遇到了同样的问题,并且发现现有的数据库 ORM 就多态性/OOP 而言缺乏,为此我一直致力于开发一个主要专注于此的新数据库。它被称为 ORB,可以在以下位置找到:http://docs.projexsoftware.com/api/orb/

    目前它仅适用于 PostgreSQL 和 Mongo,但我很快会将其扩展到更多后端。

    它非常灵活,具体取决于您要做什么,是否简单,例如:

    • 基类(数据库层)
    • 子类(纯 python 调用 - 所以控制器逻辑运行同一个表)

    • 基类(抽象层 - 通用控制器逻辑,未定义表)
    • 子类(数据库层 - 每个定义不同的数据库表)

    • 基类(数据库层 - 定义根表)
    • 子类(数据库层 - 为支持它的 Db 定义继承的表,或者为不支持它的 Db 定义重复的列式表)

    这是一个例子:

    # import the orb system
    from orb import Table, Column, ColumnType, Orb
    
    # define the base class (will create default_fruit table)
    class Fruit(Table):
        __db_columns__ = [
            Column(ColumnType.String, 'shape'),
            Column(ColumnType.String, 'color'),
            Column(ColumnType.String, 'name'),
            Column(ColumnType.String, 'classType')
        ]
    
        def initRecord( self ):
            """
            Set the initialization information from the base Table type
            """
            super(Fruit, self).initRecord()
            self.setRecordDefault('classType', self.__class__.__name__)
    
        @staticmethod
        def expanded( records ):
            """
            Expands the records based on their class type.
    
            :return   [<Table>, ..]
            """
            out = []
            for record in records:
                # map the record to their base classes
                model = Orb.instance().model(record.classType())
                if ( not model ):
                    out.append(record)
                    continue
    
                expanded_record = model(record)
                if ( not expanded_record.isRecord() ):
                    continue
    
                out.append(expanded_record)
            return out        
    
    # define the sub class (no database table)
    class Apple(Fruit):
        def initRecord( self ):
            super(Apple, self).initRecord()
            self.setRecordDefault('shape', 'round')
            self.setRecordDefault('color', 'red')
    
    # define the sub class (with a sub-table, will create default_banana table)
    class Banana(Fruit):
        __db_columns__ = [
            Column(ColumnType.String, 'isPlantain')
        ]
    
        def initRecord( self ):
            super(Banana, self).initRecord()
            self.setRecordDefault('shape', 'oblong')
            self.setRecordDefault('color', 'yellow')
    

    这将定义 3 个不同的类(Fruit、Apple、Banana)和 2 个数据库表(default_fruit、default_banana)

    使用这个系统(您必须创建数据库连接等,如文档中定义的那样)会给您带来类似的结果:

    >>> from fruits import Fruit, Apple, Banana
    >>> apple = Apple()
    >>> apple.setColor('green')
    >>> apple.setName('Granny Smith')
    >>> apple.commit()
    >>> banana = Banana()
    >>> banana.setName('Chiquita')
    >>> banana.setPlantain(False)
    >>> banana.commit()
    >>> fruits = Fruit.select() # select all fruit
    [<Fruit>, <Fruit>]
    >>> fruits = Fruit.expanded(fruits) # expand the fruit based on their types
    [<Apple>, <Banana>]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 2017-12-01
      相关资源
      最近更新 更多