December 19, 2025 (4mo ago) — last updated February 10, 2026 (2mo ago)

نمط المحول: أمثلة TypeScript و React و Node.js

تعرّف على كيفية ربط نمط المحول (Adapter) للواجهات غير المتوافقة في TypeScript وReact وNode.js من خلال أمثلة عملية لتحديث التكاملات والكود القديم.

← Back to blog
Cover Image for نمط المحول: أمثلة TypeScript و React و Node.js

تعرّف على كيفية ربط نمط المحول (Adapter) للواجهات غير المتوافقة في TypeScript وReact وNode.js من خلال أمثلة عملية لتحديث التكاملات والكود القديم.

نمط المحول: أمثلة TypeScript و React و Node.js

الملخص: تعرّف على كيفية ربط نمط المحول (Adapter) للواجهات غير المتوافقة في TypeScript وReact وNode.js من خلال أمثلة عملية وحقيقية.

المقدمة

هل سبق وأن كان لديك مكتبة جيدة أو وحدة قديمة لا تتناسب ببساطة مع بقية نظامك؟ يشبه الأمر محاولة تركيب محول أوروبي في مقبس أمريكي — كلاهما يعمل، لكن واجهاتهما لا تتطابق. نمط المحول يحل هذه المشكلة بترجمة واجهة إلى أخرى حتى تتمكن من إعادة استخدام الكود الموجود دون تغييره.

هذا الدليل يشرح النمط، ويعرض أمثلة في TypeScript وReact، ويبيّن محولًا في Node.js يحدّث الوحدات المعتمدة على الاستدعاءات المرتدة (callbacks). يركز على تقنيات عملية يمكنك تطبيقها فورًا لتنظيف التكاملات وتحسين قابليتها للاختبار.

لماذا يهم نمط المحول

نمط المحول هو نمط بنيوي يقوم بلف كائن غير متوافق ويكشف الواجهة التي يتوقعها الكود الخاص بك. تم توثيقه لأول مرة من قبل Gang of Four في 19941. المحولات ضرورية لدمج واجهات برمجة خارجية، وتحديث الكود القديم، وتوحيد مصادر بيانات متباينة.

السيناريوهات الشائعة التي تساعد فيها المحولات:

  • دمج واجهات برمجة خارجية تعيد أشكال بيانات مختلفة.
  • تحديث واجهات الاستدعاء المرتدة القديمة لتعمل مع async/await.
  • توحيد مصادر بيانات متعددة إلى واجهة واحدة لمكونات واجهة المستخدم.

استخدام محول يحافظ على منطق الأعمال نظيفًا ومنفصلًا عن الأنظمة الخارجية.

لمحة عن نمط المحول

المفهومالوصف
النوعبنيوي
الهدف الأساسيالسماح لكائنات بواجهات غير متوافقة أن تعمل معًا
الفكرة الأساسيةلف الكائن المتوافق (adaptee) لكشف واجهة الهدف
المشكلة الأساسية المحلولةإعادة استخدام الفئات الموجودة دون تغيير شفرتها المصدرية
حالات الاستخدام الشائعةمكتبات طرف ثالث، الكود القديم، مصادر بيانات متعددة

البنية والأدوار

النمط يحتوي على أربعة أدوار:

  1. العميل — الكود الذي يحتاج إلى واجهة محددة.
  2. واجهة الهدف — العقدة التي يتوقعها العميل.
  3. الـ Adaptee — الفئة أو الوحدة غير المتوافقة التي تحتوي على الوظائف المطلوبة.
  4. المحول — ينفذ واجهة الهدف ويُفوّض إلى الـ Adaptee، مترجمًا الاستدعاءات حسب الحاجة.

نمطان شائعان للمحول:

  • محول كائني (composition): المحول يحمل مثيلاً من الـ Adaptee. هذا هو النهج الأكثر مرونة.
  • محول طبقي (inheritance): المحول يرث من الـ Adaptee وينفذ واجهة الهدف. يتطلب هذا تعدد الوراثة ويُعد أقل شيوعًا في جافاسكربت وTypeScript الحديثة.

مثال عملي: TypeScript + React

تخيل لوحة تحكم تتلقى ملفات تعريف المستخدمين من خدمتين تُعيدان أشكال استجابة مختلفة. بدون محول، ستصبح المكونات مملوءة بالمنطق الشرطي.

أشكال API غير المتوافقة

// Data from UserServiceA
interface UserA {
  userId: number;
  fullName: string;
  emailAddress: string;
}

// Data from UserServiceB
interface UserB {
  id: string;
  name: string;
  contact: {
    email: string;
  };
}

واجهة الهدف التي يتوقعها تطبيقنا

interface UnifiedUser {
  id: string;
  name: string;
  email: string;
}

محولات TypeScript

// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
  return {
    id: userA.userId.toString(),
    name: userA.fullName,
    email: userA.emailAddress,
  };
}

// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
  return {
    id: userB.id,
    name: userB.name,
    email: userB.contact.email,
  };
}

مركزة التحويلات تُبقي المكونات نظيفة ومقاومة للتغييرات. إذا أعادت واجهة برمجة تسمية حقل، فإن المحول وحده هو الذي يتغير.

مكون React يستهلك البيانات الموحدة

interface UserProfileProps {
  user: UnifiedUser;
}

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

يعتمد هذا المكوّن على شكل واحد متوقع، مما يجعل الاختبار وإعادة الاستخدام مباشرًا وسهلاً.

مثال: تحديث وحدة Node.js معتمدة على الاستدعاءات المرتدة (Callback)

غالبًا ما تستخدم الوحدات القديمة استدعاءات خطأ-أول (error-first callbacks). بدلاً من تعديل وحدة مستقرة، ابنِ محولًا يكشف واجهة مرتكزة على Promise.

الـ Adaptee القديم (لا تعدّل)

// legacyFileProcessor.js
const fs = require('fs');

class LegacyFileProcessor {
  processFile(filePath, callback) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return callback(err, null);
      }
      const processedContent = data.toUpperCase();
      callback(null, processedContent);
    });
  }
}

module.exports = LegacyFileProcessor;

محول يُرجع Promise

// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');

class FileProcessorAdapter {
  constructor() {
    this.legacyProcessor = new LegacyFileProcessor();
  }

  processFile(filePath) {
    return new Promise((resolve, reject) => {
      this.legacyProcessor.processFile(filePath, (err, data) => {
        if (err) return reject(err);
        resolve(data);
      });
    });
  }
}

module.exports = FileProcessorAdapter;

هذا يُحاكي سلوك util.promisify في Node لكنه يبقي منطق التكيف صريحًا وقابلًا للاختبار3.

استخدام المحول في كود التطبيق

const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();

async function handleFileProcessing() {
  try {
    console.log('Processing file with modern async/await...');
    const content = await fileProcessor.processFile('my-file.txt');
    console.log('Processed Content:', content);
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

handleFileProcessing();

هذا يحافظ على الكود القديم دون تغيير مع تزويد باقي قاعدة الشيفرة بواجهة حديثة.

متى تستخدم محولًا

استخدم محولًا عندما لا تتمكن مكوّنان من التواصل مباشرة لأن واجهاتهما تختلف. السيناريوهات النموذجية:

  • دمج واجهة برمجة طرف ثالث لا تتطابق مداخلها أو مخرجاتها مع نماذجك.
  • تغليف واجهات الاستدعاء المرتدة القديمة لكي تعمل مع async/await.
  • دعم مصادر بيانات متعددة بتنسيقات مختلفة عبر إنشاء محول لكل مصدر.

متى لا تستخدم محولًا:

  • إذا كنت تتحكم في كلا النظامين وإعادة هيكلة صغيرة ستحل عدم التوافق، ففضّل إعادة الهيكلة المباشرة.
  • إذا كان هدفك تبسيط نظام فرعي معقّد، ففكّر باستخدام واجهة Facade بدلاً من ذلك. الفاساد يوفر نقطة دخول مبسطة وعالية المستوى؛ بينما يركز المحول فقط على التوافق.

قائمة قرار سريعة

الحالةهل تستخدم محول؟لماذا
الحاجة لاستخدام مكتبة طرف ثالث بواجهة غير متوافقةنعملا يمكنك تغيير المكتبة، لذا عدّل لها
تتحكم في الطرفين والتغيير صغيرلاأعد الهيكلة مباشرة لتجنب التعقيد الإضافي
تحتاج واجهة مبسطة عالية المستوى لنظام معقدلاالفاساد أنسب
ترحيل الأنظمة القديمة بشكل تدريجينعملف المكونات القديمة لمطابقة الواجهات الجديدة
مصادر بيانات متعددة البنىنعمالمحولات توحّدها لشكل واحد

الاختبار والأداء

المحولات تحسّن قابلية الاختبار عن طريق فصل منطق الجوهر عن الأنظمة الخارجية. يمكنك محاكاة واجهة المحول لاختبار المكونات معزولة، واختبار المحولات بشكل منفصل للتحقق من منطق الترجمة.

عبء الأداء الناجم عن المحول ضئيل — عادةً يكون استدعاء دالة إضافي واحد — وهو ضئيل مقارنةً بعمليات الشبكة أو استعلامات قواعد البيانات. بالنسبة لمعظم تطبيقات الويب، فوائد الصيانة والفصل تفوق التكلفة الصغيرة بكثير. تظل جافاسكربت اللغة الأكثر استخدامًا في استطلاع Stack Overflow للمطوّرين، مما يبرز عدد المرات التي يواجه فيها المطوّرون عمل تكامل تحلّه المحولات4.

الأسئلة المتكررة

س: ما المشكلة التي يحلها نمط المحول؟

ج: يحل تعارض الواجهات عبر ترجمة الاستدعاءات من العميل إلى استدعاءات يفهمها الـ Adaptee بحيث يمكنك إعادة استخدام الكود دون تغييره.

س: كيف يساعد المحول مع الكود القديم؟

ج: يلف المحول الوحدات القديمة ويكشف واجهة حديثة، مما يتيح دمج كود قديم ومستقر في تطبيقات جديدة دون إعادة كتابة خطرة.

س: متى أختار المحول بدلًا من أنماط أخرى؟

ج: اختر المحول عندما تحتاج للتوافق بين واجهتين غير متطابقتين. إذا أردت تبسيط نظام فرعي بأكمله، فاستخدم Facade بدلاً من ذلك.

مزيد من القراءة والروابط الداخلية


في Clean Code Guy، نساعد الفرق على تنفيذ أنماط تصميم عملية تحوّل قواعد الشيفرة الهشة والمعقّدة إلى أصول مرنة وقابلة للاختبار وممتعة للعمل عليها. إذا كنت تكافح مع نظام قديم أو تكاملات معقّدة، فإن مراجعات Clean Code الخاصة بنا يمكن أن تمنحك خارطة طريق واضحة وقابلة للتنفيذ لهندسة صحية. تعرّف كيف يمكننا مساعدتك على شحن شيفرة أفضل، بشكل أسرع.

1.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns_(book)](https://en.wikipedia.org/wiki/Design_Patterns_(book))
2.
نظرة عامة وأمثلة على نمط المحول: https://www.geeksforgeeks.org/adapter-pattern/
3.
توثيق Node.js لـ util.promisify، وهو نهج شائع لتحويل الاستدعاءات إلى وعود (Promises): https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
استطلاع مطوّري Stack Overflow 2023، الذي يبيّن انتشار جافاسكربت وتقنيات الويب التي غالبًا ما تتطلّب عمل تكامل: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

الذكاء الاصطناعي يكتب الكود.
أنت تجعله يدوم.

في عصر تسريع الذكاء الاصطناعي، الكود النظيف ليس مجرد ممارسة جيدة — إنه الفرق بين الأنظمة التي تتوسع وقواعد الكود التي تنهار تحت وزنها.