لماذا C++ بدون عمارة هو ألم
\nإذا سبق لك أن فتحت مشروع C++ لشخص آخر (أو مشروعك القديم) وشعرت بأن عينيك تدمعان من آلاف الأسطر في ملف واحد، وأصبحت عملية التجميع طقسًا يتطلب فنجان قهوة - فأنت لست وحدك. C++ يمنح حرية هائلة: يمكنك مزج الأنماط، تحميل العوامل، والقيام بسحر القوالب. لكن الحرية بدون انضباط تؤدي إلى الفوضى.
\nأنماط العمارة في C++ ليست نظرية مملة من محاضرات الجامعة. إنها وصفات قتالية مجربة تنقذ المشروع عندما ينمو من "لعبة" إلى مشروع صناعي. في هذه المقالة سنستعرض أنماط العمارة في C++ التي تبسط الحياة حقًا: من MVC إلى Observer. ونعم، سيكون هناك الكثير من الكود.
\n\nما هو النمط المعماري (وكيف يختلف عن "مجرد نمط")
\nكثيرون يخلطون بين أنماط التصميم في C++ (GoF: Singleton, Factory, Adapter) والأنماط المعمارية. الفرق بسيط:
\n- \n
- أنماط التصميم - حلول دقيقة على مستوى الفئة أو الوحدة (مثل كيفية إنشاء كائن). \n
- أنماط العمارة - حلول كلية على مستوى التطبيق بأكمله (كيف تتواصل المكونات، كيف يتم فصل الطبقات). \n
العمارة تحدد ما إذا كان مشروعك سيتحول إلى "كرة طين كبيرة" (Big Ball of Mud) بعد ستة أشهر. لذلك دعونا نلقي نظرة على ثلاثة من أكثر الأنماط فائدة لـ C++.
\n\n1. MVC (Model-View-Controller) في C++: فرق تسد
\nMVC هو كلاسيكي. يقسم التطبيق إلى ثلاثة أجزاء: البيانات (Model)، العرض (View)، ومنطق التحكم (Controller). في C++، هذا مهم بشكل خاص لتطبيقات الواجهات الرسومية (Qt, wxWidgets) أو حتى الأدوات الطرفية.
\n\nكيف يبدو ذلك في الكود
\nلنتخيل آلة حاسبة بسيطة. النموذج يخزن النتيجة، العرض يعرضها، والمتحكم يعالج الإدخال.
\n#include <iostream>\n#include <string>\n\n// Model: لا يعرف شيئًا عن الواجهة الرسومية\nclass CalculatorModel {\npublic:\n double add(double a, double b) {\n result_ = a + b;\n return result_;\n }\n double getResult() const { return result_; }\nprivate:\n double result_ = 0;\n};\n\n// View: فقط العرض\nclass CalculatorView {\npublic:\n void showResult(double value) {\n std::cout << "النتيجة: " << value << std::endl;\n }\n void showError(const std::string& msg) {\n std::cerr << "خطأ: " << msg << std::endl;\n }\n};\n\n// Controller: يربط Model و View\nclass CalculatorController {\npublic:\n CalculatorController(CalculatorModel& model, CalculatorView& view)\n : model_(model), view_(view) {}\n\n void handleAdd(double a, double b) {\n double res = model_.add(a, b);\n view_.showResult(res);\n }\nprivate:\n CalculatorModel& model_;\n CalculatorView& view_;\n};\n\nint main() {\n CalculatorModel model;\n CalculatorView view;\n CalculatorController controller(model, view);\n\n controller.handleAdd(5, 3); // الإخراج: النتيجة: 8\n return 0;\n}\nلماذا هذا رائع؟ يمكنك استبدال View الطرفية بأداة Qt دون لمس Model. أو إضافة اختبارات وحدة لـ Model دون تشغيل الواجهة الرسومية. هذا هو أساس تصميم تطبيقات C++ من الصفر.
\n\n2. Observer (المراقب): عندما يجب أن تكون التغييرات ملحوظة للجميع
\nتخيل: لديك مستشعر درجة حرارة، ويجب تحديث عدة لوحات عندما تتغير درجة الحرارة. بدلاً من استقصاء المستشعر كل ثانية (polling)، يقوم Observer بإخطار المشتركين بنفسه. هذا نمط معماري في C++ يجعل الكود موجهًا بالأحداث.
\n\nالتنفيذ باستخدام C++11 وما بعده
\nنستخدم المؤشرات الذكية و std::function للمرونة.
\n#include <iostream>\n#include <vector>\n#include <memory>\n#include <functional>\n\n// واجهة المراقب\nclass IObserv