الهندسة السداسية في C++: نقاء الكود دون ألم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

الهندسة السداسية في C++: كيف تتوقف عن الخوف من التغييرات وتحب الاختبارات

\n\n

هل سبق لك أن أعدت كتابة وحدة عمل مع قاعدة بيانات، ثم أدركت أنك بحاجة لتغيير نصف منطق الأعمال؟ أو حاولت تغطية فصل دراسي باختبارات الوحدة، وهو يسحب معه ثلاثة أطنان من التبعيات من العالم الخارجي؟ إذا كان الأمر كذلك، فمرحبًا بك في النادي. لحسن الحظ، لدينا أداة تحل هذه المشاكل مرة واحدة وإلى الأبد: الهندسة السداسية (والمعروفة أيضًا بهندسة Ports & Adapters).

\n\n

في هذه المقالة، سنشرح كيفية تطبيق هذا المفهوم القوي في C++. سترى ليس فقط مخططات مجردة، بل كودًا حقيقيًا يمكنك استخدامه الآن. ونعم، سنفعل ذلك دون تعصب - بحيث لا ينفجر المشروع القديم، ويكون الكود الجديد نظيفًا.

\n\n

ما هي الهندسة السداسية (وما علاقة الشكل السداسي بها)؟

\n\n

تخيل نواة تطبيقك - وهي منطق الأعمال الخالص الذي لا يعرف شيئًا عن قواعد البيانات، ولا عن REST API، ولا عن الواجهة الرسومية. حول هذه النواة توجد المنافذ (الواجهات)، وأبعد من ذلك توجد المحولات (التطبيقات الملموسة). بصريًا، يشبه هذا الشكل السداسي (hexagon)، ومن هنا جاء الاسم.

\n\n

الفكرة الأساسية: التبعيات تتجه نحو الداخل. العالم الخارجي يعتمد على النواة، وليس العكس. هذا يسمح بتغيير قاعدة البيانات، أو بروتوكول الاتصال، أو واجهة المستخدم دون تغيير منطق الأعمال. يبدو وكأنه حلم؟ دعنا نرى كيف ننفذ ذلك في C++.

\n\n

المشكلة الكلاسيكية: الاقتران الوثيق

\n\n

هذا ما يبدو عليه كود C++ النموذجي غالبًا:

\n\n
class OrderService {\npublic:\n    void createOrder(const OrderData& data) {\n        // منطق الأعمال\n        if (data.items.empty()) throw std::runtime_error("الطلب فارغ");\n\n        // وهنا المشكلة - اعتماد مباشر على قاعدة البيانات\n        Database db("localhost", 5432);\n        db.connect();\n        db.saveOrder(data);\n        db.disconnect();\n        \n        // وأيضًا على خدمة البريد الإلكتروني\n        EmailSender sender("smtp.example.com");\n        sender.sendInvoice(data);\n    }\n};
\n\n

ما الخطأ هنا؟ كل شيء. لا يمكنك اختبار createOrder بدون قاعدة بيانات حقيقية وخادم بريد إلكتروني. إذا قررت غدًا الانتقال من PostgreSQL إلى MongoDB - سيتعين عليك إعادة كتابة OrderService. وإذا أصبحت خدمة البريد الإلكتروني غير متزامنة عبر قائمة انتظار - مرة أخرى تعديلات في منطق الأعمال.

\n\n

حل المشكلة باستخدام الهندسة السداسية

\n\n

الخطوة 1: تحديد المنافذ (الواجهات)

\n\n

المنفذ هو عقد يحدد ما تحتاجه النواة من العالم الخارجي، ولكن ليس كيف يتم تنفيذه.

\n\n
// port/OrderRepository.h\nclass OrderRepository {\npublic:\n    virtual ~OrderRepository() = default;\n    virtual void save(const Order& order) = 0;\n    virtual std::optional<Order> findById(const std::string& id) = 0;\n};\n\n// port/NotificationService.h\nclass NotificationService {\npublic:\n    virtual ~NotificationService() = default;\n    virtual void sendInvoice(const Order& order) = 0;\n};
\n\n

لاحظ: هذه فئات مجردة خالصة. لا تبعيات لأطر العمل أو المكتبات أو الخدمات الخارجية. فقط كيانات الأعمال.

\n\n

الخطوة 2: كتابة النواة (منطق الأعمال)

\n\n

الآن خدمتنا تعتمد فقط على الواجهات، وليس على التطبيقات الملموسة:

\n\n
// core/OrderService.h\nclass OrderService {\npublic:\n    OrderService(OrderRepository& repo, NotificationService& notifier)\n        : repository_(repo), notifier_(notifier) {}\n\n    void createOrder(const OrderData& data) {\n        // التحقق - منطق أعمال خالص\n        if (data.items.empty()) {\n            throw std::invalid_argument("لا يمكن أن يكون الطلب فارغًا");\n        }\n\n        Order order = Order::create(data);\n        repository_.save(order);\n        notifier_.sendInvoice(order);\n    }\n\nprivate:\n    OrderRepository& repository_;\n    NotificationServ                

المدونات

توصيات الكتب