02 هياكل البيانات: القوائم أحادية الرابط الجزء الثاني


مستوى المقال: متوسط

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

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

أولاً اعتذر جداً عن التأخر في طرح الشرح الذي كان من المفترض يكون بعد أسبوع من الموضوع السابق

تطرقنا في شرحنا السابق الى مفهوم القائمة أحادية الرابط وشرحنا استخداماته وطريقة عملها نظريا

شرحنا هذا سيكون عمليا من الدرجة الأولى بحيث سيكون كله حول الأكواد وتفصيلها.

 

١. إنشاء العقدة 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

Abather

1


اراء المستخدمين


لاتوجد تعليقات لعرضها .



انشئ حساب جديد او قم بتسجيل دخولك لتتمكن من اضافه تعليق جديد

يجب ان تكون عضوا لدينا لتتمكن من التعليق

انشئ حساب جديد

سجل حسابك الجديد لدينا في الموقع بمنتهي السهولة .


سجل حساب جديد

تسجيل الدخول

هل تمتلك حساب بالفعل ؟ سجل دخولك من هنا.


سجل دخولك الان

Ads Belongs To This website

  • بسم الله الرحمن الرحيم
    استكمالا للحديث عن أكثر  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
    مستوى المقال: متوسط

    بواسطه 3zcs , في

  • بسم الله الرحمن الرحيم
    كما ما بدأناه بالحديث عن أكثر  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
     
    مستوى المقال: متوسط

    بواسطه 3zcs , في

  • السلام عليكم ورحمة الله وبركاته
    الأهداف:
    لهذه المسابقة هدف واحد وهو اثراء المحتوى العربي في البرمجة بمقالات ونقاشات هادفة متميزه عن غيرها بالطرح و التنوع بعالم البرمجة
    طريقة المشاركة:
    كل ماعليك هو تجهيز مقالة ممتازة وطرحها تحت احد أقسام ساحات النقاش لك الحرية باختيار ماتحب ان تكتب المهم أنه يندرج تحت البرمجة عندما تقوم بالكتابه فانك مباشرة تشترك بالمسابقة يمكنك الاستفادة من طريقة طرح المقالات في صفحة المقالات من تنسيق واسلوب في الطرح 
    طريقة الربح:
     الطرح المميز بعد مراجعته من قبل فريق عالم البرمجة سوف يتم نقله مباشرة لصفحة المقالات برئيسية الموقع عندما يتم نقل الطرح ليصبح مقال برئيسية الموقع فإنك تعتبر فائز وتستحق الجائزة مباشرة
    الجائزة:
    عندما يتم اختيار طرحك ليصبح مقال بصفحة المقالات بعالم البرمجة فإنك ستحصل مباشرة على مبلغ 200 ريال سعودي مقابل المقال
    شهريا سيتم اختيار 10 مقالات للفوز بهذه الجائزة ليفوز الطرح المميز ويثري المحتوى العربي في مجال البرمجة
    القوانين:
    ان يكون النقاش او المقال عن البرمجة وتعليمها ليكون ذو فائدة للجميع يمنع نقل المواضيع او المقالات من مواقع اخرى الا اذا كنت تملك كافة حقوق المقال ولك حق نقله لموقع عالم البرمجة يجب ان يكون الطرح منسق ومرتب ويستخدم ادوات المحرر بعالم البرمجة مثل كتابة الاكواد وغيرها يجب رفع الملفات و الصور على موقع عالم البرمجة واستخدامها بالمقال بدل رفعها على مواقع اخرى يمنع وضع روابط خارجية لموقع بهدف الدعاية بالمقال او بالصور المرفوعة مسموح وضع روابط المكتبات البرمجية و غيرها من موقع المطور بالطرح لك حرية استخدام شعار عالم البرمجة على الصور المرفوعة (بالمرفقات صورة للشعار عالي الدقة لمن يرغب بإستخدامه) عدد المشاركات مفتوح يمكن ان تشارك باكثر من طرح وتفوز باكثر من جائزة الحصول على الجائزة:
    في نهاية كل شهر يمكنك الحصول على الجائزة مباشرة بحوالة بنكية لاي بنك داخل السعودية او لاي حساب PayPal لمشتركينا من خارج السعودية كل ماعليك بعد فوزك بالجائزة مراسلتنا عبر نموذج اتصل بنا بالبيانات التالية
    الاسم الثلاثي رقم الحساب ايبان واسم البنك لمن بداخل السعودية حساب PayPal اذا لم يكن لديك حساب داخل السعودية مدة المسابقة:
    هذه المسابقة فعاله من تاريخ نشر هذه المقاله وباذن الله سوف تكون شهرية مادام لدينا الميزانية للإستمرار وسوف يتم تحديث المقال في حالة انتهت المسابقة
    طرق أخرى للربح:
    عالم البرمجة نصب كل جهدنا لمكافئة المبدعين الذين يساعدون بنشر العلم المفيد يمكنك أيضا الإستفادة والربح من كتاباتك بالموقع اذا كان لديك حساب Adsense شاهد التفاصيل من هنا
    الإقتراحات:
    تستطيع مراسلة الإدارة بجميع ماترغب من إقتراحات وتطويرات عبر نموذج  اتصل بنا
    شعار عالم البرمجة عالي الوضوح
    بخلفية

    بدون خلفية

    مستوى المقال: مبتدئ

    بواسطه Ali Majrashi , في

  • السلام عليكم ورحمة الله وبركاته 
    طرحنا مسابقة عالم البرمجة للنقاش الهادف ، و هدفنا إثراء المحتوى العربي في مجال البرمجة ، فاز معنا الطرح المتميز و المفيد حيث قام بتقييم المقالات وترقيتها من قبل فريقنا المكون من @Alhazmy13 @منصور العتيبي @Ali Majrashi @mamoudi @A7med @cammac سنذكر بهذه المقالة الفائزين معنا ، و مقالاتهم ، و نشكرهم على ماقدموا من فائدة للجميع ، و إثراء المحتوى العربي في البرمجة.
    الفائزون في شهر December-2016
    @Abather المقال الفائز:
    @وضاح العوني المقال الفائز:
    @Hana Alalwi المقال الفائز:
    @يعرب المصطفى المقال الفائز:
    في الختام:
    هدفنا في موقع عالم البرمجة إثراء المحتوى العربي في مجال البرمجة ، و تعزيز حب المساعدة بين المبرمجين تستطيع كتابة ماتحب في ساحات النقاش ، و يمكن تكون احد الفائزين معنا  بمسابقة عالم البرمجة للنقاش الهادف فهي مازالت مستمره ايضا تستطيع مساعدة المبرمجين بالإجابة عن أسئلتهم ، و حل المشاكل التي تواجههم بقسم سؤال وجواب ؛ لتكون مرجع لبقية المبرمجين شعارنا في عالم البرمجة "إن في قضاء حوائج الناس لذة لا يَعرفها إلا من جربها، فافعل الخير مهما استصغرته فإنك لا تدري أي حسنة تدخلك الجنة." -ابن القيم-  وممكن تستفيد من
    حبيبي القارئ اعلم ان فريق عالم البرمجة يصب كل جهودة لمساعدة المبرمجين الذين يخصصون من وقتهم لنشر العلم المفيد ويساعدون الغير ويارب يقدرنا نوقف معكم ونساعدكم قد مانقدر.
    مستوى المقال: مبتدئ

    بواسطه Ali Majrashi , في

  • إصدار الxcode المستخدم : 8.0
    لغة سويفت 3
     
     
    في هذا الدرس سنتعلم طريقة إضافة خصائص إضافية في شريط الخصائص ( properties inspector ) في برنامج الxcode بطريقة سهلة و بسيطة حيث ستتمكن من إضافة مؤثرات بصرية إضافية لعناصر واجهة المستخدم كالحد (stroke or border ) و الزوايا الدائرية للأشكال.. مثلا سيكون بإمكانك عمل الظل و الحدود مع الزوايا الدائرية للview و ال label  كما هو موضح في الصورة التالية.
     
    لاحظ وجود خيارات جديدة في الشريط الأيمن للتحكم بقيم الحد و الظل و الزوايا الدائرية
     
    إذن لنبدأ الدرس..
    -في البداية.. قم بفتح الxcode و اختر   create new xcode project ثم اختر  single view application 
     
    - قم بعمل كلاس جديد عن طريق File > New > File أو بالضغط على cmd+n ثم اختر cocoa touch class

     
    -قم بتسمية الclass  بأي اسم يناسبك و تأكد من اختيار UIView عند Subclass of: كما هو موضح في الصورة ثم اضغط next
    الآن قم بإضافة IBDesignable قبل بداية التعريف بالكلاس في كلاس CustomizableView ليبدو بهذه الطريقة:
    import UIKit @IBDesignable class CustomizableView: UIView { } هذه الكلمة التي قمت بإضافتها تعطيك إمكانية ربط الكلاس مع الmain story board بحيث يقوم الاكس كود بمزامنة التغييرات التي تجريها على هذا الكلاس مع ال story board و إظهار النتائج مباشرة دون الحاجة إلى تشغيل الsimulator الاختبار النتيجة التي توصلت إليها
     
    الآن قم بإضافة الكود التالي إلى الكلاس..
    @IBInspectable var cornerRadius:CGFloat = 0{ didSet{ self.layer.cornerRadius = cornerRadius } } -كما تلاحظ في البداية قمنا بإضافة @IBInspectable وهذه الكلمة تسبق المتغير الذي يحمل اسم cornerRadius و هذا أيضا يربط الكلاس مع الstory obard بطريقة أخرى بحيث يمكنك الآن التحكم بقيمة هذا المتغير من شريط الخصائص في الstory board مما سيؤدي إلى تغير قيمة هذا المتغير بناء على القيم المحددة في ال stroy board 
    - بعد ذلك قمنا بإضافة المتغير cornerRadius و هو الذي سنستخدمه لتغيير الزوايا المنحنية في الview إلى زوايا دائرية و اخترنا نوع القيمة لتكون CGFloat و هو النوع المناسب لهذه الخاصية.
    - قمنا بإسناد القيمة 0 للمتغير كقيمة ابتدائية ثم بعد ذلك اضفنا الأقواس و هذه الأقواس تساعدك في إضافة تفاصيل أخرى للمتغير.
    - بداخل الأقواس قمنا بإضافة كلمة didSet متبوعة بأقواس و هذه الكلمة تمثل مجموعة من الأوامر يحددها المستخدم بحيث تطبق هذه الأوامر مباشرة بمجرد تغيير قيمة المتغير ، و هي يمكن تشبهها بالدالة function بحيث يقوم المبرمج بتحديد الأوامر التي ستوقوم هذه الدالة بها حالما يتم تغيير قيمة المتغير cornerRadius
    -اذن ما هي هذه الأوامر التي سيتم استدعاؤها ؟ 
    self.layer.cornerRadius = cornerRadius
    كما هو موضح فإن self تمثل الاوبجكت من هذا الكلاس customizableView و بما أن هذا الكلاس يرث من الView فإنه سيرث خصائصها و التي منها layer ليقوم باستخدام الخاصية cornerRadius التي بداخل الlayer و التي تتحكم بمدى استدارة زوايا الشكل ثم يقوم بتغيير قيمتها لتساوي المتغير cornerRadius الذي نتحكم بقيمته من الstory board كما ذكرنا سابقا بفضل الخاصية @IBInspectable 
    اذن مالذي سيحدث بالضبط؟
    بمجرد إضافة عنصر view في الاكس كود و جعله يرث من الكلاس customizableView ستظهر لك الخواص المعروفة لهذا الview في شريط الخصائص كاللون و الحجم و المحاذاة ..الخ بالإضافة إلى خاصية مميزة جديدة لهذا الview فقط و هي الcornerRadius حيث ستظهر لك في شريط الخصائص بفضل كلمة @IBInspectable ثم سيكون بإمكانك تعديل قيمة ال cornerRadius و بمجرد تعديلها ستستدعى دالة didSet التي قمنا بإضافتها سابقا و بالتالي سيطبق أمر تغيير الcornerRadius الخاص بالشكل لتتغير قيمته من 0 إلى القيمة التي قمت بتحديدها له في شريط الخصائص
    و أخيرا سترى أن زوايا الview بالفعل أصبحت تتغير مباشرة و هذا بفضل كلمة @IBDesignable التي قمنا بإضافتها للكلاس customizableView
     
    لنطبق ذلك عمليا..
    - قم بالذهاب إلى الstory board  وإضافة view إليها و قم بتلوين هذا الview باللون الذي يناسبك.
    - الآن اذهب إلى الأيقونة التي بجانب أيقونة الخصائص و التي تسمى identity inspector و قم بتغيير الكلاس الخاص بالview إلى CustomizableView كما هو موضح في الصورة
     
     
     
    - انتظر قليلا ثم عد إلى الخصائص ستلاحظ و جود الخاصية الجديدة cornerRadius في أعلا شريط الخصائص كما هو واضح في الصورة.. 
     

    - الآن قم بتغيير القيم لتلاحظ تغير زوايا الشكل بطريقة جميلة
     
     
    يمكنك إضافة المزيد من الخصائص التي بإمكانك التحكم بها بنفس الطريقة..
    لإضافة خاصية الحد (border) :
    @IBInspectable var border:CGFloat = 0{ didSet{ self.layer.borderWidth = border } }  
    لإضافة خاصية الظل ( لا أنصح بها لأنها قد تؤدي إلى بطء البرنامج بسبب معالجة الظل التي تستهلك الكثير من الأداء):
    @IBInspectable var shadow:CGFloat = 0{ didSet{ self.layer.shadowRadius = shadow } } @IBInspectable var opacity:Float = 0{ didSet{ self.layer.shadowOpacity = opacity/20 } } @IBInspectable var offset:CGFloat = 0{ didSet{ self.layer.shadowOffset.height = offset self.layer.shadowOffset.width = offset } }  
     
    هذه الإضافات ليست خاصة للview فقط و إنما يمكنك إضافتها لأي عنصر رسومي في البرنامج فمثلا: بإمكانك فعل المثل للlabel كل ما عليك فعله :
    - قم بعمل كلاس جديد باسم customizableLabel على سبيل المثال 
    - اجعله يرث من UILabel في خيار subclass of عند إنشاء الكلاس 
    - قم بإضافة كلمة @IBDesignable عند بداية الكلاس 
    -قم بنسخ نفس الكود الخاص بالcustomizableView  و لصقه في كلاس customizableLabel
    - استمتع







    مستوى المقال: متوسط

    بواسطه يعرب المصطفى , في

  • Ads Belongs To This website

    عالم البرمجة

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