{"meta":{"title":"Рефакторинг слоёв access к данным","intro":"Copilot Chat Могу предложить способы отделить код доступа к данным от бизнес-логики, что облегчает обслуживание и масштабирование приложения.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ru/copilot","title":"GitHub Copilot"},{"href":"/ru/copilot/tutorials","title":"Учебники"},{"href":"/ru/copilot/tutorials/copilot-cookbook","title":"Кулинарная книга GitHub Copilot"},{"href":"/ru/copilot/tutorials/copilot-cookbook/refactor-code","title":"Код рефакторинга"},{"href":"/ru/copilot/tutorials/copilot-cookbook/refactor-code/refactor-data-access-layers","title":"Refactoring data access layers"}],"documentType":"article"},"body":"# Рефакторинг слоёв access к данным\n\nCopilot Chat Могу предложить способы отделить код доступа к данным от бизнес-логики, что облегчает обслуживание и масштабирование приложения.\n\nЖёстко закодированные SQL-запросы и тесно связанный код access к данным могут затруднять масштабирование или переключение баз данных и часто приводят к повторяющейся логике.\n\nCopilot Chat рекомендую реализовать шаблон репозитория или слой доступа к данным, который абстрагует взаимодействия с базой данных, делая код более модульным и снижая избыточность. Кроме того, он может помочь рефакторингу запросов SQL в оптимизированные версии, повысить производительность и удобство обслуживания.\n\n> \\[!NOTE] Ответы, описанные в этой статье, являются примерами.\n> Copilot Chat Ответы не детерминированы, поэтому вы можете получить другие ответы, чем показаны здесь.\n\n## Пример сценария\n\nЭтот код Python подключается к базе данных SQLite, извлекает запись пользователя и возвращает данные пользователя. Однако она не может абстрагировать логику подключения к базе данных и использует жестко закодированный запрос, который уязвим для внедрения SQL.\n\n```python id=database-connection\nimport sqlite3\n\ndef get_user_by_id(user_id):\n    conn = sqlite3.connect('database.db')\n    cursor = conn.cursor()\n    cursor.execute(f\"SELECT display_name FROM users WHERE id = {user_id}\")\n    user = cursor.fetchone()\n    conn.close()\n    return user\n```\n\n## Пример запроса 1\n\nМожно начать с общего Copilot вопроса о том, как улучшить код.\n\n```copilot copy prompt ref=database-connection\nHow can I improve this code to make it safe and easier to update and expand? List possible improvements but don't show revised code.\n```\n\n## Пример ответа 1\n\nCopilot Вносит несколько предложений по улучшению кода:\n\n* Абстрагирование сведений о подключении к базе данных.\n* Используйте параметризованные запросы, чтобы предотвратить внедрение SQL.\n* Используйте диспетчер контекстов, чтобы убедиться, что подключение к базе данных правильно закрыто при возникновении ошибки.\n* Добавьте обработку ошибок.\n\n## Пример запроса 2\n\nВы можете использовать ответ на первый запрос, чтобы написать более конкретный запрос.\n\n```copilot copy prompt ref=database-connection\nRewrite this code to make it more scalable and easier to maintain. Use a context manager. Avoid hardcoded SQL queries and tightly coupled data access code. Instead, use a repository pattern to abstract database interactions and make the code more modular and reusable. Where possible optimize the code to improve performance. Include error trapping, and make sure the code is not vulnerable to SQL injection.\n```\n\n## Пример ответа 2\n\nCopilot предлагает разделить код на отдельные файлы, каждый с определённой целью. Он также содержит пример кода для возврата сведений о пользователе.\n\nПредлагаемые изменения реализуют шаблон репозитория и используют параметризованные запросы, чтобы избежать внедрения SQL.\n\n```python\n# database.py\nimport sqlite3\n\nclass Database:\n    def __init__(self, db_path):\n        self.db_path = db_path\n\n    def __enter__(self):\n        self.conn = sqlite3.connect(self.db_path)\n        self.cursor = self.conn.cursor()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if exc_type or exc_val or exc_tb:\n            self.conn.rollback()\n        else:\n            self.conn.commit()\n        self.conn.close()\n\n# user_repository.py\nclass UserRepository:\n    def __init__(self, db):\n        self.db = db\n\n    def get_user_by_id(self, user_id):\n        try:\n            self.db.cursor.execute(\"SELECT display_name FROM users WHERE id = ?\", (user_id,))\n            user = self.db.cursor.fetchone()\n            return user\n        except sqlite3.Error as e:\n            print(f\"An error occurred: {e}\")\n            return None\n\n# main.py\nfrom database import Database\nfrom user_repository import UserRepository\n\ndef get_user_by_id(user_id):\n    with Database('database.db') as db:\n        user_repo = UserRepository(db)\n        return user_repo.get_user_by_id(user_id)\n\n# Example usage\nif __name__ == \"__main__\":\n    user = get_user_by_id(1)\n    if user:\n        print(user)\n    else:\n        print(\"User not found or an error occurred.\")\n```\n\nИзмененный код улучшает исходный код несколькими способами:\n\n* Теперь код реализует базовый слой data access.\n* Класс `Database` обрабатывает подключение к базе данных SQLite, реализуя протокол диспетчера контекстов с `__enter__` помощью методов и `__exit__` методов. Это гарантирует правильное управление подключением к базе данных, включая фиксацию транзакций и закрытие подключения.\n* Класс `UserRepository` инкапсулирует логику доступа к данным пользователя.\n* Значения запросов параметризованы, чтобы предотвратить внедрение SQL.\n* Ошибки перехватываются с подробными сведениями, напечатанными в консоли.\n\n## Дополнительные материалы\n\n* [Инженерия подсказок для GitHub Copilot Chat](/ru/copilot/concepts/prompting/prompt-engineering)\n* [Лучшие практики использования GitHub Copilot](/ru/copilot/get-started/best-practices)"}