التغليف (Encapsulation) في C#
التغليف (Encapsulation) أحد المبادئ الأساسية في البرمجة الموجهة للكائنات OOP الذي يساهم في تحقيق التنظيم والأمان في تصميم البرمجيات. فكيف يمكن تطبيق مبدأ التغليف (Encapsulation) في لغة سي شارب..؟؟!!
التغليف (Encapsulation) هو عميلة لتبسيط الاستخدام من خلال إخفاء التفاصيل الداخلية للتنفيذ. فهو يعمل على تجميع البيانات والعمليات المتعلقة بكائن برمجي معًا في وحدة واحدة وحماتها من الوصول غير المصرح به. في هذا المقال نتعرف على طريقة تطبيق مبدأ التغليف (Encapsulation) في لغة سي شارب. لنبدأ..
في هذا المقال نتعرف على:
- التغليف (Encapsulation) في OOP.
- محددات الوصول Access specifiers فيC# .
- التسمية وإمكانية الوصول Naming and accessibility.
- تطبيق التغليف Encapsulation في C# .
التغليف (Encapsulation) في OOP
التغليف Encapsulation مبدأ أساسي في البرمجة الموجهة للكائنات (OOP) يشير إلى تجميع البيانات والدوال التي تعمل على معالجة تلك البيانات داخل وحدة واحدة. في C# ، يتم تحقيق ذلك عادةً من خلال استخدام الـ Class.
ويمكن أن يُعرَّف التغليف (Encapsulation) بأنه جمع البيانات والمعلومات وحميتها تحت وحدة واحدة (كبسولة)، إنه الآلية التي تربط البيانات والوظائف التي تعالجها معًا. حيث يُعد التغليف (Encapsulation) درعًا واقيًا يمنع الوصول إلى البيانات من خارج هذا الدرع.
وتظهر أهمية التغليف (Encapsulation) عند تعريف الفئات classes. والفكرة هي أن البرنامج الذي يستخدم class ما لا ينبغي أن يضطر إلى مراعاة كيفية عمل هذا الـ class داخليًا؛ حيث يقوم البرنامج ببساطة بإنشاء كائن أو مثيل (object/ instance) لـ class ما واستدعاء الـ methods لهذا الـ class. وطالما أن هذه الدوال تؤدي الغرض الذي صُممت من أجله، فلا يحتاج البرنامج إلى معرفة كيفية تنفيذها.
على سبيل المثال، عندما تستدعي الدالة Console.WriteLine، فلن ترغب في الانزعاج من كل التفاصيل المعقدة حول كيفية ترتيب Console class فعليًا لكتابة البيانات على الشاشة. قد تحتاج الـ class إلى الاحتفاظ بجميع أنواع معلومات الحالة الداخلية لأداء الـ methods المختلفة. يتم إخفاء معلومات الحالة والأنشطة الإضافية هذه عن البرنامج الذي يستخدم هذا الـ class. لذلك، يُشار إلى التغليف (Encapsulation) أحيانًا باسم إخفاء المعلومات.
التغليف (encapsulation) في الواقع يؤدي غرضان:
- دمج الدوال (methods) والبيانات داخل الـ Class بعبارة أخرى، لدعم التصنيف (classification) .
- التحكم في إمكانية الوصول إلى methods والبيانات؛ بعبارة أخرى، للتحكم في استخدام الـ class .
مزايا التغليف (Encapsulation):
يحقق تطبيق التغليف (Encapsulation) مزايا عدة في بناء الأنظمة منها :
- إخفاء البيانات (Data Hiding): لن يكون لدى المستخدم أي فكرة عن التنفيذ الداخلي للـ Class. إنه يعرف فقط أننا نمرر القيم إلى أدوات الوصول ويتم تهيئة المتغيرات إلى تلك القيمة.
- زيادة المرونة ( Increased Flexibility): يمكننا جعل متغيرات الـ Class للقراءة فقط أو للكتابة فقط حسب متطلباتنا.
تكمن الفكرة وراء التغليف (Encapsulation) في إبقاء تفاصيل تنفيذ الـ Class مخفية من الوصول الخارجي، وعرض واجهة عامة فقط تسمح للمستخدمين بالتفاعل مع الـ Class بطريقة آمنة وخاضعة للرقابة. يساعد هذا في تعزيز الوحدات النمطية وقابلية الصيانة والمرونة في تصميم أنظمة البرامج.
محددات الوصول Access specifiers في C#
تُحدد مُحددات الوصول (Access Specifiers) في لغة C# مستوى وصول للعناصر الـ Class (مثل المتغيرات والدوال) داخل الفئات (Classes) والهياكل (Structures) إلى أجزاء أخرى من البرنامج. وهي آلية أساسية في مبدأ التغليف (Encapsulation) . وتُتيح مُحددات الوصول (Access Specifiers) التحكم في كيفية استخدام الكود، وحماية البيانات الحساسة، وتسهيل عملية تصحيح الأخطاء. وأهم مُحددات الوصول (Access Specifiers) في لغة C# هي :
مُحدِّد الوصول العام (Public) :
يسمح مُحدِّد الوصول العام للـ class أن يكون الـ Class وعناصره من متغيرات ووظائف مرئى للدوال وكائنات الـ classes الأخرى.وتعيين العنصرعلى أنه عام يعني السماح بالوصول إلى العنصر من أي جزء من البرنامج، سواءً داخل نفس الـclass أو من الـ Classes الأخرى، أو حتى من تجميعات (Assemblies) مختلفة.
في سي شارب تستخدم الكلمة "public" في بيان إعلان العناصر والـ classes العامة، وتحديد الوصول العام هو التحديد الافتراضي عند عدم تعيين أي تحديد وصول آخر.
محدد الوصول الخاص (Private):
يسمح محدد الوصول الخاص Private للـ class بإخفاء عناصره من متغيرات ووظائف عن الوظائف والكائنات الأخرى. فقط وظائف الـ Class نفسها يمكنها الوصول إلى أعضائها الخاصين. حتى الكائنات للـ class لا يمكنها الوصول إلى عناصر الـ class الخاصة.
private يُمكن الوصول إلى العنصر فقط من داخل الـ Class التي تم تعريف العنصر الخاص فيه. ولا يُمكن الوصول إليه من الـ Classes الأخرى أو من التجميعات (Assemblies) الاخرى. في سي شارب تستخدم الكلمة "private" في بيان إعلان العناصر والـ classes الخاصة.
مُحدد الوصول المحمي (Protected)
يسمح مُحدد الوصول المحمي Protected للـ classes المتفرعة من class معين بالوراثة بالوصول إلى العناصر في للفئة الأساسية (base-class). فـ protected يُمكن الوصول إلى العنصر فقط من داخل الـ class الذي تم تعريف العنصر فيه، أو من فئات مُشتقة (Derived Classes) منها، حتى لو كانت هذه الفئات في تجميعات (Assemblies) أخرى.
في سي شارب تستخدم الكلمة "protected " في بيان إعلان العناصر المحمية. (المزيد من التفاصيل في مقال الوارثة Inheritance في سي شارب).
محدد الوصول الداخلي (Internal)
يسمح محدد الوصول الداخلي (Internal) للـ class بعرض متغيراته ووظائفه على وظائف وكائنات أخرى في التجميع الحالي. (في نفس الـ namespace أو نفس التطبيق) بعبارة أخرى، internal يُمكن الوصول إلى العنصر فقط من داخل نفس التجميع (Assembly) الذي تم تعريف العنصر فيه. لا يُمكن الوصول إليه من تجميعات أخرى. وتستخدم الكلمة "internal" في بيان إعلان العناصر الداخلية.
و يوضح الشكل التالي كل مُحددات الوصول (Access Specifiers) في C# و النطاق المرئي لكل محدد :
المثال التالي يوضح طريقة استخدام مُحددات الوصول في إعلان المتغيرات، وطريقة عملها :
...........
public class AccessModifiers{
private string privateAccess = "A private member";
public string publicAccess="A public member";
protected string protectedAccess = "A protected member";
internal string internalAccess ="An internal member";
}
public class DerivedClass:AccessModifiers{
//access in the Derived class
public void display(){
//The Derived Class Can not access to the private members
//Console.WriteLine(privateAccess); //Error
Console.WriteLine(publicAccess);
Console.WriteLine(protectedAccess);
Console.WriteLine(internalAccess);
}
}
public class Program
{
public static void Main(string[] args)
{
DerivedClass obj = new DerivedClass();
obj.display();
}
}
...........
تُستخدم هذه مُحددات الوصول (Access Specifiers) لتطبيق مبدأ التغليف encapsulation، حيث تُخفي التفاصيل الداخلية للفئة عن المستخدمين، وتُتيح الوصول إلى البيانات والوظائف من خلال واجهة محددة. و يعتمد اختيار مُحدد الوصول المناسب على تصميم البرنامج واحتياجاته.
التسمية وإمكانية الوصول Naming and accessibility
تمتلك العديد من المؤسسات أسلوبها الخاص الذي تطلب من المطورين اتباعه عند كتابة التعليمات البرمجية. غالبًا ما يتضمن جزء من هذا الأسلوب قواعد لتسمية العناصر. الغرض من هذه القواعد هو عادةً تسهيل صيانة التعليمات البرمجية.
التوصيات التالية شائعة و تتعلق باتفاقيات التسمية المتغيرات والدوال بناءً على إمكانية الوصول إلى عناصر الـ class ومع ذلك، لا تفرض C# هذه القواعد. لننظر المثال التالي :
...........
class Circle {
private int radius;
public double Area(){
return Math.PI * radius * radius;
}
}
...........
- العناصر العامة (public) يجب أن تبدأ بحرف كبير. على سبيل المثال، تبدأ Area بحرف A (وليس a) لأنها عامة. يُعرف هذا النظام باسم مخطط تسمية PascalCase (لأنه تم استخدامه لأول مرة في لغة Pascal).
- العناصر غير العامة يجب أن تبدأ (التي تتضمن متغيرات محلية) بحرف صغير. على سبيل المثال، يبدأ radius بحرف r (وليس R) لأنه خاص (private). يُعرف هذا النظام باسم مخطط تسمية camelCase.
- تستخدم بعض المؤسسات نظام camelCase فقط للـ methods وتعتمد الاتفاقية لتسمية المتغيرات الخاصة التي تبدأ بـشرطة سفلية ، مثل _radius.
- لا تقم بلإعلان عن عنصرين من نفس الـ class على أنها عامة وتختلف اسمهما فقط في حالة الأحرف. إذا فعلت ذلك، فقد تواجة برامجك مشاكل عند العمل مع لغات أخرى لا تراعي حالة الأحرف (مثل Microsoft Visual Basic).
تبرز أهمية التسمية الصحيحة وإدارة إمكانية الوصول للعناصر في كتابة كود نظيف ومنظم يسهل فهمه وصيانته.
تطبيق التغليف Encapsulation في C#
عرفنا التغليف (Encapsulation) بأنه "عملية تغليف عنصر أو أكثر داخل حزمة مادية أو منطقية". بحيث يمنع الوصول إلى تفاصيل التنفيذ. ويتم تطبيق التغليف باستخدام محددات الوصول (Access specifiers) حيث يحدد محدد الوصول نطاق ورؤية عنصر الـ Class ويتم التعامل مع هذه العناصر بحسب نوع المحدد المستخدم فمثلاً :
للوصول الى المتغيرات التي تم تحدد الوصول لها بـ Internal من خارج التجمع (Assembly ) الذي تم تعريفها فيه، علينا استخدام هذا التجمع كمرجع (اضفتها في المشروع كا Referance) في للتجمع الحالي.
وللوصول للمتغيرات الـ class المحمية (protected members) نستخدم الوراثة (Inheritance) حيث تسمح الوارثة بالوصول للـ protected members في المثال السابق اتضح لنا كيف استطعنا الوصول الى المتغيرات المحمية protected بواسطة الوراثة .
للوصول لعناصر الخاصة (private members) نستخدم عناصر الـ class العامة مثل :
الدوال Methods:
يمكن للدوال العامة (public methods) المعرفة داخل الـ class الوصول الى جميع عناصر الـ Class الخاصة والمحمية، بالتاي يمكن معالجة البيانات هذه العناصر بواسطتها من خارج الـ Class .
المُنشئ الـ Constructors:
في برمجة الكائنات، يُستخدم المُنشئ (Constructor) لإنشاء وتهيئة كائن جديد من الـ Class. يُعتبر الـ Constructor دالة خاصة داخل الـ class تُستدعى تلقائيًا عند إنشاء كائن (object) جديد من الـ Class. ويُستخدم عادةً لتهيئة البيانات الأولية للكائن وإعطائه قيم ابتدائية.
الخصائص Properties:
في برمجة الكائنات في لغات البرمجة مثل Java وC# ، يُستخدم مصطلح "الـ Property" للإشارة إلى خاصية تُستخدم للوصول إلى قيمة خاصة (متغير) في كائن برمجي. تُستخدم الـ Properties لتوفير طريقة مرنة ومنظمة للوصول إلى قيمة متغير معين مع تطبيق قواعد أو محددات محددة.
فالخصائص (Properties) هي من عناصر الـ class تشبه المتغيرات، ولكنها تتيح تنفيذ التعليمات البرمجية عند القراءة أو الكتابة للقيمة، عند استخدام الخصائص، يمكننا تنفيذ منطق إضافي عند تعيين أو قراءة القيم، مثل التحقق من صحة البيانات أو تقييد الوصول.مما يعزز من أمان البيانات ويمنع الأخطاء. ويتم هذا بواسطة محددين رئيسيين هما :
- Get Accessor : يُستخدم لاسترجاع قيمة الحقل.
- Set Accessor : يُستخدم لتعيين قيمة للحقل.
ويمكن جعل المتغيرات للقراءة فقط، عندها علينا فقط استخدام Get Accessor في الكود.أما إذا أردنا جعل المتغيرات للكتابة ، فيجب علينا استخدام Set Accessor.
(المزيد عن الـ Constructor و الـ Properties الـ في مقالات مقدمة في Class و Object ومقال نظرة أعمق في الـ Class و Object كما يمكن التعرف أكثر على الدوال Methods في مقالات الدوال Methods في C# و التعامل مع الدوال Methods في C# ).
المثال التالي يوضح طريقة تطبيق التغليف Encapsulation ، بدء من تحدد الوصول للعناصر عبر استخدام محدد الوصول الخاص(private)، الى استخدام عناصر الـ Class العامة للوصول الى العناصر العامة :
...........
public class Student{
//private class attributes
private string name;
private double degree;
//constructor
public Student (string n, double d){
name = n;
degree = d;
}
//Properties
public string Name {get; set;}
public double Degree {
get { return degree;}
set { degree = value >= 0 && value <= 100 ? value : 0;}
}
//A public method
public void Grade (){
Console.WriteLine("The student {0} is: {1}", name, degree < 60 ? "Faill" : "Pass");
}
}
public class Program
{
public static void Main(string[] args)
{
//Access to private members by constructor
Student st = new Student("Maha", 85);
//Access to private members via method
st.Grade();
//Access to private members via properties
st.Degree = 59;
st.Grade();
}
}
...........
إذن في C# يمكن تحقيق التغليف (Encapsulation ) من خلال إعلان بعض متغيرات والدوال في الـ Class على أنها داخلية (Internal) أو خاصة (private) أو محمية (protected) بحسب الاستخدام واستخدام الطريقة المناسبة للوصول الى هذه العناصر.
الأمثلة عى التغليف (Encapsulation) في C# متوفر هنا.
إن التغليف (Encapsulation) مبدأً أساسيًا في البرمجة الكائنية التوجه (OOP)، و يهدف إلى حماية البيانات وتقليل التعقيد من خلال إخفاء التفاصيل الداخلية للكائنات وعرض واجهة خارجية واضحة وسهلة الاستخدام. .بتطبيق التغليف (Encapsulation) بشكل صحيح في C# ، يمكننا تقليل الاعتماد المتبادل بين مكونات النظام، مما يجعل الكود أكثر قابلية للتطوير والتحديث مع مرور الوقت.