سيكون الأمر حول مثل هذا النمط البسيط ، ولكن غالبًا ما يستخدم نمطًا كسلسلة من المسؤولية. جوهر النمط هو أننا نستخدم عدة معالجات لمعالجة حدث ، كل منها يقرر ما ومتى يتم تمريره إلى التالي. هناك العديد من الأمثلة على تطبيقات C ++ على الشبكة ، لكني أريد أن أظهر التنفيذ فقط على تعبيرات لامدا. في هذا التطبيق ، سيكون من الممكن إلقاء نظرة على سحر
الشارع الصغير.
لنفترض أن لدينا كائنًا:
class Elephant { public: std::string touch_leg() { return "it's like a pillar"; } std::string touch_trunk() { return "it's like a snake"; } std::string touch_tail() { return "it's like a rope"; } void run_away() { m_is_gone = true; std::cout << "*** Sound of running out elephant ***\n"; } bool is_elephant_here() { return !m_is_gone; } private: bool m_is_gone = false; };
وسيكون لدينا 3 معالجات ، كل منها سيمرر الكائن أكثر:
في هذا المثال ، هناك 3 معالجات ، كل منها عبارة عن تعبير لامدا ، والتي يتم دمجها في سلسلة من المسؤولية باستخدام فئة ChainOfRepsonsibility.
هنا تنفيذ الصف نفسه:
#include <functional> struct ChainOfRepsonsibility { template<typename... Args> struct Chain { template<typename Callee, typename Next> Chain(const Callee c, const Next& n) { m_impl = c; m_next = n; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain(c, *this); } void operator()(Args... e) { m_impl(e..., m_next); } std::function<void(Args..., std::function<void(Args...)>)> m_impl; std::function<void(Args...)> m_next; }; template<typename... Args> struct ChainTail { template<typename Callee> ChainTail(Callee c) { m_impl = c; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain<Args...>(c, m_impl); } void operator()(Args... e) { m_impl(e...); } std::function<void(Args... e)> m_impl; }; template<typename> struct StartChain; template<typename C, typename... Args> struct StartChain<void (C::*)(Args...) const> { using Type = ChainTail<Args...>; }; template<typename Callee> static decltype(auto) start_new(Callee c) { return StartChain<decltype(&Callee::operator())>::Type(c); } };
يعمل مثل هذا:
- أولاً ، ننشئ سلسلة من المسؤوليات باستخدام دالة start_new. تكمن المشكلة الرئيسية في هذه المرحلة في الحصول على قائمة بالحجج من لامدا التي تم تمريرها وإنشاء "نموذج أولي" للمعالج بناءً عليها.
- للحصول على حجج لامدا ، يتم استخدام فئة StartChain وموضوع صعب مع التخصص في القوالب. أولاً ، نعلن عن متغير عام للفئة ، ثم بنية التخصص StartChain <void (C :: *) (Args ...) const> . يسمح لنا هذا البناء بالوصول إلى قائمة الحجج بواسطة عضو الفئة الذي تم تمريره.
- من خلال وجود قائمة من الحجج ، يمكننا بالفعل إنشاء فئتي ChainTail و Chain ، والتي ستكون مجمعة للمعالجات. مهمتهم هي حفظ لامدا في وظيفة std :: واستدعائها في الوقت المناسب ، بالإضافة إلى تنفيذ طريقة الإرفاق (ضبط المعالج من الأعلى).
تم اختبار الرمز في visual studio 2015 ، بالنسبة إلى gcc ، قد يكون من الضروري إصلاح تخصص StartChain عن طريق إزالة الثابت من هناك ، إذا كنت ترغب في تمرير وظائف ، بالإضافة إلى lambdas ، يمكنك إضافة تخصص StartChain آخر. لا يزال بإمكانك محاولة التخلص من النسخ في مرفق ، قم بالتحرك ، ولكن لكي لا أعقد المثال ، تركت الحالة الأبسط فقط.