البرمجة الموجهة للكائنات OOP في Python
جزء2- التغليف Encapsulation
أحد مبادئ البرمجة الموجهة للكائنات (OOP) الذي يساهم في حماية البيانات والدوال داخل Class معين وتقييد الوصول إليها من الخارج، هو التغليف (Encapsulation) الذي يعتبر أحد أسس البرمجة القوية والمنظمة... في هذا المقال، سنستكشف مبدأ التغليف Encapsulation في لغة البرمجة Python وكيف يمكن استخدامه لتنظيم الكود …
في الجزء الأول من مقالات عن البرمجة الموجهة بالكائنات OOP في بايثون نعلم أن الفئة (Class) هي نموذج أولي محدد من قبل المستخدم لكائن ما. وهي تحدد مجموعة من عناصر البيانات والدوال القادرة على معالجة البيانات. ولحماية هذه البيانات وفقًا لمبدأ تغليف البيانات Encapsulation Data، فإن عناصر البيانات التي تصف كائنًا ما تكون لابد أن تكون مخفية عن البيئة الخارجية للفئة (Class)... هذا هو مبدأ التغليف (Encapsulation) بإختصار، لنرى طريقة تطبيق هذا المبدأ من خلال تعليمات لغة Python…لنبدأ…
في هذا المقال نتعرف على:
- التغليف Encapsulation في البرمجة.
- التغليف Encapsulation و تنظيم الكود.
- تنفيذ التغليف (Encapsulation) في بايثون.
التغليف Encapsulation في البرمجة
التغليف Encapsulation هو مبدأ من مبادئ البرمجة الموجهة للكائنات (OOP) والذي يتضمن تجميع البيانات (السمات - Attributes ) والوظائف (الدوال - Functions) التي تعمل على البيانات في وحدة واحدة (الفئة -Class). يساعد التغليف (Encapsulation ) في إخفاء الحالة الداخلية للكائن (Object) عن العالم الخارجي، مما يوفر واجهة خاضعة للتحكم للتفاعل مع بيانات الكائن ووظائفه.
ولكي يتضح لنا مفهوم التغليف دعونا نأخذ مثالاً واقعياً للتغليف (Encapsulation)، فعلى سبيل المثال في إحدى الشركات، توجد أقسام مختلفة مثل قسم الحسابات وقسم المالية وقسم المبيعات وما إلى ذلك. يتولى قسم المالية التعامل مع جميع المعاملات المالية ويحتفظ بسجلات لجميع البيانات المتعلقة بالتمويل. وبالمثل، يتولى قسم المبيعات التعامل مع جميع الأنشطة المتعلقة بالمبيعات ويحتفظ بسجلات لجميع المبيعات.
قد ينشأ موقف عندما يحتاج مسؤول من قسم المالية لسبب ما إلى جميع البيانات المتعلقة بالمبيعات في شهر معين. وفي هذه الحالة، لا يُسمح له بالوصول مباشرة إلى بيانات قسم المبيعات. وسيتعين عليه أولاً الاتصال بمسؤول آخر في قسم المبيعات ثم طلب البيانات المعينة منه.
هذا هو التغليف Encapsulation وهنا يتم تغليف بيانات قسم المبيعات والموظفين الذين يمكنهم اجراء التغيرات بها تحت اسم واحد "قسم المبيعات". كما يؤدي استخدام التغليف (Encapsulation) إلى إخفاء البيانات. في هذا المثال، يتم إخفاء بيانات الأقسام مثل المبيعات أو المالية أو الحسابات عن أي قسم آخر.
في البرمجة يُعرف التغليف Encapsulation على انه أحد مبادئ الأساسية في البرمجة الموجهة للكائنات (OOP). وهو يصف فكرة تغليف (Encapsulation) عناصر البيانات والدوال التي تعمل على البيانات داخل وحدة واحدة (كبسولة). مما يفرض قيودًا على الوصول إلى المتغيرات والدوال بشكل مباشر ويمكن أن يمنع التعديل العرضي للبيانات. ولمنع التغيير العرضي، لا يمكن تغيير متغير الكائن إلا من خلال دوال الكائن نفسه. وتُعرف هذه الأنواع من المتغيرات بالمتغيرات الخاصة (private variables).
الـ Class في البرمجة الموجهة للكائنات (OOP) مثالاً على التغليف (Encapsulation) لأنها تغلف جميع البيانات التي تمثل الدوال والمتغيرات وما إلى ذلك. والهدف من إخفاء المعلومات هو ضمان أن تكون حالة الكائن object صالحة دائمًا من خلال التحكم في الوصول إلى السمات المخفية عن العالم الخارجي.
أي في البرمجة التغليف (Encapsulation) هو عملية تجميع السمات (Attributes) والدوال (Methods) داخل وحدة واحدة (Class). وهو أحد الركائز الأساسية التي يقوم عليها نموذج البرمجة الموجهة للكائنات (OOP). (ننصح بلإطلاع على الجزء1 من مقالات البرمجة الموجهة للكائنات في بايثون عن Class & Object )
التغليف Encapsulation و تنظيم الكود
ذكرنا أن التغليف Encapsulation يعمل على منع الوصول المباشر إلى بيانات الكائن (Object)، فلا يمكن الوصول إليها إلا من خلال الدوال داخل نفس الفئة (Class) ومن ناحية أخرى، يمكن الوصول إلى الـ Methods نفسها من خارج الـ Class. وبالتالي، يقال إن بيانات الكائن مغلفة بالـ Method. ويكون الكود أكثر تظيماً من ناحية:
- التحكم في الوصول Controlled Access: يسمح التغليف (Encapsulation) بالتحكم بالوصول إلى الحالة الداخلية للكائن، مما يحمي البيانات من التدخل غير المقصود.
- تحسين المرونة Enhanced Flexibility: يعزز التغليف Encapsulation التصميم المعياري، مما يجعل من السهل تعديل أو توسيع الوظائف دون التأثير على أجزاء أخرى من البرنامج.
- تحسين الصيانة Improved Maintenance: لا تؤثر التغييرات على التنفيذ الداخلي للـ Class على عمل الكود الذي يستخدم الـ Class طالما ظلت الواجهة العامة دون تغيير.
- إخفاء البيانات Data Hiding: يخفي العمليات الداخلية للـ Class مما يجعل تفاصيل التنفيذ غير مرئية للكود الخارجي ويقلل من خطر تعديل البيانات عن طريق الخطأ.
إذن يمكن القول أن التغليف (Encapsulation) قائم على تجميع البيانات والوظائف التي تعمل على البيانات في وحدة واحدة، مع إمكانية الوصول إلى الحالة الداخلية بشكل محكم. والغرض من التغليف Encapsulation هو حماية الحالة الداخلية للكائن والواجهة الظاهرة للبيانات تكون خاضعة للرقابة. أما عن التنفيذ فيتم تحقيقه من خلال العناصر الخاصة (Private ) والمحمية (Protected).
تنفيذ التغليف (Encapsulation) في بايثون
يتحقق مبدأ التغليف Encapsulation في Python بدراسة نقطتين أساسيتين هما :
تقييد الوصول Access Modifiers في Python
تستخدم لغات مثل C++ وJava معرّفات الوصول لتقييد الوصول إلى عناصر البيانات في الـ Class (أي المتغيرات والدوال variables and methods). تحتوي هذه اللغات على كلمات رئيسية مثل ( public, protected, and private ) لتحديد نوع الوصول. وهو مايُعرف بتقييد الوصول ( Access Modifiers). (المزيد في مقال عن اسلوب البرمجة الموجهة للكائنات OOP) والذي يكون كما يوضح الشكل التالي:
يتم تنفيذ التغليف (Encapsulation) في بايثون باستخدام مواصفات الوصول للتحكم في الوصول إلى عناصر الـ Class (Attributes &Methods ). حيث تستخدم بايثون تشويه الأسماء (Name Mangling) لجعل الوصول إلى هذه العناصر من خارج الـ Class أكثر صعوبة، هذه اتفاقية فقط وليست ضوابط وصول صارمة. من الشكل أعلاه يأخذ تقييد الوصول (Access Modifiers) شكل من ثلاثة والتي تعرف في Python:
- العناصر العامة ( Public Items): يُقال عن عنصر الـ Class أنه عام إذا كان من الممكن الوصول إليه من أي مكان في البرنامج.وبشكل افتراضي، تكون الـ Attributes و الـ Methods عامة ويمكن الوصول إليها من خارج الـ Class.
- العناصر الخاصة (Private Items): يمكن الوصول إليها من داخل الـClass فقط. ويتم استخدام بادئة شرطتين سفليتين مزدوجة Double underscore (__) لجعل الـ Attributes و الـ Methods خاصة (private)، مما يجعل الوصول إليها من خارج الـClass أكثر صعوبة.
- العناصر المحمية (Protected Items) : يمكن الوصول إليها من داخل الـ Class وكذلك من خلال الـ Classes المشتقة من ذلك الـ Class.وتعرف باستخدم بادئة شرطة سفلية واحدة (_) للإشارة إلى أن الـ Attributes أو الـ Methods مخصصة للاستخدام الداخلي داخل الـ Class وفئاتها الفرعية (subclasses).
في العادةً، يتم تعريف الـ Instance Methods على أنها عامة (public) ، وتكون المتغيرات (variables) خاصة (private). ويضمن هذا الترتيب لمتغيرات المثيل الخاصة والدوال العامة تنفيذ التغليف (Encapsulation)..
لنأخذ مثال تطبيقي على تقييد الوصول (Access Modifiers) في Python :
عرفنا فيما سبق أن مُعدِّلات الوصول في Python تُستخدم لتقييد الوصول إلى عناصر البيانات في الـ Class من خارج الـ CLass. وعرفنا أن هناك ثلاثة أنواع من مُعدِّلات الوصول وهي عامة public ومحمية protected وخاصة private. فيما يلي سنتعرف على طُرق تقييد الوصول في Python .
في المجمل لا تفرض Python قيودًا على الوصول إلى أي متغير أو دالة من الأنواع Instance Methods و Instance Variable, ولكن توفر Python اتفاقية للدلالة على أن عنصر البيانات هذا محمي تتمثل في وضع بادئة لإسم المتغير أو الدالة بعلامة سفلية (underscore) مفردة أو مزدوجة لمحاكاة سلوك معرّفات الوصول المحمية protected والخاصة private، مثلاً:
class Employees:
def __init__(self, name, Id, salary):
self.name = name #public variable
self.__Id = Id #private variable
self._salary = salary # protected variable
#Access for instance variables via instance method
def GetEmployees(self):
print("Employee Name:", self.name)
print("Employee ID:", self.__Id)
print("Salary:", self._salary)
def main():
employee1 = Employees("Nora", 123, 12000)
#Can access to values of private and protected variables via instance method
employee1.GetEmployees()
print(employee1._salary)
#Can't access to values of private variables from out of their class
#print(employee1.__Id)
if __name__ == '__main__':
main()
في المثال السابق:
- قمنا بإضافة بادئة بشرطة سفلية مزدوجة Double underscore ، للإشارة إلى أن المتغير خاص (private variables) مثل "__ID".
- قمنا بإضافة بادئة له بشرطة سفلية واحدة Single underscore، للإشارة إلى أن متغير محمي (protected variables)،مثل "_salary".
نلاحظ في المثال عدم القدرة على الوصول الى متغير خاص private variable بواسطة اسم الـ object ، إن Python لا تمنع الوصول للبيانات الخاصة فثمة طريقة يمكن الوصول له من خلال كتابة اسم الـ object ثم نقطة ثم اسم الـ class مسبوق بشرطة سفلية واحدة ثم اسم المتغير الخاص (object._class__variable)، ولكن هذه الطريقة غير مستحبة في البرمجة ولا تستخدم إلا عند الضرورة .
استخدام الخصائص Properties في Python
عرفنا في البرمجة الموجهة للكائنات OOP يتطلب مبدأ التغليف (Encapsulation) أن يكون للمتغيرات وصول خاص مقيد. كما نعلم لا تمتلك بايثون آلية فعّالة لهذا الغرض. مع هذا توفر Python امكانية العمل الخصائص (Properties)، وتعتبر الخصاص من الطُرق المستخدمة للوصول للبيانات الخاصة في الـ Class .(المزيد عن الـ Properties في مقال مقدمة في Class & Object للغة C# ).
وتوفر Python استخدام الـ Property التي تعمل على إعادة ضبط خواص الـ Class لكل Object. وتعمل كواجهة (interface) لمتغيرات للـ Class الخاصة والمحمية .حيث تستخدم Property دوال تعرف في الـ Class لتحدد الـ Property لكل متغير. و يمكن إنشاء الخصائص (Properties) لعناصر البيانات الـ Class بطريقتين:
دالة property()
في لغة Python تتوفر دالة property() وهي دالة مدمجة تسمح لنا بإنشاء نوع خاص من السمات (Attributes) تسمى الخصائص (Properties). تُستخدم الخصائص لتغليف Encapsulation الوصول إلى المتغير. أوإضافة بعض المنطق إلى العملية مثل الحساب أو التحكم في الوصول أو التحقق من صحة القيم.والصيغة العامة للدالة الخصائص Property تكون كتالي :
property(fget, fset, fdel, doc)
حيث:
- fget : تستخدم للحصول على قيمة عنصر البيانات.
- fset : تستخدم لتعيين على قيمة عنصر البيانات.
- fdel : تستخدم لحذف على قيمة عنصر البيانات.
- doc : تحتوي نص يوثق الخاصية.
ويوضح المثال التالي طريقة استخدام property() في تغليف عناصر البيانات للـ Class :
class Employees:
def __init__(self, name, Id):
self.__name = name
self.__Id = Id
#Make get and set functions to each attribute
#Setting methods
def set_name(self, name):
self.__name = name
def set_Id(self, Id):
self.__Id = Id
#Getting methods
def get_name(self):
return self.__name
def get_Id(self):
return self.__Id
#Deleting value
def del_name(self):
del self.__name
return print("The name is deleted")
def del_Id(self):
del self.__Id
return print("The ID is deleted")
##Encapsulation the private attributes by property method
Name = property(get_name, set_name,del_name,"name")
ID = property(get_Id, set_Id,del_Id, "Id")
def main():
employee1 = Employees("Nora", 123)
#Access to private variables via property method
print("Employee name is", employee1.Name)
print("Employee ID is", employee1.ID)
print("------Try change values -------")
employee1.Name = "Sara"
employee1.ID = 111
print("Employee name is", employee1.Name)
print("Employee ID is", employee1.ID)
#Deleting value
del employee1.Name
del employee1.ID
if __name__ == '__main__':
main()
استخدام @property
العمل الرئيسي للمزينات (decorators) مثل @property هو استخدامها لإضافة وظائف إلى الكود الموجود. وتسمى أيضًا البرمجة الوصفية، حيث يحاول جزء من البرنامج تعديل جزء آخر من البرنامج في وقت التجميع. المثال التالي يوضح طريقة الاستخدام للـ @property للعمل خصائص لعناصر البيانات في الـ Class:
class Employees:
def __init__(self, name, Id):
self.__name = name
self.__Id = Id
#By using @property to make the variables properties
@property
def name(self):
return self.__name
@name.setter
def name(self,name):
self.__name = name
@name.deleter
def name(self):
del self.__name
return print("The name is deleted")
@property
def Id(self):
return self.__Id
@Id.setter
def Id(self,Id):
self.__Id = Id
@Id.deleter
def Id(self):
del self.__Id
return print ("The ID is deleted")
def main():
employee1 = Employees('Nora', 123)
#Access to private variables via property method
print("Employee name is", employee1.name)
print("Employee ID is", employee1.Id)
print("------Try change values -------")
employee1.name = "Sara"
employee1.Id = 111
print("Employee name is", employee1.name)
print("Employee ID is", employee1.Id)
#Delete value
del employee1.Id
if __name__ == '__main__':
main()
في المثال حُددت الدالة name() على أنها Attribute في Employees Class ثم نستخدم السمة name لتحديد محدد للخاصية أنها مرتبطة مع المتغير name. لاحظ/ي أنه يتم استخدام نفس الدالة name() مع تعريفات مختلفة لتحديد المُستقبِل (getting) والمُحدِّد(setting) والمُحذِف (deleter). كلما استخدمنا employee1.name، فإنها تستدعي getting و setting و deleter المناسبين.
جميع الأمثلة لهذا المقال متوفرة هنـا.
في النهاية علينا ان ندرك الآن أهمية مبدأ التغليف (Encapsulation) في لغة البرمجة Python وكيف يمكن أن يحدث تحولًا في طريقة تنظيم وبناء الكود. من خلال تطبيق التغليف (Encapsulation)، يمكننا حماية البيانات وتقييد الوصول إليها بشكل فعال، مما يسهم في تعزيز الأمان والصيانة وتحسين جودة الكود. بفضل التغليف Encapsulation، يمكننا بناء تطبيقات Python أكثر قوة وأمانًا وسهولة في الصيانة.