Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط Pimpl

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 78 من 104.

إن Pimpl idiom (Pointer to Implementation) هي تقنية تُخفي تفاصيل implementation الخاصة بـ class من خلال نقلها إلى class منفصلة مُعلَن عنها مسبقًا. وهذا يقلّل من تبعيات الترجمة ويحافظ على إخفاء الأعضاء الخاصة بشكل حقيقي عن ملف header.

الفكرة الأساسية بسيطة: بدلًا من تعريف الأعضاء الخاصة مباشرةً في class الخاص بك، تعرّف مؤشرًا إلى class من implementation، ويُعرَّف هذا class في source file فقط:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // إعلان أمامي
    std::unique_ptr<Impl> pImpl;
};
// Widget.cpp
#include "Widget.h"
#include <iostream>

class Widget::Impl {
public:
    int data = 42;
    void process() { std::cout << "Processing: " << data << "\n"; }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;

void Widget::doSomething() { pImpl->process(); }

تتمثل الفوائد الرئيسية في جدار حماية الترجمة والتوافق الثنائي. عند تغيير فئة Impl، يحتاج الملف المصدري فقط إلى إعادة الترجمة - وليس كل ملف يتضمن الترويسة. وهذا يسرّع أوقات البناء بشكل كبير في المشاريع الكبيرة.

لاحظ أن destructor يجب أن يكون defined في source file (حتى إذا كان defaulted)، لأن unique_ptr يحتاج إلى النوع complete لـ Impl لحذفه. وهذا خطأ شائع عند استخدام Pimpl لأول مرة.

challenge icon

التحدي

سهل

لنبنِ معالج رسائل آمنًا باستخدام اصطلاح Pimpl لإخفاء تفاصيل تنفيذ encryption من ملف header. يوضح هذا كيف ينشئ Pimpl جدارًا ناريًا للترجمة: لن يرى أي شخص يضمّن ملف header الخاص بك آليات العمل الداخلية لمعالجة رسالتك.

ستنظّم الكود الخاص بك عبر ثلاثة file:

  • SecureMessage.h: عرّف الواجهة العامة لـ class SecureMessage الخاص بك.

    يجب أن يحتوي class الخاص بك على class Impl مُعلن عنه مسبقًا وstd::unique_ptr يشير إليه. يجب أن تتضمن الواجهة العامة ما يلي:

    • منشئ يأخذ const std::string& للرسالة الأصلية
    • مدمّر (يجب أن يكون declared هنا، وdefined في ملف .cpp)
    • طريقة setKey(int key) لتعيين مفتاح encryption
    • طريقة getEncrypted() تُرجع الرسالة encrypted بصيغة std::string
    • طريقة getOriginal() تُرجع الرسالة الأصلية

    يجب أن يعرض header الواجهة العامة فقط: لا ينبغي أن تظهر هنا أي تفاصيل implementation حول كيفية عمل encryption.

  • SecureMessage.cpp: عرّف class Impl المتداخل ونفّذ جميع الطرق.

    يجب أن يخزّن class Impl الرسالة الأصلية، وencryption key (default إلى 0)، وأن يتولى منطق encryption الفعلي. استخدم Caesar cipher بسيطًا من أجل encryption: حرّك كل حرف بمقدار قيمة key. على سبيل المثال، مع key يساوي 3، يصبح 'a' هو 'd'، ويلتف 'z' ليصبح 'c'.

    يجب أن يؤثر encryption على الأحرف الصغيرة فقط (a-z)، مع ترك جميع الأحرف الأخرى دون تغيير. تذكّر أن تعرّف المدمّر هنا (حتى إذا كان defaulted)، لأن unique_ptr يحتاج إلى النوع Impl الكامل.

  • main.cpp: اقرأ مدخلين:
    1. سلسلة رسالة (قد contain مسافات)
    2. مفتاح encryption (عدد صحيح)

    أنشئ كائنًا من SecureMessage، واضبط key، ثم اعرض النتائج:

    1. اطبع Original: متبوعًا بالرسالة الأصلية
    2. اطبع Encrypted: متبوعًا بالرسالة encrypted

على سبيل المثال، مع المدخلين hello world و3:

Original: hello world
Encrypted: khoor zruog

مع المدخلين xyz abc و5:

Original: xyz abc
Encrypted: cde fgh

لاحظ كيف لا يكشف header file أي شيء عن implementation الخاص بـ Caesar cipher: هذه هي قوة Pimpl. إذا غيّرت لاحقًا إلى خوارزمية encryption مختلفة، فلن يحتاج إلى recompilation سوى SecureMessage.cpp، وليس أي file يضمّن header.

جرّب بنفسك

#include <iostream>
#include <string>
#include "SecureMessage.h"

int main() {
    // اقرأ الرسالة (قد تحتوي على مسافات)
    std::string message;
    std::getline(std::cin, message);
    
    // اقرأ مفتاح التشفير
    int key;
    std::cin >> key;
    
    // TODO: أنشئ كائن SecureMessage بالرسالة
    
    // TODO: عيّن مفتاح التشفير
    
    // TODO: اطبع "Original: " متبوعًا بالرسالة الأصلية
    
    // TODO: اطبع "Encrypted: " متبوعًا بالرسالة المشفّرة
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت