Sultan_Althibity

أنماط التصميم البرمجية

17 ردود في هذا الموضوع

بسم الله الرحمن الرحيم

مقدمـة إلى أنماط التصميم البرمجية

Introduction to Design Patterns

نظرة إلى السلسلة:

لا تفترض منك هذه الوحدة أي معلومات مسبقـة عـن البرمجـة ، إذا لم تكن تعلم أي شيء أو أنك أول مرة تقرأ في البرمجـة فهذه الوحدة أعدت لهذا الغرض.

تذكر أن هذه الوحدة تعتبر الأساس الذي سيقوم عليه هذا الكتاب لذلك حاول الرجوع إلى هذه الوحدة دائماً.

الوقت الخاص بإنهاء هذه السلسلة:

تستطيع إنهاء هذه الوحدة في أي وقت تريده ... بل إنه بإمكانك ترك هذه الوحدة والانتقال إلى الوحدة القادمـة ... ولكن عليك الرجـوع إلى هذه الوحدة دائماً

المواضيع التي تتناولها هذه السلسلة

• مدخل إلى الموضوع

• الأنماط البنائية

• الأنماط التركيبية

• الأنماط السلوكية

=============================

مدخل إلى الموضوع

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

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

أولاً: أن النمط التصميمي الواحـد أفضل من الأنماط التي تخترعها أنه معترف من قبل أفضل المبرمجين في العالم.

ثانياً: نتيجـة للسبب الأول ، فسيصبح بإمكان الجميع القدرة على قراءة الكود الذي تقوم بكتابته لأن الأنماط التصميمية التي تستخدمها معروفة لدى الجميع مما يزيد من إمكانية إعادة استخدام الكـود الذي تقوم بكتابته ويزيد بالتالي من الإنتاجية وهذه من أهـم أهداف البرمجة الكائنية...

هـناك فوائد كثيرة وأخرى .. أضف إلى ذلك أن النمط التصميمي الذي ستسخدمـه هـو أقل الحلول المتوفرة إنهاكاً للمعالج وحجزاً للذاكرة.

أقسام الأنماط التصميمية

Types Of Design Patterns

تسهيلاً عليك فقد جرى تقسيم الأنماط التصميمية (والتي يبلغ عـددها إلى الآن 23 نمطاً تصميمياً) وهـي كالتالي:

النـوع الأول: الأنماط الإبداعية (البنائية) Creational Patterns : هذه الأنماط تركز على كيفية بناء الكائنات التي تريدها وأيضاً على بناء كائن واحد فقط من الصنف .. أي أنه لن يكون بإمكانك استخدام عـدا كائن واحد من الصنف واحد .. وسنرى فائدة ذلك فيما بعـد.

النـوع الثاني: الأنماط التركيبية Structural Patterns :

هذه الأنماط تساعدك على تجميع عـدة كائنـات في هيكل أو تركيب واحد فقط .. وسنرى فوائد هذا فيما بعـد.

النـوع الثالث: الأنماط السلوكية Behavioral Patterns :

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

النـوع الأول: الأنماط البنائية (الإبداعية) :

First Type: Creational Patterns:

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

مثال : مصنع بسيط

Example: simple factory

ملاحظـة:

هذا ليس نمط تصميمي بل مقدمـة إلى أنماط الـمصنعات أو الـ Factories أتـمنى من أي شخص أن يكملها نيابة عـني.

ليس هذا المثال الذي سنتعرض له نمطاً معتمداً من الأنماط التصميمية بل هـو في الحقيقـة أسلوب قد تكون أنت قد استخدمتـه أثناء دراستك لأساسيات البرمجـة الشيئيـة.

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

سيكون لدينا صنف اسمـه Num اختصار لـ Number أو رقـم .. سنشتق من هذا الصنف صنفان اثنـان هـما Fraction أي الأعداد الكسرية و real الأعداد الصحيحـة التي ليس فيها كسور ..... أولاً أنظر إلى الصنف Num وهـو الصنف الأب وهـو ADT أي مجرد:

1.	class Num
2. {
3. protected:
4.  double up;
5.  double down;
6.  double num;
7. public:
8.  Num();
9.  Num(string);
10.  double GetUp();
11.  double GetDown();
12.  double GetNum();
13.  void SetUp(double);
14.  void SetDown(double);
15.  void SetNum(double);
16.  virtual void makeIt(string)=0;
17.  int changeToNum(char m);
18. };

عليك الانتباه هـنا إلى نقطـة مهـمـة للغاية وهـي الواجهـة لهذا الصنف ... عليك دائماً كتابة واجهـة قوية وليس واجهـة مهلهلة كما هـو حال أغلب المبتدئين ، كما ترى فهـناك دالتي بناء اثنتان في السطرين 8 و 9 ، الثانية تطلب منك متغير من النوع string كبارامتر لها ... أما في الأسطر من 10 إلى 15 فهـي تحـوي الدوال التي ستعالج المتغيرات الأعضاء الخاصـة ... في السطر 16 نجـد الدالة makeIt وهي تتطلب متغير string لتفعليها ... هذه الدالة دالة ظاهرية (افتراضية)خالصـة ووظيفتها تقوم على أنه يجب عليها أن تقوم بإعطاء القيم الصحيحة للمتغيرات الأعضاء ... وكما ترى فيجب عليك هـنا دائماً أن تقوم بتغيير طريقة المعالجة في هذه الدالة وهذا ما سنفعله هـنا ، أيضاً يجب عليك التفكير الآن في طريقة تركيب هذا الصنف ، يحوي هذا الصنف ثلاثة أعداد ، هـي up وهـو بسط العـدد و down وهـو مقام العـدد وأيضاً هـنا المتغير num وهـو العـدد العشري (حاصل قسمة البسط على المقام) .

ضع نفسك الآن مكان المستخدم .. ستجـد أنه قد يقوم بكتابة العـدد هـكذا 120 وقد يقوم بكتابته هـكذا 12/9 ، ويجب عليك أنت التعامل مع جميع هذه الاحتمالات ، إذا فكرت جيداً في حل هذه المشكلة فستجـد أنه لن يكون بإمكانك إنشاء أي صنف يحوي ما يقوم المستخدم بإدخاله حتى تحـدد ماهية العدد المدخل هل هـو صحيح أم كسري ، سنقوم بحل هذه المشكلة في هذا المثال ... الآن أنظر إلى تعريفات الدوال الأعضاء للصنف num :

1.	Num::Num():up(0),down(0),num(up/down){}
2. Num::Num(string a):up(0),down(0),num(up/down){}
3. /******/
4. double Num::GetUp(){return up;}
5. double Num::GetDown(){return down;}
6. double Num::GetNum(){return num;}
7. /*****/
8. void Num::SetUp(double a) {up=a;}
9. void Num::SetDown(double a) {down=a;}
10. void Num::SetNum(double a) {num=a;}
11. /*****/
12. int Num::changeToNum(char m)
13.  {
14.  switch(m){
15.  case '1': return 1;
16.  case '2': return 2;
17.  case '3': return 3;
18.  case '4': return 4;
19.  case '5': return 5;
20.  case '6': return 6;
21.  case '7': return 7;
22.  case '8': return 8;
23.  case '9': return 9;
24.  case '0': return 0;
25.  default:
26.  return 1;
27.  }
28.  }
29.

هـناك دالة مهـمـة للغاية وهـي changToNum ووظيفتها هـي تحويل الحرف الممرر إليها إلى عـدد ... سترى كيفية استخدامها في الأسطر القادمـة.

عليك الآن أن تقوم بكتابة صنف يقوم بالتعامل مع الحالة الأولى من حالات الإدخال وهـي إذا أدخل المستخدم العـدد بهذه الطريقة 124 بدون أي علامـة كسرية ، أنظر إلى هذا الصنف:

1.	class Real:public Num
2. {
3. public:
4.  Real();
5.  Real(string);
6.  void makeIt(string);
7. };

هذا الصنف Real مشتق من الصنف Num ، وحتى لا يكون هذا الصنف مجرداً كما هـو حال الصنف الأب فعليك تجاوز الدالة makeIt .... لا يحوي هذا الصنف سوى ثلاثة دوال أعضاء بالإضافة إلى دوال الصنف الأب وهـي دالتي بناء والدالة المتجاوزة makeIt ، أنظر إلى تعريف هذه الدوال:

1.	Real::Real(){}
2. Real::Real(string a){makeIt(a);}
3. /*******/
4. void Real::makeIt(string a)
5. {
6.  int i=a.size();
7.  int j=0,k=0;
8.  for(j=0,k=i-1;j<i;j++,k--)
9.  up+=changeToNum(a[j])*pow(10,k);
10.  down=1;
11.  num=up/down;
12. }

أنظر إلى دالة البناء التي تتطلب وجود متغير string كبارامتر فيها ، تجـد أن الأمر الوحيد المستخدم فيها هـو إستدعاء الدالة makeIt وتمرير المتغير string إليها ... تمعـن جيداً في الدالة makeIt تجـد أنها تقوم بتحـويل المدخلات من النوع string إلى أعـداد وفق خوارزمية قد لا تكون الأفضل إلا أنها فعالة في هذا الأمر .... تمعـن النظر جيداً تجـد أن هذه الدالة تقوم بإسناد القيمة 1 إلى المقام أو المتغير down ، وبالتأكيد تعرف لماذا تقوم هذه الدالة بهذا العـمل لأن هذا الأمر من أساسيات الرياضيات.

قد تتساءل كثيراً عـن أهمية الموضوع أو قد تكون غير فاهـم الموضوع أصلاً أو أن الصورة غير مدركـة لك ... إذا كنت كذلك فعليك المضي قدماً في هذه الصفحات فستكمل الصورة النهائيـة حينما نتعرض إلى simple Factory في نهاية هذا المثال.

الآن عليك التفكير بالأمر الآخر ألا وهـو ماذا لو قام المستخدم بإدخال العـدد بصورة كسرية أي هـكذا 12/12 ، عليك الآن بكتابة صنف يقوم بحل هذه المشكلة أو المعضلة ، وقبل أن أريك هذا الصنف ، عليك أن تتسائل ما هـو الفرق بينه وبين الصنف Real ، الفرق بين هذان الصنفان يكمن في المعالجة وليس في الواجهـة وبالتحديد في طريقة عـمل الدالة makeIt ، أنظر الآن إلى الصنف Fraction :

1.	class Fraction:public Num
2. {
3. public:
4.  Fraction();
5.  Fraction(string);
6.  void makeIt(string a);
7. };

كما ترى فإن هذا الصنف مشتق من الصنف Num ، لاحظ أنه لا يوجد أي اختلاف بين هذا الصنف وبين أخيه الصنف Real في الواجهـة؛ أنظر الآن إلى تعريفات الدوال الأعضاء لهذا الصنف:

1.	Fraction::Fraction(){}
2. Fraction::Fraction(string a){makeIt(a);}
3. /******/
4. void Fraction::makeIt(string a)
5. {
6.  int i=a.size();
7.  int j=0,k=0,kk=i-1;
8.  for(j=0,k=a.find('/')-1;j<i;j++,k--,kk--){
9.  if(a[j]=='/')break;
10.  up+=changeToNum(a[j])*pow(10,k);
11.  }
12.  j=j+1;k=a.find('/')-j;
13.
14.  for(j=j,kk=kk-1;j<i;j++,kk--)
15.  down+=changeToNum(a[j])*pow(10,kk);
16.  num=up/down;
17. }

طريقة عـمل دالتي البناء في هذا الصنف هي نفسها في الصنف Real ، لن أشرح هـنا طريقة عـمل الدالة makeIt ، إلا أنها تختلف هـنا عـن طريقة عـمل الدالة makeIt في الصنف Real ، فهي هـنا تستطيع التعامل مع المدخلات التي تكتب بهذه الطريقة 12/15 ، وتستطيع تحديد البسط وتحـديد المقام وتحـديد العـدد العشري الذي يساوي العـدد الكسري .... ليس مطلوباً منك فهـم عـمل الدالة makeIt بل فهـم الفكرة الأساسية من هذا المثال...

الآن علينا اعتماد نمط تصميمي بسيط .. وهـو صنع صنف قادر على تحـديد أي الصنفين سيتم استخدامه هل هـو Real أم Fraction ... بمعـنى أصح تحـديد هل العـدد المدخل عدد كسري أم عـدد صحيح وعلى هذا الأساس فسيتم صنع أحد الصنفين..

أنظر الآن إلى صنف المُـصنع وهو محور هذا المثال ... فحاول أن تفهـمه حتى تفهـم هذا الجزء من الكتاب برمتـه :

1.	class Factory
2. {
3. public:
4.  int IsItNum(char m);
5.  Num* GetFactory(string a);
6. };
7. /********************/
8. int Factory::IsItNum(char m)
9.  {
10.  switch(m){
11.  case '1': return 1;
12.  case '2': return 1;
13.  case '3': return 1;
14.  case '4': return 1;
15.  case '5': return 1;
16.  case '6': return 1;
17.  case '7': return 1;
18.  case '8': return 1;
19.  case '9': return 1;
20.  case '0': return 1;
21.  case '/':return 2;
22.  default:
23.  return 0;
24.  }
25.  }
26. /***********/
27. Num* Factory::GetFactory(string a)
28. {
29.  int j=0;
30.  for(int i=0;i<a.size();i++)
31.  j+=IsItNum(a[i]);
32.  if (j==a.size()) return new Real(a);
33.  else if(j>a.size()) return new Fraction(a);
34.  else return 0;
35. }

لاحظ أن هذا الصنف مشتق عـن عائلة الأصناف Num .

هـناك دالتين فقط لهذا الصنف ، الأولى هي: IsItNum وهـي تقوم بالتأكد إن كان المدخل عـدداً صحيحاً أم لأ ... والثانية وهي الأهـم GetFactory .

سأقوم الآن بشرح الدالة GetFactory ، تقوم هذه الدالة بالتأكد أولاً إن كان المدخل هل هـو عدد أم لأ وإن كان فهل يحوي العلامة / .... أي أنها تتعامل مع ثلاث احتمالات، الأولى إن كان المدخل عـدداً صحيحاً فسيتم حجز ذاكرة من الصنف Real وستقوم الدالة بإعادة هذه الذاكرة ، الحالة الثانية إذا احتوى المدخل عدداً كسرياً أو احتوى العلامة / ، فسيتم حجز ذاكرة من النوع Fraction وإعادة هذه الذاكرة إلى من قام باستدعاء هذه الدالة أصلاً ، في الحالة الثالثة أي إن كان ليس المدخل عدداً أصلاً فستعيد الدالة العـدد 0 مما يعـني انهيار البرنامج كما سترى لاحقاً. أود أن أشير هـنا إلى انه ليس مطلوباً مني أن أشرح الخوارزميات التي استخدمتها في تحـويل المتغيرات من النوع string إلى متغيرات من النوع double وحتى أنت فليس مطلوباً منك فهـمها بل الأهـم أن تفهـم كيفية عـمل البرنامج بكامله.

إذا لم تفهـم الكلام السابق أو فهـمت قليلاً منه فستفهـم إن شاء الله حينما نقوم بتنظيم الكائنـات السابقـة هاهـنا ، أنظر إلى الدالة main :

1.	void main()
2. {
3.  Factory b;
4.  string m;
5.  cout << "please Enter string\n";
6.  cin >> m;
7.  Num* a=b.GetFactory(m);
8.  cout << "Up::::\t" << a->GetUp() << endl;
9.  cout << "TheDown::::\t" << a->GetDown() << endl;
10. cout << "Num::::\t" << a->GetNum() << endl;
11. }

في السطر الرابع تم الإعلان عـن صنف من النوع Factory .

في السطر الرابع تم إنشاء متغير من النوع string ، سيقوم المستخدم بإدخال محتويات هذا المتغير في السطر السادس.

في السطر السابع تم إنشاء مؤشر إلى الصنف المجرد Num ، أما بالنسبة للذاكرة المحجوزة إليه فسيتم تفويض هذه المهـمـة إلى الكائن Factory وبالتحديد إلى دالته العضو GetFactory .. والتي ستمرر إليها المتغير string الذي أدخله المستخدم في السطر 6.

يتم حجز الذاكرة للمؤشر a حسبما تحدده الدالة GetFactory ، وحتى تفهـم أكثر حاول تتبع سير البرنامج وكيفية حجز الذاكرة له.

في الأسطر 8 و 9 و 10 يتم طباعـة البسط والمقام والعـدد العشري حتى تتأكد من صحـة عـمل الصنف.

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

أسئلـة:

السؤال الأول: إذا قرر المستخدم في المثال السابق إدخال هذا المدخل Ahmad فسينهار البرنامج ويتوقف عـن العـمل ، هل تعرف ما هـو السبب.

مشاريع:

مشروع(1) :

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

مشروع(2):

قم بتطوير المثال الحالي حتى يستطيع التعامل مع الإدخالات على هذه الشاكلة  Fadi9 ، أو حتى يستطيع التعامل مع الأعداد النسبية أي الإدخالات على هذه الشاكلة 15.6 .

============================

الدرس الثاني سيكون عـن الـ singleton وسأضيفـه في وقت آخر ، لأني حالياً مشغـول

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخوان معليش لم أراجع الموضوع قبلاً

كان هذا الموضوع جزءاً من كتاب اعتزمت تأليفـه (طبعاً اتفركش الموضوع) ، لذلك تجد فيه جمل غريبـة مثل (هذا الجزء من الكتاب ، في الصفحات القادمـة ، ستتعرف في الوحدة القادمـة على النمط ..... وهلم جرا)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

والله شكلك .. ناوي تفتح "مكتبة امازون العرب " هنا :)

ماشاء الله .

ما قرأت الموضوع بصراحة .

لكن اعجبني العنوان :D .

بالتوفيق ... وكل دروسك حافظها عندي .. ننتظر الفراغ .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

موضع رائع و فريد و مهم !

ارجوا التكملة للافادة !

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

الشمري:

كما قلت سابقاً لأخي Asm4all سأضع إحدى السلاسل التعليمية التي كتبتها وهي سلسلة الإكسير تتكون من 100 كتاب كل كتاب 15 ألف صفحـة

لذلك لن أفتح أمازون العرب هـنا بل مكتبة الكونجرس....

أحمد صالح:

شكراً ...... ولكن إكمال هذه السلسلة مسؤولية الأخ hasan وليس أنا ...

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ليش مسؤوليتي :P انا لا اريد سوى البدء, و التكملة لازم تكون بتعاون من البقية, لاني اصلا لا اعرف الكثير من الـ design patterns

حيث اعرف فقط بضعها, و هو ما احتجت ان استخدمه في بعض الأحيان.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

مشاء الله ننتظر الكتاب بفارغ الصبر

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

موضوع مميز بالفعل

مشاء الله ننتظر الكتاب بفارغ الصبر

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخ الممتاز: مين قال أصلاً إن في كتاب جـديد ما قريت الجملة (اتفركش الموضوع)

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ما شاء الله موضوع رائع ونادر في المنتديات العربية.

أرجو ان تستمر فيه وجزاك الله خيراً.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
ما شاء الله موضوع رائع ونادر في المنتديات العربية.

أرجو ان تستمر فيه وجزاك الله خيراً.

ليتك تطلب من القائمين على هذا المنتدى الاستمرار معـي حتى نكمل الـ 23 نمط ، ولكن الكل مشغـول ولا يستطيعون العـمل فجميعهـم يعـملون على كتابة نظام تشغيل عربي :D

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بصراحة موضوع حلو بس ناقص التكملة

ويعطيك العافية يا soso

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

فعلا مواضيعك رائعة .. وتتطور مهاراتنا

الله يعطيك العافية

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

وكذلك نحن ننتظر ال 15 ألف صفحه

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

جزاك الله خيرا يا استاذي

ان هذه المحاضرات تعتبر مراجع اساسية لمن يريد التعلم

ونرجو منك الاستمرار

وجزاك الله عنا خيرا إن شاء الله

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكــم ورحمـة الله وبركاتــه ،،

up

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اكمل بارك الله فيك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .