December 19, 2025 (9mo ago) — last updated June 18, 2026 (3mo ago)

نمط المحول في TypeScript و React و Node.js

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

← Back to blog
Cover Image for نمط المحول في TypeScript و React و Node.js

هل تملك مكتبة جيدة أو وحدة قديمة لا تتوافق مع واجهة تطبيقك؟ نمط المحول يوفّر طبقة ترجمة بسيطة تتيح لإصدارات ومصادر بيانات مختلفة العمل معًا دون تعديل الشيفرة المصدرية الأصلية. في هذا الدليل ستجد شرحًا عمليًا ومباشرًا للمبدأ، مع أمثلة في TypeScript وReact ومحوّل في Node.js لتحويل استدعاءات callbacks إلى Promises — حلول يمكنك تطبيقها فورًا لتقليل التعقيد وتحسين الاختبار.

نمط المحول في TypeScript و React و Node.js

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

المقدمة

هل تملك مكتبة جيدة أو وحدة قديمة لا تتوافق مع واجهة تطبيقك؟ نمط المحول يوفّر طبقة ترجمة بسيطة تتيح لإصدارات ومصادر بيانات مختلفة العمل معًا دون تعديل الشيفرة المصدرية الأصلية. في هذا الدليل ستجد شرحًا عمليًا ومباشرًا للمبدأ، مع أمثلة في TypeScript وReact ومحوّل في Node.js لتحويل استدعاءات callbacks إلى Promises — حلول يمكنك تطبيقها فورًا لتقليل التعقيد وتحسين الاختبار.

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

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

السيناريوهات الشائعة:

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

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

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

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

الأدوار الأساسية

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

نمطان شائعان:

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

مثال عملي: 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,
  };
}

تركيز التحويلات يبقي المكونات خالية من الشروط والتعاملات الخاصة بكل API؛ إذا تغيّر اسم حقل في إحدى الخدمات، فإنك تغيّر المحول فقط.

مكوّن 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 تعتمد على callbacks

الوحدات القديمة كثيرًا ما تستخدم استدعاءات خطأ-أول. بدلًا من تعديل وحدة مستقرة، أنشئ محولًا يكشف واجهة مرتكزة على Promise لتسهيل التكامل مع async/await.

الـ 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 لكن يجعل منطق التكيف صريحًا وقابلًا للاختبار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();

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

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

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

  • دمج مكتبة طرف ثالث بواجهة مختلفة.
  • تغليف واجهات callbacks لكي تعمل مع async/await.
  • توحيد مصادر بيانات متعددة.

لا تستخدم محولًا إذا كنت تتحكم في كلا النظامين ويمكنك إعادة هيكلة بسيطة تحل المشكلة أو عندما تحتاج واجهة مبسطة عالية المستوى؛ في هذه الحالة قد يكون نمط Facade أنسب.

قرار سريع

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

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

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

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

أمثلة روابط داخلية للقراءة المتقدّمة


أسئلة وأجوبة سريعة

س: لماذا أستخدم محولًا بدلًا من تعديل المكتبة الطرفية؟

ج: لأن تعديل المكتبة قد يكون مستحيلاً أو محفوفًا بالمخاطر؛ المحول يسمح بالتوافق بدون تغيير الشيفرة الأصلية.

س: هل المحول يؤثر على الأداء؟

ج: التأثير ضئيل عادةً، ويكون أقل أهمية مقارنةً للعملية التي تُنفّذ (شبكة، قاعدة بيانات).

س: كيف أختبر محوّلاتي؟

ج: اختبر المحول منفردًا عبر بيانات إدخال متباينة، ومحاكِ واجهة المحول عند اختبار المكونات التي تعتمدها.


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، نهج شائع لتحويل استدعاءات callbacks إلى Promises: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
استطلاع مطوّري Stack Overflow 2023، الذي يبرز انتشار JavaScript وتقنيات الويب: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

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

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