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

تصميم الواجهات (Interface) في C++

جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 61 من 104.

تُعد الـ interface (الواجهة) في C++ فئة مجردة (abstract class) تكون فيها كل دالة عضو—باستثناء المدمر (virtual destructor)—افتراضية بحتة (pure virtual). وبخلاف الفئات المجردة التي قد تحتوي على بعض التنفيذ (implementation)، تحدد الواجهات فقط العمليات التي يجب أن تكون موجودة - فهي تحدد عقداً دون أي سلوك.

class Drawable {
public:
    virtual void draw() = 0;
    virtual void resize(double factor) = 0;
    virtual ~Drawable() = default;
};

تضمن واجهة Drawable هذه أن أي فئة تقوم بتنفيذها ستحتوي على الطريقتين draw() و resize()، لكنها لا تذكر شيئاً عن كيفية عملهما. هذا الفصل قوي - حيث يمكن للكود أن يعتمد على الواجهة دون معرفة أي شيء عن الأنواع الملموسة.

class Circle : public Drawable {
    double radius;
public:
    Circle(double r) : radius(r) {}
    void draw() override { std::cout << "Drawing circle" << std::endl; }
    void resize(double factor) override { radius *= factor; }
};

class Button : public Drawable {
    std::string label;
public:
    Button(std::string l) : label(l) {}
    void draw() override { std::cout << "Drawing button: " << label << std::endl; }
    void resize(double factor) override { /* تغيير حجم الزر */ }
};

يمكن للفئة (class) تنفيذ واجهات (interfaces) متعددة، مما يتيح تصميمات مرنة حيث يمكن للكائنات القيام بأدوار مختلفة:

class Clickable {
public:
    virtual void onClick() = 0;
    virtual ~Clickable() = default;
};

class IconButton : public Drawable, public Clickable {
public:
    void draw() override { std::cout << "Drawing icon" << std::endl; }
    void resize(double factor) override { }
    void onClick() override { std::cout << "Clicked!" << std::endl; }
};

تعزز الواجهات (Interfaces) الاقتران الضعيف - حيث يعتمد الكود الخاص بك على التجريدات بدلاً من التنفيذات الملموسة، مما يسهل عملية التوسيع والاختبار.

challenge icon

التحدي

سهل

لنقم ببناء نظام لإدارة الأجهزة يوضح كيف تحدد الواجهات (interfaces) عقوداً لأنواع مختلفة من الأجهزة. ستقوم بإنشاء واجهتين منفصلتين يمكن للأجهزة تنفيذهما، ثم بناء فئات أجهزة ملموسة (concrete classes) تفي بأحد هذين العقدين أو كليهما.

ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:

  • Interfaces.h: تحديد فئتين مجردتين تماماً (واجهات) تمثلان قدرات الأجهزة المختلفة:

    Powerable — أي جهاز يمكن تشغيله وإيقافه:

    • دالة افتراضية بحتة (pure virtual) باسم powerOn()
    • دالة افتراضية بحتة باسم powerOff()
    • دالة افتراضية بحتة باسم getPowerStatus() تعيد std::string
    • مدمر افتراضي (virtual destructor)

    Connectable — أي جهاز يمكنه الاتصال بشبكة:

    • دالة افتراضية بحتة باسم connect(const std::string& network)
    • دالة افتراضية بحتة باسم disconnect()
    • دالة افتراضية بحتة باسم getConnectionInfo() تعيد std::string
    • مدمر افتراضي (virtual destructor)
  • Devices.h: تنفيذ فئات أجهزة ملموسة تستخدم هذه الواجهات:

    Lamp — تنفذ واجهة Powerable فقط:

    • عضو خاص bool isOn (يبدأ بـ false)
    • عضو خاص std::string name
    • منشئ (constructor) يأخذ اسم المصباح
    • powerOn() تجعل isOn تساوي true وتطبع: <name>: Light turned on
    • powerOff() تجعل isOn تساوي false وتطبع: <name>: Light turned off
    • getPowerStatus() تعيد "ON" أو "OFF" بناءً على الحالة

    SmartTV — تنفذ كلاً من Powerable و Connectable:

    • أعضاء خاصة: bool isOn (يبدأ بـ false)، std::string brand، و std::string currentNetwork (تبدأ فارغة)
    • منشئ يأخذ العلامة التجارية للتلفاز
    • powerOn() تجعل isOn تساوي true وتطبع: <brand> TV: Powered on
    • powerOff() تجعل isOn تساوي false، وتمسح الشبكة، وتطبع: <brand> TV: Powered off
    • getPowerStatus() تعيد "ON" أو "OFF"
    • connect() تخزن اسم الشبكة وتطبع: <brand> TV: Connected to <network>
    • disconnect() تمسح الشبكة وتطبع: <brand> TV: Disconnected
    • getConnectionInfo() تعيد "Connected to <network>" إذا كان متصلاً، أو "Not connected" إذا كانت فارغة
  • main.cpp: قراءة ثلاثة مدخلات (كل منها في سطر منفصل):
    1. اسم المصباح (Lamp name)
    2. العلامة التجارية للتلفاز (TV brand)
    3. اسم الشبكة (Network name)

    قم بإنشاء Lamp و SmartTV. وضح كيف يمكن استخدام نفس الواجهة مع أجهزة مختلفة:

    أولاً، تعامل مع كلا الجهازين من خلال واجهة Powerable. قم بتخزين مؤشرات لكليهما في مصفوفة من نوع Powerable*، ثم قم بالمرور عليها واستدعاء powerOn() لكل منهما، متبوعاً بطباعة حالتهما كالتالي: Status: <powerStatus>

    اطبع سطراً فارغاً، ثم تعامل مع SmartTV من خلال واجهة Connectable. أنشئ مؤشر Connectable* يشير إلى SmartTV الخاص بك، واستدعِ connect() مع اسم الشبكة، واطبع: Connection: <connectionInfo>

    اطبع سطراً فارغاً آخر، ثم قم بإيقاف تشغيل كلا الجهازين من خلال مصفوفة Powerable واعرض حالتهما النهائية.

على سبيل المثال، مع المدخلات Desk Lamp، Samsung، و HomeWiFi:

Desk Lamp: Light turned on
Status: ON
Samsung TV: Powered on
Status: ON

Samsung TV: Connected to HomeWiFi
Connection: Connected to HomeWiFi

Desk Lamp: Light turned off
Status: OFF
Samsung TV: Powered off
Status: OFF

لاحظ كيف يمكن معاملة SmartTV كـ Powerable أو Connectable اعتماداً على مؤشر الواجهة الذي تستخدمه. هذه المرونة هي قوة تنفيذ واجهات متعددة — حيث يمكن للكود الخاص بك العمل مع أي جهاز يفي بالعقد الذي يحتاجه، دون معرفة النوع الملموس للجهاز.

جرّب بنفسك

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

using namespace std;

int main() {
    // قراءة المدخلات
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: إنشاء كائن Lamp وكائن SmartTV
    
    // TODO: إنشاء مصفوفة من مؤشرات *Powerable تحتوي على كلا الجهازين
    
    // TODO: التكرار عبر المصفوفة واستدعاء ()powerOn لكل جهاز
    // بعد كل ()powerOn، اطبع: Status: <powerStatus>
    
    // TODO: طباعة سطر فارغ
    
    // TODO: إنشاء مؤشر *Connectable إلى SmartTV
    // استدعاء ()connect مع اسم الشبكة
    // اطبع: Connection: <connectionInfo>
    
    // TODO: طباعة سطر فارغ
    
    // TODO: التكرار عبر مصفوفة Powerable واستدعاء ()powerOff لكل جهاز
    // بعد كل ()powerOff، اطبع: Status: <powerStatus>
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming