1. بسم الله الرحمن الرحيم
    استكمالا للحديث عن أكثر  Design pattern استخداما في بيئة Android, وبعد أن كنا قد بدأنا في الحديث عن أنواع Structural pattern, اليوم سيكون حديثنا عن Facade Pattern,
    فنبدأ بالسؤال المعتاد ماهو Facade Pattern؟
    لنبدأ بتعريفه:
    إذا فهو Pattern يستخدم لتبسيط interfacees لأجزاء النظام, الصورة التالية ستكون شارحة جدا لوظيفة واّلية عمل هذا Pattern.

     
    جميل جدا, أعتقد أن المفهوم بات متضحاً جدا, فلندلف إلى تطبيقه بالمثال التالي.
    لتعلم أنه Pattern بسيط وواضح جدا, فلو فرضنا أن لدينا interface اسمه line, ولديه three claases قاموا بعمل implementation لهذا interface.
    وهم straight و curve و snaky.
    public interface Line { void draw(); } class straight
    public class Straight implements Line { @Override public void draw() { System.out.println("straight"); } } class curve
    public class Curve implements Line { @Override public void draw() { System.out.println("curve"); } } snaky class
    public class Snaky implements Line { @Override public void draw() { System.out.println("snaky"); } } أصبح لدينا مجموعة من classes, لو كان النظام الذي سنقوم ببنائه أكبر من ذلك سيكون من الصعب جدا تذكر هذه classes, لذلك سنقوم ببناء Facade class يحل هذه المشكلة ويبسط التعامل معها.
    public class LineMaker { private Line straight; private Line curve; private Line snaky; public LineMaker() { straight = new Straight(); curve = new Curve(); snaky = new Snaky(); } public void drawStraight(){ straight.draw(); } public void drawCurve(){ curve.draw(); } public void drawSnaky(){ snaky.draw(); } } أصبح التعامل هذه المجموعة من classes غاية في البساطة ويمكن التعامل معها في main كالتالي
    public static void main(String[] args) { LineMaker lineMaker = new LineMaker(); lineMaker.drawStraight(); lineMaker.drawCurve(); lineMaker.drawSnaky(); } } بينما لو لم نقم باستخدام facade class سيكون شكل main كالتالي:
    public static void main(String[] args) { private Line straight = new Straight(); private Line curve = new Curve(); private Line snaky = new Snaky(); straight.drawStraight(); curve.drawCurve(); snaky.drawSnaky(); } } طبعا هذا المثال يبسط المشكلة لكن في حال كان حجم النظام كبير ستواجه مشكلة في التعامل مع هذه الكمية من classes.
     
    وصلى الله وسلم وبارك على نبينا محمد وعلى اّله وصحبه أجمعين.
    المراجع:
    - sourcemaking.com/design_patterns
    - java design pattern , rohit joshi
    - head first design pattern
    - tutorialspoint.com
    مستوى المقال: متوسط
  2. بسم الله الرحمن الرحيم
    كما ما بدأناه بالحديث عن أكثر  Design pattern استخداما في بيئة Android واليوم سنتحدث عن أحد أنواع Structural pattern , ألا وهو  Adapter  pattern ,قبل أن نبدأ, وبما أننا في أول pattern في هذه السلسة من نوع Structural فماذا نعني ب Structural pattern ؟
    Structural pattern : هو تصميم للكود بطرق تسهل فهم العلاقة بين classes و objects والربط بينها.
    جميل جدا, إذا ماهو Adapter  pattern ؟ من كتاب GoF
    ولتبسيط الأمور, هذا pattern يستخدم لربط بين two classes لا يمكن الربط بينهما لعدم التوافق.
    كثيراً ما تكون التعريفات غير مفيدة, لنبدأ بكتابة بعض الأكواد تشرح هذا pattern.
    بداية دعنا نعرف المشكلة, لو فرضنا أن لديك class قديم قمت ببنائه, ومن ثم اردت الربط بينه وبين class جديد فلن تقوم بإعادة كتابته, وإنما تقوم ببناء interface يقوم بالربط بينهم كما سنقوم بذالك في المثال التالي.
    لنفترض أن لدينا نظام إدارة فصول إلكتروني, وأردنا ربطه مع نظام حضور وانصراف جديد,فكان شكل interface الخاص بنا كالتالي.
    public interface Xattendance { public String getStuedntName(); public long getStuedntId() ; public String getTime(); public void setStuedntName(String stuedntName); public void setStuedntId(long stuedntId); public void setTime(String time); } ثم قمنا بعمل implementation له داخل هذا class
    public class XattendanceImpl implements Xattendance{ String stuedntName; long stuedntId; String time; public String getStuedntName() { return stuedntName; } public void setStuedntName(String stuedntName) { this.stuedntName = stuedntName; } public long getStuedntId() { return stuedntId; } public void setStuedntId(long stuedntId) { this.stuedntId = stuedntId; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } } ولكن وقع الإشكال عندما أردنا ربط هذا class الخاص بنا نظام لتحضير والذي كان شكل interface الخاص به كالتالي:
    public interface XnewAttendaceSystem { public String getName() ; public void setName(String name); public String getId(); public void setId(String id); public String getMinute(); public void setMinute(String minute); public String getHour(); public void setHour(String hour); } وclass الذي سيقوم بعمل implementation له بهذا الشكل
    public class XnewAttendaceSystemImpl implements XnewAttendaceSystem{ String name; String id; String minute; String hour; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMinute() { return minute; } public void setMinute(String minute) { this.minute = minute; } public String getHour() { return hour; } public void setHour(String hour) { this.hour = hour; } } فكما ترى لدينا two classes من نوعين مختلفين, فيلزمنا إنشاء class لنسميه المحول, حيث يمكننا عن طريقه ربط two classes مع بعضهما.
    وهو يقوم بعمل implementation ل XnewAttendaceSystem وفي constructor يستقبل object من نوع Xattendance ويقوم بالتحويل
    public class fromOldClassToNewSystem implements XnewAttendaceSystem{ String name; String id; String minute; String hour; final Xattendance xattendance; public fromOldClassToNewSystem(Xattendance xattendance) { this.xattendance = xattendance; adapt(); } @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name = name; } @Override public String getId() { return this.id; } @Override public void setId(String id) { this.id = id; } @Override public String getMinute() { return this.minute; } @Override public void setMinute(String minute) { this.minute = minute; } @Override public String getHour() { return this.hour; } @Override public void setHour(String hour) { this.hour = hour; } public void adapt(){ setName(xattendance.getStuedntName()); setId(xattendance.getStuedntName()); setHour(xattendance.getTime().substring(0, 1)); setMinute(xattendance.getTime().substring(3, 4)); } } فكما ترى تم تحويل object من نوع إلى أخر, فدعنا نلقي نظرة عن طريقة استخدم هذا pattern.
    سنقوم بإنشاء بيانات نقوم نحن بكتابتها- من الممكن أن تكون البيانات قادمة من API أو من Database, ولكن هنا لتبسيط المقالة ستكون مكتوبة يدوياً- ومن ثم نحولها من النظام الخاص فينا, إلى النظام الذي قمنا بالربط معه.
    public static void main(String[] args) { Xattendance x = new XattendanceImpl(); x.setStuedntId(10110101); x.setStuedntName("ameen"); x.setTime("12:30"); XnewAttendaceSystem obj = new fromOldClassToNewSystem(x); //test System.out.println(obj.getName()); System.out.println(obj.getHour()); } وبهذا نلاحظ أنه تم إرسال البيانات من نظام إلى أخر باستخدام adapter pattern.
    ختاما لتعلم أن لدينا نوعان من adapter الأول هو object adapter وهو الذي تعاملنا معه في المثال اّنف الذكر, والذي استخدمنا في بنائه مبدأ composition, والأخر هو adapter class والذي يعتمد في بنائه على multiple inheritance
    وهو غير مدعوم في java تجنباً لمشكلة DDD  (deadly diamond of death), ولكن يمكنك تطبيقها باستخدام لغات تدعم multiple inheritance كلغة C++ ويكون شكل Digram كالتالي:

     
    إلى هنا, هذا ما كان في الجعبة ووفق الله الجميع وصلى الله وسلم وبارك
     
    المراجع:
    - sourcemaking.com/design_patterns
    - java design pattern , rohit joshi
    - head first design pattern
     
    مستوى المقال: متوسط
  3. القائمة الثنائية المترابطة (doubly linked list ):
    لتتمكن من الفهم الجيد لهذه القوائم لابد أن يكون لديك فهم مسبق بالقائمة الأحادية المترابطة (single linked list ).
    هي عبارة عن سلسة أشبه ماتكون بعربات قطار  ذات اتجاهين مترابطة مع بعضها البعض.
    تتكون هذه القائمة  من :

    1.العقدة (node):
    هي المكون الأهم في هذه القائمة إذ أنها تحتوي على البيانات المخزنة في القائمة.
    و كل عقدة يجب أن يكون لها مؤشرين (reference) أحدهما يشير إلى العقدة السابقة في القائمة و هو السابق (prev)  
    و الآخر يشير إلى العقدة التالية في القائمة و هو التالي (next)  لنتمكن من خلالهما بربط كافة العقد في القائمة مع بعضها البعض   .
    فبالتالي يمكننا أن نقول أن العقدة الواحدة يجب أن يكون لها  :
    2.المؤشر التالي ( next reference):
    هو سهم يخرج من الطرف الخلفي للعقدة ليُشير إلى العقدة التالية ((كما هو موضح بالرسمة)).
    3.المؤشر السابق (previous reference):
    هو سهم يخرج من الطرف الأمامي للعقدة ليُشير إلى العقدة السابقة ((كما هو موضح بالرسمة )).

    4. الرأس (header):
    و هو بداية القائمة و يكون بأولها و لا يحتوي على أي بيانات و يكون له مؤشر التالي  next  فقط , ليشير من خلاله على العقدة التالية,
    بالتالي هو نوع خاص مختلف عن بقية العقد في القائمة إذ أنه لا يحفظ أية بيانات و له مؤشر واحد فقط .
    5.الذيل (trailer):
    وهو كما يتضح من اسمه إذ أنه سيكون بآخر القائمة و لا يحوي أي بيانات إنما يحتوي على مؤشر واحد فقط ليشير إلى العقدة السابقة في القائمة و هو السابق prev .
    , و أيضاً حاله كحال الرأس إذ أنه لا يحفظ أية بيانات وله مؤشر واحد فقط أيضاً .
    فلو أردنا تطبيق كل مافي السابق و إجراء بعض العمليات على القائمة باستخدام الجافا :
    1.إنشاء القائمة   :class Node 
    فسنقوم بداية بإنشاء العناصر الأساسية للقائمة 
    package dllist; public class Node { protected int storeD; protected Node next; protected Node prev; public Node(int SD){//constructor storeD = SD; next = null; prev =null; } public int getStoreD(){return storeD;}//to return the data public Node getNext(){return next ;} public Node getPrev(){return prev;} public void setStoreD(int SD){ storeD=SD;} public void setNext(Node n){next=n;} public void setPrev(Node p){prev=p;} } public class Node { protected int storeD; protected Node next; protected Node prev; هنا قمنا بإنشاء كلاس يحوي ثلاث متغيرات من نوع protected و هي المكونات الأساسية للقائمة  
    و هي عبارة عن:
    1. storeD هو متغير يحمل قيمة البيانات المحفوظة داخل القائمة و بما أنني أود حفظ أرقام فبالتالي و ضعتها من نوع int .
    2. next  و هو المتغير الذي سيُعبر عن المؤشر التالي و هو من نوع Node  بالتالي هو object من الكلاس Node .
    3. prev هو متغير الذي سيُعبر عن المؤشر السابق و هو  من نوع Node  بالتالي هو object من الكلاس Node أيضاً .
    public Node(int SD){//constructor storeD = SD; next = null; prev =null; } هذا هو الكونستركتور للكلاس Node يأخذ قيمة البيانات المراد وضعها في القائمة و يعطي بعدها قيم مبدأية لكل من (next ,prev,storeD).
    storeD -->تم إسناد قيمة المتغير SD له و هي قيمة يتم إرسالها أثناء إنشاء العقد .
    next = null ,prev =null--> هذه القيمة المبدأية لهما لايحملان أي قيمة لأنه لم يتم إنشاء القائمة حتى الآن .
    public int getStoreD(){return storeD;}//to return the data public Node getNext(){return next ;} public Node getPrev(){return prev;} هذه الدوال لكل المتغيرات السابقة مهمتها فقط إرجاع قيم هذه المتغيرات . 
    public void setStoreD(int SD){ storeD=SD;} public void setNext(Node n){next=n;} public void setPrev(Node p){prev=p;} هذه الدوال لكل المتغيرات الثلاث السابقة و هي مهمتها إعطاء قيمة لتلك المتغيرات فقط و لاترجع أي قيمة .
     class DLL  و هو الذي سنضع فيه كافة الدوال التي ستعمل على القائمة :
    من إضافة و حذف و طباعة ...
    public class DLL { protected Node head; protected Node tail; protected int size; بداية سنقوم بتعريف المتغيرات و هي هنا ستكون head , tail,size :
    head+tail -->تمثلان رأس و ذيل القائمة .
    size -->يمثل عدد العقد في القائمة .
    public DLL(){ head=new Node(0); tail=new Node(0); tail.setNext(null);//no Node after tail head.setPrev(null);//no node before head tail.setPrev(head);//tail refer to head head.setNext(tail);//head refer to tail } head +tail --> يحملان قيمة المتغير storeD بصفر لأنها لايحملان أي بيانات و لايتم تخزين البيانات فيهما   .
    tail.setNext(null); head.setPrev(null); وضعنا tail.setNext -->null لأنه لاتوجد أي عقدة بعده بالتالي كما ذكرت سابقاً بأن له فقط مؤشر واحد  و هو السابق .
    وضعنا head.setPrev -->null لأنه لاتوجد أي عقدة قبله بالتالي كما ذكرت سابقاً بأن له فقط مؤشر واحد و هو التالي .
    tail.setPrev(head);//tail refer to head head.setNext(tail);//head refer to tail هنا يشير head إلى  tail و يشير tail إلى head بهذا الشكل :
    لأنه لا توجد عقد حتى الأن في القائمة فسيكون بشكل مبدأي كل منهما يشير إلى الآخر .

    public boolean isEmpty(){//to check if the list is empty or not return (head.getNext()==tail&&tail.getPrev()==head); } isEmpty:
    هي دالة تستخدم فقط للتأكد إذا ما كانت القائمة خالية أو لا .
    //1.add nodes to the list (in first ) public void addF(Node n){ Node w = head.getNext(); n.setNext(w); n.setPrev(head); head.setNext(n); w.setPrev(n); size ++ ; } إضافة عقدة addF :
    هي دالة تقوم بإضافة العقد في بداية القائمة و ذلك يعني أن أي عقدة جديدة سيتم إضافتها في أول القائمة و يتم إزاحة العقدة التي كانت بأول القائمة لتكون الثانية و هكذا .
    فهي تأخذ متغير من نوع Node أي يمرر لها  ليمثل العقدة الجديدة المراد إضافتها .
    كما في التالي :

    المطلوب إضافة العقدة v إلى القائمة باستخدام هذه الدالة سيكون شكل القائمة كالتالي :

    و سيكون هكذا شكل القائمة عند إضافة أي عقدة باستخدام هذه الدالة .
    //2.for print the list public void display (){ Node C = head.getNext(); while (C.getNext()!=null){ System.out.println(C.getStoreD()); C = C.getNext();} System.out.println(""); } display طباعة :
    هذه الدالة تم إنشاؤها لطباعة عناصر القائمة بعد إضافتها .
    //3.for delete last node in the list . public Node delLast(){ if(isEmpty()){ System.out.println("ERROR:the list is empty !"); return null;} else { Node v=tail.getPrev(); Node u=v.getPrev(); tail.setPrev(u); u.setNext(tail); v.setNext(null); v.setPrev(null); size--; return v;} } حذف آخر عقدة delLast :
    هذه الدالة لحذف آخر عقدة من القائمة 
    بدايةً قبل الحذف يتم التأكد من أن القائمة ليست خالية بوضع دالة
    ()isEmpty  في شرط فلو كانت هذه القائمة خالية سيقوم بطباعة رسالة خطأ و يقوم بإرجاع قيمة null.
    أما في حالة كانت القائمة تحتوي على عقد سيتم حذف العقدة الأخيرة من القائمة و بعدها يقوم بإرجاع العقدة المحذوفة .
    فلو كان هذا شكل القائمة قبل الحذف :

    فسيتم حذف العقدة V  سيبدو شكل القائمة هكذا : 

     و الان بعدما تعرفنا لبعض أنواع الدوال التي نستطيع استخدامها للتعديل على القوائم الثنائية المترابطة سنقوم باستخدامها .
    public class DLList { public static void main(String[] args) { DLL Linked = new DLL(); Linked.addF(new Node (10)); Linked.addF(new Node (20)); Linked.addF(new Node (30)); System.out.println(" after add :"); Linked.display(); Linked.delLast(); System.out.println(" after delete last node:"); Linked.display(); } هذا هو main class :
    بداية قمنا بعمل object  من كلاس DLL لنستطيع من خلاله استخدام الدوال السابقة .
    بالتالي بعد تطبيق كل ما في السابق هكذا سيكون شكل شاشة الإخراج :
    output screen :

    مستوى المقال: متوسط
  4. بسم الله الرحمن الرحيم 
    السلام عليكم ورحمة الله وبركاته ...،
    أولاً اعتذر جداً عن التأخر في طرح الشرح الذي كان من المفترض يكون بعد أسبوع من الموضوع السابق
    تطرقنا في شرحنا السابق الى مفهوم القائمة أحادية الرابط وشرحنا استخداماته وطريقة عملها نظريا
    شرحنا هذا سيكون عمليا من الدرجة الأولى بحيث سيكون كله حول الأكواد وتفصيلها.
     
    ١. إنشاء العقدة Node:
    كما نعلم بأن Node هي الوحدة الأساسية لبناء قائمة أحادية الرابط أو القوائم بشكل عام ونستطيع بأن نصفها بأنها الحجر الذي يستخدم لبنائها.
    لذلك يجب علينا بنائها ابتداءً.
    public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } } هذا الكلاس والذي سمي بـ SSLNode هو الذي يصف لنا كل عقدة على حدى. 
    وكل عقدة تتكون من info وهو المتغير الذي يحتوي على البيانات (المعلومة) وفي حالتنا هذه تكون البيانات من نوع int.
    وكذلك next وهو عبارة عن الرابط الذي يصل العقدة الحالية بالعقدة التي تليها ومن الطبيعي أن يكون من نوع  SLLNode.
    هذا الكلاس يحتوي على two Constructor وهما عبارة عن وظائف تستخدم لإنشاء العقد Nodes.
    الأولى تستخدم لإنشاء عقدة لكنها لا ترتبط بأخرى كما نرى فيها فقد قمنا باستدعاء الميثود الثانية وقمنا بوضع null مكان الرابط.
    أما الثانية فتنشأ عقدة ترتبط بأخرى. (قمت بتفصيلها في الموضوع السابق)
     
    ٢. إنشاء القائمة أحادية الرابط:
    الكلاس السابق يمكننا من إنشاء عقدة واحدة لكن في هذا الكلاس سنتمكن من إنشاء قائمة كاملة تتكون من عدد من العقد Nodes.
    أ. مؤشر لأول عنصر وآخر عنصر بالقائمة:
    لكي نسهل عملية التنقل داخل القائمة سنقوم بإنشاء مؤشرين أحدهما يشير إلى أول عنصر بالقائمة (head: الرأس)،
    والآخر يشير إلى آخر عنصر بالقائمة (tail: الذيل). هذين المؤشرين عبارة عن متغيريين من نوع SSNode.
    protected SLLNode head, tail; في بداية إنشاء القائمة (أو في حالة كانت القائمة خالية) فإن هذين العنصريين يكونان خاليين:
    public SLList(){ head = tail = null; } كما يتبين في هذه الـ Constructor.
     
    ب. فحص القائمة إذا كانت خالية أو لا:
    هذه الـ Method مهمة جداً ولها أستخدام في جل الـ Methods الأخرى، ومهمتها هي فحص إذا ما كانت القائمة خالية أو لا.
    وتقوم بإرجاع قيمة Boolean إما true أو false.
    public boolean isEmpty(){ return head == null; //or return tail == null; } بكل بساطة في حال كان الرأس خالي فإن القائمة خالية كذلك، بالإمكان الإستعانة بالذيل كذلك فمن المستحيل أن يكون أحدهما خالي دون الآخر.
     
    ب. إضافة عنصر جديد إلى الرأس (بداية القائمة):
    public void addToHead(int el){ head.next = head; head = new SLLNode(el); if (tail = null){ tail = head; } } أولا نجعل الرأس يشير إلى نفسه لكي نربط العنصر الجديد بالعنصر الأول السابق.
    عندها نقوم بإنشاء عنصر Node ونجعله الرأس الجديد
    في حالة كانت القائمة خالية نجعل الذيل يشير إلى نفس العنصر الذي يشير له الرأس.
    ملاحظة: عندما يشير الرأس والذيل إلى نفس العنصر فهذا يعني بأن لدينا عنصر واحد فقط في القائمة.
     
    ج. إضافة عنصر جديد إلى الذيل (نهاية القائمة):
    public void addToTail(int el){ if(!isEmpty()){ tail.next = new SLLNode(el); tail = tail.next; }else{ head = tail = new SLLNode(el); } } نفحص القائمة إذا كانت خالية:
    - في حالة كانت غير خالية: ننشأ عنصر جديد ونجعل الذيل الحالي يشير إليه، ومن ثم نجعله الذيل الجديد.
    - في حال كانت القائمة خالية: ننشأ عنصر جديد ونجعل الرأس والذيل يشيران إليه.
     
    د. حذف العنصر الموجود بالرأس (أول عنصر):
    public int deleteHead(){ int el = head.info; if(head == tail){ head = tail = null; }else{ head = head.next; } return el; } هذه الـ method تقوم بحذف العنصر الموجود بالرأس ومن ثم ترجع قيمته.
    أولا نحفظ القيمة داخل متغير el لكي لا تضيع بعد حذف العنصر.
    ومن ثم نفحص إذا كانت القائمة:
    - تحتوي على عنصر واحد: فعندها نجعل الرأس والذيل يسيران إلى لا شيء null وتكون القائمة حينها خالية.
    - تحتوي على أكثر من عنصر: فعندها نجعل العنصر الذي بعد الرأس هو الرأس الجديد، وفي هذه الحالة سيكون العنصر
    لا يوجود ما يشير إليه ويحذف.
    ومن ثم ترجع القيمة التي تم حذفها.
     
    هـ- حذف العنصر الموجود بالذيل (آخر عنصر):
    public int deleteTail(){ int el = tail.info; if(tail == head){ head = tail = null; }else{ SLLNode temp; for(temp = head; temp.next != tail; temp = temp.next); tail = temp; tail.next = null; } return el; } كما في السابق نحتفظ بقيمة العنصر لكي نقوم بإعادته بعد الإنتهاء من عملية الحذف لكن الحذف من الذيل ليس بسهولة الحذف من الرأس.
    نفحص التالي:
    - في حالة أن القائمة تحتوي على عنصر واحد فقط: فعندها نحذف القائمة كلها بجعل الرأس والذيل null.
    - في حالة أن القائمة تحتوي على أكثر من عنصر:
    أولا علينا أن نبحث عن العنصر السابق للذيل (العنصر قبل الأخير)، وذلك بأستخدام for loop قبلها علينا أن ننشأ مؤشر مؤقت باسم temp 
    وفي بداية الـ loop نجعله يشير إلى الرأس، وننقله من عنصر إلى آخر إلى أن يصل إلى العنصر ما قبل الأخير وذلك بفحص إذا ما كان 
    temp.next أي العنصر الذي يليه هو tail فعندها نكون وصلنا إلى العنصر ما قبل الأخير.
    بعد أن نجد العنصر ما قبل الأخير نجعل الذيل الجديد ومن ثم نجعل الذيل يشير إلى null لكي يتم حذف العنصر الأخير تماماً.
     
    و. طباعة جميع عناصر القائمة:
    public void printAll(){ System.out.print("["); for(SLLNode temp = head; temp != null; temp = temp.next){ System.out.print(temp.info + " "); } System.out.println("]"); } كما شرحنا سابقا نستخدم for loop للتنقل بين عناصر القائمة وطباعة قيمة كل عنصر على الشاشة.
     
    ز. فحص وجود العنصر في القائمة أو لا:
    public boolean isInList(int el){ if(isEmpty()){ return false; }else{ SLLNode temp; for(temp = head; temp.info != el && temp.next != null; temp = temp.next); return temp != null; } } للبحث عن وجود قيمة داخل القائمة من عدمه يجب علينا أن نتنقل داخل القائمة عنصر بعنصر، وسنستخدم الأسلوب السابق
    وهو باستخدام for loop. ولكن قبلها يجب علينا أن نتأكد من أن القائمة غير خالية.
    الـ for loop ستتوقف في إحدى حالتين في حالة وجدة العنصر داخل القائمة وذلك ببطلان الشرط (tmep.info != el)
    أو في حالة وصول temp إلى نهاية القائمة. 
    عندها سنرجع القية المعاكسة لهذا الشرط (tmep != null) في حالة وصول الـ temp إلى نهاية القائمة بدون أن يجد العنصر
    فعندا سيكون temp يساوي null وفي هذه الحالة سيرسل false أما في حالة إجاد العنصر فعندها temp لن يكون  null ويرسل true.
     
    ح. حذف عنصر من القائمة:
    public void delete(int el){ if(!isEmpty() && isInList()){ if(head == tail && head.info == el){ head = tail = null; }else if(el == head.info){ head = head.next; }else{ SLLNode pre, temp; for(temp = head.next, pre = head; temp.info != el; pre = temp, temp = temp.next); if(temp != null){ pre.next = temp.next; if(temp == tail){ tail = pre; } } } } } في حالة أردنا حذف عنصر من القائمة علينا أن نفكر في جميع الإحتمالات وهي كالتالي:
    - هل القائمة غير خالية؟! وهل العنصر في القائمة ولتححق سنستخدم isEmpty و isInList، في حالة لم تكن القائمة خالية وكان العنصر بالقائمة ننتقل للخطوة التالية:
    * هل القائمة تحتوي على عنصر واحد؟!: في هذه الحالة نقوم بحذف هذا العنصر ونجعل القائمة خالية.
    * هل القائمة تحتوي على أكثر من عنصر والعنصر المراد حذفة بالرأس؟: عنذها نحذف الرأس كما أشرنا في الفقرة (د).
    *الخيار الأخير هو أن العنصر موجود داخل القائمة: 
    في هذه الحالة نحتاج للبحث عن العنصر بنفس الأسلوب السابق لكننا هنا على خلاف الخطوات السابقة نحتاج إلى متغيرين مؤقتين
    أحدهما يشير إلى العنصر المراد حذفه والآخر يشير إلى العنصر الذي يسبقه. ولكي نقوم بذلك فعند إنشاء temp وهو العنصر المؤقت الذي سنبحث
    عن طريقه للعنصر المراد حذفه نجعل pre العنصر الذي يسبقه وذلك كما يلي:
    pre = head , temp = pre.next 
    كما نعلم فنحن لسنا بحاجة لفحص الرأس فقد تأكدنا من أن العنصر ليس بالرأس. تنتهي for loop في حال إجاد العنصر المراد حذف مع العلم بأننا لن نصل إلى نهاية القائمة
    لأننا تأكدنا من أن العنصر موجود في القائمة في الخطوة الأولى. وعند نهاية كل دورة من for loop نقوم بتحديث قيمتي pre and temp.
    بعد ذلك نجعل الـ pre يشير للعنصر الذي يلي temp أي يشير للعنصر الذي بعد العنصر المراد حذفه عندها لن يكون هناك ما يشير إليه ويحذف.
    ** تأكد أخير في حال أن العنصر الذي حذفنها هو الأخير فعندها يجب أن نجعل الذيل يشير إلى null.
     
    #شرح مفصل للـ for loop المستخدمة مع القوائم بشكل عام:
    لأهميتها القصوى سوف أفصلها أكثر، إبتداءً بالشكل العام لها:
    for (SLLNode temp = head; temp != null; temp = temp.next); أولا نقوم بإنشاء متغير مؤقت ونسميه بأي أسم كان هذا المتغير في بداية الـ loop سيشير لأول عنصر بالقائمة
    وهو الرأس head بعد ذلك يتأكد من تحقق الشرط وهو أن هذا المتغير لا يساوي null (أي أنه غير خالي)، ومن ثم
    يحدث قيمة المتغير وذلك بجعله العنصر التالي له عن طريق (temp = temp.next) ومن ثم يقوم بالتأكد من الشرط مجدداً
    هكذا حتى يصل إلى أن temp مساوي لـ tail وعندها يسنتقل للعنصر الذي يليه وهو في الحقيقة null لأنه tail.next دائما يساوي null
    وهكذا تنتهي  for loop. 
    ملاحظة: قمت بوضع ملفات للـ SLLNode and SLList classes في المرفقات تحتوي على تعليقات توضيحية.
     
    أتمنى من الله أن أكون قد وفقت في تفصيل موضوعنا هذا
    في حفظ الله إلى لقاء آخر
    SLList.java
    SLLNode.java
    مستوى المقال: متوسط
  5. بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
     
    إن شاء الله في هذه السلسلة من المواضيع سنتطرق لهيكلة البيانات بأنواعها المختلفة 
    مع أن هذه الدروس لا ترتبط بأي لغة إلا أن اللغة المستخدمة للتطبيق العملي هي Java، يجب أن يكون القارئ ملم بأساسيات اللغة، كذلك سأستخدم في أغلب الأحيان المصطلحات الإنجليزية لصعوبة ترجمتها في بعض الحالات.
     
    قبل الشروع في الموضوع ماذا نقصد بهيكلة البيانات؟
    المعنى هنا كبير جداً فقصودنا يحتوي على تشريح كامل لكل خصائص أي نوع من أنواع حفظ البيانات 
    فعلى سبيل المثال عندما نتحدث عن المصفوفات واللتي تعتبر من أبسط أنواع حفظ  البيانات فعندها سنتطرق
    لطريقة إنشاء المصفوفة طريقة حفظ أو حذف عنصر من المصفوفة وكذلك كيف لنا أن نبحث عن عنصر في المصفوفة وهكذا.
    من البديهي أن هناك أنواع مختلفة لحفظ البيانات ولكل منها خائصها وعيبوبها، في موضوعنا هذا سوف نتخطى المصفوفات والتي
    كما أشرت مسبقاً بأنها تعتبر من أبسط أنواع حفظ البيانات، وسنشرع مباشرة إلى القوائم أحادية الرابط.
     
    القوائم أحادية الرابط
     
    لماذا نحن بحاجة إلى القوائم بما أنه لدينا المصوفوفات؟
    للمصفوفات عيبين رئسيين فعندما نريد تغيير حجم المصفوفة فإننا بحاجة إلى إنشاء مصفوفة جديدة 
    ومن ثم نقوم بنسخ جميع عناصر المصفوفة القديمة إلى المصفوفة الجديدة ذات الحجم الجديد، كذلك 
    عناصر المصفوفات مخزنة في الذاكرة في مواقع متتالية لذلك عندما نريد إضافة عنصر جديد أو حذف عنصر ما
    فهذا يضطرنا إلى تحريك بقية العناصر في حال كانت عناصر المصفوفة مرتبة بشكل ما.
    آفضل حل لهاتين المشكلتين هي القوائم وأبسط شكل من أشكال القوائم هي "القوائم أحادية الرابط".
     
    ماذا نعني بالقوائم أحادية الرابط؟!
    لنفرض بأن لدينا صندوق هذا يحتوي على قسمين القسم الأول نخزن فيه ما نريد أما القسم الثاني فيحتوي على
    فتحة لكي نستطيع أن نربط هذا الصندوق بالصندوق  الذي بعده، في القوائم نطلق على هذا الصندوق أسم "العقدة" 
    كل عقدة تحتوي على مكان لحفظ البيانات ورابط يربطها بما بعدها من العقد.

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

    بعد ان انتهينا من التعريفات وفهم معنى، لنترك النظري قليلاً ونتجه إلى العملي كما نعلم فإن القائمة أحادية الرابط تتكون من عدد من العقد، والعقدة الواحدة تحتوي على مكونين رئيسين (البيانات المخزنة و الرابط الذي يربط العقدة بما بعدها).
    إذاً علينا أولاً ان نقوم بإنشاء  Class للعقدة الواحد ونوضح داخله مواصفات هذه العقدة واللتي هي مكان لحفظ البيانات (للتسهيل ستكون البيانات من نوع int) ومكان لربط هذه العقدة بالعقدة اللتي تليها (يجب أن يكون نوع هذا الرابط عقدة)
     
    لماذا يجب أن يكن الرابط من نفس نوع العقدة؟
    بكل بساطة الرابط ما هو إلا سهم يشير إلى شيء ما لذلك يجب أن يعرف إلى ماذا يشير.
    هذا هو الكود اللذي سننتهي به:
    public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } }  
    اسم الـ Class : SLLNode
    وهو اختصار لـ Single Linked Lists Node
        == عقدة لقائمة أحادية الرابط
    ويحتوي على متغيرين:
    info:
    وهو من نوع int ،  وهو المكان اللذي سنقوم بحفظ البيانات داخله.
     
    next:
    وهو من نوع SLLNode وهو الجزء اللذي سيربط هذه العقدة بالعقدة اللتي تليها
     
    هذا الـ Class يحتوي على اثنتين من الـ Constructors :
    الأولى:
    public SLLNode(int i){ this(i, null); } وهذه تمكننا من إنشاء عقدة بتخزين المعلومات فقط بدون الحاجة لربطها بالعقدة اللتي تليها، وداخلها كما نرى نداء للـ Constructor الأخرى بإعطائها البيانات المدخلة + Null لخانة الرابط اللذي يربط العقدة للعقدة اللتي تليها.
    لماذا لا نقوم بإعطاء العقدة رابط للعقدة اللتي تليها؟!
    هذا مهم ففي حالة آخر عقدة لن يكون هناك عقدة بعدها فلا يوجد ما ترتبط به لذلك نضع علامة بأنه لا يوجد شيء بعد هذه العقدة وهذه العلامة هي null .
     
    الثانية:
    public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } وهذه تمكننا من إنشاء عقدة بإعطائها البيانات التي ستحفظها ورابط للعقدة اللتي تليها.
     
    هذه هي نهاية الجزء الأول من درسنا إن شاء الله سأكمل موضوع القوائم أحادية الرابط في الموضوع التالي
    مع العلم بأن باب النقاش مفتوح لأي نقطة لتوظيحها أكثر ولإزالة أي لبس حولها.
    مستوى المقال: متوسط
  6. هياكل البيانات : المصفوفات (2)
    بسم الله الرحمن الرحيم
    في المقال السابق هنا قمت بذكر الجزء الأول من المصفوفات والذي يحتوي على ماهية المصفوفة، كيفية حجزها في الذاكرة، وخوارزميتي البحث والحذف، وفي هذا المقال بإذن الله سوف أكمل خوارزمية الإضافة وبعض التنبيهات.
    _ بعض الخوارزميات المتعلقة بالمصفوفة:
    3 ـ الإضافة لمصفوفة غير مرتبة:
    //Unsorted Array insert Algorithm // input: array, size of array, count, key 1. IF count = size: 2. PRINT “Array is full!” AND Exist 3. ELSE: 4. A[ count ] = key 5. Count++ 6. RETURN count شرح الخوارزمية:
     عندما نقوم بإضافة عنصر إلى المصفوفة يجب أن نتأكد أولا أنه يوجد مكان فارغ لهذا العنصر، لأنه كما ذكرنا سابقا أن المصفوفة حجمها لا يتغير أثناء التشغيل بل يجب تعيين الحجم المتوقع من البداية، فهذا بالنسبة للسطر الأول، وعند توفر مكان فارغ نقوم بإضافة العنصر عند الكاونت والذي ذكرنا أنه أول مكان فارغ في المصفوفة، وهذه الصورة توضح المفهوم:

    4 ـ الإضافة لمصفوفة مرتبة:
    //Sorted Array insert Algorithm // input: array, size of array, count, key 1. IF count = size: 2. PRINT “Array is full!” AND Exist 3. ELSE: 4. LOOP i = 0 UNTIL last element index 5. IF key is less/larger than A[ i ] 6. LOOP j = count UNTIL i + 1 // shift out 7. A[ j ] = A[ j - 1] 8. BREAK 9. A[ i ] = key 10. Count++ 11. RETURN count شرح الخوارزمية:
    رقم 1 و 2 نفس السابق للتأكد من وجود مكان فارغ، رقم 4 نقوم بعمل لوب يمشي على جميع عناصر المصفوفة بشكل مرتب، وأثناء ذلك يقوم بمقارنة العنصر الذي يريد إضافته بجميع العناصر إذا وجد عنصر يحقق الشرط – أصغر من أو أكبر – عندها يقوم بعمل لوب آخر يبدأ من عند الكاونت وهو أول مكان فارغ في المصفوفة ويبدأ بإزاحة العناصر – أي من رقم إندكس أصغر إلى أكبر – إلى أن يصل للمكان الذي يضيف فيه يعمل بريك ويضيف العنصر،وتسمى هذه العملية بالشفت آوت، والصورة التالية ربما توضح المفهوم أكثر:

    : java.util.Arrays class
    نظرا لأن المصفوفات مهمة جداً ، فالجافا وفرّت لنا العديد من الدوال " الميثودز" الجاهزة لاستعمالها،  سوف أذكر منها ثلاث فقط:
    1- مقارنة مصفوفتين ببعضهما:
    وذلك باستخدام هذه الدالة Arrays..equals( A,B) ,وتقوم بإرجاع قيمة true or false على حسب عناصر المصفوفة.
     
      2- ترتيب المصفوقة على حسب عناصرها:
    أي لو كانت العناصر أرقاما فسترتبها تصاعديا، ولو أحرفا فسترتبها أبجديا وهكذا، وذلك باستخدام : Arrays.sort(A)
     
    " :String3- تحويل المصفوفة إلى نص "
    وذلك باسخدام: Arrays.toString(A)
    وهذا مثال في الجافا يوضح طريقتها:
     
     
    int A[] = { 1,2,3,4,5,7}; int B[] = { 2,3,4,1,6,7}; System.out.println(Arrays.equals(A, B)); System.out.println(Arrays.toString(B)); Arrays.sort(B); System.out.println(Arrays.toString(B)); وهذه هي مخرجات الكود:
     

     
     
    هذا ما تيسر ذكره بالنسبة للمصفوفات، أرجو أن يكون واضحا مفهوما، والحمد لله
    والسلام عليكم ورحمة الله .
    مستوى المقال: مبتدئ
  7. هياكل البيانات : المصفوفات (1)
    بسم الله الرحمن الرحيم
    * ما هي الـمصفوفة؟
    هي عبارة عن طريقة لتخزين البيانات من نوع واحد، ذات حجم محدد يتم تحديده من بداية تكوين الـمصفوفة.
    *من أنواع المصفوفات:
    - المصفوفة ذات البُعد الواحد.

    - طريقة تعريف مصفوفة ذات بعد واحد:
    في الجافا يتم تعريف المصفوفة بتحديد نوع البيانات التي ستقوم بتخزينها، وبعد ذلك تقوم بتحديد اسم المصفوفة والذي ستتعامل معه فيما بعد، وأخيرًا تضع رمز المصفوفة وهو [ ].
    - مثال في الجافا:
    int a[] = {1 , 2, 3, 4, 5}; char b[] = {'a', 'b'}; - تكوين المصفوفة وحجز مكانها في الذاكرة:
    المصفوفة يتم حجزها في الذاكرة بطريقة متسلسلة، فمثلا إن قمت بتحديد حجم المصفوفة    بـ 6 بايت سوف يقوم بحجز 6 بايت في الذاكرة متسلسلة، ولكن إن كنت تريد حجز مصفوفة بحجم 10 بايت وذاكرتك لا يوجد فيها مساحة بحجم 10 متسلسلة، فإذا لن تستطيع تكوين هذه المصفوفة.
    وهذه الصورة توضح هذا المفهوم.

    _ بعض الخوارزميات المتعلقة بالمصفوفة:

    - هذه الصورة توضح ما سنحتاجه للتعامل مع البيانات المدخلة والمخرجة من المصفوفة –
    1 ـ البحث عن عنصر معين داخل المصفوفة:
    // Array Search Algorithm // input: array, size of array, count, key LOOP I = first element index UNTIL last element index IF current element = key: RETURN current index RETURN -1 شرح الخوارزمية:
     نبدأ من المدخلات والتي من الممكن أن تكون على صورة باراميتر لهذه الميثود، سوف نقوم بإرسال المصفوفة، حجمها، أول موقع غير مشغول في المصفوفة بمعنى لو أنك أنشأت مصفوفة بحجم 10 ولكنك قمت بتعبئة 6 فقط فالكاونت هنا سيكون 7، وأخيراً العنصر الذي سوف نقوم بالبحث عنه.
    أما المخرجات فستكون عبارة عن رقم صحيح، في حالة وجود العنصر سوف تقوم الميثود بإرجاع رقم الإندكس الخاص في العنصر، وأما إذا لم تجد العنصر فسوف يرجع -1 .
    أيضاً هناك ما يجب الانتباه له عند كتابة الخوارزميات وهي الحالات الحرجة أو الخاصة، في حالة المصفوفات لدينا حالتين فقط:
    الأولى: أن تكون المصفوفة فارغة وهذا إذا كان الكاونت يساوي صفر.
    الثانية: أن تكون المصفوفة ممتلئة وهذا إذا كان الكاونت يساوي الحجم.
     
    2 ـ حذف عنصر باستخدام قيمته لكلا من المصفوفات المرتبة والغير مرتبة:
    // Array Delete Algorithm // input: array, size of array, count, key 1. IF count = 0 : 2. PRINT “ Array is empty” AND Exit 3. ELSE continue to step 2. 4. i = Search( array, count, size, key) 5. IF i = -1 : 6. PRINT “ Not found “ AND Exit 7. ELSE // shift in 8. LOOP j = i UNTIL before last element index: 9. A[ j ] = A[ j+1 ] 10. Count - - 11. RETURN count شرح الخوارزمية:
    نبدأ من البداية، رقم 1و 2 نختبر إذا كانت المصفوفة فارغة فماذا سنحذف أساسا، رقم 4 نقوم بمناداة ميثود البحث السابقة والتي كانت ترجع لنا قيمة عدد صحيح، ومن ثم نقوم باختبار هذا الرقم، رقم 7 إلى 9 هذه عملية شرحها في الصورة التالية وهي عندما تجد العنصر الذي تريد حذفه، رقم 10 نغير مكان الكاونت والذي يعتبر أول مكان فارغ في المصفوفة، وأخيراً نقوم بإرجاع القيمة لاستخدامها لاحقا.

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

عالم البرمجة

عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل