علوم البرمجة

[تطوير وبرمجة]: ما هي البرمجة غرضية-كائنية التوّجه Object Oriented Programming

تتضمن عملية البرمجة استخدام طرقًا وأساليب مختلفة تُعرف باسم “نماذج البرمجة Programming Paradigms” بحيث يشتمل كل نموذج على مجموعةٍ من الخصائص والصفات التي تميّزه عن النماذج الأخرى، وبحيث تدعم كل لغة برمجة نموذجًا أو عدة نماذج بنفس الوقت.

كنا قد استعرضنا بمقالٍ سابق مفهوم البرمجة الإجرائية Procedural Programming الذي يعتبر المنهج أو النموذج الأساسيّ الذي يتم استخدامه وتعلمه من قبل المبرمجين الجدد. في هذا المقال، سنستعرض مفهوم البرمجة غرضية/كائنية التوّجه Object Oriented Programming وأهميته البالغة في لغات البرمجة المستخدمة في وقتنا الحاليّ.

تعريف البرمجة غرضية التوجه Definition of OOP

البرمجة غرضية/كائنية التوّجه (اختصارًا OOP) هي إحدى نماذج البرمجة الحتمية Imperative Programming التي تتيح للمبرمج إنجاز الوظيفة المطلوبة عبر تحديد مجموعةٍ من الإجرائيات والخطوات المنطقية، وما يميز نموذج البرمجة غرضية التوجه أنها تعتمد على إنشاء علاقة ارتباط عضوي بين البيانات والمتحولات من جهة، وبين التوابع والإجرائيات من جهةٍ أخرى، وذلك بخلاف البرمجة الإجرائية التي تعتمد على تنفيذ المهمة المطلوبة عبر إجرائياتٍ لا تتضمن أي علاقة أو ارتباط عضوي بين المتحولات، التوابع وبنى التحكم.

يمكن القول أن البنية الأساسية للبرمجة كائنية التوجه هو مفهوم “الكائنات Objects“، بحيث يمكن النظر للكائن على أنه كتلة تتضمن بداخلها مجموعةً من الأعضاء Members، وهذه العناصر قد تكون متحولات Variables وقد تكون توابع Functions وقد تكون بنى Structures. الفكرة الأساسية هنا هو أن تنفيذ البرنامج سيكون عبارة عن تفاعلٍ مع عددٍ من الكائنات المختلفة والأعضاء التي تنتمي لها.

هنالك طرقٌ متنوعة يمكن استخدامها في لغات البرمجة من أجل إنشاء الكائنات، إلا أن المنهجية الأكثر شيوعًا هي “الصفوف Classes” وهي المعتمدة في غالبية اللغات كائنية التوجه الشهيرة. من ناحيةٍ أخرى، تعتبر معظم لغات البرمجة كائنية التوجه على أنها لغات متعددة النماذج Multi-Paradigm، أي أنها تدعم البرمجة كائنية التوجه بالإضافة لنماذج أخرى، خصوصًا البرمجة الإجرائية.

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

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

يمثل الصف Class نمطًا مُعرّف من قبل المستخدم User-Defined Type، وهو بذلك يختلف عن الأنماط الافتراضية التي تمتلكها لغات البرمجة. يجب أن يمتلك الصف اسم، وبنية، وداخل البنية الخاصة به تعريف أعضاء Members مختلفة، بحيث قد تكون الأعضاء عبارة عن متحولات Variables أو توابع Functions أو بنى Structures. يجب الإشارة هنا إلى بعض المصطلحات المستخدمة عند الإشارة إلى أعضاء الصفوف، بحيث يتم استخدام مصطلح “الميزة Attribute” عند الحديث عن المتحولات، ويتم استخدام مصطلح “الطرق Methods” عند الحديث أو الإشارة للتوابع الأعضاء ضمن الصف. أخيرًا، تمتلك الصفوف توابع مضمنة (يمكن أيضًا التصريح عنها) تُعرف باسم “التوابع البناءة Constructor” والتي يؤدي استدعاؤها لإنشاء كائنٍ يتبع لهذا الصف.

بهذه الصورة، وعبر التوضيح الموجز (جدًا) لمفهوم الصفوف، نستطيع أن نفهم أن إنشاء الكائنات يتطلب بالبداية كتابة النص البرمجيّ الخاص بالصفوف التي تحدد خصائص وصفات هذه الكائنات. لو أردنا أخذ مثالٍ بسيطٍ على ذلك، لنقم بكتابة نصٍ برمجيّ بلغة ++C يتضمن صفًا يحمل اسم Polygon ومن ثم نقوم بإنشاء كائنٍ من هذا الصف ضمن التابع الرئيسيّ ()main:

تنويه: بالنسبة للنص البرمجيّ التالي، فإن كافة الأسطر التي تبدأ بالرمز // أو المكتوبة ضمن كتلة محددة بالرمزين /**/ هي عبارة عن “تعليقات Comments” لا يتم ترجمتها وتفسيرها من قبل مترجم اللغة، وينحصر استخدامها لتوضيح النص البرمجيّ وجعله أكثر فهمًا للقراءة.

#include <iostream>
#include <string>

class Polygon
{
private: // Only accessible by methods from within the class 
    unsigned int m_width; 
    unsigned int m_height; 
public: // Accessible from outside the class
       Polygon(); 
       void SetValues(unsigned int width, unsigned int height)
       {
           m_width = width; 
           m_height = height;  
       };  
       unsigned int area() { return m_width * m_height};
}; 

int main()
{
     // Instantiate an object from class Polygon
     Polygon MyFirstPolygon(); 
     
     // Assign values to member attributes
     unsigned int polygonWidth = 10; 
     unsigned int polygonHeight = 20; 
     MyFirstPolygon.SetValues(polygonWidth, polygonHeight); 
     
     // Caclculate Polygon Area and print it 
     unsigned int polygArea = MyFirstPolygon.area(); 
     std::cout << "The Area of the Polygon is :: " << polygArea << std::endl; 
     
     return 0; 
}

ضمن النص البرمجيّ السابق، سنجد أننا قد بدأنا عبر تعريف الصف Polygon ومن ثم تحديد أعضائه، حيث قمنا بتحديد عضوين على أنهما أعضاء خاصة Private أي لا يمكن الوصول إليها عبر توابع وإجرائيات من خارج الصف، وقمنا بتعريف ثلاث توابع على أنها أعضاء عامة Public، وهي التابع البناء ()Polygon الذي سنحتاجه لإنشاء كائن يتبع للصف، والتابع الثاني هو تابع يقوم بإسناد قيم للأعضاء الخاصة، والتابع الثالث هو تابع بسيط لحساب مساحة المضلع عبر إجراء عملية جداء للأعضاء الخاصة التي تمثل الطول والعرض. الخطوة التالية هي إنشاء كائن Object من الصف Polygon ضمن التابع الرئيسيّ ()main، حيث تمت هذه العملية عبر التعليمة ;()Polygon MyFirstPolygon، والتي تعني أننا قمنا بإنشاء كائن اسمه MyFirstPolygon ونوعه Polygon، وهذا يعني أن نمط الكائن هو اسم الصف المستخدم في إنشائه (تذكروا أن الصفوف عبارة عن نمط معطيات معرّف من قبل المستخدم). هذا الكائن يمتلك داخله كل الأعضاء المعرفة ضمن الصف Polygon: أي أنه يمتلك عضوين خاصين وثلاث أعضاء عامة. يمكننا بالطبع إنشاء كائنات أخرى من الصف Polygon وستمتلك جميعها نفس الأعضاء. الاختلاف أنها يمكننا تخصيص القيم التي ستمتلكها الأعضاء الخاصة للكائنات المختلفة، أي أنه يمكننا إنشاء كائن يمثل مضلع بطول 10 وعرض 20، وكائن آخر يمثل مضلع بطول 5 وعرض 3، وكائن ثالث بطول 20 وعرض 30 وهكذا، وبالتالي فإن التابع الخاصة بحساب المساحة ضمن كل كائن سيقوم بإرجاع قيم مختلفة، وليس نفس القيمة.

الآن يمكننا الانتقال إلى الأعمدة الأساسية للبرمجة كائنية التوجه، حيث هنالك 4 مفاهيم أساسية تمثل الخصائص الفريدة لهذا النموذج البرمجيّ، وهذه المفاهيم هي:

التغليف Encapsulation: أي القدرة على جمع البيانات، المتحولات، التوابع والبنى المختلفة ضمن بنيةٍ واحدة هي الكائن (الغرض)، وهذا يعني أنه يمكننا النظر للكائن على أنه “الغلاف” الذي يجمع بداخله العديد من الأعضاء المختلفة والتي تمتلك علاقاتٍ مع بعضها البعض.

التجريد Abstraction: بعالم البرمجة كائنية التوجه يمكن تحديد مدى ظهور البيانات التي تنتمي للكائن، بحيث يمكن جعل بعضها “عام Public” والآخر “خاص Private“، وهذا يعني أنه يمكن التعامل مع الأعضاء العامة عبر توابع وإجرائيات خارجية لا تنتمي للكائن نفسه، بينما لا يمكن التعامل مع الأعضاء “الخاصة” إلا من قبل التوابع والإجرائيات المعرّفة داخل الكائن نفسه. القدرة على إظهار بيانات محددة وإخفاء بيانات أخرى هي ما يعرف بمفهوم التجريد ضمن البرمجة كائنية التوجه.

الوراثة Inheritance: نعلم من علم الأحياء أن الوراثة هي المجال الذي يشرح ويوضح الصفات المنتقلة من جيلٍ لآخر، وبعالم البرمجة كائنية التوجه، فإن الوراثة هي المفهوم الذي يتيح إنشاء كائناتٍ “ترث” صفات كائناتٍ أخرى. كمثالٍ بسيط، يمكننا تعريف صف عام يمثل الأشكال الهندسية Geometrical Shapes والذي يمتلك أعضاءً هي الطول، العرض، المحيط والمساحة. من هذا الصف نستطيع أن “نرث” صفًا آخر هو الدائرة، والذي يمثل أيضًا شكلًا هندسيًا وبالتالي فإنه “سيرث” كافة الصفات الخاصة به، إلا أنه شكل هندسي فريد إذ يمتلك خاصية أخرى هي نصف القطر. عندما نقوم بإنشاء صف الدائرة اعتمادًا عبر اشتقاقه من صف الأشكال الهندسية، فإننا بعالم البرمجة نقوم بـ “توريث” صفات من صفٍ إلى آخر.

تعدد الأشكال Polymorphism: تتيح البرمجة كائنية التوجه إعادة استخدام نفس التابع أو العملية ولكن لغاياتٍ وأهدافٍ مختلفة، وبالتالي فإن التابع قد أصبح له أشكال (وجوه) متعددة. لو أردنا أن نأخذ مثالًا من الحياة الواقعية، فإنه يمكن لأي رجل أن يكون أب، زوج، موظف وهاوي قراءة بنفس الوقت، وبالتالي فإن هذا الرجل يمتلك “أشكالًا” متعددة تختلف بحسب السياق والظروف التي يتواجد بها. تعدد الأشكال في البرمجة كائنية التوّجه يمكن أن يتم عبر مفهوم التحميل الزائد Overloading، والذي بدوره ينقسم للتحميل الزائد للعمليات Operators-Overloading والتحميل الزائد للتوابع Functions Overloading. التحميل الزائد للعمليات أو التابع هو الإجرائية التي تتيح إعادة استخدام عملية رياضية ما (مثل الجمع) لتنفذ أهدافًا مختلفة بحسب السياق الذي تأتي ضمنه، وكذلك الأمر تمامًا بالنسبة للتوابع. يأخذ مفهوم تعدد الأشكال نمطًا آخر يرتبط بالصفوف والكائنات المشتقة عبر الوراثة، والتي تتيح استخدام الصف المشتق Derived Class وكأنه الصف الأساسيّ Base Class الذي ورث بعض صفاته منه.

من المهم الذكر أن هنالك خصائص ومفاهيم خاصة بالبرمجة كائنية التوجه قد تختلف من لغةٍ إلى أخرى، وما قد ينطبق على لغةٍ محددة قد لا يعني وجوده حتمًا ضمن لغةٍ أخرى.

فهم البرمجة كائنية التوّجه : مثال الأشكال المُضلعة

أحد الأمور الممتعة بالبرمجة كائنية التوجه هي إمكانية شرح مفاهيمها باستخدام الكثير من الأمثلة من حولنا، سواء كانت لكائناتٍ حية أو لكائناتٍ مجرّدة، وكون البرمجة تتعامل مع مفاهيم مجرّدة، فإننا سنأخذ مثالًا من عالم الرياضيات وهو الأشكال الهندسية المضلعة Polygons.

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

لنقل الآن أننا نريد كتابة برنامج يقوم بحساب محيط ومساحة المضلعات البسيطة، بحيث يقوم المستخدم بإدخال نوع المضلع المطلوب بالإضافة لأبعاده. عبر الاعتماد على مفهوم البرمجة كائنية التوّجه والأعمدة الأساسية الخاصة بها، فإنه يمكننا إنشاء صف Class أساسيّ يمتلك متحولات تمثل الخصائص الأساسية للمضلعات، أي الطول والعرض، كما أنه يمتلك توابع تساعد على حساب المحددات الأساسية، أي المحيط والمساحة. الخطوة التالية هي إنشاء صفوف مشتقة من الصف الأساسيّ، بحيث يمثل كل صف نمطًا من أنماط المضلعات البسيطة: المثلث والمربع والمستطيل. بما أن هذه الصفوف مشتقة من الصف الأساسيّ، فإنها سترث الخصائص التي يتضمنها، ويمكن لها أن تتضمن خصائص وعناصر أخرى. عند قيام المستخدم باختيار نوعٍ محددٍ من المضلعات، فإن هذا سيؤدي إلى إنشاء “كائن Object” يمثل المضلع المطلوب التعامل معه وحساب محدداته.

الآن وعبر الاستفادة من النص البرمجيّ السابق الخاص بإنشاء الصف Polygon، يمكننا توسيع البرنامج لجعله ينفذ المهمة الجديدة، أي حساب محيط ومساحة مضلعات مختلفة:

#include <iostream>
typedef enum { 	RECTANGLE, TRIANGLE, SQUARE } t_shape;

// Base Class to define a Polygon
class Polygon
{
protected:
	unsigned int m_width;
	unsigned int m_height;
	t_shape m_type;
public:
	void SetValues(unsigned int width, unsigned int height, t_shape type)
	{
		m_width = width;
		m_height = height;
		m_type = type;
	}
};  // End of class Polygon 

// Derived class from Polygon to define a square
class Square : public Polygon
{
public:
	unsigned int area()
	{
		unsigned int result;
		if (m_type == SQUARE)
		{
			result = m_width * m_width;
		}
		else
		{
			result = 0;
		}
		return result;
	}
}; // End of class SQUARE

// Derived class from Polygon to define a Rectangle
class Rectangle : public Polygon
{
public:
	unsigned int area()
	{
		unsigned int result;
		if (m_type == RECTANGLE)
		{
			result = m_width * m_height;
		}
		else
		{
			result = 0;
		}
		return result;
	}
}; // End of class RECTANGLE 

// Derived class from Polygon to define a Triangle
class Triangle : public Polygon
{
private:
	unsigned int m_long;
public:
	unsigned int area()
	{
		unsigned int result;
		if (m_type == TRIANGLE)
		{
			result = (m_width * m_height) / 2;
		}
		else
		{
			result = 0;
		}
		return result;
	}
}; // End of class Triangle

int main()
{
	// Create rectangle, triangle and square objects
	Rectangle  MyRectangle;
	Triangle myTriangle;
	Square mySquare;

	// Use the inherited "SetValues" function to assign values to the members
	MyRectangle.SetValues(10,20, RECTANGLE);
	myTriangle.SetValues(5,10, TRIANGLE);
	mySquare.SetValues(10,0, SQUARE);

	// Calculate the area of each object using the polymorphic function area()
	std::cout << "The Area of Triangle is : " << myTriangle.area() << std::endl;
	std::cout << "The Are of Rectangle is : " << MyRectangle.area() << std::endl;
	std::cout << "The Are of Square is : " << mySquare.area() << std::endl;

	return 0;
}

بعد إجراء عملية بناء Build للنص البرمجيّ السابق وتشغيله ضمن أحد محررات النصوص فإننا سنحصل على الخرج التالي:

The Area of Triangle is : 25
The Are of Rectangle is : 200
The Are of Square is : 100

نظرة في العمق: تطبيق أعمدة البرمجة كائنية التوّجه

في البداية، تم إنشاء صف أساسي Base Class يحمل اسم Polygon، وما نريده من هذا الصف هو أن يمثل نمطًا يجمع الصفات الأساسية للأشكال الهندسية المضلعة، ولذلك فإنه يتضمن ثلاث متحولات هي العرض m_width، الطول m_hieght والنوع m_type. تم التصريح عن هذه المتحولات ضمن المجال protected لأننا نريدها أن تكون قابلة للتوريث لصفوف أخرى، ولو تم التصريح عنها ضمن المجال private لما كان بالإمكان القيام بذلك. الخطوة التالية كانت التصريح عن ثلاث صفوف أخرى كل منها يمثل شكلًا هندسيًا مضلعًا: الصف Rectangle، الصف Triangle والصف Square وهي جميعها مشتقة Derived من الصف الأساسيّ Polygon. ضمن كل صف من هذه الصفوف تم تعريف تابع لحساب المساحة، وهو يحمل الاسم area في كل هذه الصفوف، إلا أن كيفية حساب المساحة داخل التابع تختلف من صفٍ لآخر. أخيرًا، وضمن التابع الرئيسيّ ()main تم إنشاء ثلاث كائنات تتبع لهذه الصفوف: الكائن MyRectangle من الصف Rectangle، الكائن myTriangle من الصف Triangle والكائن mySquare من الصف Square. تم استخدام التابع SetValues لإسناد قيم لأعضاء هذه الكائنات ومن ثم تم استخدام تابع المساحة area لحساب مساحة كل شكل هندسيّ ومن ثم طباعته على الشاشة.

بالعودة لأعمدة البرمجة كائنية التوجه الأربعة، فإن الكود البرمجيّ السابق يستغلها وفقًا لما يلي:

التغليف Encapsulation: كل كائن من الكائنات التي تم إنشاؤها يشتمل على أعضاء وتوابع، وبهذه الصورة فإننا قمنا بتغليف الأعضاء m_width، m_height و m_type مع التوابع SetValues و area ضمن كيانٍ واحد.

التجريد Abstraction: تمتلك الأعضاء التي تنتمي للكائنات خصائص تحدد مدى قابلية استخدامها داخل وخارج الكائن. الأعضاء المعرفة ضمن المجال protected تعني أنه يمكن الوصول لها حصرًا عبر الصفوف المشتقة، والأعضاء المعرفة ضمن المجال pubic تعني أنه يمكن الوصول لها والتعامل معها من توابع وإجرائيات لا تنتمي للكائن نفسه.

الوارثة Inheritance: الصف Polygon قام بتوريث صفاته وخصائصه للصفوف الأخرى، وبذلك، وعلى الرّغم من عدم التصريح عن المتحولات m_width، m_height و m_type ضمن الصفوف الأخرى، إلا أنها وكونها مشتقة من الصف Polygon فهي تمتلك أيضًا هذه المتحولات. كذلك الأمر بالنسبة للتابع SetValues الذي لم يتم التصريح عنه ضمن بنية الصفوف المشتقة، ولكنها حصلت عليه عبر “وراثته” من الصف Polygon.

تعدد الأشكال Polymorphism: تم التصريح عن التابع area في النص البرمجيّ السابق ثلاث مرّات، وهو تابع يقوم بحساب قيمة من نوع unsigned int. اسم التابع نفسه في الصفوف الثلاثة، وكذلك نوعه، فضلًا عن كونه لا يقوم بتمرير أي وسيط، ولكن وعند استخدامه مثلًا مع كائن من نوع Triangle، فإن هذا سيؤدي لحساب مساحة المثلث وفقًا للمعادلة الموجودة ضمن التابع area في الصف Triangle. بهذه الصورة، امتلك التابع area نفس الاسم ولكن استخداماته تعددت وتنوعت بحسب السياق الذي تم عبره استدعاؤه.

البرمجة كائنية التوجه: الصفوف ليست كل شيء

تعتمد معظم لغات البرمجة كائنية التوجه على مفهوم الصفوف من أجل إنشاء الكائنات، إلا أن هذه الطريقة ليست الوحيدة في مجال البرمجة كائنية التوّجه، إذ يوجد نمطٌ آخر من اللغات كائنية التوّجه والتي لا تعتمد على مفهوم الصفوف بل على مفهومٍ آخر هو البرمجة المعتمدة على النماذج الأولية Prototype-based Languages.

في اللغات المعتمدة على مفهوم النماذج الأولية لا يوجد صفوف من أجل إنشاء الكائنات، بل يُمكن إنشاء الكائنات مباشرةً بدون الحاجة للتعريف عن الصفوف، وبدلًا من ذلك يتم إنشاء “نماذج أولية Prototypes” والتي تمثل كائنات أساسية يمكن اشتقاق كائنات أخرى منها. بهذه الطريقة، وإذا عدنا للمثال السابق الخاص بإنشاء الأشكال الهندسية المضلعة، فإنه يمكننا إنشاء نموذج أولي هو “المضلع” الذي يمثل بحد ذاته كائن، ومنه نستطيع اشتقاق كائنات أخرى هي المربع، المستطيل والمثلث. الفرق الأساسيّ هنا أنه لا يوجد داعي لإنشاء صف لكل نمط من الكائنات، بل يتم إنشاء نموذج أوليّ واحد ومن ثم يتم اشتقاق كائنات أخرى منه. تعتبر لغة جافاسكريبت JavaScript إحدى أشهر اللغات كائنية التوجه التي تعتمد على مفهوم النماذج الأولية بدلًا من مفهوم الصفوف لإنشاء الكائنات.

أشهر لغات البرمجة كائنية التوجه

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

  • سي بلس بلس ++C
  • جافا Java
  • بايثون Python
  • سي شارب #C
  • روبي Ruby
  • جافاسكريبت JavaScript
  • بي إتش بي PHP

من ناحيةٍ أخرى، ينظر لبعض اللغات على أنها مصممة لتكون كائنية التوجه بالمطلق Pure OOP Languages، ومن الأمثلة الشهيرة على ذلك لغات بايثون وروبي، بينما يتم النظر لبعض اللغات على أنها مصممة لتدعم نموج البرمجة كائنية التوجه مع امتلاكها لقدراتٍ تتيح البرمجة الإجرائية، مثل سي بلس بلس، جافا وسي شارب. أخيرًا، لا يزال هنالك بعض لغات البرمجة الشهيرة واسعة الاستخدام التي لا تدعم نموذج البرمجة كائنية التوّجه، وأشهر مثال على ذلك هو لغة سي C.

كلمة أخيرة: متى نستخدم نموذج البرمجة كائنية التوجه؟

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

كمثالٍ شهيرٍ على ذلك، لا تزال لغة سي C البرمجية هي الأكثر انتشارًا في مجال برمجة العتاد والمتحكمات الصغرية ضمن الأنظمة المضمنة Embedded Systems كونها توّفر كافة الأدوات والطرق التي يحتاجها المبرمج لكتابة برامج تُخبر المتحكم الصغريّ بما يجب أن يقوم به، وذلك مع سرعة تنفيذ عالية جدًا للشيفرة المصدرية الخاصة بالبرنامج. على الرّغم من دخول لغة ++C مجال برمجة المتحكمات الصغرية، إلا أنه ليس ضروريًا استخدامها من أجل كافة التطبيقات والمهام، خصوصًا أن تحويل النص البرمجيّ لنموذج البرمجة كائنية التوّجه قد ينطوي على تعقيداتٍ إضافية تتمثل بضرورة التصريح عن الصفوف والكائنات والعلاقات فيما بينها، بينما قد يستطيع نص برمجي إجرائي Procedural Code تنفيذ نفس المهمة بعددٍ أقل من السطور والتعليمات. هل نحتاج لتعريف صفوف وكائنات إذا كنا نريد فقط قراءة خرج حساس وإرساله عبر أحد معايير الاتصال للحاسوب؟ ممكن، ولكننا سنكون قادرين على تنفيذ نفس المهمة عبر كودٍ إجرائيّ بسيط بعددٍ أقل من السطور وبزمن ترجمة أقل وباستهلاك مساحةٍ أقل على الذاكرة.

يبرز بهذا السياق اسم “لينوس تورفالدس Linus Torvalds“، مطور نظام لينوكس Linux مفتوح المصدر، والذي يُعتبر من أبرز منتقدي لغة ++C خصوصًا والبرمجة كائنية التوّجه عمومًا، حيث يجد أن الانتشار الواسع لهذا المفهوم يعود للمطورين الجدد غير القادرين على إنجاز المهام بكفاءة باستخدام قدرات لغة سي، فضلًا عن عدم إيجاده لأي إيجابيةٍ ملموسة للبرمجة كائنية التوّجه طالما أن لغةً مثل C قادرة على تنفيذ نفس المهام وبدون التعقيدات المرافقة للبرمجة كائنية التوّجه (للمزيد حول هذا الموضوع: اضغط هنا). لسنا هنا بصدد القول أن لينوس تورفالدس على صواب أو على خطأ، ولكن المقصد من المثال السابق أن وجود البرمجة كائنية التوّجه وخصائصها لا يعني حتمًا استخدامها، وكمثالٍ شهير على ذلك، فإن الشيفرة المصدرية الخاصة بنظام لينوكس مكتوبة بلغة C وكذلك الشيفرة المصدرية الخاصة بمنصة Git الشهيرة، ولكن من المنصف القول أن شركة عملاقة مثل مايكروسوفت قد اعتمدت على لغة ++C لتطوير الشيفرة المصدرية لأنظمة تشغيل ويندوز وبرامجها المختلفة.

سبب اختيار لغة برمجة محددة ومفهوم برمجة محددة يتبع بشكلٍ أساسيّ للتطبيق المراد تنفيذه، ولكل مبرمج حرية اختيار ما يناسبه من أدوات تحقق الغاية المنشودة بأفضل شكلٍ ممكن.

مقالات ذات صلة

زر الذهاب إلى الأعلى