С недавнего времени, мы в команде завели такую практику как обмен опытом. Сначала мы пробовали просто готовить доклады, на всякого рода интересные темы касающиеся технологий, практик и подходов. Но, этот подход не совсем себя оправдал, и мы стали готовить небольшие доклады на заранее оговоренные темы, например, — паттерны проектирования.
Идея состоит в том что мы выбираем несколько паттернов, делаем лаконичный пример использования, на каком-то языке не связанной с основной работой (php). Затем мы собираемся, показываем свои примеры, и обсуждаем конкретный паттерн и реализацию. Так у нас накопилось некоторое количество реализаций на Python, Ruby, Groovy. На самом деле, как мы потом убедились, выбор скриптового языка для иллюстрации паттерна, идея не очень хорошая, — нельзя выделить интерфейс, абстрактный класс, вследствие отсутствия строгой типизации смысл некоторых паттернов теряется, некоторые патерны трудно отличить один от другого. Наверное в дальнейшем мы ограничимся языками со строгой типизацией для паттернов, а скриптовые будем использовать для иллюстрации алгоритмов. Время покажет.
После столь долгого вступления, расскажу о первом паттерне Адаптер и примере его реализации на питоне. Этот паттерн относится к структурным паттернам (structural). Вот как он выглядит на диаграмме классов:
Этот паттерн используется в случае когда надо преобразовать интерфейс одного класса, в интерфейс другого, ожидаемого клиентом. Другими словами, — делает возможным работу классов с несовместимыми интерфейсами. В моем примере это какая-то, уже написанная и реализованная клиентская библиотека, использующая медленную библиотеку для взаимодействия с базой. новая библиотека отличается способом инициализации и вызова запросов. Используя адаптер, мы позволяем клиентской программе использовать новый движок баз данных, прозрачно для клиента, оставляя возможность использовать старый движок (если надо).
Код примера реализации паттерна:
class SlowDbEngine:
def __init__(self, host, user, password, db):
pass
def runQuery(self, sql):
return ["row1", "row2", "row3"]
class FastDbEngine:
def __init__(self, connection):
pass
def query(self, sql):
return range(3, 0, -1)
def fetch(self, result):
return "row%d" % (result.pop())
def recordsCount(self, result):
return len(result)
class DbAdapter:
def __init__(self, host, user, password, db, type = "fast"):
self._type = type
if(type == "slow"):
self._db = SlowDbEngine(host, user, password, db)
else:
self._db = FastDbEngine([host, user, password, db])
def runQuery(self, sql):
if self._type == "slow":
return self._db.runQuery(sql)
else:
result = self._db.query(sql)
return [self._db.fetch(result) for i in xrange(self._db.recordsCount(result))]
if __name__ == '__main__':
print(DbAdapter("localhost", "root", "password", "db").runQuery("select * from table"))
-----------------------
['row1', 'row2', 'row3']