Фабрика классов Python … или?

#c# #python

Вопрос:

У нас есть библиотека баз данных на C#, которую мы можем использовать следующим образом:

 DatabaseConnection conn = DatabaseConnection.FromConnectionString("...");
 

Эта библиотека скрывает многие различия между различными ядрами баз данных, такими как имена функций SQL, имена параметров и спецификации и т.д.

Внутренне DatabaseConnection класс является абстрактным классом, реализующим некоторые из основных методов, но FromConnectionString метод проходит через список зарегистрированных специализированных типов, который обрабатывает фактические различия и создает объект нужного класса. Другими словами, я не получаю объект DatabaseConnection обратно, вместо этого я получаю объект MSSQLDatabaseConnection или OracleDatabaseConnection, которые, конечно, наследуются от DatabaseConnection.

Строка подключения содержит информацию о том, для какого типа ядра базы данных и версии предназначено это подключение.

Я хотел бы создать аналогичную библиотеку на Python. Является ли правильным подходом создание чего-то, что можно построить подобным образом?

 conn = DatabaseConnection("...")
 

или использовать метод класса?

 conn = DatabaseConnection.FromConnectionString("...")
 

возможно ли вообще первое, то есть… создание такого объекта и получение чего-то другого, специализированного объекта, в зависимости от данных в переданной строке?

Хорошо, позвольте мне задать другой вопрос… Каков питонический способ сделать это?

В принципе, я также хочу иметь базовый класс DatabaseConnection в Python, реализующий общие методы, и специализируюсь на производных классах, и где-то должен быть метод или функция, которые на основе строки подключения конструируют и возвращают правильный тип объекта.

Ответ №1:

Это возможно в Python, но, вероятно, это не лучший способ сделать это. Шаблон фабрики классов, по сути, является обходным путем для языков, в которых нет классов первого класса. Поскольку в Python есть классы первого класса, вы можете хранить класс в переменной и использовать этот класс непосредственно для создания экземпляров. Чтобы изменить созданный класс, сохраните в переменной другой класс.

Например:

 class class1:
  def greet(self):
    print "hi"

class class2:
  def greet(self):
    print "hello"

maker = class1
obj1 = maker()

maker = class2
obj2 = maker()

obj1.greet() # prints "hi"
obj2.greet() # prints "hello"
 

Комментарии:

1. Хорошо, хм, не совсем то, что я имел в виду, но это дает мне представление о том, как реализовать «фабрику соединений по умолчанию», возможно, я воспользуюсь этой идеей, даже если она не совсем соответствовала тому, что я хотел, спасибо.

Ответ №2:

Python не волнует, почему вы возвращаете тип.

 def DatabaseConnection( str ):   
    if ( IsOracle( str ) ):   
        return OracleConnection( str )  
    else: 
        return SomeOtherConnection( str )
 

Ответ №3:

Первый вариант абсолютно возможен и, на мой взгляд, предпочтительнее. В python на самом деле не так много магии, стоящей за конструкторами. Во всех смыслах и целях они такие же, как и любая другая функция. Я несколько раз использовал этот шаблон проектирования, чтобы указать, что класс не должен создаваться напрямую, например:

 def DatabaseConnectionFromString(connection_string)
    return _DatabaseConnection(connection_string)

def DatabaseConnectionFromSomethingElse(something_else)
    connection_string = convert_something_else_into_string(something_else)
    return _DatabaseConnection(connection_string)

class _DatabaseConnection(object):
    def __init__(self, connection_string):
        self.connection_string = connection_string
 

Конечно, это надуманный пример, но это должно дать вам общее представление.

ПРАВКА: Это также одна из областей, где наследование также не так осуждается в python. Вы также можете сделать это:

 DatabaseConnection(object):
    def __init__(self, connection_string):
        self.connection_string = connection_string

DatabaseConnectionFromSomethingElse(object)
    def __init__(self, something_else):
        self.connection_string = convert_something_else_into_string(something_else)
 

Извините, что так многословно, но я хотел внести ясность.

Комментарии:

1. Спасибо, хотя и ваши, и ответы Glomeks в некотором роде отвечают на мой вопрос, мне не совсем нравится переименовывать базовый класс подобным образом

2. Забавно, что вы опубликовали это, в то время как я внес правку, которая не требует именования класса. Если это не сработает, дайте мне знать, и я уверен, что смогу помочь вам с чем-то еще.