<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/page/3/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; &#x62C;&#x627;&#x641;&#x627;</description><language>ar</language><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x646;&#x634;&#x627;&#x621; &#x639;&#x62F;&#x629; &#x62E;&#x64A;&#x648;&#x637; &#x648;&#x641;&#x647;&#x645; &#x627;&#x644;&#x62A;&#x632;&#x627;&#x645;&#x646; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%B9%D8%AF%D8%A9-%D8%AE%D9%8A%D9%88%D8%B7-%D9%88%D9%81%D9%87%D9%85-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1270/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_07/60f2910d271be_---(--).png.d95703a978b9db946896b50b20a7846c.png" /></p>

<p>
	يصف هذا المقال كيفية تحقيق برمجة متزامنة في جافا، إذ يُغطي مبادئ البرمجة المتوازية والثبات والخيوط وإطار العمل التنفيذي (تجمُعات الخيوط thread pools)، إلى جانب واجهات Futures وCompletableFuture القابلة للاستدعاء وإطار عمل fork-join.
</p>

<h2>
	التزامن
</h2>

<p>
	يعرف التزامن بأنه القدرة على تشغيل عدة برامجٍ أو عدة أجزاءٍ من البرنامج على التوازي، فعندما نتمكن من إجراء مهمةٍ تستغرق وقتًا بالتزامن أو على التوازي مع مهمةٍ أخرى، فسيرفع هذا من الإنتاجية وتفاعلية البرنامج.
</p>

<p>
	يمتلك أي حاسوبٍ حديثٍ عدة وحدات معالجةٍ مركزية CPU أو عدة نوىً ضمن الوحدة الواحدة، ومن الممكن أن تكون القدرة على زيادة هذه النوى المتعددة هي مفتاح النجاح لتطبيقٍ يمتلك حجمًا ضخمًا من المُستخدمين.
</p>

<h3>
	العملية Process مقابل الخيوط Threads
</h3>

<p>
	تجري العملية بصورة مستقلة ومعزولة عن العمليات الأخرى، حيث لا تستطيع الوصول مباشرةً للبيانات المشتركة في العمليات الأخرى، وتكون الموارد من ذاكرة وزمن وحدة معالجة مركزية محجوزةً لهذه العملية من قبل نظام التشغيل.
</p>

<p>
	يُدعى الخيط بالعملية البسيطة ويمتلك مكدسًا خاصًا به، ومع ذلك يستطيع الوصول للبيانات المشتركة للخيوط الأخرى ضمن نفس العملية، كما يمتلك كل خيطٍ ذاكرة تخزينٍ مؤقتٍ خاصة، وعندما يقرأ الخيط بيانات مشتركة يحفظها في ذاكرته المؤقتة، بحيث يُمكن للخيط إعادة قراءة البيانات المشتركة.
</p>

<p>
	يعمل تطبيق جافا افتراضيًا ضمن عمليةٍ واحدةٍ، ولتحقيق عملية المعالجة المتوازية أو السلوك غير المتزامن؛ فيجب أن يعمل تطبيق الجافا ضمن عدة خيوط.
</p>

<h2>
	التحسينات والمشاكل المرافقة للتزامن
</h2>

<h3>
	حدود مكاسب التزامن
</h3>

<p>
	يجب أن يعمل تطبيق جافا ضمن خيوطٍ متعددةٍ لتحقيق المعالجة المتوازية أو السلوك غير المتزامن، حيث يسمح التزامن بتنفيذٍ أسرع لمهمةٍ ما، لأن هذه المهمة سوف تُقسم إلى مهماتٍ فرعية تُنفّذ على التوازي وبالطبع فإن زمن التشغيل مُقيد بأجزاء المهمة الممكن تنفيذها على التوازي.
</p>

<p>
	يُمكن حساب الربح النظري باستخدام القاعدة التالية المُشار إليها بقانون Amdahl.
</p>

<p>
	إذا كانت "F" هي النسبة المئوية لأجزاء البرنامج التي يُمكن أن تعمل على التوازي و"N" هو عدد العمليات، فإن ربح الأداء الأعظمي هو:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_10" style="">
<span class="pln"> </span><span class="lit">1</span><span class="pun">/(</span><span class="pln">F</span><span class="pun">+((</span><span class="lit">1</span><span class="pun">-</span><span class="pln">F</span><span class="pun">)/</span><span class="pln">N</span><span class="pun">))</span></pre>

<h3>
	مشاكل التزامن
</h3>

<p>
	تمتلك الخيوط مكدساتٍ خاصة بها، إلا أنها لا تستطيع الوصول إلى البيانات المُشاركة، ولذلك توجد مشكلتان مرتبطتان بالوصول ومجال الرؤية؛ حيث تظهر مشكلة مجال الرؤية عندما يقرأ الخيط "أ" البيانات المُشاركة التي يُغيرها الخيط "ب" فيما بعد، دون معرفة الخيط "أ" بهذا التغيير؛ بينما تظهر مشكلة الوصول عند محاولة عدة خيوطٍ الوصول وتغيير نفس البيانات المُشاركة في الوقت ذاته.
</p>

<p>
	تؤدي مشكلتا الوصول ومجال الرؤية إلى:
</p>

<ul>
<li>
		عدم تجاوب التطبيق بسبب المشاكل الحاصلة بفعل التزاحم للوصول للبيانات.
	</li>
	<li>
		بيانات غير صحيحة ناتجة عن البرنامج.
	</li>
</ul>
<h2>
	التزامن في جافا
</h2>

<h3>
	العمليات والخيوط
</h3>

<p>
	يعمل برنامج جافا افتراضيًا ضمن عمليةٍ خاصةٍ به وضمن خيطٍ واحد، حيث تُعد الخيوط جزءًا من لغة جافا وتتعامل جافا معها باستخدام الشيفرة البرمجية <code>Thread</code>، ويستطيع تطبيق جافا إنشاء خيوطٍ جديدةٍ باستخدام هذا الصنف، كما يوفِّر الإصدار 1.5 من جافا دعمًا متقدمًا للتزامن باستخدام حزمة <code>java.util.concurrent</code>.
</p>

<h3>
	الأقفال ومزامنة الخيوط
</h3>

<p>
	توفّر لغة جافا أقفالًا لحماية أجزاءٍ مُعينةٍ من الشيفرة البرمجية لتنفيذها من قِبل عدة خيوطٍ بنفس الوقت، والطريقة الأسهل لقفل طريقة method أو صنف Class جافا هي من خلال تعريف هذه الطريقة أو الصنف باستخدام الكلمة المفتاحية <code>synchronized</code>.
</p>

<p>
	تضمن الكلمة المفتاحية <code>synchronized</code> في جافا ما يلي:
</p>

<ul>
<li>
		إمكانية تنفيذ كتلةٍ من الشيفرة البرمجية في نفس الوقت من قِبل خيطٍ واحدٍ فقط.
	</li>
	<li>
		يستطيع أي خيطٍ الاطلاع على التعديلات السابقة للكتلة المتزامنة من الشيفرة البرمجية والمحمية بنفس القفل عند دخوله إليها.
	</li>
</ul>
<p>
	عملية التزامن ضرورية لضمان الوصول الحصري والمتبادل إلى الكتل البرمجية وضمان اتصالٍ موثوقٍ بين الخيوط.
</p>

<p>
	تستطيع استخدام الكلمة المفتاحية <code>synchronized</code> عند تعريف الطريقة لتضمن أن خيطًا واحدًا فقط يستطيع استخدام هذه الطريقة بنفس الوقت، وبالتالي عند محاولة خيطٍ آخر لاستخدام هذه الطريقة، فسوف ينتظر حتى ينتهي الخيط الأول من استخدام هذه الطريقة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_8" style="">
<span class="kwd">public</span><span class="pln"> synchronized </span><span class="kwd">void</span><span class="pln"> critial</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// some thread critical stuff</span><span class="pln">
    </span><span class="com">// here</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن أيضًا استخدام الكلمة المفتاحية <code>synchronized</code> لحماية كتل من الشيفرة البرمجية ضمن الطريقة method، حيث تكون الكتلة محميةً بمفتاحٍ من الممكن أن يكون محرفًا أو كائنًا، ويُدعى هذا المفتاح بالقفل، حيث لا يُمكن للشيفرات البرمجية المحمية بنفس القفل الوصول إلا من قِبل خيطٍ واحدٍ بنفس الوقت، فعلى سبيل المثال تضمن هيكلية البيانات التالية وصول خيطٍ واحدٍ فقط إلى داخل كتلتي الطريقتين <code>()add</code> و<code>()next</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_12" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">pagerank</span><span class="pun">.</span><span class="pln">crawler</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">ArrayList</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">List</span><span class="pun">;</span><span class="pln">

</span><span class="com">/**
 * Data structure for a web crawler. Keeps track of the visited sites and keeps
 * a list of sites which needs still to be crawled.
 *
 * @author Lars Vogel
 *
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CrawledSites</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> crawledSites </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;();</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> linkedSites </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;();</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> add</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> site</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        synchronized </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">crawledSites</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="pln">site</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                linkedSites</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">site</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Get next site to crawl. Can return null (if nothing to crawl)
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">linkedSites</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        synchronized </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// Need to check again if size has changed</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">linkedSites</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">String</span><span class="pln"> s </span><span class="pun">=</span><span class="pln"> linkedSites</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
                linkedSites</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
                crawledSites</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> s</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<h3>
	الذاكرة المتطايرة
</h3>

<p>
	يضمن التصريح عن أي متحولٍ باستخدام الكلمة المفتاحية <code>volatile</code> قراءة الخيط لآخر قيمة مكتوبة في الحقل، حيث أن الكلمة المفتاحية <code>volatile</code> لا تُطبق أي قفلٍ على المتغير.
</p>

<p>
	بدءًا من الإصدار 5 لجافا، فقد صارت قيم المتغيرات الغير متطايرة -التي تُعدل من قبل نفس الخيط- تُحدَّث عند الكتابة على متغيرٍ متطايرٍ، كما يُمكن استخدام هذه الخاصية لتحديث قيمٍ ضمن متغيرٍ مرجعي مثل المتغير المتطاير "person"، ففي هذه الحالة لا بُدّ من استخدام متغير مؤقتٍ "person" واستخدام أداة ضبط لتهيئة متغير، ثم تعيين قيمة متغيرٍ مؤقتٍ للمتغير النهائي، وسوف يُغير هذا عنوان المتغير ويجعل القيم مرئيةً للخيوط الأخرى.
</p>

<h2>
	نموذج الذاكرة في جافا
</h2>

<p>
	يصف هذا النموذج عملية الاتصال بين ذاكرة الخيوط والذاكرة الرئيسية للتطبيق، فهو يُحدد شروط انتشار التغييرات في الذاكرة بين الخيوط، والحالات التي يُحدِث فيها الخيط ذاكرته الخاصة من الذاكرة الرئيسية، والعمليات الذرية وترتيب هذه العمليات.
</p>

<h3>
	العمليات الذرية
</h3>

<p>
	العملية الذرية هي عملية تُطبق كوحدة عملٍ منفردة دون أي تدخل من عملياتٍ أخرى، حيث تضمن مواصفات لغة جافا أن تكون عملية قراءة أو كتابة متغير ذريةً، إلا في حال كان نوع المتغير <code>long</code> أو <code>double</code>، وتكون العمليات على المتغيرات من نوع <code>long</code> أو <code>short</code> ذريةً فقط إذا صُرح عنها باستخدام الكلمة المفتاحية <code>volatile</code>.
</p>

<p>
	بفرض أنه قد صُرِّح عن المتغير <code>i</code> على أنه صحيح <code>int</code>، فسوف تكون الزيادة <code>i++‎</code> عمليةً غير ذرية في جافا، وهذا ينطبق على القيم الرقمية الأخرى مثل <code>long</code>. تقرأ العملية <code>i++‎</code> أولًا القيمة المُخزنة حاليًا في i (عمليةٌ ذريةٌ)، ثم تُضيف قيمة واحد لها (عمليةٌ ذريةٌ)، ولكن يُحتمل أن تتغير القيمة بين عمليتي الكتابة والقراءة.
</p>

<p>
	توفّر لغة جافا بدءًا من الإصدار 1.5 متغيراتٍ ذريةٍ مثل AtomicInteger، أو AtomicLong، والتي توفّر توابعًا مثل:
</p>

<ul>
<li>
		<code>getAndDecrement()‎</code>
	</li>
	<li>
		<code>getAndIncrement()‎</code>
	</li>
	<li>
		<code>getAndSet()‎</code>
	</li>
</ul>
<p>
	وجميعها ذرية.
</p>

<h3>
	تحديثات الذاكرة في الشيفرة البرمجية المتزامنة
</h3>

<p>
	يضمن نموذج الذاكرة في جافا أن يستطيع كل خيط يدخل كتلة شيفرة برمجية متزامنة، الاطلاع على جميع التعديلات التي حدثت تحت حماية نفس القفل.
</p>

<h2>
	الثبات والنسخ الوقائية
</h2>

<h3>
	الثبات
</h3>

<p>
	يمكن تجنب مشاكل التزامن بطريقةٍ بسيطةٍ من خلال مشاركة البيانات الثابتة فقط بين الخيوط، وهي البيانات التي لا يُمكن تغييرها، كما يجب التصريح عن صنف مع كل الحقول الخاصة به على أنها نهائية final لجعله ثابتًا، ويجب التأكد من عدم تسرُّب أي مرجع للحقول أثناء عملية البناء، لذلك يجب أن يكون الحقل:
</p>

<ul>
<li>
		خاص private.
	</li>
	<li>
		لا يمتلك طريقةً ضابطة.
	</li>
	<li>
		أن يُنسخ ضمن الباني إذا كان كائنًا متغيرًا لتجنب تغيير البيانات من خارج الصنف.
	</li>
	<li>
		لا يجب إعادة قيمته مباشرةً أو التعامل معه مباشرةً.
	</li>
	<li>
		لا يتغير وإذا حصل تغيير فيجب ألا يكون ظاهرًا للخارج.
	</li>
</ul>
<p>
	يُمكن أن يتضمن الصنف الثابت بعض البيانات المتغيرة والتي تُستخدم لإدارة حالته، ولكن لا يُمكن تغيير هذا الصنف أو أي سمةٍ ضمنه من خارج الصنف، إذ يجب أن يُنشئ هذا الصنف نُسخًا وقائيةً عن عناصر الحقول الثابتة التي تُمرر له من الخارج مثل المصفوفة، وذلك لضمان عدم إمكانية أي كائنٍ خارجيٍ تغيير البيانات.
</p>

<h3>
	النسخ الوقائية
</h3>

<p>
	يجب حماية الأصناف من أي شيفرة استدعاء، إذ من الممكن لهذه الشيفرة تغيير البيانات بطريقةٍ لا تتوقعها حتى من أجل البيانات الثابتة التي لا تتوقع أن تغير قيمتها من خارج الصنف. ولحماية الصنف، يجب نسخ البيانات المُستقبلة وإعادة نُسخٍ فقط عن البيانات إلى شيفرة الاستدعاء.
</p>

<p>
	يُنشئ المثال التالي نسخةً من اللائحة ArrayList ويُعيد النسخة فقط وبالتالي لا يستطيع عميل هذا الصنف إزالة العناصر من اللائحة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_14" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">performance</span><span class="pun">.</span><span class="pln">defensivecopy</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">ArrayList</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Collections</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">List</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MyDataStructure</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;();</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> add</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">list</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">s</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Makes a defensive copy of the List and return it
     * This way cannot modify the list itself
     *
     * @return List&lt;String&gt;
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> getList</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Collections</span><span class="pun">.</span><span class="pln">unmodifiableList</span><span class="pun">(</span><span class="typ">list</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	الخيوط في جافا
</h2>

<p>
	إن أساس التزامن في جافا هو الصنف <code>java.lang.Threads</code>، حيث يُنفّذ <code>Thread</code> كائنًا من النوع <code>java.lang.Runnable</code>، وتتضمن واجهة <code>Runnable</code> تعريفًا للطريقة <code>()run</code> التي تُستدعى من قِبل الكائن <code>Thread</code> وتحتوي العمل الواجب تنفيذه، لذلك فإن <code>Runnable</code> هو مهمة يجب تنفيذها، أما <code>Thread</code> فهو العامل الذي يُنفذ هذه المهمة.
</p>

<p>
	يوضح المثال التالي مهمة <code>Runnable</code> بحساب مجموع مجال مُعطى من الأرقام، لذا أنشِئ مشروع جافا وسمِّه <code>de.vogella.concurrency.threads</code> لهذا المثال.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_16" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">threads</span><span class="pun">;</span><span class="pln">

</span><span class="com">/**
 * MyRunnable will count the sum of the number from 1 to the parameter
 * countUntil and then write the result to the console.
 * &lt;p&gt;
 * MyRunnable is the task which will be performed
 *
 * @author Lars Vogel
 *
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MyRunnable</span><span class="pln"> implements </span><span class="typ">Runnable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> final </span><span class="kwd">long</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln">

    </span><span class="typ">MyRunnable</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> countUntil</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">countUntil </span><span class="pun">=</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="lit">@Override</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> run</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            sum </span><span class="pun">+=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يوضح المثال التالي استخدام صنفي <code>Thread</code> و<code>Runnable</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_18" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">threads</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">ArrayList</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">List</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// We will store the threads so that we can check if they are done</span><span class="pln">
        </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Thread</span><span class="pun">&gt;</span><span class="pln"> threads </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Thread</span><span class="pun">&gt;();</span><span class="pln">
        </span><span class="com">// We will create 500 threads</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">500</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Runnable</span><span class="pln"> task </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyRunnable</span><span class="pun">(</span><span class="lit">10000000L</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">Thread</span><span class="pln"> worker </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Thread</span><span class="pun">(</span><span class="pln">task</span><span class="pun">);</span><span class="pln">
            </span><span class="com">// We can set the name of the thread</span><span class="pln">
            worker</span><span class="pun">.</span><span class="pln">setName</span><span class="pun">(</span><span class="typ">String</span><span class="pun">.</span><span class="pln">valueOf</span><span class="pun">(</span><span class="pln">i</span><span class="pun">));</span><span class="pln">
            </span><span class="com">// Start the thread, never call method run() direct</span><span class="pln">
            worker</span><span class="pun">.</span><span class="pln">start</span><span class="pun">();</span><span class="pln">
            </span><span class="com">// Remember the thread for later usage</span><span class="pln">
            threads</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">worker</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> running </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            running </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Thread</span><span class="pln"> thread </span><span class="pun">:</span><span class="pln"> threads</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">thread</span><span class="pun">.</span><span class="pln">isAlive</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    running</span><span class="pun">++;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"We have "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> running </span><span class="pun">+</span><span class="pln"> </span><span class="str">" running threads. "</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">running </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سلبيات الصنف <code>Thread</code>:
</p>

<ul>
<li>
		يُؤثر إنشاء خيطٍ جديدٍ بعض الشيء على الأداء.
	</li>
	<li>
		سوف يُسبب إنشاء العديد من الخيوط تراجعًا في الأداء لأن وحدة المعالجة المركزية CPU سوف تضطر للتبديل بين هذه الخيوط.
	</li>
	<li>
		لا تستطيع التحكم بعدد الخيوط بسهولة لذلك سوف تواجه أخطاءً بسبب امتلاء الذاكرة الناتج عن العدد الكبير للخيوط.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		تُقدم حزمة <code>java.util.concurrent</code> دعمًا مُحسّنًا للتزامن مقابل الاستخدام المُباشر للخيوط، وهذه الحزمة مشروحة في القسم التالي.
	</p>
</blockquote>

<h2>
	تجمعات الخيوط Thread Pools مع الإطار المنفذ Executer Framework
</h2>

<p>
	تُدير تجمُعات الخيوط مجموعةً من الخيوط العاملة، حيث تتضمن رتل عمل يحتفظ بالمهمات التي تنتظر أن تُنفذ، ويُمكن وصف تجمُّعات الخيوط على أنها مجموعةٌ من كائنات <code>Runnable</code> (رتل عمل) واتصالٌ من الخيوط العاملة، حيث تعمل هذه الخيوط باستمرار وتتحقق من استدعاء العمل لعملٍ جديد، فإذا كان عملًا جديدًا يجب تنفيذه، فستُنفذ هذا الكائن <code>Runnable</code>، حيث يوفّر صنف <code>Thread</code> نفسه طريقةً مثل <code>(execute(Runnable r</code> لإضافة كائن <code>Runnable</code> جديدٍ إلى رتل العمل.
</p>

<p>
	يوفر الإطار المُنفذ مثالًا لتطبيق واجهة <code>java.util.concurrent.Executor</code>، مثل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6365_6" style="">
<span class="pun">(</span><span class="typ">Executor</span><span class="pun">.</span><span class="pln">newFixedThreadPool</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n
</span></pre>

<p>
	والذي سوف يُنشئ n خيط عامل، وتُضيف ExecutorService توابع دورة الحياة للمُنفذ، مما يسمح له بإطفاء المُنفذ وانتظار عملية الإغلاق.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		إذا كنت تريد استخدام تجمع خيوط مع خيطٍ واحدٍ يُنفذ عدة كائنات <code>Runnable</code>، فتستطيع استخدام طريقة <code>()Executors.newSingleThreadExecutor</code>.
	</p>
</blockquote>

<p>
	أنشئ <code>Runnable</code> مرةً ثانية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_20" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">threadpools</span><span class="pun">;</span><span class="pln">

</span><span class="com">/**
 * MyRunnable will count the sum of the number from 1 to the parameter
 * countUntil and then write the result to the console.
 * &lt;p&gt;
 * MyRunnable is the task which will be performed
 *
 * @author Lars Vogel
 *
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MyRunnable</span><span class="pln"> implements </span><span class="typ">Runnable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> final </span><span class="kwd">long</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln">

    </span><span class="typ">MyRunnable</span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> countUntil</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">countUntil </span><span class="pun">=</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="lit">@Override</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> run</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">long</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> countUntil</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            sum </span><span class="pun">+=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تستطيع الآن تشغيل كائنات <code>Runnable </code>باستخدام الإطار المُنفذ.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_22" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">threadpools</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">ExecutorService</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">Executors</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> final </span><span class="typ">int</span><span class="pln"> NTHREDS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">ExecutorService</span><span class="pln"> executor </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Executors</span><span class="pun">.</span><span class="pln">newFixedThreadPool</span><span class="pun">(</span><span class="pln">NTHREDS</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">500</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Runnable</span><span class="pln"> worker </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyRunnable</span><span class="pun">(</span><span class="lit">10000000L</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> i</span><span class="pun">);</span><span class="pln">
            executor</span><span class="pun">.</span><span class="pln">execute</span><span class="pun">(</span><span class="pln">worker</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="com">// This will make the executor accept no new threads</span><span class="pln">
        </span><span class="com">// and finish all existing threads in the queue</span><span class="pln">
        executor</span><span class="pun">.</span><span class="pln">shutdown</span><span class="pun">();</span><span class="pln">
        </span><span class="com">// Wait until all threads are finish</span><span class="pln">
        executor</span><span class="pun">.</span><span class="pln">awaitTermination</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Finished all threads"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تستطيع استخدام صنف <code>java.util.concurrent.Callable</code> عند الحاجة لإعادة قيمةٍ معينة من الخيوط.
</p>

<h2>
	البرمجة غير المتزامنة
</h2>

<p>
	يُفضل تنفيذ أي عملية تستهلك وقتًا بصورةٍ غير متزامنة حيث توجد مقاربتان للتعامل مع المهام بصورةٍ غير متزامنة ضمن تطبيق جافا، وهما:
</p>

<ul>
<li>
		حجب منطق التطبيق إلى حين إكمال المهمة.
	</li>
	<li>
		استدعاء منطق التطبيق حالما تكتمل المهمة وتُدعى هذه بالمقاربة دون إعاقة.
	</li>
</ul>
<p>
	يدعم <code>CompletableFuture</code> وهو توسّع عن واجهة <code>Future</code>، الاستدعاءات غير المتزامنة، فهو يستعمل واجهة <code>CompletionStage</code> التي توفر توابعًا تسمح بربط الاستدعاءات التي سوف تُنفذ عند إكمالها، كما يُضيف تقنيات قياسية لتنفيذ شيفرة تطبيق برمجية عند إكمال مهمةٍ ما، وهذا يتضمن طرقًا متعددةً من أجل دمج المهام. يدعم <code>CompletableFuture</code> أيضًا المقاربتين بإعاقة ودون إعاقة، إضافةً إلى الاستدعاءات العادية. ويٌمكن تنفيذ الاستدعاء في خيطٍ آخر عند تنفيذ <code>CompletableFuture</code> في الخيط ذاته.
</p>

<p>
	يُوضح المثال التالي كيفية إنشاء <code>CompletableFuture</code> أساسي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_24" style="">
<span class="typ">CompletableFuture</span><span class="pun">.</span><span class="pln">supplyAsync</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">::</span><span class="pln">doSomething</span><span class="pun">);</span></pre>

<p>
	يُشغّل <code>CompletableFuture.supplyAsync</code> المهمة بصورةٍ غير متزامنة ضمن تجمُّع الخيط الافتراضية لجافا، ويكون الخيار مُتاحًا بالسماح للمنفذ المُخصص بتعريف <code>ThreadPool</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_26" style="">
<span class="pln">package snippet</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">CompletableFuture</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">ExecutionException</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CompletableFutureSimpleSnippet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> started </span><span class="pun">=</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">currentTimeMillis</span><span class="pun">();</span><span class="pln">

        </span><span class="com">// configure CompletableFuture</span><span class="pln">
        </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> futureCount </span><span class="pun">=</span><span class="pln"> createCompletableFuture</span><span class="pun">();</span><span class="pln">

            </span><span class="com">// continue to do other work</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Took "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">started </span><span class="pun">-</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">currentTimeMillis</span><span class="pun">())</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">" milliseconds"</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

            </span><span class="com">// now its time to get the result</span><span class="pln">
            </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="typ">int</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> futureCount</span><span class="pun">.</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"CompletableFuture took "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">started </span><span class="pun">-</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">currentTimeMillis</span><span class="pun">())</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">" milliseconds"</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

               </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Result "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> count</span><span class="pun">);</span><span class="pln">
             </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="typ">ExecutionException</span><span class="pln"> ex</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="com">// Exceptions from the future should be handled here</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> createCompletableFuture</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> futureCount </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">.</span><span class="pln">supplyAsync</span><span class="pun">(</span><span class="pln">
                </span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                        </span><span class="com">// simulate long running task</span><span class="pln">
                        </span><span class="typ">Thread</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">5000</span><span class="pun">);</span><span class="pln">
                    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
                    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">});</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> futureCount</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	يُمكن استخدام <code>thenApply</code> لتعريف استدعاء يُنفذ حالما ينتهي <code>CompletableFuture.supplyAsync</code>، ويوضح المثال التالي استخدام طريقة <code>thenApply</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_28" style="">
<span class="pln">package snippet</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">CompletableFuture</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">ExecutionException</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CompletableFutureCallback</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> started </span><span class="pun">=</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">currentTimeMillis</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln">  data </span><span class="pun">=</span><span class="pln"> createCompletableFuture</span><span class="pun">()</span><span class="pln">
                </span><span class="pun">.</span><span class="pln">thenApply</span><span class="pun">((</span><span class="typ">Integer</span><span class="pln"> count</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="typ">int</span><span class="pln"> transformedValue </span><span class="pun">=</span><span class="pln"> count </span><span class="pun">*</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
                    </span><span class="kwd">return</span><span class="pln"> transformedValue</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}).</span><span class="pln">thenApply</span><span class="pun">(</span><span class="pln">transformed </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="str">"Finally creates a string: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> transformed</span><span class="pun">);</span><span class="pln">

            </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">get</span><span class="pun">());</span><span class="pln">
            </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="typ">ExecutionException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

            </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> createCompletableFuture</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln">  result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">.</span><span class="pln">supplyAsync</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="com">// simulate long running task</span><span class="pln">
                </span><span class="typ">Thread</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">5000</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	تستطيع أيضًا تشغيل <code>CompletableFuture</code> مُؤخر بدءًا من الإصدار 9 لجافا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_30" style="">
<span class="typ">CompletableFuture</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> future </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">&lt;&gt;();</span><span class="pln">
 future</span><span class="pun">.</span><span class="pln">completeAsync</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"inside future: processing data..."</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">},</span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">.</span><span class="pln">delayedExecutor</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TimeUnit</span><span class="pun">.</span><span class="pln">SECONDS</span><span class="pun">))</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">thenAccept</span><span class="pun">(</span><span class="pln">result </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"accept: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">));</span></pre>

<h2>
	خوارزميات دون إعاقة
</h2>

<p>
	يوفر الإصدار 5.0 لجافا دعمًا لعملياتٍ ذريةٍ إضافية، وهذا يسمح بتطوير خوارزميات دون إعاقة لا تتطلب تزامنًا، ولكن تعتمد على تعليمات عتاد صلب ذري منخفضة المستوى مثل الموازنة والتبديل compare-and-swap أو اختصارًا CAS، حيث تتحقق عمليتا الموازنة والتبديل فيما إذا كان المتغير يحتوي على قيمةٍ ما، وفي حال احتوائه على تلك القيمة سوف تُنفذ العملية.
</p>

<p>
	تكون الخوارزميات دون إعاقة عادةً أسرع من خوارزميات الإعاقة لأن تزامن الخيوط يظهر في مستويات أدنى (عتاد صلب).
</p>

<p>
	يُنشئ المثال التالي عدّادا دون إعاقة يزداد باستمرار، وهذا المثال موجود ضمن مشروع project يُدعى:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6365_12" style="">
<span class="pln"> de</span><span class="pun">.</span><span class="pln">volgella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">nonblocking</span><span class="pun">.</span><span class="pln">counter</span><span class="pun">.</span><span class="pln">
</span></pre>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_32" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">nonblocking</span><span class="pun">.</span><span class="pln">counter</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="pln">atomic</span><span class="pun">.</span><span class="typ">AtomicInteger</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Counter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">AtomicInteger</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AtomicInteger</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getValue</span><span class="pun">(){</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> increment</span><span class="pun">(){</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">incrementAndGet</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// Alternative implementation as increment but just make the</span><span class="pln">
    </span><span class="com">// implementation explicit</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> incrementLongVersion</span><span class="pun">(){</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> oldValue </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">value</span><span class="pun">.</span><span class="pln">compareAndSet</span><span class="pun">(</span><span class="pln">oldValue</span><span class="pun">,</span><span class="pln"> oldValue</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)){</span><span class="pln">
             oldValue </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">get</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> oldValue</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	واختبارًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_34" style="">
<span class="pln">package de</span><span class="pun">.</span><span class="pln">vogella</span><span class="pun">.</span><span class="pln">concurrency</span><span class="pun">.</span><span class="pln">nonblocking</span><span class="pun">.</span><span class="pln">counter</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">ArrayList</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">HashSet</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">List</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Set</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">Callable</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">ExecutionException</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">ExecutorService</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">Executors</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="pln">concurrent</span><span class="pun">.</span><span class="typ">Future</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Test</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> final </span><span class="typ">int</span><span class="pln"> NTHREDS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            final </span><span class="typ">Counter</span><span class="pln"> counter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Counter</span><span class="pun">();</span><span class="pln">
            </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Future</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Future</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;&gt;();</span><span class="pln">

            </span><span class="typ">ExecutorService</span><span class="pln"> executor </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Executors</span><span class="pun">.</span><span class="pln">newFixedThreadPool</span><span class="pun">(</span><span class="pln">NTHREDS</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">500</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">Callable</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> worker </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln">  </span><span class="typ">Callable</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="lit">@Override</span><span class="pln">
                    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Integer</span><span class="pln"> call</span><span class="pun">()</span><span class="pln"> throws </span><span class="typ">Exception</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                        </span><span class="typ">int</span><span class="pln"> number </span><span class="pun">=</span><span class="pln"> counter</span><span class="pun">.</span><span class="pln">increment</span><span class="pun">();</span><span class="pln">
                        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">number </span><span class="pun">);</span><span class="pln">
                        </span><span class="kwd">return</span><span class="pln"> number </span><span class="pun">;</span><span class="pln">
                    </span><span class="pun">}</span><span class="pln">
                </span><span class="pun">};</span><span class="pln">
                </span><span class="typ">Future</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> submit</span><span class="pun">=</span><span class="pln"> executor</span><span class="pun">.</span><span class="pln">submit</span><span class="pun">(</span><span class="pln">worker</span><span class="pun">);</span><span class="pln">
                </span><span class="typ">list</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">submit</span><span class="pun">);</span><span class="pln">

            </span><span class="pun">}</span><span class="pln">


            </span><span class="com">// This will make the executor accept no new threads</span><span class="pln">
            </span><span class="com">// and finish all existing threads in the queue</span><span class="pln">
            executor</span><span class="pun">.</span><span class="pln">shutdown</span><span class="pun">();</span><span class="pln">
            </span><span class="com">// Wait until all threads are finish</span><span class="pln">
            </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">executor</span><span class="pun">.</span><span class="pln">isTerminated</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">set</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HashSet</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;();</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Future</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> future </span><span class="pun">:</span><span class="pln"> </span><span class="typ">list</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="typ">set</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">future</span><span class="pun">.</span><span class="pln">get</span><span class="pun">());</span><span class="pln">
                </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    e</span><span class="pun">.</span><span class="pln">printStackTrace</span><span class="pun">();</span><span class="pln">
                </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">ExecutionException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    e</span><span class="pun">.</span><span class="pln">printStackTrace</span><span class="pun">();</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">list</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()!=</span><span class="typ">set</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()){</span><span class="pln">
                </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RuntimeException</span><span class="pun">(</span><span class="str">"Double-entries!!!"</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">

        </span><span class="pun">}</span><span class="pln">


</span><span class="pun">}</span></pre>

<p>
	إن الجزء المثير للاهتمام هو كيفية تنفيذ طريقة <code>()incrementAndGet</code>، فهي تستخدم عملية CAS.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_36" style="">
<span class="kwd">public</span><span class="pln"> final </span><span class="typ">int</span><span class="pln"> incrementAndGet</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(;;)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> current </span><span class="pun">=</span><span class="pln"> get</span><span class="pun">();</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> next </span><span class="pun">=</span><span class="pln"> current </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">compareAndSet</span><span class="pun">(</span><span class="pln">current</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">))</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> next</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يستفيد JDK من الخوارزميات بدون إعاقة باستمرار لرفع الأداء لجميع المطورين، كما أن عملية تطوير خوارزميات دون إعاقة صحيحة ليس بالأمر السهل، ويمكنك الاطلاع على المزيد من التفاصيل عن الخوارزميات بدون إعاقة <a data-ss1628955754="1" href="http://www.ibm.com/developerworks/java/library/j-jtp04186/index.html" rel="external nofollow">من ibm.com</a>.
</p>

<h2>
	إطار عمل Fork-Join ضمن جافا 7
</h2>

<p>
	طرح الإصدار 7 من جافا آليةً جديدةً تفرعيةً لتنفيذ المهمات المكثفة، وأُطلق الاسم fork-join على إطار العمل التابع لها، حيث يسمح إطار العمل هذا على توزيع مهمةٍ ما على عدة عاملين ثم انتظار النتيجة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يمكن تنزيل الحزمة <a data-ss1628955754="1" href="http://gee.cs.oswego.edu/dl/concurrency-interest/index.html" rel="external nofollow">jsr166y</a> للإصدار 6 لجافا.
	</p>
</blockquote>

<p>
	أنشئ المشروع <code>de.vogella.performance.forkjoin</code> لتجريبها، وإن كنت تستخدم نسخةً مختلفةً عن جافا 7، فعليك إضافة jsr166y.jar إلى مسار الأصناف.
</p>

<p>
	أنشئ أولًا الحزمة <code>algorithm</code> ثم الصنف التالي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_38" style="">
<span class="pln">package algorithm</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Random</span><span class="pun">;</span><span class="pln">

</span><span class="com">/**
 *
 * This class defines a long list of integers which defines the problem we will
 * later try to solve
 *
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Problem</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> final </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">2000000</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Problem</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Random</span><span class="pln"> generator </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Random</span><span class="pun">(</span><span class="lit">19580427</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">list</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">nextInt</span><span class="pun">(</span><span class="lit">500000</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> getList</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	بعد ذلك عرّف الصنف <code>Solver</code> كما هو موضح في المثال التالي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_40" style="">
<span class="pln">package algorithm</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Arrays</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> jsr166y</span><span class="pun">.</span><span class="pln">forkjoin</span><span class="pun">.</span><span class="typ">RecursiveAction</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Solver</span><span class="pln"> extends </span><span class="typ">RecursiveAction</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Solver</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> array</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="lit">@Override</span><span class="pln">
    </span><span class="kwd">protected</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> compute</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">list</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> midpoint </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">length </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
            </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> l1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOfRange</span><span class="pun">(</span><span class="typ">list</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> midpoint</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> l2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOfRange</span><span class="pun">(</span><span class="typ">list</span><span class="pun">,</span><span class="pln"> midpoint</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">Solver</span><span class="pln"> s1 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Solver</span><span class="pun">(</span><span class="pln">l1</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">Solver</span><span class="pln"> s2 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Solver</span><span class="pun">(</span><span class="pln">l2</span><span class="pun">);</span><span class="pln">
            forkJoin</span><span class="pun">(</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> s2</span><span class="pun">);</span><span class="pln">
            result </span><span class="pun">=</span><span class="pln"> s1</span><span class="pun">.</span><span class="pln">result </span><span class="pun">+</span><span class="pln"> s2</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الآن عرّف صنفًا بسيطًا للتجريب وسمِّه <code>Test</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8548_42" style="">
<span class="pln">package testing</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> jsr166y</span><span class="pun">.</span><span class="pln">forkjoin</span><span class="pun">.</span><span class="typ">ForkJoinExecutor</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> jsr166y</span><span class="pun">.</span><span class="pln">forkjoin</span><span class="pun">.</span><span class="typ">ForkJoinPool</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> algorithm</span><span class="pun">.</span><span class="typ">Problem</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> algorithm</span><span class="pun">.</span><span class="typ">Solver</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Test</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Problem</span><span class="pln"> test </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Problem</span><span class="pun">();</span><span class="pln">
        </span><span class="com">// check the number of available processors</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> nThreads </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Runtime</span><span class="pun">.</span><span class="pln">getRuntime</span><span class="pun">().</span><span class="pln">availableProcessors</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">nThreads</span><span class="pun">);</span><span class="pln">
        </span><span class="typ">Solver</span><span class="pln"> mfj </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Solver</span><span class="pun">(</span><span class="pln">test</span><span class="pun">.</span><span class="pln">getList</span><span class="pun">());</span><span class="pln">
        </span><span class="typ">ForkJoinExecutor</span><span class="pln"> pool </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ForkJoinPool</span><span class="pun">(</span><span class="pln">nThreads</span><span class="pun">);</span><span class="pln">
        pool</span><span class="pun">.</span><span class="pln">invoke</span><span class="pun">(</span><span class="pln">mfj</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> mfj</span><span class="pun">.</span><span class="pln">getResult</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Done. Result: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">long</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="com">// check if the result was ok</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> test</span><span class="pun">.</span><span class="pln">getList</span><span class="pun">().</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            sum </span><span class="pun">+=</span><span class="pln"> test</span><span class="pun">.</span><span class="pln">getList</span><span class="pun">()[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Done. Result: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> sum</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	التوقف التام أو الجمود Deadlock
</h2>

<p>
	يتضمن التطبيق المتزامن خطر حدوث جمود، حيث نقول عن مجموعةٍ من العمليات أنها تسبب جمودًا إذا كانت جميع هذه العمليات تنتظر حصول حدثٍ معينٍ لعمليةٍ أخرى موجودةٍ في نفس المجموعة، فمثلًا إذا كان الخيط "أ" ينتظر إغلاق الكائن "د" المحجوز من قِبل الخيط "ب"، والخيط "ب" ينتظر إغلاق الكائن "ت" المحجوز من قِبل العملية "أ"، فعندها سوف تُقفل هاتان العمليتان ولا يُمكنهما متابعة عملياتهما.
</p>

<p>
	يُمكن تشبيه هذه الحادثة بالازدحام المروري، حيث تحتاج السيارات (الخيوط) الوصول لشارعٍ مُعين (موارد)، وهذا الشارع محجوزٌ حاليًا من قِبل سيارةٍ أخرى (قفل).
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72181" data-ss1628955754="1" href="https://academy.hsoub.com/uploads/monthly_2021_07/001_concurrent_deadlock_cars.png.095a6f80a0dfa30d2fd7cc208787bcd3.png" rel=""><img alt="001_concurrent_deadlock_cars.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72181" data-unique="d7ux7mpbb" src="https://academy.hsoub.com/uploads/monthly_2021_07/001_concurrent_deadlock_cars.png.095a6f80a0dfa30d2fd7cc208787bcd3.png"></a>
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a data-ss1628955754="1" href="https://www.vogella.com/tutorials/JavaConcurrency/article.html#nonblocking-algorithms" rel="external nofollow">Java concurrency (multi-threading) - Tutorial</a> لصاحبه Lars Vogel.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a data-ss1628955754="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D8%B3-polling-loops-%D9%88%D8%A7%D9%84%D9%85%D9%82%D8%A7%D8%B7%D8%B9%D8%A7%D8%AA-interrupts-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-r963/" rel="">الأحداث غير المتزامنة: حلقات الجس Polling Loops والمقاطعات Interrupts في المعالج</a>
	</li>
	<li>
		<a data-ss1628955754="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D9%84%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-java-r599/" rel="">الدليل السريع للغة البرمجة Java</a>
	</li>
	<li>
		<a data-ss1628955754="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D9%85%D8%AA%D8%AF%D8%A7%D8%AE%D9%84%D8%A9-nested-classes-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1115/" rel="">الأصناف المتداخلة Nested Classes في جافا</a>
	</li>
	<li>
		<a data-ss1628955754="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-r1016/" rel="">المتغيرات والأنواع البسيطة في لغة جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1270</guid><pubDate>Mon, 05 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x62B;&#x646;&#x627;&#x626;&#x64A;&#x629; &#x627;&#x644;&#x628;&#x639;&#x62F; Two-dimensional Arrays &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-two-dimensional-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1161/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e106d6b335_--.png.d412292c7e305b3c817b60d82fae128a.png" /></p>

<p>
	تَعرَّضنا بالقسم الفرعي 3.8.5 للمصفوفات ثنائية البعد (two-dimensional arrays)، ولكننا لم نَستخدِمها بالقدر الكافي منذ ذلك الحين. تَملُك المصفوفات ثنائية البعد نوعًا أيضًا مثل <code>int[][]‎</code> أو <code>String[][]‎</code> بزوجين من الأقواس المربعة. تُرتَّب عناصر المصفوفات ثنائية البعد ضِمْن مجموعة من الصفوف والأعمدة بحيث يُخصِّص العامل <code>new</code> كُلًا من عدد تلك الصفوف والأعمدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_7" style="">
<span class="typ">int</span><span class="pun">[][]</span><span class="pln"> A</span><span class="pun">;</span><span class="pln">
A </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">3</span><span class="pun">][</span><span class="lit">4</span><span class="pun">];</span></pre>

<p>
	تُنشِئ تلك التَعليمَة مصفوفة ثنائية البعد مُكوَّنة من 12 عنصر مُرتَّبين ضِمْن 3 صفوف و 4 أعمدة. يَتَوفَّر أيضًا مُهيِئ (initializers) للمصفوفات ثنائية البعد. فمثلًا، تُنشِئ التَعْليمَة التالية مصفوفة 3x4:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_9" style="">
<span class="typ">int</span><span class="pun">[][]</span><span class="pln">  A  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">{</span><span class="pln">  </span><span class="pun">{</span><span class="pln">  </span><span class="lit">1</span><span class="pun">,</span><span class="pln">  </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                  </span><span class="pun">{</span><span class="pln">  </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">3</span><span class="pun">,</span><span class="pln">  </span><span class="lit">2</span><span class="pun">,</span><span class="pln">  </span><span class="lit">5</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                  </span><span class="pun">{</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln">  </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">9</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
               </span><span class="pun">};</span></pre>

<p>
	يَتَكوَّن مُهيِئ المصفوفات ثنائية البعد من عدة صفوف مفصولة بفواصل (comma) ومُضمَّنة داخل أقواس. بالمثل، كل صف عبارة عن قائمة من القيم مفصولة بفواصل ومُضمَّنة داخل أقواس. إلى جانب ذلك، تَتَوفَّر قيم مُصنَّفة النوع (literals) لتَمثيِل المصفوفات ثنائية البعد لها نفس قواعد الصيغة (syntax)، ويُمكِن اِستخدَامها بأي مكان وليس فقط تَعْليمَات التّصرِيح (declarations). اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_11" style="">
<span class="pln">A  </span><span class="pun">=</span><span class="pln">  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[][]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="pun">{</span><span class="pln">  </span><span class="lit">1</span><span class="pun">,</span><span class="pln">  </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                     </span><span class="pun">{</span><span class="pln">  </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">3</span><span class="pun">,</span><span class="pln">  </span><span class="lit">2</span><span class="pun">,</span><span class="pln">  </span><span class="lit">5</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                     </span><span class="pun">{</span><span class="pln"> </span><span class="pun">-</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln">  </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">9</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
                  </span><span class="pun">};</span></pre>

<p>
	في الواقع، يُمكِنك أن تُطبِق نفس الأمر على المصفوفات ثلاثية البعد ورباعية البعد..إلخ، ولكنها غَيْر شائعة الاِستخدَام في العموم.
</p>

<h2>
	حقيقة المصفوفات ثنائية البعد (two-dimensional arrays)
</h2>

<p>
	قبل أن نتعمق أكثر من ذلك، هنالك مفاجأة صغيرة! لا تَملُك الجافا مصفوفات ثنائية البعد (two-dimensional arrays) بصورة فعلية. تَحتلّ العناصر بمصفوفة فعلية ثنائية البُعد مَواضِعًا مُتصِلة من الذاكرة، ولكن هذا ليس صحيحًا بالجافا. في الحقيقة، تَكشِف الصيغة (syntax) المُستخدَمة لتمثيل أنواع المصفوفة ذلك: بِفرض وجود النوع <code>BaseType</code>، ينبغي إذًا أن نَكُون قادرين على إنشاء النوع <code>BaseType[]‎</code>، وهو ما يَعنِي "مصفوفة من النوع <code>BaseType</code>". إذا اِستخدَمنا النوع <code>int[]‎</code> ذاته كنوع أساسي للمصفوفة، فإننا نَحصُل على النوع <code>int[][]‎</code> الذي يُمثِل "مصفوفة من النوع <code>int[]‎</code>" أو "مصفوفة من مصفوفة من النوع <code>int</code> ". بمصفوفة ثنائية البعد من النوع <code>int[][]‎</code>، تَكُون العناصر نفسها مُتْغيِّرات من النوع <code>int[]‎</code>. ولمّا كان أي مُتْغيِّر من النوع <code>int[]‎</code> بدوره قادرًا على حَمْل مؤشر (pointer) إلى مصفوفة من النوع <code>int</code>. يُمكِننا إذًا أن نقول أن أي مصفوفة ثنائية البعد هي في الواقع عبارة عن مصفوفة من المؤشرات (pointers) بحيث يُشيِر كل مؤشر منها بدوره إلى مصفوفة أحادية البعد (one-dimensional array). تُمثِل المصفوفات أحادية البعد إذًا صفوفًا (rows) ضِمْن المصفوفة ثنائية البعد (two-dimensional). تُبيِّن الصورة التالية مصفوفة مُكوَّنة من 3 صفوف و 4 أعمدة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59588" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/001Two_Dimensional_Array.png.601336eb277de8ebcde5c1bf2b704f62.png" rel=""><img alt="001Two_Dimensional_Array.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59588" data-unique="ocn1mf5kr" src="https://academy.hsoub.com/uploads/monthly_2021_03/001Two_Dimensional_Array.png.601336eb277de8ebcde5c1bf2b704f62.png"></a>
</p>

<p>
	غالبًا ما نتجاهل تلك الحقيقة، ونُفكِر بالمصفوفة ثنائية البعد كما لو كانت شبكة (grid). ولكننا سنحتاج أحيانًا لأن نتذكَّر أن كل صف ضِمْن تلك الشبكة هو مصفوفة بحد ذاته. يُمكننا في الواقع أن نُشيِر إلى تلك المصفوفات باِستخدَام <code>A[0]‎</code> و <code>A[1]‎</code> و <code>A[2]‎</code> أي أن كل صف هو قيمة من النوع <code>int[]‎</code> يُمكِننا أن نُمرِّرها حتى إلى برنامج فرعي (subroutine) يَستقبِل مُعاملًا (parameter) من النوع <code>int[]‎</code> على سبيل المثال.
</p>

<p>
	يترتَّب على التفكير بمصفوفة ثنائية البعد <code>A</code> على أنها مصفوفة من المصفوفات عدة نتائج: أولًا، يُساوِي <code>A.length</code> عدد الصفوف (rows) ضِمْن المصفوفة، وهو ما يَتضِح معناه عند التفكير بها بتلك الطريقة. ثانيًا، إذا كانت <code>A</code> تَملُك الشكل المعتاد للمصفوفات ثنائية البعد، فسيَكُون عدد الأعمدة بالمصفوفة <code>A</code> هو نفسه عدد العناصر بالصف الأول أي <code>A[0].length</code>. لاحِظ مع ذلك أنه لا توجد قاعدة تَنصّ على ضرورة أن تَكُون جميع الصفوف ضِمْن مصفوفة ثنائية البعد بنفس الطول حيث يُعدّ كل صف بمثابة مصفوفة مُنفصِلة أحادية البعد، ويُمكِن لها إذًا أن تَملٌك طولًا مختلفًا. في الواقع، قد يَكُون صف معين ضِمْن مصفوفة فارغًا (null). اُنظر التَعْليمَة التالية على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7692_13" style="">
<span class="pln">A </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">int</span><span class="pun">[</span><span class="lit">3</span><span class="pun">][];</span></pre>

<p>
	لا يَتَضمَّن التعريف السابق عددًا داخل الأقواس الثانية، ولهذا فإنه يُنشِئ مصفوفة مُكوَّنة من ثلاثة عناصر جميعها فارغة (null) أي أنه يُوفِّر مساحة لثلاثة صفوف (rows)، ولكنه لا يُنشِئ تلك الصفوف بشكل فعليّ. يُمكِنك أن تُنشِئ الصفوف <code>A[0]‎</code> و <code>A[1]‎</code> و <code>A[2]‎</code> بشكل منفصل.
</p>

<p>
	كمثال آخر، لنُفكِر بمصفوفة مُتماثِلة <code>M</code> -المصفوفة المتماثلة (symmetric) عبارة عن مصفوفة ثنائية البعد يَتساوَى عدد صفوفها مع عدد أعمدتها كما تَتَساوَى قيم <code>M[j]‎</code> و <code>M[j]‎</code> لجميع قيم <code>i</code> و <code>j</code>-، نحتاج لتَخْزِين قيم <code>M[j]‎</code> التي تُحقِّق الشَّرط <code>i &gt;= j</code> أي يُمكِننا أن نُخزِّن البيانات بمصفوفة مُثلثة (triangular array) كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59589" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/002Symmetric_Matrix.png.a6ebe32ea8075e28788cef39944a0a0d.png" rel=""><img alt="002Symmetric_Matrix.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59589" data-unique="50309spqb" src="https://academy.hsoub.com/uploads/monthly_2021_03/002Symmetric_Matrix.png.a6ebe32ea8075e28788cef39944a0a0d.png"></a>
</p>

<p>
	يُمكِننا أن نُنشِئ مصفوفة مثلثية (triangular array) إذا أنشأنا كل صف على حدى. تُنشِئ الشيفرة التالية مصفوفة مثلثية 7x7 من النوع <code>double</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_15" style="">
<span class="kwd">double</span><span class="pun">[][]</span><span class="pln"> matrix </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="lit">7</span><span class="pun">][];</span><span class="pln"> </span><span class="com">// لم ننشئ الصفوف بعد</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">7</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫انشئ الصف i بحيث يحتوي على i+1 من العناصر</span><span class="pln">
    matrix</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">  
</span><span class="pun">}</span></pre>

<p>
	إذا أردنا أن نَجلب قيمة <code>matrix</code> بالمَوضِع (i,j)، وكان <code>i &lt; j</code>، فإنه ينبغي أن نَجلب قيمة <code>matrix[j]‎</code>، ويَنطبِق الأمر نفسه على ضَبْط قيم تلك المصفوفة. تُعرِّف الشيفرة التالية صَنْفًا لتمثيل المصفوفات المُتماثِلة (symmetric matrices):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_17" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[][]</span><span class="pln"> matrix</span><span class="pun">;</span><span class="pln">  </span><span class="com">// مصفوفة مثلثية لحمل البيانات</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        matrix </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="pln">n</span><span class="pun">][];</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
            matrix</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> get</span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> col </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">row </span><span class="pun">&gt;=</span><span class="pln"> col</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> matrix</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">];</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> matrix</span><span class="pun">[</span><span class="pln">col</span><span class="pun">][</span><span class="pln">row</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">set</span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> value </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">row </span><span class="pun">&gt;=</span><span class="pln"> col</span><span class="pun">)</span><span class="pln">
            matrix</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
            matrix</span><span class="pun">[</span><span class="pln">col</span><span class="pun">][</span><span class="pln">row</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> matrix</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">  </span><span class="com">// يمثل عدد الصفوف</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class SymmetricMatrix</span></pre>

<p>
	يُمكِنك أن تجد تعريف الصَنْف بالأعلى داخل الملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/SymmetricMatrix.java" rel="external nofollow">SymmetricMatrix.java</a> كما يَحتوِي الملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/TestSymmetricMatrix.java" rel="external nofollow">TestSymmetricMatrix.java</a> على برنامج صغير لاختبار الصَنْف.
</p>

<p>
	بالمناسبة، لا تستطيع الدالة (function) القياسية <code>Arrays.copyOf()‎</code> أن تُنشِئ نسخة كاملة من مصفوفة ثنائية البعد (2d array) ضِمْن خطوة واحدة، وإنما ينبغي أن تَفعَل ذلك لكل صف (row) ضِمْن المصفوفة على حدى. نستطيع إذًا كتابة الشيفرة التالية لنسخ مصفوفة ثنائية البعد من الأعداد الصحيحة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_19" style="">
<span class="typ">int</span><span class="pun">[][]</span><span class="pln"> B </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="pln">A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">][];</span><span class="pln">  </span><span class="com">// ‫B و A لديهم نفس عدد الصفوف</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    B</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">length</span><span class="pun">));</span><span class="pln"> </span><span class="com">// ‫انسخ الصف i</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	لعبة الحياة (Game of Life)
</h2>

<p>
	كمثال آخر على معالجة المصفوفات ثنائية البعد، سنَفْحَص مثال مشهور آخر: لعبة الحياة (Game of Life) الذي اخترعها عالم الرياضيات جون هورتون كونواي (John Horton Conway) بعام 1970. لا تُعدّ لعبة الحياة (Game of Life) لعبة بحق، فهي تُمثِل آلة أوتوماتيكية ثنائية البعد. يَعنِي ذلك أنها مُكوَّنة من شبكة (grid) من الخلايا (cells) تَتَغيَّر محتوياتها بمرور الوقت وفقًا لقواعد محدَّدة. يُمكِن لأي خلية أن تَكُون بحالة من اثنين: "حية (alive)" أو "ميتة (dead)". سنَستخدِم مصفوفة ثنائية البعد لتَمثيِل تلك الشبكة بحيث يُمثِل كل عنصر بالمصفوفة حالة خلية واحدة ضِمْن الشبكة. ستُهيِئ اللعبة شبكة (grid) مبدئية بحيث تَكُون جميع خلاياها إما "حية" أو "ميتة". بَعْد ذلك، ستتطوَّر الشبكة وفقًا لسِلسِلة من الخطوات. ستُحدَّد حالة خلايا الشبكة بكل خطوة وفقًا لحالة خلاياها بالخطوة السابقة ووفقًا لعدد من القواعد البسيطة: ستَفحَص كل خلية بالشبكة الخلايا المجاورة لها (أفقيًا ورأسيًا وقطريًا)، وتَحسِب عدد الخلايا الحية ثم تُحدَّد حالة الخلية بالخطوة التالية وفقًا للقواعد التالية:
</p>

<ul>
<li>
		في حالة كانت الخلية "حية" بالخطوة الحالية، وكانت أيضًا مُحاطَة بعدد 2 أو 3 خلايا حية، فإنها ستَبقَى حية بالخطوة التالية أما إذا لم تَكُن مُحاطَة بذلك العدد، فإنها ستموت. (أي تَموُت الخلية الحية نتيجة للوحدة إذا كانت مُحاطة بعدد أقل من خليتين حيتين أو نتيجة للازدحام إذا كانت مُحاطَة بأكثر من 3 خلايا حية).
	</li>
	<li>
		في حالة كانت الخلية "ميتة" بالخطوة الحالية، وكانت أيضًا مُحاطَة بثلاث خلايا حية، فإنها تتحوَّل لتُصبِح حية بالخطوة التالية أما إذا لم تَكُن مُحاطَة بذلك العدد، فإنها ستَبقَى ميتة. (أي يَنتُج عن وجود ثلاثة خلايا حية خلية حية جديدة).
	</li>
</ul>
<p>
	تُظهِر الصورة التالية شكل شبكة الخلايا قَبْل تطبيق قواعد اللعبة وبَعْدها. تُطبَق القواعد على كل خلية بالشبكة، وتُبيِّن الصورة كيفية تطبيقها على أربعة خلايا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59590" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/003Life_Rules.png.5cdd679be1a6972e8ca1462e92e88dfb.png" rel=""><img alt="003Life_Rules.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59590" data-unique="qjq6nikyd" src="https://academy.hsoub.com/uploads/monthly_2021_03/003Life_Rules.png.5cdd679be1a6972e8ca1462e92e88dfb.png"></a>
</p>

<p>
	تُعدّ لعبة الحياة (Game of Life) شيقة حيث تَعرِض الكثير من الأنماط المفاجئة (اُنظر بصفحة ويكيبيديا الخاصة باللعبة). نحن هنا مُهتمين فقط بكتابة برنامج لمحاكاة تلك اللعبة. يُمكِنك أن تَطلِع على شيفرة البرنامج بالكامل بالملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/Life.java" rel="external nofollow">Life.java</a>. تَظهَر رقعة الحياة كشبكة من المربعات بحيث تَكون المربعات الميتة سوداء اللون أما المربعات الحية فتَكُون بيضاء اللون. (سيَستخدِم البرنامج الصنف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/MosaicCanvas.java" rel="external nofollow">MosaicCanvas.java</a> من القسم 4.7 لتَمثيِل تلك الشبكة، لذلك ستحتاج إلى إضافة ذلك الملف لتَصرِيف [compile] البرنامج وتَشْغِيله). يُمكِننا أن نملئ رقعة الحياة بخلايا حية وميتة عشوائيًا أو قد نَستخدِم الفأرة لضَبطها. يُوفِّر البرنامج الزر "Step" المسئول عن تحديد حالة الشبكة بَعْد خطوة واحدة فقط" بالإضافة إلى الزر "Start" المسئول عن تَشْغِيل تلك الخطوات بهيئة تحريكة (animation).
</p>

<p>
	سنَفْحَص العمليات المُتعلِقة بمعالجة المصفوفات (array processing) اللازمة لتّنفِيذ برنامج لعبة الحياة (Game of Life). أولًا، يُمكِن لأي خلية أن تَكُون حية أو ميتة، لذلك سنَستخدِم بطبيعة الحال مصفوفة ثنائية البعد من النوع <code>boolean[][]‎</code> لتمثيل حالة جميع الخلايا، وليَكُن اسم تلك المصفوفة هو <code>alive</code>. ستُساوِي قيمة أي عنصر <code>alive[r][c]‎</code> القيمة <code>true</code> إذا كانت الخلية برقم الصف <code>r</code> ورقم العمود <code>c</code> حية. سنَستخدِم أيضًا نفس قيمة الثابت <code>GRID_SIZE</code> لتمثيل عدد الصفوف والأعمدة بتلك المصفوفة. يُمكِننا إذًا أن نَستخدِم حَلْقة التَكْرار <code>for</code> المُتداخلة (nested) التالية لمَلْئ قيم تلك المصفوفة المُمثِلة لشبكة الحياة بقيم عشوائية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_21" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> r </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> r</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> c</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫اضبط الخلية لتكون حية بنسبة احتمال تساوي 25%</span><span class="pln">
        alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.25</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعيد التعبير <code>Math.random() &lt; 0.25</code> القيمة <code>true</code> أو <code>false</code>، والتي يُمكِننا أن نُسنِدها (assign) إلى عنصر مصفوفة من النوع <code>boolean</code>. سنَستخدِم تلك المصفوفة لضَبْط لون كل خلية بالشاشة. نظرًا لأننا سنَرسِم شبكة الخلايا على شاشة من الصَنْف <code>MosaicCanvas</code>، فإننا سنَستخدِم واجهة برمجة التطبيقات (ِ<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) الخاصة بذلك الصنف لضَبْط ألوانها. ونظرًا لأن الصَنْف <code>MosaicCanvas</code> هو المسئول عن الرسم الفعليّ، سيَكُون برنامج لعبة الحياة مسئولًا فقط عن ضَبْط الألوان باِستخدَام واجهة برمجة التطبيقات الخاصة بالصَنْف. سنُعرِّف ذلك بتابع اسمه <code>showBoard()‎</code>، والذي ينبغي أن يُستدعَى بكل مرة تَتَغيَّر فيها رقعة الحياة. سنَستخدِم مجددًا حَلْقة تَكْرار <code>for</code> مُتداخِلة لضَبْط لون كل مربع بالشبكة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_23" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> r </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> r</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> c</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">])</span><span class="pln">
            display</span><span class="pun">.</span><span class="pln">setColor</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln">c</span><span class="pun">,</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">WHITE</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
            display</span><span class="pun">.</span><span class="pln">setColor</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln">c</span><span class="pun">,</span><span class="pln">null</span><span class="pun">);</span><span class="pln">  </span><span class="com">// اعرض لون الخلفية بالأسود</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بالطبع، يُعدّ حساب الحالة الجديدة لشبكة الخلايا بَعْد تطبيق قواعد اللعبة على حالتها الحالية هو الجزء الأكثر تشويقًا من البرنامج. لمّا كانت القواعد ستُطبَق على كل خلية على حدى، فإننا سنحتاج إلى اِستخدَام حَلْقة تَكْرار <code>for</code> مُتداخِلة للمرور عبر جميع خلايا الشبكة، ولكن ستَكُون المعالجة أكثر تعقيدًا هذه المرة. لاحظ أنه لا يُمكِننا أن نُجرِي أي تغييرات على قيم المصفوفة أثناء معالجتها؛ لأننا سنحتاج إلى معرفة الحالة القديمة لخلية معينة أثناء معالجة خلاياها المجاورة. سيَستخدِم البرنامج مصفوفة آخرى للاحتفاظ بالحالة الجديدة أثناء المعالجة، وعندما ننتهي من معالجة جميع الخلايا ضِمْن الشبكة، يُمكننا أن نَستخدِم المصفوفة الجديدة بدلًا من القديمة. يُمكِننا كتابة الخوارزمية (algorithm) بالشيفرة الوهمية (pseudocode) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_25" style="">
<span class="pln">let newboard be a </span><span class="kwd">new</span><span class="pln"> boolean</span><span class="pun">[][]</span><span class="pln"> array
</span><span class="kwd">for</span><span class="pln"> each row r</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> each column c</span><span class="pun">:</span><span class="pln">
        </span><span class="typ">Let</span><span class="pln"> N be the number of neighbors of cell </span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> in the alive array
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">((</span><span class="pln">N is </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> or </span><span class="pun">(</span><span class="pln">N is </span><span class="lit">2</span><span class="pln"> and alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]))</span><span class="pln">
            newboard</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
            newboard</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
alive </span><span class="pun">=</span><span class="pln"> newboard</span></pre>

<p>
	سيُشيِر <code>alive</code> عند انتهاء المعالجة إلى مصفوفة جديدة، وهو أمر لا ينبغي أن نقلق بشأنه طالما كانت محتويات المصفوفة الجديدة مُمثِلة للحالة الجديدة لشبكة الخلايا أما المصفوفة القديمة فسيُحرِّرها كانس المُهملات (garabage collector). قد لا يَكون اختبار ما إذا كان <code>newboard[r][c]‎</code> مُتحقِقًا أم لا واضحًا بما فيه الكفاية، ولكنه يُنفِّذ القواعد بشكل سليم. سنحتاج أيضًا لحِسَاب عدد الخلايا المجاورة. إذا كان لدينا خلية بالصف <code>r</code> والعمود <code>c</code>، ولم تَكُن تلك الخلية واقعة على حافة الرقعة، فإن الخلايا المجاورة لتلك الخلية هي كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59591" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/004Life_Neighbors.png.cbc2338a71acdf5dc1d55e4d24656348.png" rel=""><img alt="004Life_Neighbors.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59591" data-unique="iaba3jalp" src="https://academy.hsoub.com/uploads/monthly_2021_03/004Life_Neighbors.png.cbc2338a71acdf5dc1d55e4d24656348.png"></a>
</p>

<p>
	لاحِظ أن رقم الصف فوق الصف <code>r</code> يُساوِي <code>r-1</code> أما الصف أسفله فهو <code>r+1</code>. يَنطبِق نفس الأمر على الأعمدة. ينبغي إذًا أن نَفْحَص القيم <code>alive[r-1][c-1]‎</code> و <code>alive[r-1][c]‎</code> و <code>alive[r-1][c+1]‎</code> و <code>alive[r][c-1]‎</code> و <code>alive[r][c+1]‎</code> و <code>alive[r+1][c-1]‎</code> و <code>alive[r+1][c]‎</code> و <code>alive[r+1][c+1]‎</code>، ونَعِد منها تلكم المُحتوية على القيمة <code>true</code>. اِحرِص على فهم كيفية عمل فهارس المصفوفة.
</p>

<p>
	في المقابل، ستواجهنا مشكلة إذا كانت الخلية وَاقِعة على إحدى حواف الشبكة. في تلك الحالة، لا تَكُون بعض العناصر ضِمْن القائمة السابقة موجودة، وستَتَسبَّب محاولة الاشارة إليها إلى حدوث اِعترَاض (exception). لكي نَتَجنَّب حُدوث ذلك، ينبغي أن نُعامِل الخلايا الواقعة على الحواف بطريقة مختلفة. يُمكِننا مثلًا أن نَفْحَص ما إذا كان عنصر المصفوفة موجودًا قبل محاولة الإشارة إليه. في تلك الحالة، يُمكِننا مثلًا أن نَكْتُب شيفرة حساب عدد الخلايا المجاورة الحية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_27" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">c</span><span class="pun">-</span><span class="lit">1</span><span class="pun">])</span><span class="pln">
    N</span><span class="pun">++;</span><span class="pln"> </span><span class="com">// ‫خلية بالموضع (r-1,c-1) موجودة وحية</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">c</span><span class="pun">])</span><span class="pln">
    N</span><span class="pun">++;</span><span class="pln"> </span><span class="com">// خلية بالموضع‫ (r-1,c) موجودة وحية</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c</span><span class="pun">+</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> GRID_SIZE </span><span class="pun">&amp;&amp;</span><span class="pln"> alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">c</span><span class="pun">+</span><span class="lit">1</span><span class="pun">])</span><span class="pln">
    N</span><span class="pun">++;</span><span class="pln"> </span><span class="com">// ‫خلية بالموضع (r-1,c+1) موجودة وحية</span><span class="pln">
</span><span class="com">// إلخ</span></pre>

<p>
	سنتجنَّب بذلك كل الاعتراضات (exceptions). اِستخدَمنا في الواقع حلًا آخر شائع الاِستخدَام بألعاب الحاسوب ثنائية البعد. سنَتَظاهَر كما لو أن الحافة اليسرى للرقعة مُرتبطِة بالحافة اليمنى وكما لو أن الحافة العلوية مُرتبطِة بالحافة السفلية. بالنسبة لخلية برقم الصف 0 على سبيل المثال، فإن الصف أعلاها هو الصف الأخير أي بالرقم <code>GRID_SIZE-1</code>. سنَستخدِم أيضًا مُتْغيِّرات لتمثيل المَواضِع <code>above</code> و <code>below</code> و <code>left</code> و <code>right</code> الخاصة بخلية معينة. اُنظر شيفرة التابع المسئولة عن حساب الحالة الجديدة للرقعة، والتي ستَجِد أنها أبسط كثيرًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_29" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> doFrame</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// احسب الحالة الجديدة لرقعة لعبة الحياة</span><span class="pln">
    boolean</span><span class="pun">[][]</span><span class="pln"> newboard </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> boolean</span><span class="pun">[</span><span class="pln">GRID_SIZE</span><span class="pun">][</span><span class="pln">GRID_SIZE</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> r </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> r</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫تعد الصفوف أعلى وأسفل الصف r</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> above</span><span class="pun">,</span><span class="pln"> below</span><span class="pun">;</span><span class="pln"> 
        </span><span class="com">// ‫تعد الأعمدة على يمين ويسار العمود c</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> left</span><span class="pun">,</span><span class="pln"> right</span><span class="pun">;</span><span class="pln">  
        above </span><span class="pun">=</span><span class="pln"> r </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> r</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> GRID_SIZE</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">  </span><span class="com">// (for "?:" see Subsection 2.5.5)</span><span class="pln">
        below </span><span class="pun">=</span><span class="pln"> r </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> r</span><span class="pun">+</span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">;</span><span class="pln"> c</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            left </span><span class="pun">=</span><span class="pln">  c </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> c</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> GRID_SIZE</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
            right </span><span class="pun">=</span><span class="pln"> c </span><span class="pun">&lt;</span><span class="pln"> GRID_SIZE</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> c</span><span class="pun">+</span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// عدد الخلايا الحية المجاورة</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">above</span><span class="pun">][</span><span class="pln">left</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">above</span><span class="pun">][</span><span class="pln">c</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">above</span><span class="pun">][</span><span class="pln">right</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">left</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">right</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">below</span><span class="pun">][</span><span class="pln">left</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">below</span><span class="pun">][</span><span class="pln">c</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">below</span><span class="pun">][</span><span class="pln">right</span><span class="pun">])</span><span class="pln">
                n</span><span class="pun">++;</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(</span><span class="pln">alive</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> n </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pun">))</span><span class="pln">
                newboard</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">else</span><span class="pln">
                newboard</span><span class="pun">[</span><span class="pln">r</span><span class="pun">][</span><span class="pln">c</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    alive </span><span class="pun">=</span><span class="pln"> newboard</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُمكِنك الإطلاع على شيفرة البرنامج بالملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/Life.java" rel="external nofollow">Life.java</a>، وأن تُجرِبه. لا تنسى أنك ستحتاج إلى اِستخدَام الملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/MosaicCanvas.java" rel="external nofollow">MosaicCanvas.java</a> أيضًا.
</p>

<h2>
	لعبة داما (Checkers)
</h2>

<p>
	سنَفْحَص الآن مثالًا أكثر واقعية لاستخدام المصفوفات ثنائية البعد. يُعدّ هذا البرنامج هو الأطول حتى الآن حيث يَتَكوَّن من 745 سطر. يَسمَح ذلك البرنامج للمُستخدمين بلعب مباراة داما (checkers). تتكوَّن لعبة الداما من رقعة مُكوَّنة من 8 صفوف و8 أعمدة. سنعتمد على المثال الذي كتبناه بالقسم الفرعي 6.5.1. سنُسمِي اللاعبين بأسماء "أحمر" و "أسود" وفقًا للون قطع الداما الخاصة بهما. لن نشرح قواعد لعبة الداما هنا، ولربما تستطيع أن تتعلَّمها بينما تُجرِّب البرنامج.
</p>

<p>
	يستطيع اللاعب أن يَتحرَك بالنَقْر على القطعة التي يريد أن يُحرِكها ثُمَّ بالنَقْر على المربع الفارغ الذي يريد أن يَنقِل إليه تلك القطعة. سيَرسِم البرنامج بأي لحظة إطارًا حول المربعات التي يستطيع اللاعب أن يَنقُر عليها بلون أفتح قليلًا كنوع من المساعدة. على سبيل المثال، سيُحَاط المربع المُتضمِّن للقطعة التي اختارها المُستخدِم للحركة بإطار أصفر اللون بينما ستُحَاط القطع الآخرى التي بإمكانه تَحرِيكها بإطار أزرق سماوي. إذا كان المُستخدِم قد اختار قطعة بالفعل لتَحرِيكها، فستُحَاط جميع المربعات الفارغة التي يستطيع أن يُحرِك إليها تلك القطعة بإطار أخضر اللون. ستَتبِع اللعبة قاعدة تَنُص على أنه إذا كان اللاعب الحالي قادرًا على القفز (jump) على إحدى قطع الخصم، فإنه لابُدّ أن يَقفِز. عندما تُصبِح إحدى القطع "قطعة ملك" أي بَعْد وصولها إلى الحافة الأخرى من الرقعة، سيَرسِم البرنامج حرف "K" أبيض كبير على تلك القطعة. تَعرِض الصورة التالية لقطة شاشة مبكرة من اللعبة، والتي تُظهِر اللاعب الأسود وقد اختار القطعة الموجودة بالمربع ذو الإطار الأصفر للحركة. يستطيع اللاعب الآن أن يَنقُر على إحدى المربعات المُحاطَة بإطار أخضر لكي يُكمِل تلك الحركة أو قد يَنقُر على إحدى المربعات المُحاطَة بإطار أزرق سماوي ليختار قطعة آخرى لتَحرِيكها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59592" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/005Checkers_Game.png.486900ac818614abcf9282eca42ded26.png" rel=""><img alt="005Checkers_Game.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59592" data-unique="el9kv67tm" src="https://academy.hsoub.com/uploads/monthly_2021_03/005Checkers_Game.png.486900ac818614abcf9282eca42ded26.png"></a>
</p>

<p>
	سنَمُر عبر جزءًا من شيفرة ذلك المثال، ولكن يُمكِنك الاطلاع على الشيفرة بالكامل بالملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/Checkers.java" rel="external nofollow">Checkers.java</a>. قد يَكُون البرنامج طويلا ومعقدًا، ولكنه بقليل من الدراسة، ستَتَمكَّن من فهم جميع التقنيات المُستخدَمة به. يُعدّ البرنامج مثالًا جيدًا على البرمجة كائنية التوجه (object-oriented) المُعتمدِة على كُلًا من الأحداث (event-driven) والحالات (state-based).
</p>

<p>
	سنُخزِّن بيانات قطع الرقعة بمصفوفة ثنائية البعد (two-dimensional array). نظرًا لأن البرنامج مُعقَد نوعًا ما، سنُقسِّمه إلى عدة أصناف. إلى جانب الصَنْف الأساسي، سنُعرِّف بعض الأصناف المُتداخِلة (nested) الآخرى منها الصَنْف <code>CheckersData</code> المُستخدَم لمعالجة بيانات الرقعة. لا يُعدّ ذلك الصَنْف مسئولًا مباشرًا عن أي جزء من الرسوم (graphics) أو مُعالجة الأحداث (event-handling)، ولكنه يُوفِّر التوابع (methods) التي يُمكِننا أن نستدعيها بأصناف آخرى لأغراض معالجة الرسوم والأحداث (events). سنناقش ذلك الصَنْف قليلًا.
</p>

<p>
	يَحتوِي الصَنْف <code>CheckersData</code> على مُتْغيِّر نُسخة (instance variables) اسمه هو <code>board</code> من النوع <code>int[][]‎‎</code>. تُضبَط قيمة ذلك المُتْغيِّر إلى <code>new int[8][8]‎</code> لتُمثِل شبكة 8x8 من الأعداد الصحيحة (integers). تُستخدَم ثوابت (constants) لتعريف القيم المُحتمَل تَخْزِينها بمربع برقعة شطرنج (checkboard):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_31" style="">
<span class="kwd">static</span><span class="pln"> final </span><span class="typ">int</span><span class="pln">
          EMPTY </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">           </span><span class="com">// يمثل مربعًا فارغًا</span><span class="pln">
          RED </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">             </span><span class="com">// قطعة حمراء عادية</span><span class="pln">
          RED_KING </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln">        </span><span class="com">// قطعة ملك حمراء</span><span class="pln">
          BLACK </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">           </span><span class="com">// قطعة سوداء عادية</span><span class="pln">
          BLACK_KING </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">      </span><span class="com">// قطعة ملك سوداء</span></pre>

<p>
	سنَستخدِم أيضًا الثابتين <code>RED</code> و <code>BLACK</code> بالبرنامج لتَمثيِل لاعبي المباراة. عندما تبدأ المباراة، ستُضبَط قيم المصفوفة لتمثيل الحالة المبدئية للرقعة، وستبدو كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59593" data-ss1615728745="1" data-ss1615728959="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/006Checkerboard_Contents.png.9edbcab068d8b33858fd3218a9f1ec8c.png" rel=""><img alt="006Checkerboard_Contents.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59593" data-unique="thto50uix" src="https://academy.hsoub.com/uploads/monthly_2021_03/006Checkerboard_Contents.png.9edbcab068d8b33858fd3218a9f1ec8c.png"></a>
</p>

<p>
	يُمكِن لأي قطعة سوداء عادية أن تتحرك لأسفل الشبكة فقط أي لابُدّ أن يكون رقم الصف للمربع الذي ستنتقل إليه أكبر من رقم الصف للمربع القادمة منه. بالمقابل، تستطيع أي قطعة حمراء عادية أن تَتَحرك لأعلى الشبكة فقط. يُمكِن للملوك بأي من اللونين الحركة بكلا الاتجاهين.
</p>

<p>
	لابُدّ أن ينتبه الصَنْف <code>CheckersData</code> لأي تَغييرات ينبغي إجراؤها على هياكل البيانات (data structures) عندما يُحرِك أحد اللاعبين قطعة معينة بالرقعة. في الواقع، يُعرِّف الصَنْف تابع النسخة <code>makeMove()‎</code> المُخصَّص لذلك الغرض. عندما يُحرِك لاعب قطعة من مربع إلى آخر، سيُعدِّل التابع قيمة عنصرين ضِمْن المصفوفة. بالإضافة إلى ذلك، إذا كانت الحركة عبارة عن قفزة (jump)، ستُحذَف القطعة التي كانت موجودة بالمربع المقفوز إليه من الرقعة. يستطيع التابع أن يُحدِّد ما إذا كانت حركة معينة عبارة عن قفزة أم لا بِفْحَص ما إذا كان المربع الذي تَحرَكت إليه القطعة يَبعُد بمقدار صفين عن المربع الذي بدأت منه. إلى جانب ما سبق، تُصبِح القطعة الحمراء <code>RED</code> التي تتحرك إلى الصف 0 أو القطعة السوداء <code>Black</code> التي تتحرك إلى الصف 7 بمثابة قطعة ملك (king). سنَضَع جميع ما سبق ببرنامج فرعي (subroutine)، وبالتالي، لن يضطّر باقي البرنامج للقلق بشأن أي من تلك التفاصيل السابقة. يُمكِنه فقط أن يَستدعِي التابع <code>makeMove()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_33" style="">
<span class="kwd">void</span><span class="pln"> makeMove</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> fromRow</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> fromCol</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> toRow</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> toCol</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   board</span><span class="pun">[</span><span class="pln">toRow</span><span class="pun">][</span><span class="pln">toCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">fromRow</span><span class="pun">][</span><span class="pln">fromCol</span><span class="pun">];</span><span class="pln"> </span><span class="com">// حرك القطعة</span><span class="pln">
   board</span><span class="pun">[</span><span class="pln">fromRow</span><span class="pun">][</span><span class="pln">fromCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> EMPTY</span><span class="pun">;</span><span class="pln"> </span><span class="com">// المربع الذي تحركت منه أصبح فارغًا</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fromRow </span><span class="pun">-</span><span class="pln"> toRow </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> fromRow </span><span class="pun">-</span><span class="pln"> toRow </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// الحركة كانت قفزة لذلك احذف القطعة المقفوز إليها من الرقعة</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> jumpRow </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fromRow </span><span class="pun">+</span><span class="pln"> toRow</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> </span><span class="com">// صف القطعة المقفوز إليها</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> jumpCol </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fromCol </span><span class="pun">+</span><span class="pln"> toCol</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> </span><span class="com">// عمود القطعة المقفوز إليها</span><span class="pln">
      board</span><span class="pun">[</span><span class="pln">jumpRow</span><span class="pun">][</span><span class="pln">jumpCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> EMPTY</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">toRow </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">toRow</span><span class="pun">][</span><span class="pln">toCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> RED</span><span class="pun">)</span><span class="pln">
      board</span><span class="pun">[</span><span class="pln">toRow</span><span class="pun">][</span><span class="pln">toCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> RED_KING</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تصبح القطعة الحمراء ملك</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">toRow </span><span class="pun">==</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">toRow</span><span class="pun">][</span><span class="pln">toCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLACK</span><span class="pun">)</span><span class="pln">
      board</span><span class="pun">[</span><span class="pln">toRow</span><span class="pun">][</span><span class="pln">toCol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BLACK_KING</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تصبح القطعة السوداء ملك</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// end makeMove()</span></pre>

<p>
	ينبغي أن يَسمَح الصَنْف <code>CheckersData</code> بالعثور على جميع الحركات الصالحة بالرقعة. سنستخدم كائنًا ينتمي للصَنْف التالي لتمثيل الحركة داخل لعبة داما:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_35" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> fromRow</span><span class="pun">,</span><span class="pln"> fromCol</span><span class="pun">;</span><span class="pln">  </span><span class="com">// موضع القطعة المطلوب تحريكها</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> toRow</span><span class="pun">,</span><span class="pln"> toCol</span><span class="pun">;</span><span class="pln">      </span><span class="com">// المربع الذي انتقلت إليه القطعة</span><span class="pln">

   </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> r1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// اضبط قيم متغيرات النسخة</span><span class="pln">
      fromRow </span><span class="pun">=</span><span class="pln"> r1</span><span class="pun">;</span><span class="pln">
      fromCol </span><span class="pun">=</span><span class="pln"> c1</span><span class="pun">;</span><span class="pln">
      toRow </span><span class="pun">=</span><span class="pln"> r2</span><span class="pun">;</span><span class="pln">
      toCol </span><span class="pun">=</span><span class="pln"> c2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   boolean isJump</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// اختبر ما إذا كانت الحركة عبارة عن قفزة</span><span class="pln">
       </span><span class="com">// بالقفزة، تتحرك القطعة مسافة صفين</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fromRow </span><span class="pun">-</span><span class="pln"> toRow </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> fromRow </span><span class="pun">-</span><span class="pln"> toRow </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// end class CheckersMove.</span></pre>

<p>
	يُعرِّف الصَنْف <code>CheckersData</code> تابع نسخة (instance method) للعثور على جميع الحركات المتاحة حاليًا لكل لاعب. يُعدّ ذلك التابع بمثابة دالة (function) تُعيِد مصفوفة من النوع <code>CheckersMove[]‎</code> تحتوي على جميع الحركات المتاحة مُمثَلة بهيئة كائنات من النوع <code>CheckersMove</code>. يُمكِننا إذًا كتابة توصيف التابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_37" style="">
<span class="typ">CheckersMove</span><span class="pun">[]</span><span class="pln"> getLegalMoves</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> player</span><span class="pun">)</span></pre>

<p>
	يُمكِننا كتابة خوارزمية التابع بالشيفرة الوهمية (pseudocode) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_39" style="">
<span class="com">// ابدأ بقائمة فارغة من الحركات</span><span class="pln">
</span><span class="typ">Start</span><span class="pln"> with an empty </span><span class="typ">list</span><span class="pln"> of moves
</span><span class="com">// ابحث عن أي قفزات صالحة و أضفها إلى القائمة</span><span class="pln">
</span><span class="typ">Find</span><span class="pln"> any legal jumps and add them to the </span><span class="typ">list</span><span class="pln">
</span><span class="com">// إذا لم يكن هناك أي قفزات صالحة</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> there are no jumps</span><span class="pun">:</span><span class="pln">
    </span><span class="com">// ابحث عن الحركات العادية الأخرى و أضفها إلى القائمة</span><span class="pln">
   </span><span class="typ">Find</span><span class="pln"> any other legal moves and add them to the </span><span class="typ">list</span><span class="pln">
</span><span class="com">// إذا كانت القائمة فارغة</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> the </span><span class="typ">list</span><span class="pln"> is empty</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> null
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> the </span><span class="typ">list</span></pre>

<p>
	الآن، ما هو المقصود بالقائمة (list)؟ في الواقع، ينبغي أن نُعيد الحركات المسموح بها ضِمْن مصفوفة، ولكن لأن المصفوفات ثابتة الحجم، لا يُمكِننا أن نُنشِئها حتى نَعرِف عدد الحركات، وهو في الواقع ما لا يُمكِننا معرفته حتى نَصِل إلى نهاية التابع أي بَعْد أن نَكُون قد أنشأنا القائمة بالفعل! أحد الحلول إذًا هو اِستخدَام مصفوفة ديناميكية من النوع <code>ArrayList</code> بدلًا من مصفوفة عادية لكي تَحمِل الحركات بينما نَجِدها. يَستخدِم البرنامج بالأسفل كائنًا من النوع <code>ArrayList&lt;CheckersMove&gt;‎</code> لكي يُمكِّن القائمة من حَمْل كائنات من الصَنْف <code>CheckersMove</code> فقط. بينما نُضيِف الحركات إلى تلك القائمة، سيزداد حجمها بما يتناسب مع عدد الحركات. يُمكِننا أن نُنشِئ المصفوفة المطلوبة ونَنسَخ البيانات إليها بنهاية التابع. اُنظر الشيفرة الوهمية التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_41" style="">
<span class="com">// ‫أنشئ قائمة moves فارغة للحركات</span><span class="pln">
</span><span class="typ">Let</span><span class="pln"> </span><span class="str">"moves"</span><span class="pln"> be an empty </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">CheckersMove</span><span class="pun">&gt;</span><span class="pln">
</span><span class="com">// ابحث عن القفزات المسموح بها و أضفها إلى القائمة</span><span class="pln">
</span><span class="typ">Find</span><span class="pln"> any legal jumps and add them to moves
</span><span class="com">// إذا كان عدد الحركات ما يزال يساوي 0</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> is </span><span class="lit">0</span><span class="pun">:</span><span class="pln">  </span><span class="com">// There are no legal jumps!</span><span class="pln">
    </span><span class="com">// ابحث عن الحركات العادية الصالحة و أضفها إلى القائمة</span><span class="pln">
   </span><span class="typ">Find</span><span class="pln"> any other legal moves and add them to moves
</span><span class="com">// إذا لم يكن عدد الحركات يساوي 0</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> is </span><span class="lit">0</span><span class="pun">:</span><span class="pln">  </span><span class="com">// There are no legal moves at all!</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> null
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="com">// ‫عرف مصفوفة moveArray من النوع CheckersMoves</span><span class="pln">
    </span><span class="com">// ‫طولها يساوي moves.size()</span><span class="pln">
   </span><span class="typ">Let</span><span class="pln"> moveArray be an array of </span><span class="typ">CheckersMoves</span><span class="pln"> of length moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln">
   </span><span class="com">// ‫انسخ محتويات المصفوفة moves إلى moveArray</span><span class="pln">
   </span><span class="typ">Copy</span><span class="pln"> the contents of moves into moveArray
   </span><span class="kwd">return</span><span class="pln"> moveArray</span></pre>

<p>
	علينا الآن أن نُحدِّد الحركات والقفزات المسموح بها. تَتَوفَّر المعلومات التي نحتاج إليها بالمصفوفة <code>board</code>، ولكن يَلزَمنا بعض العمل لاِستخراجها. لابُدّ أن نَفْحَص جميع المواضع بالمصفوفة ونَعثُر على القطع التي تنتمي للاعب الحالي. بَعْد ذلك، ينبغي أن نَفْحَص المربعات التي يُمكِن لكل قطعة أن تنتقل إليها، ونُحدِّد ما إذا كانت تلك الحركة صالحة أم لا. إذا كنا نبحث عن القفزات المسموح بها، فينبغي أن نَفْحَص المربعات التي تَبعُد بمسافة صفين وعمودين من كل قطعة. يُمكِننا إذًا أن نُوسِّع سطر الخوارزمية "اِبحَث عن القفزات المسموح بها ثم أَضِفها إلى الحركات" كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_43" style="">
<span class="com">// لكل صف بالرقعة</span><span class="pln">
</span><span class="typ">For</span><span class="pln"> each row of the board</span><span class="pun">:</span><span class="pln">
</span><span class="com">// لكل عمود بالرقعة</span><span class="pln">
   </span><span class="typ">For</span><span class="pln"> each column of the board</span><span class="pun">:</span><span class="pln">    
        </span><span class="com">// إذا كانت إحدى قطع اللاعبين بذلك المكان</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> one of the player</span><span class="str">'</span><span class="pln">s pieces is at </span><span class="kwd">this</span><span class="pln"> location</span><span class="pun">:</span><span class="pln">
          </span><span class="com">// ‫إذا كان من الممكن القفز إلى الموضع row+2,column+2</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> it is legal to jump to row </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
             </span><span class="com">// ‫أضف تلك الحركة إلى moves</span><span class="pln">
             add </span><span class="kwd">this</span><span class="pln"> move to moves
         </span><span class="com">// ‫إذا كان من الممكن القفز إلى الموضع row-2,column+2</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> it is legal to jump to row </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
             add </span><span class="kwd">this</span><span class="pln"> move to moves
         </span><span class="com">// ‫إذا كان من الممكن القفز إلى الموضع row+2,column-2</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> it is legal to jump to row </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
             add </span><span class="kwd">this</span><span class="pln"> move to moves
         </span><span class="com">// ‫إذا كان من الممكن القفز إلى الموضع row-2,column-2</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> it is legal to jump to row </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
             add </span><span class="kwd">this</span><span class="pln"> move to moves</span></pre>

<p>
	ينبغي أن نُوسِّع السطر "ابحث عن الحركات الصالحة الآخرى وأضفها إلى قائمة الحركات" بنفس الطريقة باستثناء أن علينا الآن فَحْص المربعات الأربعة التي تَبعُد مسافة صف واحد وعمود واحد من كل قطعة. لاحِظ أن اختبار ما إذا كان لاعب معين يستطيع أن يُحرِك قطعة من مربع معين إلى مربع آخر ليس بالأمر السهل. لابُدّ أن يَكُون المربع الذي ينتقل إليه اللاعب موجودًا بالرقعة، كما ينبغي أن يَكُون فارغًا. علاوة على ذلك، تستطيع القطع الحمراء والسوداء العادية أن تَتَحرَك باتجاه واحد. سنَستخدِم التابع (method) التالي لفْحَص ما إذا كان لاعب معين قادر على القيام بتحريك مربع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_45" style="">
<span class="kwd">private</span><span class="pln"> boolean canMove</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> player</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">r2 </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> r2 </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">8</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> c2 </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> c2 </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">8</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ‫(r2,c2) خارج الرقعة </span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">board</span><span class="pun">[</span><span class="pln">r2</span><span class="pun">][</span><span class="pln">c2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> EMPTY</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ‫(r2,c2) يحتوي بالفعل على قطعة</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">==</span><span class="pln"> RED</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">board</span><span class="pun">[</span><span class="pln">r1</span><span class="pun">][</span><span class="pln">c1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> RED </span><span class="pun">&amp;&amp;</span><span class="pln"> r2 </span><span class="pun">&gt;</span><span class="pln"> r1</span><span class="pun">)</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// القطع الحمراء العادية يمكنها أن تتحرك للأسفل فقط</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">  </span><span class="com">// الحركة صالحة</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">board</span><span class="pun">[</span><span class="pln">r1</span><span class="pun">][</span><span class="pln">c1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> BLACK </span><span class="pun">&amp;&amp;</span><span class="pln"> r2 </span><span class="pun">&lt;</span><span class="pln"> r1</span><span class="pun">)</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// القطع السوداء العادية يمكنها أن تتحرك للأعلى فقط</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">  </span><span class="com">// الحركة صالحة</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// end canMove()</span></pre>

<p>
	يَستدعِى التابع <code>getLegalMoves()‎</code> التابع <code>canMove()‎</code> المُعرَّف بالأعلى ليَفْحَص ما إذا كانت حركة مُحتمَلة لقطعة معينة صالحة فعليًا أم لا. سنُعرِّف تابعًا مشابهًا لفَحْص ما إذا كانت قفزة (jump) معينة صالحة أم لا. في تلك الحالة، سنُمرِّر للتابع كُلًا من مربع القطعة والمربع الذي يُحتمَل أن تَنتقِل إليه وكذلك المربع الواقع بين هذين المربعين أي ذلك الذي سيَقفِز اللاعب فوقه. لابُدّ أن يَحتوِي المربع المقفوز إليه على إحدى قطع الخصم. يُمكننا تَوصِيف ذلك التابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_47" style="">
<span class="kwd">private</span><span class="pln"> boolean canJump</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> player</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c1</span><span class="pun">,</span><span class="pln"> 
                                   </span><span class="typ">int</span><span class="pln"> r2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> r3</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> c3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	ينبغي الآن أن تَكُون قادرًا على فِهم شيفرة التابع <code>getLegalMoves()‎</code> بالكامل. يَدمِج ذلك التابع عدة مواضيع معًا: المصفوفات أحادية البعد (one-dimensional) والمصفوفات الديناميكية <code>ArrayLists</code> والمصفوفات ثنائية البعد (two-dimensional):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7692_49" style="">
<span class="typ">CheckersMove</span><span class="pun">[]</span><span class="pln"> getLegalMoves</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">!=</span><span class="pln"> RED </span><span class="pun">&amp;&amp;</span><span class="pln"> player </span><span class="pun">!=</span><span class="pln"> BLACK</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">  </span><span class="com">// لن يحدث هذا ببرنامج سليم</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> playerKing</span><span class="pun">;</span><span class="pln">  </span><span class="com">// The constant for a King belonging to the player.</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">player </span><span class="pun">==</span><span class="pln"> RED</span><span class="pun">)</span><span class="pln">
      playerKing </span><span class="pun">=</span><span class="pln"> RED_KING</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">else</span><span class="pln">
      playerKing </span><span class="pun">=</span><span class="pln"> BLACK_KING</span><span class="pun">;</span><span class="pln">

   </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">CheckersMove</span><span class="pun">&gt;</span><span class="pln"> moves </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">CheckersMove</span><span class="pun">&gt;();</span><span class="pln">  
    </span><span class="com">// ستخزن الحركة ضمن القائمة</span><span class="pln">

    </span><span class="com">// تحقق من جميع القفزات الممكنة</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> row </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span><span class="pln"> row</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> col </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> col </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span><span class="pln"> col</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">board</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> player </span><span class="pun">||</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> playerKing</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canJump</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
               moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canJump</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
               moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">+</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canJump</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
               moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">+</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canJump</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
               moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">-</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// إذا وجدت أي قفزات ممكنة، فلابد أن يقفز اللاعب. لذلك لن نضيف أي </span><span class="pln">
    </span><span class="com">// حركات عادية. أما إذا لم تجد أي قفزات، ابحث عن أي حركات عادية</span><span class="pln">

   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> row </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span><span class="pln"> row</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> col </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> col </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">8</span><span class="pun">;</span><span class="pln"> col</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">board</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> player </span><span class="pun">||</span><span class="pln"> board</span><span class="pun">[</span><span class="pln">row</span><span class="pun">][</span><span class="pln">col</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> playerKing</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canMove</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
                 moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
              </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canMove</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
                 moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">+</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
              </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canMove</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
                 moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
              </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canMove</span><span class="pun">(</span><span class="pln">player</span><span class="pun">,</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
                 moves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">(</span><span class="pln">row</span><span class="pun">,</span><span class="pln">col</span><span class="pun">,</span><span class="pln">row</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln">col</span><span class="pun">-</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// إذا لم تجد أي حركات صالحة، أعد فراغا</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// انشئ مصفوفة وأضف إليها الحركات المسموح بها</span><span class="pln">
      </span><span class="typ">CheckersMove</span><span class="pun">[]</span><span class="pln"> moveArray </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckersMove</span><span class="pun">[</span><span class="pln">moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()];</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> moves</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
         moveArray</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> moves</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> moveArray</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// end getLegalMoves</span></pre>

<p>
	يُعدّ برنامج الداما مُعقدًا نوعًا ما، ويحتاج إلى الكثير من التصميم الجيد لتَقْرِير الأصناف (classes) والكائنات (classes) المُستخدمَة، وكذلك لتَقرِير التوابع (methods) التي ينبغي كتابتها، وكذلك لتقرير الخوارزميات (algorithms) التي ينبغي لتلك التوابع اِستخدَامها. يُمكِنك الاطلاع على شيفرة البرنامج بالكامل بالملف <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/source/chapter7/Checkers.java" rel="external nofollow">Checkers.java</a>.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/c7/s5.html" rel="external nofollow">Section 5: Two-dimensional Arrays</a> من فصل Chapter 7: Arrays and ArrayLists من كتاب <a data-ss1615728745="1" data-ss1615728959="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1161</guid><pubDate>Mon, 05 Apr 2021 13:06:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x62D;&#x62B; &#x648;&#x627;&#x644;&#x62A;&#x631;&#x62A;&#x64A;&#x628; &#x641;&#x64A; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; Array &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-array-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1160/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e068909df1_-.png.b696029750bb14d4295008d22ff5ed05.png" /></p>
<p>
	تُعدّ عمليتي البحث (searching) والترتيب (sorting) أكثر الأساليب شيوعًا لمعالجة المصفوفات. يُشير البحث (searching) هنا إلى محاولة العثور على عنصر بمواصفات معينة ضِمْن مصفوفة بينما يُشير الترتيب (sorting) إلى إعادة ترتيب عناصر المصفوفة ترتيبًا تصاعديًا أو تنازليًا. عادةً ما يَعتمِد المقصود بالترتيب التصاعدي والتنازلي على السياق المُستخدَم به الترتيب. في الواقع، تُوفِّر الجافا تُوفِّر بعض التوابع المَبنية مُسْبَقّا (built-in methods) الخاصة بعمليات البحث والترتيب -كما رأينا بالقسم الفرعي 7.2.2-، ومع ذلك ينبغي أن تَكُون على اطلاع ومعرفة بالخوارزميات (algorithms) التي تَستخدِمها تلك التوابع. ولهذا، سنَتعلَّم بعضًا من تلك الخوارزميات بهذا القسم.
</p>

<p>
	عادةً ما يُناقَش البحث (searching) والترتيب (sorting) نظريًا باِستخدَام مصفوفة من الأعداد. ولكن من الناحية العملية، هناك أمور أكثر تشويقًا بكثير. فمثلًا، قد تَكُون المصفوفة عبارة عن قائمة بريدية بحيث يَكُون كل عنصر بها عبارة عن كائن (object) يَحتوِي على اسم وعنوان. إذا كان لدينا اسم شخص معين، يُمكِننا العثور على عنوانه، وهو ما يُعدّ مثالًا على عملية البحث (searching)؛ لأنك ببساطة تَبحَث عن كائن ضِمْن مصفوفة بالاعتماد على اسم الشخص. سيَكُون من المفيد أيضًا لو تَمَكَّنا من ترتيب المصفوفات وفقًا لمعيار معين مثل أن نُرتِّب المصفوفة السابقة بحيث تَكُون الأسماء مُرتَّبة ترتيبًا أبجديًا أو قد نُرتِّبها وفقًا للرقم البريدي.
</p>

<p>
	سنُعمِّم الآن المثال السابق إلى تصور مُجرَّد بعض الشئ: لنتخيل أن لدينا مصفوفة تَحتوِي على عدة كائنات (objects)، وأننا نرغب بالبحث داخل تلك المصفوفة أو نرغب بترتيبها بالاعتماد على احدى مُتْغيِّرات النُسخ (instance variables) المُعرَّفة بتلك الكائنات. سنلجأ إلى اِستخدَام بعض المصطلحات الشائعة بقواعد البيانات (databases). سنُطلِق اسم "تسجيل (record)" على كل كائن ضِمْن المصفوفة بينما سنُطلِق اسم "الحقول (fields)" على مُتْغيِّرات النُسخ المُعرَّفة بتلك الكائنات. نستطيع الآن أن نُعيِد صياغة مثال القائمة البريدية إلى ما يلي: يَتَكوَّن كل تسجيل (record) من اسم وعنوان. قد تَتَكوَّن حقول التسجيل من الاسم الأول والأخير والعنوان والمدينة والدولة والرقم البريدي. ينبغي أن نختار إحدى تلك الحقول لتُصبِح "مفتاحًا (key)" لكي نَتَمكَّن من إجراء عمليتي البحث والترتيب. وفقًا لهذا التصور، سيُمثِل البحث محاولة العثور على تسجيل بالمصفوفة بحيث يَحتوِي مفتاحه على قيمة معينة بينما سيُمثِل الترتيب (sorting) تَبديِل مواضع تسجيلات المصفوفة إلى أن تُصبِح مفاتيحها (keys) مُرتَّبة ترتيبًا تصاعديًا أو تنازليًا.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	البحث (Searching)
</h2>

<p>
	هناك خوارزمية واضحة للبحث عن عنصر معين داخل مصفوفة: افحص كل عنصر بالمصفوفة بالترتيب، واختبر ما إذا كانت قيمة ذلك العنصر هي نفسها القيمة التي تبحث عنها. إذا كان كذلك، فقد انتهت عملية البحث إذًا. أما إذا لم تعثر عليه بعد فحص جميع عناصر المصفوفة، فهو إذًا غير موجود بها. يُمكننا كتابة برنامج فرعي لتنفيذ تلك الخوارزمية (algorithm) بسهولة. بفرض أن المصفوفة التي تريد البحث بها عبارة عن مصفوفة من النوع <code>int[]‎</code>، يبحث التابع (method) التالي عن قيمة عددية ضمن مصفوفة. سيعيد التابع فهرس (index) العنصر بالمصفوفة إذا عثر عليه بينما سيعيد -1 إذا لم يعثر عليه كإشارة أن العدد الصحيح غير موجود:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_7" style=""><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> find</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> index</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> N </span><span class="pun">)</span><span class="pln"> 
          </span><span class="com">// ‫عثرنا على N بهذا الفهرس</span><span class="pln">
         </span><span class="kwd">return</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">  
   </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// ‫إذا وصلنا إلى هنا، فإن N غير موجودة بأي مكان ضمن المصفوفة </span><span class="pln">
    </span><span class="com">// أعد القيمة -1</span><span class="pln">

   </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	يُطلَق على طريقة البحث السابقة اسم البحث الخطي (linear search). إذا لم يَكُن لدينا أي معلومة عن كيفية ترتيب العناصر ضِمْن المصفوفة، فإنها تُمثِل الخيار الأفضل. في المقابل، إذا كنا على علم بكَوْن عناصر المصفوفة مُرتَّبة تصاعديًا أو تنازليًا، يُمكِننا إذًا أن نستغل تلك الحقيقة، ونَستخدِم خوارزميات أسرع. إذا كانت عناصر مصفوفة معينة مرتبة، يُقال عليها أنها "مصفوفة مرتبة (sorted)". يَستغرق ترتيب عناصر مصفوفة بعض الوقت بالطبع، ولكن إذا كانت المصفوفة مرتبة بالفعل، فيُمكننا إذًا أن نستغل تلك الحقيقة.
</p>

<p>
	يُعدّ البحث الثنائي (binary search) أحد الطرائق المُتاحة للبحث عن عنصر ضِمْن مصفوفة مُرتَّبة (sorted). على الرغم من أن تّنْفِيذه (implement) ليس سهلًا تمامًا، فإن فكرته بسيطة: إذا كنت تَبحَث عن عنصر ضِمْن قائمة مُرتَّبة، يُمكِنك أن تَستبعِد نُصف العناصر ضِمْن القائمة بِفَحْص عنصر واحد فقط. على سبيل المثال، لنَفْترِض أننا نبحث عن العدد 42 ضِمْن مصفوفة مُرتَّبة مُكوَّنة من 1000 عدد صحيح، ولنَفْترِض أن المصفوفة مُرتَّبة ترتيبًا تصاعديًا، ولنَفْترِض أننا فَحصَنا العنصر الموجود بالفهرس 500، ووجدنا أن قيمته تُساوِي 93. لمّا كان العدد 42 أقل من 93، ولمّا كانت العناصر بالمصفوفة مُرتَّبة ترتيبًا تصاعديًا، يُمكِننا إذًا أن نَستنتج أنه في حالة كان العدد 42 موجودًا بتلك المصفوفة من الأساس، فإنه لابُدّ وأنه يَقَع بمَوضِع فهرسه أقل من 500. يُمكِننا إذًا أن نَستبِعد جميع العناصر الموجودة بمَواضِع فهرسها أكبر من 500؛ فهي بالضرورة أكبر من أو تُساوِي 93.
</p>

<p>
	الخطوة التالية ببساطة هي فَحْص قيمة العنصر بالمَوْضِع 250. إذا كان العدد بذلك المَوْضِع يُساوِي -21 مثلًا، يُمكِننا إذًا أن نَستبعِد جميع العناصر قَبل المَوْضِع 250، ونُقصِر بحثنا على المَواضِع من 251 إلى 499. سيُقصِر الاختبار التالي بحثنا إلى 125 مَوضِع فقط ثُمَّ إلى 62. سيَتَبقَّى مَوضِع واحد فقط بعد 10 خطوات. تُعدّ تلك الطريقة أفضل كثيرًا من فَحْص كل عنصر ضِمْن المصفوفة. فمثلًا، إذا كانت المصفوفة تَحتوِي على مليون عنصر، ستَستغرِق خوارزمية البحث الثنائي 20 خطوة فقط للبحث بكامل المصفوفة. في العموم، يُساوِي عدد الخطوات اللازمة للبحث بأي مصفوفة لوغاريتم عدد العناصر بتلك المصفوفة بالنسبة للأساس 2.
</p>

<p>
	لكي نَتَمكَّن من تَحْوِيل خوارزمية البحث الثنائي (binary search) إلى برنامج فرعي (subroutine) يَبحَث عن عنصر <code>N</code> ضِمْن مصفوفة <code>A</code>، ينبغي أن نَحتفِظ بنطاق المَوْاضِع التي يُحتمَل أن تَحتوِي على <code>N</code> بحيث نَستبعِد منها تدريجيًا المزيد من الاحتمالات، ونُقلِّل من حجم النطاق. سنَفْحَص دائمًا العنصر الموجود بمنتصف النطاق. إذا كانت قيمته أكبر من <code>N</code>، يُمكِننا إذًا أن نَستبعِد النصف الثاني من النطاق أما إذا كانت قيمته أقل من <code>N</code>، يُمكِننا أن نَستبعِد النصف الأول. أما إذا كانت قيمته تُساوي <code>N</code>، فإن البحث يَكُون قد انتهى. إذا لم يَتبقَّى أية عناصر، فإن العدد <code>N</code> غَيْر موجود إذًا بالمصفوفة. اُنظر شيفرة البرنامج الفرعي (subroutine):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_9" style=""><span class="com">/**
 * Precondition:  A must be sorted into increasing order.
 * Postcondition: If N is in the array, then the return value, i,
 *    satisfies A[i] == N.  If N is not in the array, then the
 *    return value is -1.
 */</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> binarySearch</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">int</span><span class="pln"> lowestPossibleLoc </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> highestPossibleLoc </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">highestPossibleLoc </span><span class="pun">&gt;=</span><span class="pln"> lowestPossibleLoc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="typ">int</span><span class="pln"> middle </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">lowestPossibleLoc </span><span class="pun">+</span><span class="pln"> highestPossibleLoc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">middle</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="com">// ‫عثرنا على N بهذا الفهرس</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> middle</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">middle</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="com">// ‫استبعد المواضع الأكبر من أو تساوي middle</span><span class="pln">
          highestPossibleLoc </span><span class="pun">=</span><span class="pln"> middle </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="com">// ‫استبعد المواضع الأقل من أو تساوي middle</span><span class="pln">
          lowestPossibleLoc </span><span class="pun">=</span><span class="pln"> middle </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">   
       </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// ‫إذا وصلنا إلى هنا فإن highestPossibleLoc &lt; LowestPossibleLoc</span><span class="pln">
    </span><span class="com">// ‫أي أن N غير موجود بالمصفوفة.</span><span class="pln">
    </span><span class="com">// أعد القيمة -1 لكي تشير إلى عدم وجود‫ N بالمصفوفة</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}</span></pre>

<h2>
	القوائم الارتباطية (Association Lists)
</h2>

<p>
	تُعدّ القوائم الارتباطية (association lists) مثل القاموس (dictionary) واحدة من أشهر التطبيقات على البحث (searching). يَربُط القاموس (dictionary) مجموعة من التعريفات مع مجموعة من الكلمات. يُمكِنك مثلًا أن تَستخدِم كلمة معينة لمعرفة تَعرِيفها. قد تُفكِر بالقاموس على أنه قائمة من الأزواج (pairs) على الهيئة (w,d) حيث تُمثِل w كلمة معينة بينما d هي تعريف تلك الكلمة. بالمثل، تَتَكوَّن القوائم الارتباطية (association list) من قائمة من الأزواج (k,v) حيث تُمثِل k مفتاحًا (key) معينًا بينما تُمثِل v القيمة (value) المرتبطة بذلك المفتاح. لا يُمكِن لزوجين (pairs) ضِمْن قائمة معينة أن يَملُكا نفس قيمة المفتاح (key). عادةً ما نُطبِق عمليتين أساسيتين على القوائم الارتباطية: أولًا، إذا كان لديك مفتاح معين k، يُمكِنك أن تَجلُب القيمة v المُرتبِطة به إن وجدت. ثانيًا، يُمكِننا أن نُضيِف زوجًا جديدًا (k,v) إلى قائمة ارتباطية (association list). لاحِظ أنه في حالة إضافة زوج (pair) إلى قائمة، وكانت تلك القائمة تَحتوِي على زوج له نفس المفتاح، فإن القيمة الجديدة المضافة تَحلّ محلّ القديمة. يُطلق عادةً على تلك العمليتين اسم الجَلْب (get) والإضافة (put).
</p>

<p>
	يُشاع اِستخدَام القوائم الارتباطية (association lists) في العموم بعلوم الحاسوب (computer science). على سبيل المثال، لابُدّ أن يَحتفِظ المُصرِّف (compiler) بمَوضِع الذاكرة (memory location) الخاص بكل مُْتْغيِّر. يستطيع المُصرِّف إذًا أن يَستخدِم قائمة ارتباطية بحيث يُمثِل كل مفتاح (key) بها اسمًا لمُتْغيِّر بينما تُمثِل قيمة (value) ذلك المفتاح عنوانه بالذاكرة. مثال آخر هو القوائم البريدية إذا كان العنوان مَربُوطًا باسم ضِمْن تلك القائمة. فمثلًا، يَربُط دليل الهاتف كل اسم برقم هاتف. سنَفْحَص فيما يَلي نُسخة مُبسَطة من ذلك المثال.
</p>

<p>
	يُمكِننا أن نُمثِل عناصر دليل الهاتف بالقائمة الارتباطية (association list) بهيئة كائنات تنتمي إلى الصَنْف التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_11" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">PhoneEntry</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">String</span><span class="pln"> phoneNum</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تَتَكوَّن البيانات الموجودة بدليل الهاتف من مصفوفة من النوع <code>PhoneEntry[]‎</code> بالإضافة إلى مُتْغيِّر من النوع العددي الصحيح (integer) للاحتفاظ بعدد المُدْخَلات المُخزَّنة فعليًا بذلك الدليل. يُمكِننا أيضًا أن نَستخدِم تقنية المصفوفات الديناميكية (dynamic arrays) -اُنظر القسم الفرعي 7.2.4- لكي نَتجنَّب وَضْع حد أقصى عشوائي على عدد المُدْخَلات التي يُمكِن لدليل الهاتف أن يَحتوِيه أو قد نَستخدِم النوع <code>ArrayList</code>. ينبغي أن يَحتوِي الصَنْف <code>PhoneDirectory</code> على توابع نسخ (instance methods) لتّنْفِيذ (implement) كلًا من عمليتي الجَلْب (get) والإضافة (put). تُمثِل الشيفرة التالية تعريفًا (definition) بسيطًا لذلك الصَنْف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_13" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PhoneDirectory</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PhoneEntry</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">     </span><span class="com">// الاسم</span><span class="pln">
      </span><span class="typ">String</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">   </span><span class="com">// رقم الهاتف</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">PhoneEntry</span><span class="pun">[]</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">  </span><span class="com">// مصفوفة لحمل أزواج مكونة من أسماء وأرقام هاتف</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> dataCount</span><span class="pun">;</span><span class="pln">      </span><span class="com">// عدد الأزواج ضمن المصفوفة</span><span class="pln">

   </span><span class="com">/**
    * Constructor creates an initially empty directory.
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">PhoneDirectory</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      data </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PhoneEntry</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
      dataCount </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> find</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> dataCount</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">name</span><span class="pun">.</span><span class="pln">equals</span><span class="pun">(</span><span class="pln">name</span><span class="pun">))</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">  </span><span class="com">// الاسم موجود بالموضع‫ i</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">  </span><span class="com">// الاسم غير موجود بالمصفوفة</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * @return The phone number associated with the name; if the name does
    *    not occur in the phone directory, then the return value is null.
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getNumber</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> position </span><span class="pun">=</span><span class="pln"> find</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">position </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">return</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">   </span><span class="com">// لا يوجد بيانات لذلك الاسم</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln">
         </span><span class="kwd">return</span><span class="pln"> data</span><span class="pun">[</span><span class="pln">position</span><span class="pun">].</span><span class="pln">number</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> putNumber</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> number </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> null </span><span class="pun">||</span><span class="pln"> number </span><span class="pun">==</span><span class="pln"> null</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">IllegalArgumentException</span><span class="pun">(</span><span class="str">"name and number cannot be null"</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> find</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// ‫الاسم موجود بالفعل بالموضع i بالمصفوفة </span><span class="pln">
          </span><span class="com">// استبدل العدد الجديد بالعدد القديم بذلك الموضع</span><span class="pln">
         data</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">number </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// أضف زوج جديد مكون من اسم وعدد إلى المصفوفة</span><span class="pln">
          </span><span class="com">// إذا كانت المصفوفة ممتلئة، أنشئ مصفوفة جديدة أكبر</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">dataCount </span><span class="pun">==</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            data </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> data</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">data</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
         </span><span class="typ">PhoneEntry</span><span class="pln"> newEntry </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PhoneEntry</span><span class="pun">();</span><span class="pln">  </span><span class="com">// أنشئ زوجا جديدا</span><span class="pln">
         newEntry</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
         newEntry</span><span class="pun">.</span><span class="pln">number </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
         data</span><span class="pun">[</span><span class="pln">dataCount</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newEntry</span><span class="pun">;</span><span class="pln">   </span><span class="com">// أضف الزوج الجديد إلى المصفوفة</span><span class="pln">
         dataCount</span><span class="pun">++;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class PhoneDirectory</span></pre>

<p>
	يُعرِّف الصَنْف تابع النسخة <code>find()‎</code>. يَستخدِم ذلك التابع أسلوب البحث الخطي (linear search) للعثور على مَوْضِع اسم معين بالمصفوفة المُكوَّنة من أزواج من الأسماء والأرقام. يَعتمِد كُلًا من التابعين <code>getNumber()‎</code> و <code>putNumber()‎</code> على التابع <code>find()‎</code>. لاحِظ أن التابع <code>putNumber(name,number)‎</code> يَفحَص أولًا ما إذا كان الاسم موجودًا بدليل الهاتف أم لا. إذا كان موجودًا، فإنه فقط يُغيِّر الرقم المُرتبِط بذلك الاسم أما إذا لم يَكُن موجودًا، فإنه يُنشِئ مُدخلًا جديدًا ويُضيفه إلى المصفوفة.
</p>

<p>
	قد نُضيِف الكثير من التحسينات بالطبع على الصَنْف المعرَّف بالأعلى. فمثلًا، قد نَستخدِم البحث الثنائي (binary search) بالتابع <code>getNumber()‎</code> بدلًا من البحث الخطي، ولكن يَتَطلَّب ذلك أن تَكُون الأسماء المُخزَّنة بقائمة المُدْخَلات مُرتَّبة ترتيبًا أبجديًا، وهو ليس أمرًا صعبًا كما ستَرَى بالقسم الفرعي التالي.
</p>

<p>
	عادةً ما يُطلَق اسم الخارطة (maps) على القوائم الارتباطية (association lists)، وتُوفِّر الجافا صَنْفًا قياسيًا ذو معاملات غَيْر محددة النوع (parameterized) اسمه هو <code>Map</code> كتنفيذ (implementation) لها. تستطيع أن تَستخدِم ذلك الصَنْف لكي تُنشِئ قوائمًا ارتباطية مُكوَّنة من مفاتيح (keys) وقيم (values) من أي نوع. يُعدّ ذلك التنفيذ (implementation) أكفأ بكثير من أي شيء قد تَفعَلُه باِستخدَام مصفوفات بسيطة. سنَتعرَّض بالفصل العاشر لذلك الصَنْف.
</p>

<h2>
	الترتيب بالإدراج (Insertion Sort)
</h2>

<p>
	اتضح لنا الآن الحاجة إلى ترتيب المصفوفات. في الواقع، تَتَوفَّر الكثير من الخوارزميات المُخصَّصة لذلك الغرض، منها خوارزمية الترتيب بالإدراج (insertion sort). تُعدّ تلك الخوارزمية واحدة من أسهل الطرائق لترتيب مصفوفة، كما يُمكِننا أن نُطبِقها للإبقاء على المصفوفة مُرتَّبة بينما نُضيِف إليها عناصر جديدة. لنَفْحَص المثال التالي أولًا:
</p>

<p>
	لنَفْترِض أن لدينا قائمة مُرتَّبة ونريد إضافة عنصر جديد إليها. إذا كنا نريد التأكُّد من أنها ستظلّ مُرتَّبة، ينبغي أن نُضيِف ذلك العنصر بمَوْضِعه الصحيح بحيث تأتي قبله جميع العناصر الأصغر بينما تَحِلّ بَعْده جميع العناصر الأكبر. يَعنِي ذلك تَحرِيك جميع العناصر الأكبر بمقدار خانة واحدة لترك مساحة للعنصر الجديد.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_15" style=""><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> insert</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> itemsInArray</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> newItem</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> loc </span><span class="pun">=</span><span class="pln"> itemsInArray </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ابدأ من نهاية المصفوفة</span><span class="pln">

    </span><span class="com">// ‫حرك العناصر الأكبر من newItem للأعلى بمقدار مسافة واحدة</span><span class="pln">
    </span><span class="com">// وتوقف عندما تقابل عنصر أصغر أو عندما تصل إلى بداية المصفوفة</span><span class="pln">

   </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">loc </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">loc</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> newItem</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">loc </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">loc</span><span class="pun">];</span><span class="pln">  </span><span class="com">// Bump item from A[loc] up to loc+1.</span><span class="pln">
      loc </span><span class="pun">=</span><span class="pln"> loc </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">        </span><span class="com">// انتقل إلى الموضع التالي</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// ‫ضع newItem بآخر موضع فارغ</span><span class="pln">
   A</span><span class="pun">[</span><span class="pln">loc </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newItem</span><span class="pun">;</span><span class="pln">  

</span><span class="pun">}</span></pre>

<p>
	يُمكِننا أن نَمِد ذلك إلى تابع للترتيب (sorting) بأخذ جميع العناصر الموجودة بمصفوفة غَيْر مُرتَّبة (unsorted) ثم اضافتها تدريجيًا واحدًا تلو الآخر إلى مصفوفة آخرى مع الإبقاء عليها مُرتَّبة. نستطيع أن نَستخدِم البرنامج <code>insert</code> بالأعلى أثناء كل عملية إدراج (insertion) لعنصر جديد. ملحوظة: بالخوارزمية الفعليّة، لا نأخُذ جميع العناصر من المصفوفة، ولكننا فقط نتذكَّر الأجزاء المُرتَّبة منها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_17" style=""><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> insertionSort</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫رتب المصفوفة A ترتيبًا تصاعديا</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> itemsSorted</span><span class="pun">;</span><span class="pln"> </span><span class="com">// عدد العناصر المُرتَّبة إلى الآن</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">itemsSorted </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> itemsSorted </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> itemsSorted</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// ‫افترض أن العناصر A[0]‎ و A[1]‎ .. إلخ</span><span class="pln">
       </span><span class="com">// ‫مرتبة بالفعل. أضف A[itemsSorted]‎ إلى الجزء المرتب من </span><span class="pln">
       </span><span class="com">// القائمة</span><span class="pln">

      </span><span class="typ">int</span><span class="pln"> temp </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">itemsSorted</span><span class="pun">];</span><span class="pln">  </span><span class="com">// العنصر المطلوب إضافته</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> loc </span><span class="pun">=</span><span class="pln"> itemsSorted </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ابدأ بنهاية القائمة</span><span class="pln">

      </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">loc </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">loc</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> temp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         A</span><span class="pun">[</span><span class="pln">loc </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">loc</span><span class="pun">];</span><span class="pln"> </span><span class="com">// Bump item from A[loc] up to loc+1.</span><span class="pln">
         loc </span><span class="pun">=</span><span class="pln"> loc </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">       </span><span class="com">// Go on to next location.</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

       </span><span class="com">// ‫ضع temp بآخر موضع فارغ</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">loc </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln"> 
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُوضِح الصورة التالية مرحلة واحدة من عملية الترتيب بالإدراج (insertion sort) حيث تُبيِّن ما يحدث أثناء تّنفِيذ تَكْرار واحد من الحلقة <code>for</code> بالأعلى بالتحديد عندما يَكُون عدد العناصر ضِمْن المصفوفة <code>itemsSorted</code> مُساوِيًا للقيمة 5:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59587" data-ss1615726214="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/001Insertion_Sort.png.4599cd952e1944d2ef4289b2cc70ab07.png" rel="" data-fileext="png"><img alt="001Insertion_Sort.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59587" data-unique="nesoss2zl" src="https://academy.hsoub.com/uploads/monthly_2021_03/001Insertion_Sort.png.4599cd952e1944d2ef4289b2cc70ab07.png"></a>
</p>

<h2>
	الترتيب الانتقائي (Selection Sort)
</h2>

<p>
	تَستخدِم خوارزمية ترتيب (sorting) آخرى فكرة العُثور على أكبر عنصر ضِمْن القائمة، وتَحرِيكه إلى نهايتها حيث ينبغي أن يتواجد إذا كنا نريد ترتيبها ترتيبًا تصاعديًا. بمُجرَّد تَحرِيك أكبر عنصر بالقائمة إلى مَوْضِعه الصحيح، يُمكِننا أن نُطبِق نفس الفكرة على العناصر المُتبقَاة أي أن نَعثُر على العنصر الأكبر التالي، ونُحرِكه إلى المَوْضِع قبل الأخير، وهكذا. يُطلَق اسم "الترتيب الانتقائي (selection sort)" على تلك الخوارزمية (algorithm). يُمكِننا كتابتها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_19" style=""><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> selectionSort</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// رتب المصفوفة A ترتيبا تصاعديا باستخدام الترتيب الانتقائي</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> lastPlace </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> lastPlace </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> lastPlace</span><span class="pun">--)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> maxLoc </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">  </span><span class="com">// موضع أكبر عنصر إلى الآن</span><span class="pln">

      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> j </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;=</span><span class="pln"> lastPlace</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">maxLoc</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
             </span><span class="com">// ‫لأن A[j]‎ أكبر من أكبر قيمة رأيناها إلى الآن، فإن j هو</span><span class="pln">
             </span><span class="com">// الموضع الجديد لأكبر قيمة وجدناها إلى الآن</span><span class="pln">
            maxLoc </span><span class="pun">=</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">  
         </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="typ">int</span><span class="pln"> temp </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">maxLoc</span><span class="pun">];</span><span class="pln">  </span><span class="com">// Swap largest item with A[lastPlace].</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">maxLoc</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">lastPlace</span><span class="pun">];</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">lastPlace</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">

   </span><span class="pun">}</span><span class="pln">  </span><span class="com">// end of for loop</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	يَستخدِم الصَنْف <code>Hand</code> الذي كتبناه بالقسم الفرعي 5.4.1 نسخة مختلفة قليلًا من الترتيب الانتقائي (selection sort). يُعرِّف الصَنْف <code>Hand</code> مُتْغيِّرًا من النوع <code>ArrayList&lt;Card&gt;‎</code> لتَمثيِل اليد (hand) يحتوي بطبيعة الحال على كائنات من النوع <code>Card</code>. يَحتوِي أي كائن من النوع <code>Card</code> على توابع النسخ <code>getSuit()‎</code> و <code>getValue()‎</code>، والتي يُمكِننا أن نَستخدِمها لمَعرِفة كُلًا من قيمة ورقة اللعب ورمزها (suit). يُنشِئ التابع (method) المسئول عن عملية الترتيب (sorting) قائمة جديدة، بحيث يَختار ورق اللعب من القائمة القديمة تدريجيًا وبترتيب مُتصاعِد ثم يُحرِكها من القائمة القديمة إلى الجديدة. يَستخدِم التابع (method) القائمة الجديدة بالنهاية لتمثيل اليد بدلًا من القديمة. قد لا يَكُون ذلك هو الأسلوب الأكفأ لإنجاز الأمر، ولكن نظرًا لأن عدد ورق اللعب ضِمْن أي يد (hand) عادةً ما يَكون صغيرًا، فإنه لا يُمثِل مشكلة كبيرة. اُنظر شيفرة ترتيب (sorting) ورق اللعب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_21" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> sortBySuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Card</span><span class="pun">&gt;</span><span class="pln"> newHand </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Card</span><span class="pun">&gt;();</span><span class="pln">
   </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">hand</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> pos </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">  </span><span class="com">// موضع البطاقة الأقل</span><span class="pln">
      </span><span class="typ">Card</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">  </span><span class="com">// البطاقة الأقل</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="typ">Card</span><span class="pln"> c1 </span><span class="pun">=</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> c1</span><span class="pun">.</span><span class="pln">getSuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getSuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">||</span><span class="pln">
              </span><span class="pun">(</span><span class="pln">c1</span><span class="pun">.</span><span class="pln">getSuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getSuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> c1</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">())</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            pos </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">  </span><span class="com">// Update the minimal card and location.</span><span class="pln">
            c </span><span class="pun">=</span><span class="pln"> c1</span><span class="pun">;</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      hand</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="pln">pos</span><span class="pun">);</span><span class="pln">  </span><span class="com">// احذف ورقة اللعب من اليد الأصلية</span><span class="pln">
      newHand</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">c</span><span class="pun">);</span><span class="pln">    </span><span class="com">// أضف ورقة اللعب إلى اليد الجديدة</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   hand </span><span class="pun">=</span><span class="pln"> newHand</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحِظ أن موازنة العناصر ضِمْن قائمة ليست دائمًا ببساطة اِستخدَام <code>&lt;</code> كالمثال بالأعلى. في هذه الحالة، تُعدّ ورقة لعب معينة أصغر من ورقة لعب آخرى إذا كان رمز (suit) الأولى أقل من رمز الثانية أو أن يَكُونا متساويين بشرط أن تَكُون قيمة ورقة اللعب الثانية أقل من قيمة الأولى. يتأكَّد الجزء الثاني من الاختبار من ترتيب ورق اللعب بنفس الرمز ترتيبًا صحيحًا بحسب قيمته.
</p>

<p>
	يُمثِل ترتيب قائمة من النوع <code>String</code> مشكلة مشابهة؛ فالعامل <code>&lt;</code> غَيْر مُعرَّف للسَلاسِل النصية. في المقابل، يُعرِّف الصنف <code>String</code> التابع <code>compareTo</code>. إذا كان <code>str1</code> و <code>str2</code> من النوع <code>String</code>، يُمكِننا كتابة ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_23" style=""><span class="pln">str1</span><span class="pun">.</span><span class="pln">compareTo</span><span class="pun">(</span><span class="pln">str2</span><span class="pun">)</span></pre>

<p>
	يُعيِد التابع السابق قيمة من النوع <code>int</code> تُساوِي 0 إذا كان <code>str1</code> يُساوِي <code>str2</code> أو قيمة أقل من 0 عندما يأتي <code>str1</code> قبل <code>str2</code> أو أكبر من 0 عندما يأتي <code>str1</code> بَعْد <code>str2</code>. تستطيع مثلًا أن تختبر ما إذا كان <code>str1</code> يَسبِق <code>str2</code> أو يُساويه باِستخدَام الاختبار التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_25" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> str1</span><span class="pun">.</span><span class="pln">compareTo</span><span class="pun">(</span><span class="pln">str2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	يُقصَد بكلمات مثل "يَسبِق" أو "يَتبَع" -عند اِستخدَامها مع السَلاسِل النصية (string)- ترتيبها وفقًا للترتيب المعجمي (lexicographic ordering)، والذي يَعتمِد على قيمة اليونيكود (Unicode) للمحارف (characters) المُكوِّنة للسِلسِلة النصية (strings). يَختلِف الترتيب المعجمي (lexicographic ordering) عن الترتيب الأبجدي (alphabetical) حتى بالنسبة للكلمات المُكوَّنة من أحرف أبجدية فقط؛ لأن جميع الأحرف بحالتها الكبيرة (upper case) تَسبِق جميع الأحرف بحالتها الصغيرة (lower case). في المقابل، يُعدّ الترتيب الأبجدي والمعجمي للكلمات المُقتصِرة على 26 حرف بحالتها الصغيرة فقط أو الكبيرة فقط هو نفسه. يُحوِّل التابع <code>str1.compareToIgnoreCase(str2)‎</code> أحرف سِلسِلتين نصيتين (strings) إلى الحالة الصغيرة (lowercsae) أولًا قبل موازنتهما.
</p>

<p>
	يتناسب كُلًا من الترتيب الانتقائي (selection sort) والترتيب بالادراج (insertion sort) مع المصفوفات الصغيرة المُكوَّنة من مئات قليلة من العناصر. أما بالنسبة للمصفوفات الكبيرة، فتَتَوفَّر خوارزميات آخرى أكثر تعقيدًا ولكنها أسرع بكثير. هذه الخوارزميات أسرع بكثير ربما بنفس الكيفية التي يُعدّ بها البحث الثنائي (binary search) أسرع من البحث الخطي (linear search). يَعتمِد التابع القياسي <code>Arrays.sort</code> على الخوارزميات السريعة. سنَتعرَّض لإحدى تلك الخوارزميات بالفصل التاسع.
</p>

<h2>
	الترتيب العشوائي (unsorting)
</h2>

<p>
	يُعدّ الترتيب العشوائي لعناصر مصفوفة مشكلة أقل شيوعًا ولكنها شيقة. قد نحتاجها مثلًا لخَلْط مجموعة ورق لعب ضِمْن برنامج. تَتًوفَّر خوارزمية شبيهة بالترتيب الانتقائي (selection sort)، فبدلًا من تَحرِيك أكبر عنصر بالمصفوفة إلى نهايتها، سنختار عنصرًا عشوائيًا ونُحرِكه إلى نهايتها. يَخلِط البرنامج الفرعي (subroutine) التالي عناصر مصفوفة من النوع <code>int</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3144_27" style=""><span class="com">/**
 * Postcondition:  The items in A have been rearranged into a random order.
 */</span><span class="pln">
</span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> shuffle</span><span class="pun">(</span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> A</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> lastPlace </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> lastPlace </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> lastPlace</span><span class="pun">--)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// ‫اختر موضعا عشوائيًا بين 0 و 1 و... حتى lastPlace</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> randLoc </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*(</span><span class="pln">lastPlace</span><span class="pun">+</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
       </span><span class="com">// ‫بدل العناصر بالموضعين randLoc و lastPlace</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> temp </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">randLoc</span><span class="pun">];</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">randLoc</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">lastPlace</span><span class="pun">];</span><span class="pln">
      A</span><span class="pun">[</span><span class="pln">lastPlace</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1615726214="1" href="http://math.hws.edu/javanotes/c7/s4.html" rel="external nofollow">Section 4: Searching and Sorting</a> من فصل Chapter 7: Arrays and ArrayLists من كتاب <a data-ss1615726214="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1160</guid><pubDate>Fri, 02 Apr 2021 13:09:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x627;&#x644;&#x62F;&#x64A;&#x646;&#x627;&#x645;&#x64A;&#x643;&#x64A;&#x629; (ArrayLists) &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A%D8%A9-arraylists-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1159/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e051e8c694_-.png.66c40c16ac3f5daac461e31503172a79.png" /></p>

<p>
	يُمكِننا أن نُضمِّن نمط المصفوفة الديناميكية (dynamic array) داخل صَنْف كما رأينا بالقسم الفرعي 7.2.4، ولكن بدا الأمر كما لو أننا سنحتاج إلى تعريف صَنْف مختلف لكل نوع من البيانات (data type). في الحقيقة، تُوفِّر جافا خاصية تُعرَف باسم "الأنواع ذات المعاملات غَيْر مُحدَّدة النوع (parameterized types)"، والتي يُمكِنها أن تُجنِّبنا مشكلة تعدد الأصناف كما تُوفِّر جافا الصَنْف <code>ArrayList</code> الذي يُنفِّذ (implement) نمط المصفوفة الديناميكية لجميع أنواع البيانات.
</p>

<h2>
	الصنف <code>ArrayList</code> والأنواع ذات المعاملات غير محددة النوع (Parameterized Types)
</h2>

<p>
	تُوفِّر جافا النوع القياسي <code>ArrayList&lt;String&gt;‎</code> لتمثيل مصفوفة ديناميكية (dynamic arrays) من النوع <code>String</code>. وبالمثل، يُمثِل النوع <code>ArrayList&lt;Button&gt;‎</code> مصفوفة ديناميكية من النوع <code>Button</code>. بنفس الكيفية، إذا كان <code>Player</code> صنفًا (class) يُمثِل اللاعبين ضِمْن لعبة، فإن النوع <code>ArrayList&lt;Player&gt;‎</code> يُمثِل مصفوفة ديناميكية من النوع <code>Player</code>.
</p>

<p>
	قد يبدو الأمر كما لو أننا نَستخدِم أصنافًا كثيرة، ولكن، في الواقع، هنالك صَنْف (class) واحد فقط هو الصَنْف <code>ArrayList</code> المُعرَّف بحزمة <code>java.util</code>. يُعدّ ذلك الصَنْف صَنْفًا ذي مُعامِلات غَيْر مُحدَّدة النوع (parameterized type) -نوع يُمكِنه أن يَأخُذ معامل نوع (type parameter)-. يُمكِننا ذلك من اِستخدَام صَنْف واحد فقط للحصول على عدة أنواع مثل <code>ArrayList&lt;String&gt;‎</code> و <code>ArrayList&lt;Button&gt;‎</code> وحتى <code>ArrayList&lt;T&gt;‎</code>. لابُدّ أن يَكُون معامل النوع <code>T</code> نوعًا كائنيًا (object type) أي اسم صَنْف (class) أو اسم واجهة (interface)، ولا يُمكِنه أن يَكُون نوعًا أساسيًا (primitive type). يعني ذلك أنك لا تستطيع الحصول على <code>ArrayList</code> من النوع <code>int</code> أو <code>ArrayList</code> من النوع <code>char</code>.
</p>

<p>
	يُمكِننا مثلًا أن نَستخدِم النوع <code>ArrayList&lt;String&gt;‎</code> للتّصرِيح (declare) عن مُتْغيِّر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_7" style="">
<span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> namelist</span><span class="pun">;</span></pre>

<p>
	أو قد نَستخدِمه كنوع لمعامل صوري (formal parameter) ضِمْن تعريف برنامج فرعي (subroutine) أو كنوع مُعاد (return type) من برنامج فرعي. علاوة على ذلك، قد نَستخدِمه مع العامل <code>new</code> لإنشاء كائنات (objects):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_9" style="">
<span class="pln">namelist </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;();</span></pre>

<p>
	يَكُون الكائن المُنشَئ في هذه الحالة من النوع <code>ArrayList&lt;String&gt;‎</code>، ويُمثِل قائمة ديناميكية من السَلاسِل النصية (strings). يُوفِّر الصَنْف مجموعة من توابع النُسخ (instance methods) مثل التابع <code>namelist.add(str)‎</code> لإضافة سِلسِلة نصية من النوع <code>String</code> إلى القائمة، والتابع <code>namelist.get(i)‎</code> لجَلْب السِلسِلة النصية الموجود برقم الفهرس <code>i</code>، والتابع <code>namelist.size()‎</code> لحِسَاب عدد العناصر الموجودة بالقائمة حاليًا.
</p>

<p>
	إلى جانب ما سبق، يُستخدَم الصَنْف <code>ArrayList</code> مع أنواع آخرى أيضًا. فمثلًا، إذا كان الصَنْف <code>Player</code> يُمثِل اللاعبين ضِمْن لعبة، يُمكِننا أن نُنشِئ قائمة من اللاعبين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_11" style="">
<span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Player</span><span class="pun">&gt;</span><span class="pln"> playerList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Player</span><span class="pun">&gt;();</span></pre>

<p>
	والآن، يُمكِنك أن تَستخدِم <code>playerList.add(plr)‎</code> لإضافة لاعب <code>plr</code> إلى اللعبة أو قد تَستخدِم <code>playerList.remove(k)‎</code> لحَذْف اللاعب الموجود برقم الفهرس <code>k</code>.
</p>

<p>
	علاوة على ذلك، إذا كان <code>playerList</code> مُتْغيِّرًا محليًا (local variable)، يُمكِنك أن تَستخدِم صيغة التّصرِيح (declaration) المختصرة -اُنظر القسم الفرعي 4.8.2- كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_13" style="">
<span class="pln">var playlerList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Player</span><span class="pun">&gt;();</span></pre>

<p>
	يَعتمِد مُصرِّف (compiler) الجافا على القيمة المبدئية المُسنَدة إلى <code>playerList</code> لاستنتاج أن نوعه هو <code>ArrayList&lt;Player&gt;‎</code>.
</p>

<p>
	عندما تَستخدِم نوعًا (type) مثل <code>ArrayList&lt;T&gt;‎</code>، فإن المُصرِّف يتأكد من أن جميع الكائنات (objects) المضافة إليه من النوع <code>T</code>، وستُعدّ أي محاولة لإضافة كائن (object) من نوع آخر خطأً في بناء الجملة (syntax error)، ولا يُصرَّف عندها البرنامج. لمّا كانت الكائنات المنتمية لصَنْف فرعي من <code>T</code> هي بالنهاية من النوع <code>T</code>، فإنه من المُمكن إضافتها أيضًا إلى القائمة. فمثلًا، يُمكِن لمُتْغيِّر من النوع <code>ArrayList&lt;Pane&gt;‎</code> أن يَحمِل كائنات من النوع <code>BorderPane</code> أو <code>TilePane</code> أو <code>GridPane</code> أو أي صنف فرعي (subclass) آخر من الصَنْف <code>Pane</code> (في الواقع، يُشبِه ذلك طريقة عمل المصفوفات حيث يستطيع كائن من النوع <code>T[]‎</code> أن يَحمِل أي كائنات تنتمي لصَنْف فرعي من <code>T</code>). بالمثل، إذا كان <code>T</code> عبارة عن واجهة (interface)، فمن الممكن إضافة أي كائن (objects) إلى القائمة طالما كان يُنفِّذ (implement) تلك الواجهة <code>T</code>.
</p>

<p>
	تَملُك الكائنات من النوع <code>ArrayList&lt;T&gt;‎</code> جميع توابع النسخ (instance methods) التي قد تَتَوقَّعها من مصفوفة ديناميكية (dynamic array). بِفَرْض أن <code>list</code> عبارة عن مُتْغيِّر من النوع <code>ArrayList&lt;T&gt;‎</code>، اُنظر التوابع التالية:
</p>

<ul>
<li>
		<p>
			<code>list.size()‎</code>: يُعيد حجم القائمة أي عدد العناصر الموجودة بها حاليًا. قد يَكون حجم القائمة مُساويًا للصفر. فمثلًا، يُنشِئ باني الكائن الافتراضي <code>new ArrayList&lt;T&gt;()‎</code> قائمة حجمها يُساوي صفر، وفي العموم، تتراوح أرقام المواضع الصالحة من 0 وحتى <code>list.size()-1</code>.
		</p>
	</li>
	<li>
		<p>
			<code>list.add(obj)‎</code>: يُضيِف كائنًا (object) إلى نهاية القائمة مع زيادة حجمها بمقدار الواحد. لاحِظ أن المُعامل <code>obj</code> لابُد أن يَكُون كائنًا من النوع <code>T</code> أو قد يَكُون فارغًا.
		</p>
	</li>
	<li>
		<p>
			<code>list.get(N)‎</code>: يَستقبِل المُعامل <code>N</code> الذي لابُدّ أن يكون عددًا صحيحًا (integer) يتراوح من 0 إلى <code>list.size()-1</code> ثُمَّ يُعيد القيمة المُخزَّنة بالمَوْضِع <code>N</code>، والتي بطبيعة الحال تَكُون من النوع <code>T</code>. إذا كان <code>N</code> خارج النطاق المسموح به، يَقَع اعتراض من النوع <code>IndexOutOfBoundsException</code>. في الواقع، تُشبه تلك الدالة الأمر <code>A[N]‎</code> -بِفَرْض أن <code>A</code> عبارة عن مصفوفة- بفارق أنه لا يُمكِن اِستخدَام <code>list.get(N)‎</code> بالجانب الأيسر من أي تَعْليمَة إِسْناد (assignment statement).
		</p>
	</li>
	<li>
		<p>
			<code>list.set(N, obj)‎</code>: يُسنِد الكائن <code>obj</code> إلى عنصر المصفوفة بالمَوْضِع <code>N</code> أي يَحلّ ذلك الكائن محلّ الكائن المُخزَّن مُسْبَقًا بذلك الموضع. لابُدّ أن يَكُون المُعامل <code>obj</code> من النوع <code>T</code> كما ينبغي أن يَكُون <code>N</code> عددًا صحيحًا (integer) يتراوح بين 0 و <code>list.size()-1</code>. في الواقع، تُشبِه تلك الدالة الأمر <code>A[N] = obj</code> بِفَرْض أن <code>A</code> عبارة عن مصفوفة.
		</p>
	</li>
	<li>
		<p>
			<code>list.clear()‎</code>: يحذِف جميع العناصر الموجودة بالقائمة، ويَضبُط حجمها إلى الصفر.
		</p>
	</li>
	<li>
		<p>
			<code>list.remove(N)‎</code>: يَحذِف العنصر الموجود بالمَوْضِع <code>N</code> من القائمة، ويُنقِص حجمها بمقدار الواحد كما يَنقِل العناصر الموجودة بَعْد العنصر المحذوف مَوْضِعًا للأعلى. لاحِظ أن المُعامل <code>N</code> لابُدّ أن يَكُون عددًا صحيحًا (integer) يتراوح بين 0 و <code>list.size()-1</code>.
		</p>
	</li>
	<li>
		<p>
			<code>list.remove(obj)‎</code>: يَحذِف الكائن المُمرَّر من القائمة إذا كان موجودًا بها، ويُنقِص حجمها بمقدار الواحد كما يَنقِل العناصر الموجودة بَعْد العنصر المحذوف مَوضِعًا للأعلى. لاحِظ أنه في حالة وجود الكائن <code>obj</code> أكثر من مرة ضِمْن القائمة، فإنه يَحذِف أول حدوث له فقط أما إذا لم يَكُن موجودًا، فإنه لا يَفعَل أي شيء أي لا يُعدّ ذلك بمثابة خطأ.
		</p>
	</li>
	<li>
		<p>
			<code>list.indexOf(obj)‎</code>: يَبحَث عن الكائن <code>obj</code> داخل القائمة، ويُعيد أول مَوْضِع لحدوثه إذا كان موجودًا أما إذا لم يَكُن موجودًا، فإنه يُعيِد العدد -1.
		</p>
	</li>
</ul>
<p>
	ملحوظة: يَستخدِم آخر تابعين الاستدعاء <code>obj.equals(item)‎</code> لموازنة <code>obj</code> -لو لم يَكُن <code>obj</code> فارغًا- مع عناصر القائمة أي أنهما يَفْحَصا تَساوِي السَلاسِل النصية بِفْحَص محتوياتها وليس مَواضِعها بالذاكرة.
</p>

<p>
	تُوفِّر جافا أصنافًا كثيرة ذات مُعاملات غَيْر مُحدَّدة النوع (parameterized classes) لتمثيل هياكل البيانات المختلفة (data structure)، وتُشكِّل تلك الأصناف إطار جافا للتجميعات (Java Collection Framework). لقد ناقشنا هنا الصَنْف <code>ArrayList</code> فقط، ولكننا سنُعود لمناقشة هذا الموضوع على نحو أكثر تفصيلًا بالفصل 10.
</p>

<p>
	ملحوظة: يُستخدَم الصَنْف <code>ArrayList</code> أيضًا بشكل عادي بدون مُعاملات غَيْر مُحدَّدة النوع (non-parametrized) أي يُمكنِنا أن نُصرِّح (declare) عن مُتْغيرِّات، ونُنشِئ كائنات من النوع <code>ArrayList</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_15" style="">
<span class="typ">ArrayList</span><span class="pln"> </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">();</span></pre>

<p>
	يُكافِئ ذلك التّصرِيح عن مُتْغيٍّر <code>list</code> من النوع <code>ArrayList&lt;Object&gt;‎</code>. يَعنِي ذلك أن <code>list</code> يُمكِنه أن يَحمِل أي كائن ينتمي إلى صَنْف فرعي من <code>Object</code>. ولأن أي صَنْف هو بالنهاية صَنْف فرعي (subclass) من <code>Object</code>، فإنه من الممكن إضافة أي كائن (object) إلى <code>list</code>.
</p>

<h2>
	الأصناف المُغلِّفة (Wrapper Classes)
</h2>

<p>
	كما أوضحنا مُسْبَقًا، لا تتوافق الأنواع ذات المعاملات غير محدَّدة النوع (parameterized types) مع الأنواع الأساسية (primitive types) أي لا يُمكِنك مثلًا أن تَستخدِم شيئًا مثل <code>ArrayList&lt;int&gt;‎</code>. مع ذلك، نظرًا لتوفُّر أصناف مُغلِّفة (wrapper classes) مثل <code>Integer</code> و <code>Character</code>، فلربما الأمر ليس مُقيَدًا تمامًا.
</p>

<p>
	لقد تَعرضنا بالقسم 2.5 للأصناف <code>Double</code> و <code>Integer</code>. تُعرِّف تلك الأصناف التوابع الساكنة <code>Double.parseDouble()‎</code> و <code>Integer.parseInteger()‎</code> المُستخدَمة لتَحْوِيل سِلسِلة نصية (string) إلى قيمة عددية. تَتَضمَّن تلك الأصناف أيضًا ثوابتًا (constants) مثل <code>Integer.MAX_VALUE</code> و <code>Double.NaN</code>. لقد تَعرضنا أيضًا للصَنْف <code>Character</code> ببعض الأمثلة. يُعرِّف ذلك الصَنْف التابع الساكن <code>Character.isLetter()‎</code> المُستخدَم لفْحَص ما إذا كانت قيمة معينة من النوع <code>char</code> عبارة عن حرف أبجدي (letter) أم لا. تَتوفَّر في الواقع أصناف مشابهة لكل نوع أساسي (primitive type) مثل <code>Long</code> و <code>Short</code> و <code>Byte</code> و <code>Float</code> و <code>Boolean</code>. تُعدّ جميع تلك الأصناف أصنافًا مُغلِّفة (wrapper classes)، وعلى الرغم من أنها تَحوِي بعض الأعضاء الساكنة (static members) المفيدة عمومًا، فإنها تُستخدَم أيضًا لغرض آخر: تَمثيِل الأنواع الأساسية ككائنات (objects).
</p>

<p>
	تذكَّر دومًا أن الأنواع الأساسية ليست أصنافًا (classes)، وأن قيم تلك الأنواع ليست كائنات (objects). مع ذلك، قد يَكُون من المفيد أحيانًا التَعامُل مع قيمة من نوع أساسي كما لو كانت كائنًا (object)، فمثلًا قد نرغب بتَخْزِين قيمة من نوع أساسي (primitive type) داخل قائمة من النوع <code>ArrayList</code>. لا نستطيع في الواقع فِعِل ذلك حرفيًا، ولكن يُمكِننا التَحايُل على ذلك قليلًا بتَغْليف (wrap) تلك القيمة داخل كائن ينتمي إلى صَنْف مُغلِّف (wrapper classes).
</p>

<p>
	على سبيل المثال، يحتوي أي كائن من النوع <code>Double</code> على مُتْغيِّر نُسخة (instance variable) وحيد من النوع <code>double</code>. يَعمَل ذلك الكائن بمثابة مُغلِّف (wrapper) للقيمة العددية. يُمكِنك إذًا أن تُنشِئ كائنًا (object) لتَغليف قيمة من النوع <code>double</code> مثل 6.0221415e23 كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_17" style="">
<span class="typ">Double</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Double</span><span class="pun">(</span><span class="lit">6.0221415e23</span><span class="pun">);</span></pre>

<p>
	في الواقع، يَحتوِي <code>d</code> على نفس المعلومات التي تَحتوِيها قيمة من النوع <code>double</code>، ولكنه كائن (object). يُمكِنك أن تَستدِعي <code>d.doubleValue()‎</code> لاسترجاع القيمة العددية المُغلَّفة داخل الكائن. بالمثل، يُمكِنك أن تُغلِّف قيمة من النوع <code>int</code> ضِمْن كائن من النوع <code>Integer</code> أو قيمة من النوع <code>boolean</code> ضِمْن كائن من النوع <code>Boolean</code>، وهكذا.
</p>

<p>
	إذا كنت تريد أن تُنشِئ كائنًًا من النوع <code>Double</code> لتَغْلِيف قيمة عددية <code>x</code>، يُفضَّل أن تَستخدِم التابع الساكن <code>Double.valueOf(x)‎</code> بدلًا من أن تَستدعِي الباني <code>new Double(x)‎</code>؛ لأن استدعاء التابع <code>Double.valueOf()‎</code> بنفس قيمة المُعامل (parameter) أكثر من مرة دائمًا ما يُعيِد نفس ذات الكائن. تحديدًا، بَعْد أول استدعاء لذلك التابع بقيمة مُعامِل معينة، فإنه سيُعيد نفس ذلك الكائن بالاستدعاءات المتتالية لنفس قيمة المُعامل. لا يُعدّ ذلك مشكلة كما قد تَظّن؛ لأن الكائنات من النوع <code>Double</code> غَيْر قابلة للتَعْدِيل (immutable) أي أن الكائنات التي لها نفس القيمة المبدئية ستَظِلّ دائمًا متطابقة. يُعدّ التابع <code>Double.valueOf</code> تابعًا مُصنِّعًا (factory method) مثل تلك التوابع التي تَعرَّضنا لها بالقسم 6.2 أثناء تَعامُلنا مع الصَنْفين <code>Color</code> و <code>Font</code>، وتحتوي جميع الأصناف المُغلِّفة عمومًا على تابع مُصنِّع مشابه مثل <code>Character.valueOf(ch)‎</code> و <code>Integer.valueOf(n)‎</code>.
</p>

<p>
	يَتَوفَّر أيضًا تحويل أتوماتيكي بين كل نوع أساسي (primitive type) وصَنْفه المُغلِّف (wrapper class) لتسهيل الاِستخدَام. على سبيل المثال، إذا كنت تَستخدِم قيمة من النوع <code>int</code> ضِمْن سياق يَتَطلَّب كائنًا (object) من النوع <code>Integer</code>، يُمكِنك أن تُغلِّف تلك القيمة ضِمْن كائن من النوع <code>Integer</code> أتوماتيكيًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_19" style="">
<span class="typ">Integer</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span></pre>

<p>
	سيقرأ الحاسوب التَعْليمَة بالأعلى كما لو كانت مَكْتُوبة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_21" style="">
<span class="typ">Integer</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">.</span><span class="pln">valueOf</span><span class="pun">(</span><span class="lit">42</span><span class="pun">);</span></pre>

<p>
	يُطلَق على ذلك اسم "التغليف الأوتوماتيكي (autoboxing)"، ويَعمَل من الجهة الآخرى أيضًا. فمثلًا، إذا كان <code>d</code> يُشير إلى كائن من النوع <code>Double</code>، يُمكِنك أن تَستخدِم <code>d</code> بتعبير عددي (numerical expression) مثل <code>2*d</code>. تُفَك (unboxing) القيمة العددية داخل <code>d</code> أتوماتيكيًا، ويُحسَب حاصل ضربها مع العدد 2. كثيرًا ما يَسمَح لك التَغْلِيف الأتوماتيكي (autoboxing) وفكه بتَجاهُل الفارق بين الأنواع الأساسية (primitive types) والكائنات (objects).
</p>

<p>
	يُعدّ ما سبق صحيحًا فيما يَتَعلَّق بالأنواع ذات المعاملات غَيْر مُحدَّدة النوع (parameterized types). على الرغم من عدم توفُّر النوع <code>ArrayList&lt;int&gt;‎</code>، يَتَوفَّر <code>ArrayList&lt;Integer&gt;‎</code> حيث تَحمِل مصفوفة من ذلك النوع كائنات من النوع <code>Integer</code> التي تُمثِل مُجرّد قيمة من النوع <code>int</code> ضِمْن مُغلِّف صغير. لنَفْترِض أن لدينا كائن (object) من النوع <code>ArrayList&lt;Integer&gt;‎</code>، اُنظر التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_23" style="">
<span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> integerList</span><span class="pun">;</span><span class="pln">
integerList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;();</span></pre>

<p>
	يُمكِننا الآن أن نُضيف كائنًا مُمثلًا للعدد 42 مثلًا إلى <code>integerList</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_25" style="">
<span class="pln">integerList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">.</span><span class="pln">valueOf</span><span class="pun">(</span><span class="lit">42</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	أو قد نعتمد على التَغْلِيف الأتوماتيكي (autoboxing) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_29" style="">
<span class="pln">integerList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سيُغلِّف المُصرِّف (compiler) العدد 42 أتوماتيكيًا داخل كائن (object) من النوع <code>Integer</code> قبل إضافته إلى القائمة. بالمثل، يُمكِننا كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_31" style="">
<span class="typ">int</span><span class="pln"> num </span><span class="pun">=</span><span class="pln"> integerList</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	يُعيد التابع <code>integerList.get(3)‎</code> قيمة من النوع <code>Integer</code>، ولكن بِفَضل خاصية فك التَغْلِيف (unboxing)، سيُحوِّل المُصرِّف (compiler) القيمة المُعادة (return value) أتوماتيكيًا إلى قيمة من النوع <code>int</code> كما لو كنا قد كتبنا ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_33" style="">
<span class="typ">int</span><span class="pln"> num </span><span class="pun">=</span><span class="pln"> integerList</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="lit">3</span><span class="pun">).</span><span class="pln">intValue</span><span class="pun">();</span></pre>

<p>
	نستطيع إذًا أن نَستخدِم المصفوفة <code>integerList</code> كما لو كانت مصفوفة ديناميكية (dynamic array) من النوع <code>int</code> لا من النوع <code>Integer</code>. وفي العموم، يَنطبِق نفس الأمر على القوائم (lists) من الأصناف المُغلِّفة الآخرى مثل <code>ArrayList&lt;Double&gt;‎</code> و <code>ArrayList&lt;Character&gt;‎</code>. يَتَبقَّى لنا مشكلة أخيرة: يُمكِن لأي قائمة أن تَحمِل القيمة <code>null</code>، ولكن لا تَتَوفَّر قيمة من النوع الأساسي (primitive type) مناظرة للقيمة <code>null</code>. وبالتالي، إذا أعاد استدعاء مثل <code>integerList.get(3)‎</code> ضِمْن التَعْليمَة <code>int num = integerList.get(3);‎</code> القيمة <code>null</code>، سيَحدُث اِعتراض مُتعلِّق بوجود مُؤشر فارغ (null pointer). ينبغي إذًا أن تَأخُذ ذلك بالحسبان إذا لم تَكُن متأكدًا بشأن وجود قيم فارغة ضِمْن القائمة.
</p>

<h2>
	البرمجة باستخدام <code>ArrayList</code>
</h2>

<p>
	كمثال بسيط، سنُعيِد كتابة البرنامج <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/source/chapter7/ReverseWithDynamicArray.java" rel="external nofollow">ReverseWithDynamicArray.java</a> من القسم السابق باِستخدَام النوع <code>ArrayList</code> بدلًا من الصَنْف المُخصَّص (custom) الذي كنا قد كتبناه. نظرًا لأننا سنُخزِّن أعدادً صحيحة (integers) ضِمْن القائمة، سنَستخدِم إذًا النوع <code>ArrayList&lt;Integer&gt;‎</code>. اُنظر شيفرة البرنامج بالكامل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_35" style="">
<span class="kwd">import</span><span class="pln"> textio</span><span class="pun">.</span><span class="typ">TextIO</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">ArrayList</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ReverseWithArrayList</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Enter some non-zero integers.  Enter 0 to end."</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">print</span><span class="pun">(</span><span class="str">"? "</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> number </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TextIO</span><span class="pun">.</span><span class="pln">getlnInt</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">number </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
            </span><span class="typ">list</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">number</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Your numbers in reverse are:"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">--)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">printf</span><span class="pun">(</span><span class="str">"%10d%n"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	كما أوضحنا مُسْبَقًا، عادةً ما تُستخدَم حَلْقة التَكْرار <code>for</code> لمعالجة المصفوفات الديناميكية من النوع <code>ArrayList</code> بنفس طريقة مُعالجة المصفوفات العادية. على سبيل المثال، تَطبَع حَلْقة التَكْرار (loop) التالية جميع العناصر بالمصفوفة <code>namelist</code> من النوع <code>ArrayList&lt;String&gt;‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_37" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> namelist</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> namelist</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">item</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُمكِنك أيضًا أن تَستخدِم حَلْقة التَكْرار <code>for-each</code> مع المصفوفات من النوع <code>ArrayList</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_39" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> item </span><span class="pun">:</span><span class="pln"> namelist </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">item</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند التَعامُل مع الأصناف المُغلِّفة (wrapper classes)، يُمكن للمُتْغيِّر المُتحكِّم بحَلْقة التَكْرار <code>for-each</code> أن يَكُون من النوع الأساسي (primitive type) بفضل خاصية فك التغليف (unboxing). على سبيل المثال، إذا كان <code>numbers</code> مُتْغيِّر من النوع <code>ArrayList&lt;Double&gt;‎</code>، يُمكِنك أن تَستخدِم حَلْقة التَكْرار التالية لحِسَاب حاصل مجموع القيم بالقائمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_41" style="">
<span class="kwd">double</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> num </span><span class="pun">:</span><span class="pln"> numbers </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   sum </span><span class="pun">=</span><span class="pln"> sum </span><span class="pun">+</span><span class="pln"> num</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستَعمَل الشيفرة بالأعلى طالما لم يَكُن هناك أي عنصر ضِمْن القائمة مُساوِي للقيمة <code>null</code>. إذا كان هنالك احتمالية لحُدوث ذلك، فينبغي أن تَستخدِم مُتْغيِّرًا مُتحكِّمًا بالحَلْقة (loop control variable) من النوع <code>Double</code> ثُمَّ تَفْحَص ما إذا كانت قيمته فارغة أم لا. على سبيل المثال، تَحسِب الشيفرة التالية حاصل مجموع القيم غَيْر الفارغة بالقائمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_43" style="">
<span class="kwd">double</span><span class="pln"> sum</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">Double</span><span class="pln"> num </span><span class="pun">:</span><span class="pln"> numbers </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> num </span><span class="pun">!=</span><span class="pln"> null </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يتم فك التغليف للحصول على قيمة‫ double</span><span class="pln">
        sum </span><span class="pun">=</span><span class="pln"> sum </span><span class="pun">+</span><span class="pln"> num</span><span class="pun">;</span><span class="pln">  
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنَفْحَص الآن البرنامج <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/source/chapter7/SimplePaint2.java" rel="external nofollow">SimplePaint2.java</a> الذي يُعدّ نسخة مُحسَّنة من البرنامج <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/source/chapter6/SimplePaint.java" rel="external nofollow">SimplePaint.java</a> الذي كتبناه بالقسم الفرعي 6.3.3. بالبرنامج الجديد، يستطيع المُستخدِم أن يَرسِم منحنيات داخل مساحة رسم (drawing area) بالنَقْر على زر الفأرة والسَحب كما يستطيع أن يختار كُلًا من اللون المُستخدَم للرسم ولون خلفية مساحة الرسم من قائمة. تحتوي أيضًا قائمة "Control" على عدة أوامر منها: الأمر "Undo" المُستخدَم لحَذْف آخر منحنى رسمه المُستخدِم على الشاشة، والأمر "Clear" المُستخدَم لحَذْف جميع المنحنيات. يَتَوفَّر أيضًا مربع الاختيار "Use Symmetry" المسئول عن تَفْعِيل خاصية التماثلية أو تَعْطِيلها. تَنعكِس المنحنيات التي يَرسِمها المُستخدِم أثناء تَفْعِيل تلك الخاصية أفقيًا ورأسيًا لتُنتِج نمطًا متماثلًا.
</p>

<p>
	بخلاف البرنامج الأصلي <code>SimplePaint</code>، تَستخدِم النسخة الجديدة هيكلًا بيانيًا (data structure) لتَخْزِين عدة معلومات عما رَسمه المُستخدِم. عندما يختار المُستخدِم لون خلفية جديد، تُملَئ الحاوية (canvas) بذلك اللون ثم يُعاد رَسْم جميع المنحنيات مجددًا على الخلفية الجديدة، ولذلك لابُدّ أن نُخزِّن كل المعلومات اللازمة لإعادة رَسْم تلك المنحنيات. بالمثل، عندما يختار المُستخدِم الأمر "Undo"، تُحَذَف بيانات آخر منحنى رسمه المُستخدِم ثُمَّ يُعاد رَسْم الصورة بالكامل مُجددًا بالاعتماد على البيانات المُتبقية.
</p>

<p>
	سنعتمد في العموم على الهيكل البياني <code>ArrayList</code> لتَخْزِين تلك البيانات. تَتَكوَّن البيانات الأساسية لأي منحنى من قائمة من النقط الواقعة على المنحنى، والتي سنُخزِّنها بكائن من النوع <code>ArrayList&lt;Point2D&gt;‎</code>. لاحِظ أن الصَنْف <code>Point2D</code> هو صَنْف قياسي (standard) مُعرَّف بحزمة <code>javafx.geometry</code>، ويُمكِننا أن نُنشِئ كائنًا منه باِستخدَام قيمتين من النوع <code>double</code> يُمثِلان الإحداثي x والإحداثي y. يَمتلك أي كائن من النوع <code>Point2D</code> تابعي جَلْب <code>pt.getX()‎</code> و <code>pt.getY()‎</code> لاسترجاع قيمة x و y. إلى جانب ذلك، سنحتاج إلى مَعرِفة لون المنحنى، وما إذا كان ينبغي تطبيق خاصية التماثلية عليه أم لا. سنُخزِّن جميع البيانات المطلوبة لرَسْم منحنى ضِمْن كائن من النوع <code>CurveData</code>، والذي سنُعرِّفه كصَنْف مُتْدِاخل (nested class) بالبرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_45" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CurveData</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">Color</span><span class="pln"> color</span><span class="pun">;</span><span class="pln">  </span><span class="com">// لون المنحنى</span><span class="pln">
   boolean symmetric</span><span class="pun">;</span><span class="pln">  </span><span class="com">// خاصية التماثلية</span><span class="pln">
   </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Point2D</span><span class="pun">&gt;</span><span class="pln"> points</span><span class="pun">;</span><span class="pln">  </span><span class="com">// نقاط المنحنى</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نظرًا لأن الصورة قد تَحتوِي على عدة منحنيات وليس فقط منحنى وحيد، ولأننا نحتاج إلى تَخْزِين كل البيانات الضرورية لإعادة رَسْم الصورة بالكامل، سنحتاج إذًا إلى قائمة من الكائنات من النوع <code>CurveData</code>. سيَستخدِم البرنامج المُتْغيِّر <code>curves</code> من النوع <code>ArrayList</code> لذلك الغرض، وسنُصرِّح عنه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_47" style="">
<span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">CurveData</span><span class="pun">&gt;</span><span class="pln"> curves </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">CurveData</span><span class="pun">&gt;();</span></pre>

<p>
	لدينا هنا قائمة من الكائنات (objects)، ويَحتوِي كل كائن منها على قائمة من النقط كجزء من البيانات المُعرَّفة بداخله. سنَفْحَص الآن عدة أمثلة على معالجة ذلك الهيكل البياني (data structure). أولًا، عندما يَنقُر المُستخدِم بزر الفأرة على مساحة الرسم (drawing surface)، يَعنِي ذلك أنه يَرسِم منحنى جديدًا، ولذلك ينبغي أن نُنشِئ كائنًا جديدًا من النوع <code>CurveData</code>، ونُهيئ جميع مُتْغيِّرات النُسخ (instance variables) المُعرَّفة بداخله. بَفْرَض أن <code>currentCurve</code> هو مُتْغيِّر عام (global) من النوع <code>CurveData</code>، يُمكِننا تعريف البرنامج <code>mousePressed()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_49" style="">
<span class="pln">currentCurve </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CurveData</span><span class="pun">();</span><span class="pln">       </span><span class="com">// ‫أنشئ كائن من النوع CurveData</span><span class="pln">

</span><span class="com">// ‫يُنسخ اللون المستخدم لرسم المنحنى من متغير النسخة الممثل للون </span><span class="pln">
</span><span class="com">// المستخدم للرسم</span><span class="pln">
currentCurve</span><span class="pun">.</span><span class="pln">color </span><span class="pun">=</span><span class="pln"> currentColor</span><span class="pun">;</span><span class="pln">    

</span><span class="com">// ‫تنسخ خاصية التماثلية أيضًا من القيمة الحالية للمتغير useSymmetry</span><span class="pln">
currentCurve</span><span class="pun">.</span><span class="pln">symmetric </span><span class="pun">=</span><span class="pln"> useSymmetry</span><span class="pun">;</span><span class="pln"> 

currentCurve</span><span class="pun">.</span><span class="pln">points </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Point2D</span><span class="pun">&gt;();</span><span class="pln">  </span><span class="com">// A new point list object.</span></pre>

<p>
	عندما يَسحَب المُستخدِم الفأرة، ينبغي أن نُضيف نقطًا جديدة إلى المُتْغيِّر <code>currentCurve</code>، وأن نَرسِم قطعًا مستقيمة بين تلك النقط أثناء إضافتها. في المقابل، عندما يُحرِّر المُستخدِم الفأرة، يَكُون المنحنى قد اكتمل، وينبغي إذًا أن نُضيِفه إلى قائمة المنحنيات باستدعاء ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_51" style="">
<span class="pln">curves</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> currentCurve </span><span class="pun">);</span></pre>

<p>
	عندما يُغيِّر المُستخدِم لون الخلفية أو يختار الأمر "Undo"، ينبغي أن نُعيِد رَسْم الصورة. يَحتوِي البرنامج على التابع <code>redraw()‎</code> المسئول عن إعادة رَسْم الصورة بالكامل. يَعتمِد التابع على البيانات الموجودة بالمُتْغيِّر <code>curves</code> لإعادة رَسْم جميع المنحنيات، ويَستخدِم حَلْقة تَكْرار <code>for-each</code> لمعالجة بيانات كل منحنى على حدى كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_53" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">CurveData</span><span class="pln"> curve </span><span class="pun">:</span><span class="pln"> curves </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
       </span><span class="com">// ‫ارسم المنحنى الذي يمثله الكائن curve من النوع CurveData</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">  
</span><span class="pun">}</span></pre>

<p>
	لاحِظ أن <code>curve.points</code> عبارة عن مُتْغيِّر من النوع <code>ArrayList&lt;Point2D&gt;‎</code> أي عبارة عن قائمة من النقط الواقعة على المنحنى. يُمكِننا على سبيل المثال أن نَسترجِع النقطة <code>i</code> على المنحنى باستدعاء التابع <code>get()‎</code> المُعرَّف بالقائمة <code>curve.points.get(i)‎</code>. يُعيِد ذلك الاستدعاء قيمة من النوع <code>Point2D</code> التي تُعرِّف بدورها توابع الجَلْب <code>getX()‎</code> و <code>getY()‎</code>.
</p>

<p>
	يُمكِننا إذًا أن نُشير إلى الإحداثي x للنقطة <code>i</code> على المنحنى مباشرةً كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_57" style="">
<span class="pln">curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">).</span><span class="pln">getX</span><span class="pun">()</span></pre>

<p>
	قد يبدو الاستدعاء السابق مُعقدًا، ولكنه مثال جيد على الأسماء المركبة (complex names). يُخصِّص ذلك الاسم مسارًا إلى جزء من البيانات: اذهب إلى الكائن <code>curve</code>. بداخل ذلك الكائن، إذهب إلى <code>points</code>. بداخل <code>points</code>، إذهب إلى العنصر برقم المَوْضِع <code>i</code>. من هذا العنصر، اِسترجِع الإحداثي <code>x</code> من خلال استدعاء تابعه <code>getX()‎</code>. اُنظر تعريف التابع <code>redraw()‎</code> بالكامل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4400_59" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln">backgroundColor</span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="lit">0</span><span class="pun">,</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">(),</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">getHeight</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">CurveData</span><span class="pln"> curve </span><span class="pun">:</span><span class="pln"> curves </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="pln">curve</span><span class="pun">.</span><span class="pln">color</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// ‫ارسم قطعة مستقيمة من رقم الموضع i-1 إلى رقم الموضع i</span><span class="pln">
            </span><span class="kwd">double</span><span class="pln"> x1 </span><span class="pun">=</span><span class="pln"> curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">).</span><span class="pln">getX</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">double</span><span class="pln"> y1 </span><span class="pun">=</span><span class="pln"> curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">).</span><span class="pln">getY</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">double</span><span class="pln"> x2 </span><span class="pun">=</span><span class="pln"> curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">).</span><span class="pln">getX</span><span class="pun">();</span><span class="pln">
            </span><span class="kwd">double</span><span class="pln"> y2 </span><span class="pun">=</span><span class="pln"> curve</span><span class="pun">.</span><span class="pln">points</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">).</span><span class="pln">getY</span><span class="pun">();</span><span class="pln">
            drawSegment</span><span class="pun">(</span><span class="pln">curve</span><span class="pun">.</span><span class="pln">symmetric</span><span class="pun">,</span><span class="pln">x1</span><span class="pun">,</span><span class="pln">y1</span><span class="pun">,</span><span class="pln">x2</span><span class="pun">,</span><span class="pln">y2</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَرسِم التابع <code>drawSegment()‎</code> قطعة مستقيمة من (x1,y1) إلى (x2,y2). بالإضافة إلى ذلك، إذا كان المُعامل (parameter) الأول يُساوِي <code>true</code>، فإنه أيضًا يَرسِم انعكاسًا أفقيًا ورأسيًا لتلك القطعة.
</p>

<p>
	لقد رَكَّزنا هنا على اِستخدَام الصَنْف <code>ArrayList</code> ضِمْن البرنامج، ولكن يُفضَّل بالطبع أن تَتَطلِع على الشيفرة الكاملة للبرنامج <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/source/chapter7/SimplePaint2.java" rel="external nofollow">SimplePaint2.java</a>. بالإضافة إلى كَوْنه مثال على اِستخدَام الأنواع ذات المُعاملات غَيْر محدَّدة النوع (parameterized types)، فإنه يُعدّ أيضًا مثالًا جيدًا على إنشاء القوائم (menus) واِستخدَامها. ينبغي عمومًا أن تَكُون قادرًا على فهم البرنامج بالكامل.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/c7/s3.html" rel="external nofollow">Section 3: ArrayList</a> من فصل Chapter 7: Arrays and ArrayLists من كتاب <a data-ss1615725850="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1159</guid><pubDate>Sun, 14 Mar 2021 12:46:57 +0000</pubDate></item><item><title>&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; Arrays &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1158/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e000cb5029_-.png.597d78fb9e157cf55f612a32b79d3431.png" /></p>

<p>
	تَعرَّضنا حتى الآن إلى بعض الأمثلة على معالجة المصفوفات في <a data-ss1615724553="1" data-ss1615725670="1" href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1157/" rel="">المقال السابق</a>، ولكن غالبيتها كان مُجرد أمثلة بسيطة على معالجة عناصر مصفوفة من البداية إلى النهاية أو جَلْب عشوائي لقيمة عنصر ضِمْن مصفوفة. سنَفْحَص بهذا القسم وما يليه من أقسام بعضًا من الأمور الآخرى الأكثر تشويقًا.
</p>

<h2>
	أمثلة على المعالجة
</h2>

<p>
	لابُدّ أن تكون حريصًا فيما يتعلَّق بالفهارس من خارج نطاق المصفوفة. لنَفْترِض مثلًا أن <code>lines</code> عبارة عن مصفوفة من النوع <code>String[]‎</code>، وأننا نريد مَعرِفة ما إذا كانت تلك المصفوفة تحتوي على أية عناصر مكررة بموضعين متتاليين. ينبغي إذًا أن نَفْحَص الاختبار <code>lines.equals(lines[i+1])‎</code> لأي فهرس <code>i</code>. اُنظر المحاولة الخاطئة التالية:
</p>

<pre class="ipsCode">
boolean dupp = false;  // Assume there are no duplicates
for ( int i = 0; i &lt; list.length; i++ ) {
    if ( lines[i].equals(lines[i+1]) ) {  // THERE IS AN ERROR HERE!
        dupp = true;   // we have found a duplicate!
        break;
    }
}
</pre>

<p>
	تبدو حلقة التكرار <code>for</code> بالأعلى مثل الكثير من الحلقات التي تعرضنا لها من قبل، لذلك ما هي المشكلة إذًا؟ يحدث الخطأ عندما تسند القيمة النهائية إلى <code>i</code> بالحلقة أي عندما <code>i</code> تساوي <code>lines.length-1</code>. في هذه الحالة، <code>i+1</code> تساوي <code>lines.length</code>. لكن فهرس العنصر الأخير بالمصفوفة هو <code>lines.length-1</code>، لذلك <code>lines.length</code> ليست فهرس صالح. يعني ذلك أن <code>lines[i+1]‎</code> يتسبب بحدوث اعتراض من النوع <code>ArrayIndexOutOfBoundsException</code>. يمكنك إصلاح ذلك بسهولة عن طريق إيقاف حلقة التكرار قبل <code>i+1</code> من النطاق المسموح به، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_8" style="">
<span class="pln">boolean dupp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// Assume there are no duplicates</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> lines</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">equals</span><span class="pun">(</span><span class="pln">lines</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">])</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
        dupp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">   </span><span class="com">// we have found a duplicate!</span><span class="pln">
        </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يظهر أحيانًا نوع شبيه من الأخطاء عند العمل مع المصفوفات المملوءة جزئيًا (partially full arrays) -اُنظر القسم الفرعي 3.8.4-، ولكنه قد لا يَكُون بهذا الوضوح. إذا كان جزء معين فقط من المصفوفة مملوءًا بحيث يُستخدَم عداد (counter) لمعرفة عدد الخانات المُستخدَمة فعليًا ضِمْن تلك المصفوفة. لا تتعلَّق المشكلة في تلك الحالة بمحاولة الوصول لعنصر بموضع خارج المصفوفة ولكنها تتعلق بفحص جزء من المصفوفة قيد الاستخدام بالفعل. عندما يُحاوِل البرنامج الوصول إلى مَوْضِع خارج المصفوفة، ينهار (crash) البرنامج على الأقل مما يَسمَح لك برصد المشكلة لكن في حالة المصفوفات المملوءة جزئيًا، لن تتمكن من رصد الخطأ بسهولة.
</p>

<p>
	لقد رأينا الكيفية التي يُمكِننا بها أن نُضيف عناصر إلى مصفوفة مملوءة جزئيًا، ولكننا لم نَتعرَّض لطريقة حَذْف تلك العناصر؟ لنَفْترِض مثلًا أننا نَكْتُب برنامجًا عبارة عن لعبة يستطيع اللاعبون الانضمام إليها ومغادرتها بأي وقت. يُمكِننا إذًا أن نُنشِئ الصَنْف <code>Player</code> لتمثيل كل لاعب موجود باللعبة. سنُخزِّن بطبيعة الحال قائمة اللاعبين الموجودين باللعبة داخل مصفوفة من النوع <code>Player[]‎</code>، وليَكُن اسمها هو <code>playerList</code>. نظرًا لأن عدد اللاعبين قد يَتَغيَّر بأي وقت، سنُطبِق نمط المصفوفة المملوءة جزئيًا، لذا سنُعرِّف مُتغيِّر <code>playerCt</code> لتَخْزِين عدد اللاعبين الموجودين باللعبة. بفَرْض عدم احتمالية وجود أكثر من 10 لاعبين بأي لحظة، يُمكِننا إذًا أن نُصرِّح عن المُتْغيِّرات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_10" style="">
<span class="typ">Player</span><span class="pun">[]</span><span class="pln"> playerList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Player</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">  </span><span class="com">// Up to 10 players.</span><span class="pln">
</span><span class="typ">int</span><span class="pln">      playerCt </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">  </span><span class="com">// At the start, there are no players.</span></pre>

<p>
	بَعْد انضمام بعض اللاعبين إلى اللعبة، سيُصبِح المُتْغيِّر <code>playerCt</code> أكبر من الصفر كما ستُخزَّن الكائنات (objects) المُمثِلة للاعبين داخل عناصر المصفوفة <code>playerList[0]‎</code> و <code>playerList[1]‎</code> .. وحتى <code>playerList[playerCt-1]‎</code>. لاحِظ أننا لم نُشِر إلى العنصر <code>playerList[playerCt]‎</code>. يُمثِل المُتْغيِّر <code>playerCt</code> كُلًا من عدد العناصر الفعلية الموجودة داخل المصفوفة، وكذلك فهرس (index) الخانة التالية المتاحة بالمصفوفة. تُضيِف الشيفرة التالية كائنًا جديدًا <code>newPlayer</code> إلى اللعبة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_12" style="">
<span class="pln">playerList</span><span class="pun">[</span><span class="pln">playerCt</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newPlayer</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Put new player in next</span><span class="pln">
                                  </span><span class="com">//     available spot.</span><span class="pln">
playerCt</span><span class="pun">++;</span><span class="pln">  </span><span class="com">// And increment playerCt to count the new player.</span></pre>

<p>
	في المقابل، قد يَكُون حَذْف لاعب من اللعبة أصعب قليلًا، لأننا لا نُريد بالطبع أن نَترُك مَوْضِع اللاعب المطلوب حَذْفه فارغًا. لنَفْترِض مثلًا أننا نُريد حَذْف اللاعب الموجود بالفهرس <code>k</code> داخل المصفوفة <code>playerList</code>. يَعنِي ذلك أن عدد الخانات المُستخدَمة فعليًا بالمصفوفة سيُصبِح أقل بمقدار الواحد. إذا لم يَكُن ترتيب اللاعبين داخل المصفوفة مُهِمًا، يُمكنِنا إذًا أن نَنقِل اللاعب الأخير الموجود فعليًا داخل المصفوفة إلى المَوْضِع <code>k</code> ثُمَّ نُنقِص قيمة <code>playerCt</code> بمقدار الواحد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_14" style="">
<span class="pln">playerList</span><span class="pun">[</span><span class="pln">k</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerList</span><span class="pun">[</span><span class="pln">playerCt </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">];</span><span class="pln">
playerCt</span><span class="pun">--;</span></pre>

<p>
	لم يَعُد اللاعب الذي كان موجودًا مُسْبَقًا بالمَوْضِع <code>k</code> موجودًا بالمصفوفة، فقد حَذْفناه ببساطة من القائمة. في المقابل، أصبح اللاعب الذي كان موجودًا مُسْبَقًا بالموضع <code>playerCt - 1</code> موجودًا بالمصفوفة مرتين، ولكن نظرًا لأننا انقصنا المُتْغيِّر <code>playerCt</code> بمقدار الواحد، فإن واحدة منهما فقط تَقَع ضِمْن الجزء المُستخدَم فعليًا بالمصفوفة. يَحمِل أي عنصر بالمصفوفة في العموم قيمة ما، ولكن القيم من الموضع 0 وحتى <code>playerCt - 1</code> هي فقط الصالحة أي يُمكِننا معالجتها. حاول أن تُفكِر بما سيَحدُث إذا كان اللاعب المطلوب حَذْفه هو اللاعب الأخير بالمصفوفة، وهل ترى أن الشيفرة بالأعلى ستَظِل صالحة؟
</p>

<p>
	إذا كان ترتيب اللاعبين بالمصفوفة مُهِمًّا (ربما لأنه يُسمَح لهم باللعب وفقًا لترتيب تَخْزِينهم بالمصفوفة)، ينبغي عندها تحريك كل لاعب موجود من المَوْضِع <code>k+1</code> وحتى آخر مَوْضِع مملوء فعليًا بالمصفوفة إلى المَوْضِع الذي يَسبِقُه أي سيَحلّ مثلًا اللاعب بالمَوْضِع <code>k+1</code> محلّ اللاعب بالمَوْضِع <code>k</code> الذي أصبح خارج اللعبة للتو، وبدوره سيَملئ اللاعب بالمَوْضِع <code>k+2</code> البقعة التي تركها اللاعب <code>k+1</code> للتو، وهكذا. اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_16" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> k</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> playerCt</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    playerList</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerList</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
playerCt</span><span class="pun">--;</span></pre>

<p>
	تُوضِح الصورة التالية طريقتي حَذْف عنصر -اللاعب "C" تحديدًا- من مصفوفة مملوءة جزئيًا (partially full array):
</p>

<p style="text-align: center;">
	<img alt="001Delete_From_Array.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59584" data-unique="q7suo7sfm" src="https://academy.hsoub.com/uploads/monthly_2021_03/001Delete_From_Array.png.1f5607aae5db94412ad9c1e7177e8b96.png"></p>

<p>
	يترك ذلك السؤال مفتوحًا عما سيَحدُث إذا كانت المصفوفة المملوءة جزئيًا (partially full) مملوءة بالكامل بينما تَرغَب بإضافة عناصر جديدة إليها؟ بالطبع، لا يُمكِننا أن نُغيِّر حجم المصفوفة (array size)، ولكن يُمكِننا أن نُنشِئ مصفوفة جديدة أكبر ثُمَّ نَنَسَخ البيانات الموجودة بالمصفوفة القديمة إلى المصفوفة الجديدة. ينقلنا ذلك إلى السؤال التالي: ما الذي يعنيه نَسخ مصفوفة بالأساس؟
</p>

<p>
	لنَفْترِض أن <code>A</code> و <code>B</code> عبارة عن مُتْغيِّري مصفوفة (array variables) لهما نفس النوع الأساسي (base type). تُشير <code>A</code> بالفعل إلى مصفوفة، ونريد الآن جَعْل <code>B</code> تُشيِر إلى نُسخة من <code>A</code>. أول ما ينبغي أن تُدركه هو أن تَعْليمَة الإِسْناد (assignment statement) التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_18" style="">
<span class="pln">B </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">;</span></pre>

<p>
	لا تُنشِئ نسخة من <code>A</code>. نظرًا لأن المصفوفات عبارة عن كائنات (objects)، يَحمِل أي مُتْغيِّر مصفوفة (array variable) -إن لم يَكُن فارغًا- مؤشِرًا (pointer) إلى مصفوفة. تَنَسَخ تَعْليمَة الإِسْناد (assignment) بالأعلى ذلك المُؤشر من <code>A</code> إلى <code>B</code>، وبالتالي سيُشيِر كُلًا من <code>A</code> و <code>B</code> إلى نفس ذات المصفوفة، فيُعدّ مثلًا كُلًا من <code>A[0]‎</code> و <code>B[0]‎</code> أسماءً مختلفة لنفس عناصر المصفوفة (array element). في المقابل، إذا كنا نريد جَعْل <code>B</code> يُشيِر إلى نسخة من <code>A</code>، ينبغي إذًا أن نُنشِئ مصفوفة جديدة كليًا ثُمَّ نَنَسخ العناصر من <code>A</code> إلى <code>B</code>. بِفَرض أن <code>A</code> و <code>B</code> من النوع <code>double[]‎</code>، يُمكِننا إذًا كتابة ما يَلي لإنشاء نسخة من <code>A</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_20" style="">
<span class="kwd">double</span><span class="pun">[]</span><span class="pln"> B</span><span class="pun">;</span><span class="pln">
B </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="pln">A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">];</span><span class="pln">  </span><span class="com">// Make a new array with the same length as A.</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    B</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكي نَتَمكَّن من إضافة عناصر جديدة إلى مصفوفة مملوءة جزئيًا (partially full array) ممتلئة، ينبغي أن نُنشِئ مصفوفة جديدة أكبر، وعادةً ما تَكُون بحجم يُساوِي ضعف المصفوفة الحالية. لما كان مُتْغيِّر المصفوفة هو ما يَسمَح لنا بالوصول إلى البيانات التي أصبحت موجودة بالمصفوفة الجديدة، ينبغي إذًا أن نُعدِّل ذلك المُتْغيِّر لكي يُشير إلى المصفوفة الجديدة. يُمكِننا أن نُجرِي ذلك لحسن الحظ بتَعْليمَة إِسْناد (assignment statement) بسيطة. بِفَرْض اِستخدَام <code>playerList</code> و <code>playerCt</code> لتَخْزِين اللاعبين باللعبة -كالمثال بالأعلى-، تُوضِح الشيفرة التالية طريقة إضافة لاعب جديد <code>newPlayer</code> إلى اللعبة حتى لو كانت المصفوفة <code>playerList</code> ممتلئة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_22" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> playerCt </span><span class="pun">==</span><span class="pln"> playerList</span><span class="pun">.</span><span class="pln">length </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// The number of players is already equal to the size of the array.</span><span class="pln">
        </span><span class="com">// The array is full.  Make a new array that has more space.</span><span class="pln">
    </span><span class="typ">Player</span><span class="pun">[]</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">   </span><span class="com">// A variable to point to the new array.</span><span class="pln">
    temp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Player</span><span class="pun">[</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">playerList</span><span class="pun">.</span><span class="pln">length </span><span class="pun">];</span><span class="pln">  </span><span class="com">// Twice as big as the old array.</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> playerList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        temp</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerList</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">  </span><span class="com">// Copy item from old array into new array.</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    playerList </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">;</span><span class="pln">  </span><span class="com">// playerList now points to the new, bigger array.    </span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// At this point, we know that there is room in the array for newPlayer.</span><span class="pln">
playerList</span><span class="pun">[</span><span class="pln">playerCt</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newPlayer</span><span class="pun">;</span><span class="pln">
playerCt</span><span class="pun">++;</span></pre>

<p>
	الآن، لم يَعُد هناك أي مُتْغيِّر يُشير إلى المصفوفة القديمة، ولذلك يَتَولَّى كانس المُهملات (garbage collector) مُهِمَة تَحرِيرها.
</p>

<h2>
	بعض التوابع القياسية للمصفوفات
</h2>

<p>
	تحتاج الكثير من البرامج إلى عمليات مثل نَسْخ مصفوفة، ولهذا تُوفِّر الجافا العديد من التوابع (methods) القياسية لمعالجة المصفوفات. هذه التوابع مُعرَّفة كتوابع ساكنة (static methods) بصَنْف اسمه <code>Arrays</code> ضِمْن حزمة <code>java.util</code>. اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_24" style="">
<span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> </span><span class="typ">list</span><span class="pun">,</span><span class="pln"> lengthOfCopy </span><span class="pun">)</span></pre>

<p>
	تُعيد الدالة <code>copyOf</code> مصفوفة جديدة طولها يُساوِي قيمة المعامل <code>lengthOfCopy</code>، وبحيث تحتوي على عناصر منسوخة من المُعامل <code>list</code>. إذا كان <code>lengthOfCopy</code> أكبر من <code>list.length</code>، ستَحمِل الخانات الإضافية بالمصفوفة الجديدة قيمها الافتراضية (أي صفر في حالة المصفوفات العددية، و<code>null</code> في حالة مصفوفات الكائنات [array objects]، ..إلخ). في المقابل، إذا كان <code>lengthOfCopy</code> أقل من <code>list.length</code>، تُنسَخ العناصر بمقدار ما يَتَناسَب مع حجم المصفوفة الجديدة. على سبيل المثال، إذا كانت <code>A</code> مصفوفة، فإن الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_26" style="">
<span class="pln">B </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> A</span><span class="pun">,</span><span class="pln"> A</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span></pre>

<p>
	تَضبُط <code>B</code> لكي تُشيِر إلى نُسخة مُتطابِقة مع <code>A</code>، أما الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_28" style="">
<span class="pln">playerList </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> playerList</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">playerList</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span></pre>

<p>
	يُمكِنها أن تُستخدَم لمضاعفة حجم المساحة المتاحة بمصفوفة مملوءة جزئيًا (partially full array). علاوة على ذلك، قد نَستخدِم نفس ذلك التابع <code>Arrays.copyOf</code> لإنقاص حجم مصفوفة مملوءة جزئيًا، وهو ما يُساعِدنا على تَجنُّب وجود خانات إضافية كثيرة غَيْر مُستخدَمة. يُمكِننا أن نُطبِق ذلك أثناء حَذْف لاعب <code>k</code> من قائمة اللاعبين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_30" style="">
<span class="pln">playerList</span><span class="pun">[</span><span class="pln">k</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> playerList</span><span class="pun">[</span><span class="pln">playerCt</span><span class="pun">-</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
playerCt</span><span class="pun">--;</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> playerCt </span><span class="pun">&lt;</span><span class="pln"> playerList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">/</span><span class="lit">4</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// More than 3/4 of the spaces are empty. Cut the array size in half.</span><span class="pln">
    playerList </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> playerList</span><span class="pun">,</span><span class="pln"> playerList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">/</span><span class="lit">2</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يحتوي الصَنْف <code>Arrays</code> في الواقع على نُسخ مختلفة من التابع <code>copyOf</code>: نُسخة لكل نوع أساسي (primitive type)، بالإضافة إلى نُسخة آخرى للكائنات (objects). ملحوظة: عند نَسْخ مصفوفة كائنات (objects)، تُنسَخ فقط مؤشرات (pointers) الكائنات لا محتوياتها إلى المصفوفة الجديدة، ويُمثِل ذلك عمومًا القاعدة العامة لإِسْناد مؤشر (pointer) إلى آخر.
</p>

<p>
	إذا كان كل ما تُريده هو مُجرد نسخة بسيطة من مصفوفة بنفس حجمها الأصلي، فهناك طريقة سهلة للقيام بذلك. في الواقع، تَملُك أي مصفوفة تابع نُسخة (instance method) اسمه هو <code>clone()‎</code>. يُنشِئ ذلك التابع نُسخة من المصفوفة. يُمكِنك مثلًا أن تَستخدِم الشيفرة التالية لإنشاء نسخة من مصفوفة من النوع <code>int</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_32" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> B  </span><span class="pun">=</span><span class="pln">  A</span><span class="pun">.</span><span class="pln">clone</span><span class="pun">();</span></pre>

<p>
	يَحتوِي الصَنْف <code>Array</code> على توابع (methods) مفيدة آخرى سنَعرِض بعضًا منها هنا. بالمثل من التابع <code>Arrays.copyOf</code>، تَتَوفَّر نُسخ متعددة من كل تلك التوابع للأنواع المختلفة من المصفوفات:
</p>

<ul>
<li>
		<p>
			<code>Arrays.fill( array, value )</code>‎: تَملَئ مصفوفة كاملة بقيمة محددة. لابُدّ أن يَكُون <code>value</code> من نوع مُتوافق مع النوع الأساسي للمصفوفة <code>array</code>. إذا كان <code>numlist</code> عبارة عن مصفوفة من النوع <code>double[]‎</code> مثلًا، ستَملَئ التَعْليمَة <code>Arrays.fill(numlist,17)‎</code> جميع عناصر تلك المصفوفة بالقيمة 17.
		</p>
	</li>
	<li>
		<p>
			<code>Arrays.fill( array, fromIndex, toIndex, value )‎</code>: تَملَئ جزءًا من المصفوفة <code>array</code> من الفهرس <code>fromIndex</code> حتى الفهرس <code>toIndex-1</code> بالقيمة <code>value</code>. لاحِظ أن الفهرس <code>toIndex</code> غَيْر مُضمَّن.
		</p>
	</li>
	<li>
		<p>
			<code>Arrays.toString( array )‎</code>: عبارة عن دالة (function) تُعيِد سِلسِلة نصية من النوع <code>String</code> مُكوَّنة من قيم المصفوفة <code>array</code> مفصولة بفاصلة (comma) ومُحاطَة بأقواس مربعة. تُحوَّل القيم إلى سَلاسِل نصية (strings) بنفس الطريقة التي تُحوَّل بها تلك القيم أثناء طباعتها.
		</p>
	</li>
	<li>
		<p>
			<code>Arrays.sort( array )‎</code>: تُعيد ترتيب جميع القيم الموجودة بمصفوفة ترتيبًا تصاعديًا. لاحِظ أنها لا تَعمَل مع جميع المصفوفات، فلابُدّ أن تكون قيم المصفوفة قابلة للموازنة لمعرفة أيهما أصغر. افتراضيًا، تَعمَل مع المصفوفات من النوع <code>String</code> والأنواع البسيطة (primitive types) باستثناء النوع <code>boolean</code>. سنناقش خوارزميات ترتيب المصفوفات (array sorting) بالقسم 7.4.
		</p>
	</li>
	<li>
		<p>
			<code>Arrays.sort( array, fromIndex, toIndex )‎</code>: تُرتِّب العناصر من <code>array[fromIndex]‎</code> إلى <code>array[toIndex-1]‎</code>.
		</p>
	</li>
	<li>
		<p>
			<code>Arrays.binarySearch( array, value )‎</code>: تبحث تلك الدالة عن <code>value</code> داخل <code>array</code>، وتُعيد قيمة من النوع <code>int</code> عبارة عن فهرس (index) عنصر المصفوفة المُتضمِّن للقيمة المُمرَّرة إذا كانت موجودة. إذا لم تَكُن تلك القيمة موجودة بالمصفوفة، تُعيد تلك الدالة القيمة -1. ملحوظة: لابُدّ أن تَكُون المصفوفة مُرتَّبة ترتيبًا تصاعديًا. سنُناقِش خوارزمية البحث الثنائي (binary search) بالقسم 7.4.
		</p>
	</li>
</ul>
<h3>
	7.2.3 برنامج <code>RandomStrings</code>
</h3>

<p>
	صَمَّمنا -بالقسم الفرعي 6.2.4- برنامج واجهة مُستخدِم رسومية (GUI) يَعرِض نُسخًا مُتعددة من سِلسِلة نصية بمواضع عشوائية وبألوان وخطوط عشوائية، وعندما يَنقُر المُستخدِم على نافذة البرنامج، تَتَغيَّر كُلًا من مَواضِع وألوان وخطوط تلك السَلاسِل النصية (strings) إلى قيم عشوائية. لنَفْترِض الآن أننا نُريد أن نُنشِئ تحريكة (animation) تتحرك خلالها تلك السَلاسِل عبر النافذة. سنحتاج في تلك الحالة إلى تَخْزِين خاصيات كل سِلسِلة نصية منها لكي نَتَمكَّن من إعادة رسمها بكل إطار ضِمْن التحريكة. يُمكِنك الإطلاع على نسخة البرنامج الجديدة بالملف <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/source/chapter7/RandomStringsWithArray.java" rel="external nofollow">RandomStringsWithArray.java</a>.
</p>

<p>
	سنَرسِم 25 سِلسِلة نصية. لكل سِلسِلة نصية منها، ينبغي أن نُخزِّن إحداثيات مَوْضِعها (x,y) ولون ونوع الخط المُستخدَم للرَسْم. لكي نَتَمكَّن من تَحرِيك السَلاسِل النصية، سنُخزِّن سرعة الحركة الخاصة بكل سِلسِلة نصية بهيئة عددين <code>dx</code> و <code>dy</code>. بكل إطار (frame)، ينبغي أن تزداد قيمة الإحداثي <code>x</code> لكل سِلسِلة نصية بمقدار <code>dx</code> الخاص بتلك السِلسِلة بينما ستزداد قيمة الإحداثي <code>y</code> بمقدار <code>dy</code> المناظر. سنحتاج إذًا إلى الست مصفوفات التالية لتَخْزِين بيانات البرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_34" style="">
<span class="kwd">double</span><span class="pun">[]</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">  
</span><span class="kwd">double</span><span class="pun">[]</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">double</span><span class="pun">[]</span><span class="pln"> dx </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">  
</span><span class="kwd">double</span><span class="pun">[]</span><span class="pln"> dy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">
</span><span class="typ">Color</span><span class="pun">[]</span><span class="pln"> color </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">
</span><span class="typ">Font</span><span class="pun">[]</span><span class="pln"> font </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span></pre>

<p>
	ستُملَئ هذه المصفوفات بقيم عشوائية. يَرسَم التابع <code>draw()‎</code> -المسئول عن رَسْم الحاوية (canvas)- السَلاسِل النصية. تُرسَم كل سِلسِلة نصية <code>i</code> بإحداثيات نقطة تُساوِي <code>(x,y)‎</code> وبلون يُساوِي <code>color‎</code> وبنوع خط يُساوِي <code>font‎</code>. لم نَستخدِم كُلًا من <code>dx</code> و <code>dy</code> بالتابع <code>draw()‎</code>، وإنما سنَستخدِمهما أثناء تَحدِيث البيانات أثناء رَسْم الإطار التالي. يُمكِننا إذًا كتابة تعريف التابع <code>draw()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_36" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">GraphicsContext</span><span class="pln"> g </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getGraphicsContext2D</span><span class="pun">();</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">WHITE</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (Fill with white, erasing previous picture.)</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="lit">0</span><span class="pun">,</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">(),</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">getHeight</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">25</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> color</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln"> font</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">fillText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> y</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLACK</span><span class="pun">);</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">strokeText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> y</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَستخدِم هذا الأسلوب المصفوفات المتوازية (parallel arrays) حيث قُسِّمت بيانات رسالة واحدة بين عدة مصفوفات، ولكي تَرَى جميع البيانات المُتعلِّقة برسالة واحدة، ستحتاج إلى وَضْع المصفوفات إلى جانب بعضها كخطوط مُتوازية، وبحيث تَقَع العناصر برقم المَوضِع <code>i</code> ضِمْن تلك المصفوفات إلى جوار بعضها البعض. قد لا يُعدّ اِستخدَام المصفوفات المتوازية (parallel arrays) بهذا المثال البسيط خطأً فادحًا، ولكنها في العموم لا تَتَبِع الفلسفة كائنية التوجه (object-oriented) فيما يَتَعلَّق بتَخْزِين البيانات المُرتبطة معًا ضِمْن كائن واحد. إذا اتبعنا تلك القاعدة، فإننا لن نحتاج إلى تَخيُل العلاقة بين تلك البيانات؛ لأنها ستَكُون موجودة بمكان واحد بالفعل. سنُعرِّف الصَنْف <code>StringData</code> لهذا الغرض تحديدًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_38" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">StringData</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// Info needed to draw one string.</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">;</span><span class="pln">       </span><span class="com">// location of the string;</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> dx</span><span class="pun">,</span><span class="pln">dy</span><span class="pun">;</span><span class="pln">     </span><span class="com">// velocity of the string;</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> color</span><span class="pun">;</span><span class="pln">      </span><span class="com">// color of the string;</span><span class="pln">
    </span><span class="typ">Font</span><span class="pln"> font</span><span class="pun">;</span><span class="pln">        </span><span class="com">// the font that is used to draw the string</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنَستخدِم مصفوفة من النوع <code>StringData[]‎</code> لتَخْزِين بيانات النُسخ المختلفة من الرسالة النصية. تُصرِّح التَعْليمَة التالية عن مصفوفة <code>stringData</code> بهيئة مُتْغيِّر نسخة (instance variable):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_40" style="">
<span class="typ">StringData</span><span class="pun">[]</span><span class="pln"> stringData</span><span class="pun">;</span></pre>

<p>
	ستَكُون قيمة المُتْغيِّر <code>stringData</code> فارغة بالبداية. ينبغي إذًا أن نُنِشئ مصفوفة ونملأها بالبيانات ثم نُسنِدها (assign) إليه. لاحِظ أن كل عنصر بتلك المصفوفة عبارة عن كائن (object) من النوع <code>StringData</code>، والذي ينبغي أن نُنشِئه قبل اِستخدَامه. يُنشِئ البرنامج الفرعي (subroutine) التالي المصفوفة ويملؤها ببيانات عشوائية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_42" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> createStringData</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    stringData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StringData</span><span class="pun">[</span><span class="lit">25</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">25</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StringData</span><span class="pun">();</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">x </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">y </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getHeight</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">)</span><span class="pln"> </span><span class="com">// 50% chance that dx is negative</span><span class="pln">
            stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dx </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dx</span><span class="pun">;</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dy </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">)</span><span class="pln"> </span><span class="com">// 50% chance that dy is negative</span><span class="pln">
            stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dy </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">dy</span><span class="pun">;</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">color </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">hsb</span><span class="pun">(</span><span class="pln"> </span><span class="lit">360</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
        stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">font </span><span class="pun">=</span><span class="pln"> fonts</span><span class="pun">[</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="lit">5</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">())</span><span class="pln"> </span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُستدعَى التابع (method) السابق داخل <code>start()‎</code> وكذلك عندما يَنقُر المستخدم على الزر لإنشاء بيانات عشوائية جديدة. يُمكِننا الآن أن نَستخدِم حَلْقة تَكْرار <code>for</code> لرَسْم السلاسل النصية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_44" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">25</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">color </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">font </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">fillText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLACK</span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">strokeText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> stringData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أو قد نَستخدِم حَلْقة التَكْرار <code>for-each</code> لكي نَتَجنَّب التَعامُل مع فهارس المصفوفة (array indices) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_46" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">StringData</span><span class="pln"> data </span><span class="pun">:</span><span class="pln"> stringData </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">color </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">font</span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">fillText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLACK </span><span class="pun">);</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">strokeText</span><span class="pun">(</span><span class="pln"> MESSAGE</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أثناء كل تَكْرار (iteration)، سيَحمِل المُتْغيِّر المُتحكِّم بالحلقة <code>data</code> نسخة من إحدى قيم المصفوفة. تلك القيمة عبارة عن مَرجِع (reference) إلى كائن (object) من النوع <code>StringData</code> المُتْضمِّن لمُتْغيِّرات النُسخة <code>color</code> و <code>font</code> و <code>x</code> و <code>y</code>.
</p>

<p>
	ناقشنا التحريكات (animations) بالقسم الفرعي 6.3.5. يُمكِنك أيضًا الإطلاع على شيفرة البرنامج بالكامل بالملف <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/source/chapter7/RandomStringsWithArray.java" rel="external nofollow">RandomStringsWithArray.java</a> لمعرفة طريقة تّنْفيِذ التحريكة (animation).
</p>

<p>
	ينبغي أن يختار البرنامج <code>RandomStringsWithArray</code> نوع الخط المُستخدَم لرَسْم الرسالة عشوائيًا من خمسة خطوط محتملة. بالنسخة الأصلية من البرنامج، صَرَّحنا عن خمسة مُتْغيِّرات أسمائها هي <code>font1</code> و <code>font2</code> و <code>font3</code> و <code>font4</code> و <code>font5</code> من النوع <code>Font</code> لتمثيل تلك الخطوط ثُمَّ اِستخدَمنا تَعْليمَة <code>switch</code> لاختيار إحدى تلك الخطوط عشوائيًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_48" style="">
<span class="typ">Font</span><span class="pln"> randomFont</span><span class="pun">;</span><span class="pln">  </span><span class="com">// One of the 5 fonts, chosen at random.</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> rand</span><span class="pun">;</span><span class="pln">         </span><span class="com">// A random integer in the range 0 to 4.</span><span class="pln">

fontNum </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fontNum</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
      randomFont </span><span class="pun">=</span><span class="pln"> font1</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
      randomFont </span><span class="pun">=</span><span class="pln"> font2</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2</span><span class="pun">:</span><span class="pln">
      randomFont </span><span class="pun">=</span><span class="pln"> font3</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">3</span><span class="pun">:</span><span class="pln">
      randomFont </span><span class="pun">=</span><span class="pln"> font4</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">4</span><span class="pun">:</span><span class="pln">
      randomFont </span><span class="pun">=</span><span class="pln"> font5</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنُخزِّن الخطوط الخمسة داخل مصفوفة <code>fonts</code> بالنسخة الجديدة من البرنامج. صَرَّحنا عنها بهيئة مُتْغيِّر نسخة (instance variable) من النوع <code>Font[]‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_50" style="">
<span class="typ">Font</span><span class="pun">[]</span><span class="pln"> fonts</span><span class="pun">;</span></pre>

<p>
	اِستخدمنا الباني (constructor) للإنشاء الفعليّ للمصفوفة بصيغة مُصنَّفة النوع (array literal) -اُنظر القسم الفرعي 7.1.3- كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_52" style="">
<span class="pln">fonts</span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Times New Roman"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Arial"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontPosture</span><span class="pln"> </span><span class="pun">.</span><span class="pln">ITALIC</span><span class="pun">,</span><span class="pln"> </span><span class="lit">28</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Verdana"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="lit">40</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Times New Roman"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontPosture</span><span class="pln"> </span><span class="pun">.</span><span class="pln">ITALIC</span><span class="pun">,</span><span class="pln"> </span><span class="lit">60</span><span class="pun">)</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يُسهِل ذلك من عملية الاختيار العشوائي لنوع الخط المُستخدَم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_54" style="">
<span class="typ">Font</span><span class="pln"> randomFont</span><span class="pun">;</span><span class="pln">  </span><span class="com">// One of the 5 fonts, chosen at random.</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> fontIndex</span><span class="pun">;</span><span class="pln">    </span><span class="com">// A random number in the range 0 to 4.</span><span class="pln">
fontIndex </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
randomFont </span><span class="pun">=</span><span class="pln"> fonts</span><span class="pun">[</span><span class="pln"> fontIndex </span><span class="pun">];</span></pre>

<p>
	وبذلك نَكُون قد اِستخدَمنا أسطر قليلة من الشيفرة بدلًا من تَعْليمَة <code>switch</code>. يُمكِننا حتى أن نَضُم تلك الأسطر إلى سطر واحد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_56" style="">
<span class="typ">Font</span><span class="pln"> randomFont </span><span class="pun">=</span><span class="pln"> fonts</span><span class="pun">[</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">];</span></pre>

<p>
	يُعدّ ذلك تطبيقًا نمطيًا على المصفوفات (arrays). لاحِظ أن التَعْليمَة السابقة تَستخدِم خاصية الجَلْب العشوائي (random access) بمعنى اختيار فهرس مصفوفة (array index) بشكل عشوائي لجَلْب قيمة العنصر بذلك الفهرس مباشرةً.
</p>

<p>
	لنَفْحَص مثالًا آخر: عادةً ما تُخزَّن الأشهر بهيئة أعداد صحيحة: 1 و 2 و 3 .. وحتى 12. ستحتاج أحيانًا إلى تَحْوِيل تلك الأعداد إلى أسماء الأشهر المناظرة: يناير و فبراير و .. وحتى ديسمبر. يُمكِننا ببساطة أن نَستخدِم مصفوفة لإجراء ذلك التَحْوِيل. تُصرِّح (declare) التَعْليمَة التالية عن المصفوفة وتُهيِئها (initialize) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_58" style="">
<span class="kwd">static</span><span class="pln"> </span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> monthName </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="str">"January"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"February"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"March"</span><span class="pun">,</span><span class="pln">
                              </span><span class="str">"April"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"May"</span><span class="pun">,</span><span class="pln">      </span><span class="str">"June"</span><span class="pun">,</span><span class="pln">
                              </span><span class="str">"July"</span><span class="pun">,</span><span class="pln">    </span><span class="str">"August"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"September"</span><span class="pun">,</span><span class="pln">
                              </span><span class="str">"October"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"November"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"December"</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	إذا كان <code>mnth</code> عبارة عن مُتْغيِّر يَحمِل عددًا صحيحًا يتراوح بين 1 و 12، فإن <code>monthName[mnth-1]‎</code> يُمثِل اسم الشهر المناظر. ينبغي أن نُضيِف "-1" لأن الأشهر مُرقَّمة بدءًا من 1 بينما عناصر المصفوفة (array elements) مُرقَّمة بدءًا من صفر.
</p>

<h2>
	المصفوفات الديناميكية (Dynamic Arrays)
</h2>

<p>
	لقد ناقشنا الكيفية التي يُمكِننا بها اِستخدَام المصفوفات المملوءة جزئيًا (partially full array) لتَخْزِين قائمة من اللاعبين داخل لعبة، بحيث تَكبُر تلك القائمة أو تَتقلَّص أثناء تَشْغِيل اللعبة. يُطلَق الاسم "ديناميكي (dynamic)" على أي قائمة (list) يُمكِن أن يَتَغيَّر حجمها أثناء تّنْفِيذ البرنامج. تُعدّ القوائم الديناميكية شائعة الاستخدام، لذلك ربما من الأفضل كتابة صَنْف (class) يُمثِل ذلك المفهوم، وهو ما سيُمكِّننا من تَجنُّب إعادة كتابة نفس ذات الشيفرة بكل مرة نُريد فيها اِستخدَام هيكل بياني (data structure) مُشابِه. سنُصمِّم إذًا شيئًا مشابهًا للمصفوفة باستثناء أن يَكُون حجمها (size) قابلًا للتَغيِير. فَكِر بالعمليات التي نحتاج إلى إِجرائها على مصفوفة ديناميكية (dynamic array)، مثلًا:
</p>

<ul>
<li>
		إضافة عنصر إلى نهاية المصفوفة
	</li>
	<li>
		حَذْف عنصر بمَوِضْع معين ضِمْن المصفوفة
	</li>
	<li>
		جَلْب قيمة إحدى عناصر المصفوفة
	</li>
	<li>
		ضَبْط قيمة إحدى عناصر المصفوفة
	</li>
	<li>
		جَلْب عدد العناصر الموجودة حاليًا بالمصفوفة
	</li>
</ul>
<p>
	عندما نُصمِّم ذلك الصَنْف، ستُصبِح تلك العمليات توابع نسخة (instance methods) داخل الصَنْف. سنُطبِق أيضًا نمط المصفوفة المملوءة جزئيًا (partially full array) لتَخْزِين العناصر بالمصفوفة الديناميكية (dynamic) أي ستُخزَّن العناصر بالنهاية داخل مصفوفة عادية. علاوة على ذلك، لابُدّ أن نُقرِّر ما ينبغي أن يَحدُث عند محاولة جَلْب عنصر مصفوفة غَيْر موجود، فقد نُبلِّغ مثلًا عن اِعتراض من النوع <code>ArrayIndexOutOfBoundsException</code>. بِفْرَض كَوْن عناصر المصفوفة من النوع <code>int</code>، يُمكِننا كتابة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3606_60" style="">
<span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Arrays</span><span class="pun">;</span><span class="pln">

</span><span class="com">/**
 * Represents a list of int values that can grow and shrink.
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">DynamicArrayOfInt</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> items </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">8</span><span class="pun">];</span><span class="pln">  </span><span class="com">// partially full array holding the ints</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> itemCt</span><span class="pun">;</span><span class="pln">

    </span><span class="com">/**
     * Return the item at a given index in the array.  
     * Throws ArrayIndexOutOfBoundsException if the index is not valid.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> get</span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> index </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> index </span><span class="pun">&gt;=</span><span class="pln"> itemCt </span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayIndexOutOfBoundsException</span><span class="pun">(</span><span class="str">"Illegal index, "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> index</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> items</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Set the value of the array element at a given index. 
     * Throws ArrayIndexOutOfBoundsException if the index is not valid.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">set</span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> index</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> index </span><span class="pun">&gt;=</span><span class="pln"> itemCt </span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayIndexOutOfBoundsException</span><span class="pun">(</span><span class="str">"Illegal index, "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> index</span><span class="pun">);</span><span class="pln">
        items</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Returns the number of items currently in the array.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> itemCt</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Adds a new item to the end of the array.  The size increases by one.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> add</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">itemCt </span><span class="pun">==</span><span class="pln"> items</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln">
            items </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">copyOf</span><span class="pun">(</span><span class="pln"> items</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">items</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span><span class="pln">
        items</span><span class="pun">[</span><span class="pln">itemCt</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
        itemCt</span><span class="pun">++;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Removes the item at a given index in the array.  The size of the array
     * decreases by one.  Items following the removed item are moved up one
     * space in the array.
     * Throws ArrayIndexOutOfBoundsException if the index is not valid.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> remove</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> index </span><span class="pun">&gt;=</span><span class="pln"> itemCt </span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayIndexOutOfBoundsException</span><span class="pun">(</span><span class="str">"Illegal index, "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> index</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> j </span><span class="pun">=</span><span class="pln"> index</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> itemCt</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++)</span><span class="pln">
            items</span><span class="pun">[</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> items</span><span class="pun">[</span><span class="pln">j</span><span class="pun">];</span><span class="pln">
        itemCt</span><span class="pun">--;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class DynamicArrayOfInt</span></pre>

<p>
	ينبغي أن تَكُون الشيفرة بالأعلى واضحة. ربما قد تَتَساءل مع ذلك عن سبب اختيار العدد 8 كحجم مبدئي للمصفوفة. في الحقيقة، أنه مجرد رقم عشوائي أي يُمكِنك أن تختار أي عدد صحيح آخر، ولن يُؤثِر ذلك على الصَنْف (class). في العموم، لا تبدأ بمصفوفة ضخمة جدًا، فهي بالفعل مُصمَّمة لتُكيف حجمها مع عدد العناصر المطلوب.
</p>

<p>
	بالمثال <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/source/chapter3/ReverseInputNumbers.java" rel="external nofollow">ReverseInputNumbers.java</a>، اِستخدَمنا مصفوفة مملوءة جزئيًا (partially full array) من النوع <code>int</code> لطباعة قائمة من الأعداد المُدْخَلة من قِبَل المُستخدِم بترتيب معاكس. اِستخدَمنا تحديدًا مصفوفة عادية طولها يُساوِي 100 لحَمْل الأعداد. في الواقع، قد يَكُون حجم المصفوفة كبيرًا جدًا أو صغيرًا جدًا مما قد يَتَسبَّب بحُدوث اِعترَاض (exception). نستطيع الآن إعادة كتابة البرنامج باستخدام الصَنْف <code>DynamicArrayOfInt</code>، والذي سيَتَكيَف مع أي عدد معقول من المُدْخَلات. يُمكِنك الإطلاع على شيفرته بالملف <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/source/chapter7/ReverseWithDynamicArray.java" rel="external nofollow">ReverseWithDynamicArray.java</a>. على الرغم من أنه برنامج بسيط جدًا، لكن بإمكانك تطبيق نفس المبدأ بأي تطبيق آخر لا يُمكِنك تَوقُّع مقدار البيانات التي قد يحتاجها مقدمًا. تستطيع هياكل البيانات الديناميكية (dynamic data structure) عمومًا أن تَتَكيَف مع أي مقدار من البيانات، ولكن بالطبع بما يتناسب مع مساحة الذاكرة (memory) المُتاحة للبرنامج.
</p>

<p>
	يُعدّ الصَنف بالأعلى مثالًا جيدًا نوعًا ما على المصفوفات الديناميكية، ولكنه يُعانِي من مشكلة صغيرة. لنَفْترِض مثلًا أننا نريد أن نُنشِئ مصفوفة ديناميكية من النوع <code>String</code>. نظرًا لأن الصَنْف <code>DynamicArrayOfInt</code> مُصمَّم للتَعامُل مع النوع <code>int</code> فقط، لا يُمكِننا بطبيعة الحال أن نَستخدِم كائنًا (object) منه لحَمْل سلاسل نصية (strings). يبدو أننا سنضطّر إذًا إلى كتابة صَنْف جديد <code>DynamicArrayOfString</code>. بالمثل، إذا أردنا أن نُنشِئ مصفوفة ديناميكية لتَخْزِين لاعبين من النوع <code>Player</code>، سنضطّر إلى كتابة صَنْف جديد <code>DynamicArrayOfPlayer</code>، وهكذا. يَعنِي ذلك ضرورة كتابة صَنْف جديد لكل نوع من البيانات، وهو ما لا يُمكِن أن يَكُون أمرًا منطقيًا. في الواقع، توفِّر الجافا الصَنْف القياسي <code>ArrayList</code> كحلّ لتلك المشكلة حيث يُمثِل مصفوفة ديناميكية (dynamic arrays) يُمكِنها التَعامُل مع أي نوع من البيانات.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/c7/s2.html" rel="external nofollow">Section 2: Array Processing</a> من فصل Chapter 7: Arrays and ArrayLists من كتاب <a data-ss1615724553="1" data-ss1615725670="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1158</guid><pubDate>Fri, 26 Mar 2021 13:02:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; (Arrays) &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1157/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604dfdf4e6878_--.png.9ff1e93103cae7cd45c7069844404477.png" /></p>

<p>
	ناقشنا أساسيات المصفوفات (arrays) بالفصول السابقة، ولكن ما يزال هنالك بعض التفاصيل الأخرى الخاصة بقواعد الصيغة (syntax) التي نحتاج إلى تناولها كما أن هنالك الكثير لذِكره بشأن اِستخدَامات المصفوفات في العموم. سنَفْحَص خلال هذا القسم بعضًا من تلك التفاصيل.
</p>

<p>
	المصفوفة عبارة عن متتالية من العناصر المُرقَّمة بحيث يُمثِل كل عنصر مُتْغيِّرًا منفصلًا. لابُدّ أن تَكُون جميع العناصر ضِمْن مصفوفة معينة من نفس النوع. يُطلق على هذا النوع اسم نوع المصفوفة الأساسي (base type) كما أن المصفوفة ككل لها نوع أيضًا. فمثلًا، إذا كان النوع الأساسي لمصفوفة هو <code>btype</code>، فإن المصفوفة ككل تَكُون من النوع <code>btype[]‎</code>. يَملُك كل عنصر فهرسًا (index) يُمثِل مَوضِعه العددي بمتتالية العناصر. إذا كان لدينا مثلًا مصفوفة <code>A</code>، فإننا نَستخدِم <code>A<em>‎</em></code><em> </em>للإشارة إلى عنصر المصفوفة الموجود برقم المَوضِع <code>i</code>. يُطلَق على عدد العناصر الموجودة ضِمْن مصفوفة اسم طول المصفوفة (length)، ويُستخدَم <code>A.length</code> لمَعرِفة طول مصفوفة <code>A</code>. لاحِظ أنه من غَيْر الممكن تَغيِير طول مصفوفة معينة بَعْد انشائها. يُشار إلى عناصر مصفوفة <code>A</code> باستخدام <code>A[0]‎</code> و <code>A[1]‎</code> و .. حتى <code>A[A.length-1]‎</code>، وتُؤدِي أي محاولة للإشارة إلى عنصر مصفوفة برقم فهرس خارج النطاق المسموح به أي خارج نطاق يتراوح بين 0 و <code>A.length-1</code> إلى حُدوث اِعتراض من النوع <code>ArrayIndexOutOfBoundsException</code>.
</p>

<p>
	المصفوفات بلغة الجافا عبارة عن كائنات (objects) أي يستطيع أي مُتْغيِّر مصفوفة (array variable) أن يُشير إلى مصفوفة معينة لا أن يَحتوِي قيمة المصفوفة نفسها. قد تَكُون قيمة المُتْغيِّر فارغة (null)، وفي تلك الحالة، لا يُشير المُتْغيِّر إلى أي مصفوفة، وستَتَسبَّب إذًا أي محاولة للإشارة إلى عنصر ضِمْن تلك المصفوفة مثل <code>A‎</code> بحدوث اعتراض من النوع <code>NullPointerException</code>. تُنشَئ المصفوفات باِستخدَام صيغة خاصة من العامل <code>new</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_7" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> A </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span></pre>

<p>
	تُنشِئ التَعْليمَة بالأعلى مصفوفة جديدة نوعها الأساسي (base type) هو <code>int</code> وطولها يُساوِي 10 ثم تَضبُط المُتْغيِّر <code>A</code> لكي يشير إلى تلك المصفوفة الجديدة المُنشئة للتو.
</p>

<h2>
	حلقات تكرار For-each
</h2>

<p>
	تعالج المصفوفات عادةً باستخدام حلقات التكرار (loop). تسهل حلقات التكرار من معالجة كل عنصر بالمصفوفة. على سبيل المثال، إذا كان <code>namelist</code> مصفوفة من النوع <code>String</code>، يمكننا طباعة قيم جميع عناصر تلك المصفوفة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_11" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> namelist</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> namelist</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا النوع من المعالجة شائع جدًا لدرجة وجود صياغة مختلفة من حلقات التكرار تسهل أكثر من ذلك. يطلق على تلك الصياغة اسم <code>for-each</code>. يستخدم المثال التالي حلقة التكرار <code>for-each</code> لطباعة جميع قيم عناصر مصفوفة من النوع <code>String</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_13" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name </span><span class="pun">:</span><span class="pln"> namelist </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> name </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُقصَد من التَعْليمَة <code>for (String name : namelist)‎</code> "نَفِّذ ما يلي لكل عنصر <code>name</code> من النوع <code>String</code> ضِمْن المصفوفة <code>namelist</code>". يُسنَد إلى المُتْغيِّر <code>name</code> خلال كل تَكْرار (iteration) إحدى قيم عناصر المصفوفة <code>namelist</code> ثُم يُنفَّذ مَتْن الحلقة. يُمثِل مُتْغيِّر التَحكُّم بالحلقة <code>name</code> قيمة عنصر المصفوفة وليس فهرسه (index) أي أننا لا نَملُك فهرس (index) العنصر داخل الحلقة.
</p>

<p>
	تجنبك حَلْقة التَكْرار <code>for-each</code> التعقيد المُرتبط بفهارس عناصر المصفوفة. إلى جانب ذلك، يُمكِنك في العموم اِستخدَامها لمعالجة مجموعة من القيم ضِمْن شتى هياكل البيانات (data structure) كما سنرى بالفصل 10 حيث تَسمَح بمعالجة تلك القيم بغض النظر عن هيكل البيانات المُستخدَم.
</p>

<p>
	تُنفِّذ حلقة التكرار <code>for-each</code> نفس العملية على كل قيمة مُخزَّنة بالمصفوفة. على سبيل المثال، إذا كان <code>itemArray</code> مصفوفة من النوع <code>BaseType[]‎</code>، يُمكنك كتابة حَلْقة التَكْرار <code>for-each</code> بالصياغة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_15" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">BaseType</span><span class="pln"> item </span><span class="pun">:</span><span class="pln"> itemArray </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">  </span><span class="com">// عالج العنصر</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الأقواس بالأعلى اختيارية في حالة وجود تَعْليمَة واحدة فقط ضِمْن المَتْن. نظرًا لأن النوع الأساسي للمصفوفة (base type) هو <code>BaseType</code>، صَرَّحنا (declare) عن المُتْغيَّر المُتحكِّم بالحلقة (loop control variable)‏ <code>item</code> ليَكُون من النوع <code>BaseType</code>. لاِحظ أن التَّصريح (declare) عن المُتْغيِّر المُتحكِّم بحَلْقات التَكْرار <code>for-each</code> لابُدّ أن يَقَع داخل حَلْقة التَكْرار، فلا يُمكِنك اِستخدَام مُتْغيِّر مُصرَّح عنه مُسبَقًا خارج الحلقة (loop). عند تّنفيذ الحلقة، تُسنَد كل قيمة بالمصفوفة إلى المُتْغيِّر <code>item</code> ثم يُنْفَّذ مَتْن الحلقة (loop body) لتلك القيمة. يُمكِنك في الواقع استخدام <code>for</code> لكتابة نفس حَلْقة التَكْرار بالأعلى كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_17" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> itemArray</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> index</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">BaseType</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
   item </span><span class="pun">=</span><span class="pln"> itemArray</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">  </span><span class="com">// اجلب قيمة أحد عناصر المصفوفة</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">  </span><span class="com">// process the item</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">  
</span><span class="pun">}</span></pre>

<p>
	على سبيل المثال، إذا كان المُتْغيِّر <code>A</code> عبارة عن مصفوفة من النوع <code>int[]‎</code>، يُمكِننا طباعة جميع قيم <code>A</code> باستخدام <code>for-each</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_19" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">:</span><span class="pln"> A </span><span class="pun">)</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> item </span><span class="pun">);</span></pre>

<p>
	ويُمكِننا حساب مجموع قيم الأعداد الصحيحة الموجبة داخل المصفوفة <code>A</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_21" style="">
<span class="typ">int</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">   </span><span class="com">// حاصل مجموع الأعداد الموجبة ضمن المصفوفة</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">:</span><span class="pln"> A </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">item </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">
      sum </span><span class="pun">=</span><span class="pln"> sum </span><span class="pun">+</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	قد لا يَكون اِستخدَام حَلْقة التَكْرار <code>for-each</code> ملائمًا ببعض الأحيان. على سبيل المثال، لا توجد طريقة بسيطة لاستخدامها لمعالجة العناصر الموجودة بجزء معين من مصفوفة أو لمعالجة عناصر مصفوفة بترتيب معكوس. في المقابل، هي في العموم أكثر ملائمة في حالة أردت معالجة جميع قيم عناصر مصفوفة معينة بنفس ترتيب وجودها داخل تلك المصفوفة؛ لأنها تُجنِّبك اِستخدَام الفهارس (indices).
</p>

<p>
	تُعالِج حَلْقة التَكْرار <code>for-each</code> "القيم (values)" الموجودة بالمصفوفة وليس "العناصر (elements)" أي أنها لا تُعالِج مواضع الذاكرة الفعلية لتلك القيم. على سبيل المثال، تُحاوِل الشيفرة التالية مَلْئ مصفوفة من الأعداد الصحيحة بالقيمة 17 بطريقة خاطئة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_23" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> intList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">:</span><span class="pln"> intList </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">   </span><span class="com">// غير صحيح</span><span class="pln">
   item </span><span class="pun">=</span><span class="pln"> </span><span class="lit">17</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُسنِد التَعْليمَة <code>item = 17</code> القيمة 17 إلى المُتْغيِّر المُتحكِّم بالحَلْقة <code>item</code>، ولكن هذا ليس له أي علاقة بالمصفوفة. عند تّنفِيد مَتْن الحَلْقة (body loop)، تُنسَخ إحدى قيم عناصر المصفوفة إلى المُتْغيِّر <code>item</code> أي أن ما تحاول التَعْليمَة <code>item = 17</code> تَعْدِيله هو تلك القيمة المَنسُوخَة، وبالتالي ليس لها أي تأثير على عنصر المصفوفة المَنسُوخ ذاته ولا تَتَغيَّر قيمته. تُكافِيء حَلْقة التَكْرار السابقة ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_25" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> intList </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> intList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> intList</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
   item </span><span class="pun">=</span><span class="pln"> </span><span class="lit">17</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والتي بوضوح لا تُغيِّر قيمة أي عنصر داخل المصفوفة.
</p>

<h2>
	التوابع متغيرة الرتبية (Variable Arity Methods)
</h2>

<p>
	قبل الإصدار الخامس من الجافا، كانت جميع التوابع (method) ثابتة الرتبية (fixed arity) -الرتبية عمومًا هي عدد المُعاملات المُمرَّرة لتابع ضِمْن تَعْليمَة الاستدعاء-. في حالة التوابع ثابتة الرتبية (fixed arity methods)، لابُدّ من تمرير نفس عدد المعاملات (parameters) بكل تعليمة استدعاء (call) لتابع ثابت الرتبية، كما لابُدّ أن يَكُون ذلك العدد هو نفسه عدد المُعاملات الصُوريَّة (formal parameters) ضِمْن تعريف ذلك التابع (method definition). منذ الإصدار الخامس من الجافا، تَتَوفَّر توابع مُتْغيِّرة الرتبية (variable arity methods)، والتي يُمكِن أن يُمرَّر إليها عدد مختلف من المعاملات (parameters). على سبيل المثال، يُعدّ التابع <code>System.out.printf</code> -ناقشناه بالقسم الفرعي 2.4.1- مُتْغيِّر الرتبية (variable arity). بالإضافة إلى مُعامله الأول الذي لابُدّ أن يَكُون عبارة عن سِلسِلة نصية من النوع <code>String</code>، فإنه يَستقبِل أي عدد ممكن من المعاملات الإضافية ومن أي نوع.
</p>

<p>
	لا تختلف طريقة استدعاء تابع مُتْغيِّر الرتبية (variable arity method) عن طريقة استدعاء أي تابع آخر، ولكن تَتَطلَّب كتابته بعض قواعد الصيغة الجديدة (syntax). على سبيل المثال، إذا أردنا كتابة تابع (method) يَحسِب قيمة متوسط أي عدد من القيم من النوع <code>double</code>، يُمكِننا أن نُعرِّفه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_27" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> average</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">...</span><span class="pln">  numbers </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	لاحظ اِستخدَام الترميز <code>...</code> بَعْد اسم النوع <code>double</code> ضِمْن التعريف بالأعلى، وهو في الواقع ما يَجعَل ذلك التابع مُتْغيِّر الرتبية (variable arity) حيث يُشير ذلك الترميز إلى إمكانية تمرير أي عدد من القيم من النوع <code>double</code> بتعليمة استدعاء البرنامج الفرعي (subroutine) أي يُمكِنك اِستدعائه باِستخدَام <code>average(1,4,9,16)‎</code> أو <code>average(3.14,2.17)‎</code> أو <code>average(0.375)‎</code> أو <code>average()‎</code>. يُمكِنك أيضًا أن تُمرِّر قيم من النوع <code>int</code> كمُعاملات فعليّة (actual parameters) للتابع حيث تُحوَّل كالعادة تلقائيًا إلى أعداد حقيقية (real).
</p>

<p>
	عند استدعاء تابع مُتْغيِّر الرتبية (variable arity method)، تُوضَع قيم المُعاملات الفعليّة المناظرة لمعامل مُتْغيِّر الرتبية (variable arity) بمصفوفة ثم تُمرَّر تلك المصفوفة إلى التابع أي يبدو مُعامل مُتْغيِّر الرتبية من النوع <code>T</code> مثلًا بهيئة مصفوفة من النوع <code>T[]‎</code> داخل مَتْن التابع، ويُحدِّد عدد المُعاملات الفعليّة (actual parameters) المُمرَّرة أثناء الاستدعاء طول تلك المصفوفة. بالنسبة للتابع <code>average()‎</code>، فإن مَتْنه (body) سيَرى مصفوفة اسمها <code>numbers</code> من النوع <code>double[]‎</code>، ويُحدِّد <code>numbers.length</code> عدد المعاملات الفعليّة بتَعْليمَة الاستدعاء، وتُمثِل <code>numbers[0]‎</code> و <code>numbers[1]‎</code> ..إلخ قيم تلك المعاملات المُمرَّرة. يُمكِننا تعريف (define) ذلك التابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_29" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> average</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">double</span><span class="pun">...</span><span class="pln"> numbers </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫داخل التابع، المتغير numbers من النوع double[]</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> sum</span><span class="pun">;</span><span class="pln">      </span><span class="com">// مجموع قيم المعاملات الفعلية</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> average</span><span class="pun">;</span><span class="pln">  </span><span class="com">// متوسط قيم المعاملات الفعلية</span><span class="pln">
   sum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> numbers</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// أزد أجد المعاملات الفعلية إلى حاصل المجموع</span><span class="pln">
      sum </span><span class="pun">=</span><span class="pln"> sum </span><span class="pun">+</span><span class="pln"> numbers</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">  
   </span><span class="pun">}</span><span class="pln">
   average </span><span class="pun">=</span><span class="pln"> sum </span><span class="pun">/</span><span class="pln"> numbers</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> average</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بالمناسبة، من الممكن تمرير مصفوفة واحدة إلى تابع متغير الرتبية (variable arity mehtod) بدلًا من قائمة من القيم المفردة. على سبيل المثال، لنفترض أن <code>salesData</code> عبارة عن متغير من النوع <code>double[]‎</code>، فإنه من الممكن استخدام الاستدعاء <code>average(salesData)‎</code> لحساب قيمة متوسط جميع الأعداد بالمصفوفة. قد تتضمن قائمة المعاملات الصورية (formal parameter list) بتعريف تابع متغير الرتبية (variable-arity method) أكثر من معامل واحد، ولكن الترميز <code>...</code> يمكن أن يطبق على المعامل الصوري الأخير فقط.
</p>

<p>
	كمثال، إذا أردنا كتابة تابع (method) لرسم مضلع يُحدَّد موضعه عبر أي عدد من النقاط. تكون تلك النقاط من النوع <code>Point</code>، وبحيث يحتوي أي كائن (object) من ذلك النوع على مُتْغيِّري نُسخة (instance variables) هما <code>x</code> و <code>y</code> من النوع <code>double</code> يُحدِّدان موضع النقطة. سيَستقبِل التابع أولًا مُعاملًا عاديًا هو كائن السياق الرسومي (graphics context) المُستخدَم لرَسْم المضلع بالإضافة إلى مُعامل مُتْغيِّر الرتبية (variable arity parameter). يبدو ذلك المُعامل بهيئة مصفوفة من النوع <code>Point</code> ضِمن مَتْن التابع. يُمكننا كتابة تعريف التابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_31" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> drawPolygon</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">...</span><span class="pln"> points</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// نحتاج إلى نقطتين على الأقل لرسم أي شيء</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">points</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
       </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> points</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="com">// ‫ارسم خطا من النقطة i إلى النقطة i+1</span><span class="pln">
           g</span><span class="pun">.</span><span class="pln">strokeLine</span><span class="pun">(</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">y</span><span class="pun">,</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">].</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
        </span><span class="com">// ارسم خطًا عائدًا إلى نقطة البداية</span><span class="pln">
       g</span><span class="pun">.</span><span class="pln">strokeLine</span><span class="pun">(</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">points</span><span class="pun">.</span><span class="pln">length</span><span class="pun">-</span><span class="lit">1</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> points</span><span class="pun">[</span><span class="pln">points</span><span class="pun">.</span><span class="pln">length</span><span class="pun">-</span><span class="lit">1</span><span class="pun">].</span><span class="pln">y</span><span class="pun">,</span><span class="pln">
                       points</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> points</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">y </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لابُدّ أن تحتوي تَعْليمَة استدعاء ذلك البرنامج الفرعي (subroutine call) على مُعامل فعلي واحد من النوع <code>GraphicsContext</code> متبوعًا بأي عدد من المعاملات الفعلية من النوع <code>Point</code>.
</p>

<p>
	كمثال أخير، يَضُمّ التابع (method) التالي قائمة من السَلاسِل النصية (strings) معًا ليُكوِّن سِلسِلة نصية واحدة طويلة. تَستخدِم الشيفرة التالية حَلْقة تَكْرار <code>for-each</code> لمعالجة المصفوفة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_33" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> concat</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pun">...</span><span class="pln"> values </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫استخدم الصنف StringBuilder لضم أكثر كفاءة</span><span class="pln">
   </span><span class="typ">StringBuilder</span><span class="pln"> buffer</span><span class="pun">;</span><span class="pln">  
   buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StringBuilder</span><span class="pun">();</span><span class="pln">  </span><span class="com">// ابدأ بكائن فارغ</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> str </span><span class="pun">:</span><span class="pln"> values </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// استخدم حلقة تكرار لمعالجة القيم</span><span class="pln">
       buffer</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أضف سلسلة نصية إلى المخزن المؤقت</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> buffer</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"> </span><span class="com">// أعد محتويات المخزن المؤقت</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعيد الاستدعاء <code>concat("Hello", "World")‎</code> مثلًا السِلسِلة النصية "HelloWorld" بينما يُعيد الاستدعاء <code>concat()‎</code> سِلسِلة نصية فارغة. لمّا كان مُمكِنًا تمرير مصفوفة كمعامل فعلي (actual parameter) لتابع مُتْغيِّر الرتبية (variable arity method)، فبإمكاننا أيضًا استدعاء <code>concat(lines)‎</code> حيث <code>lines</code> عبارة عن مصفوفة من النوع <code>String[]‎</code>. سيَضُمّ (concatenate) التابع جميع العناصر الموجودة داخل المصفوفة إلى سِلسِلة نصية (string) واحدة.
</p>

<h2>
	مصفوفات مجردة مصنفة النوع (Array Literals)
</h2>

<p>
	لقد رأينا أنه بإمكاننا أن نُهيِئ (initialize) مُتْغيِّر مصفوفة (array variable) بقائمة من القيم أثناء التّصرِيح (declare) عنه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_35" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> squares </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">36</span><span class="pun">,</span><span class="pln"> </span><span class="lit">49</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	تُهيِئ التَعليمَة بالأعلى المُتْغيِّر <code>squares</code> لكي يُشيِر إلى مصفوفة جديدة تحتوي على قائمة من 7 قيم. يُمكِنك اِستخدَام مُهيِئ القائمة (list initializer) بالصياغة السابقة مع تَعْليمَات التّصرِيح (declaration statement) فقط لكي تُسنِد قيمة مبدئية لمُتْغيِّر المصفوفة (array variable) المُصرَّح عنه للتو. في المقابل، لا يُمكنِك اِستخدَامه مع تَعْليمَة إِسْناد (assignment statement) بغرض إِسْناد قيمة جديدة إلى مُتْغيِّر مَوجود مُسْبَقًا. تَتَوفَّر طريقة آخرى لإنشاء مصفوفات جديدة، ويُمكِنك حتى اِستخدَامها بمواضع آخرى غير التّصرِيح. تَستخدِم تلك الطريقة صياغة مختلفة من العامل <code>new</code> لإنشاء كائن مصفوفة جديد ومن ثَمَّ مَلْئه. يُمكِننا مثلًا أن نُسنِد قيمة جديدة إلى مُتْغيِّر مصفوفة <code>cubes</code> من النوع <code>int[]‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_37" style="">
<span class="pln">cubes </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">27</span><span class="pun">,</span><span class="pln"> </span><span class="lit">64</span><span class="pun">,</span><span class="pln"> </span><span class="lit">125</span><span class="pun">,</span><span class="pln"> </span><span class="lit">216</span><span class="pun">,</span><span class="pln"> </span><span class="lit">343</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	لاحِظ أن التَعْليمَة بالأعلى هي تَعْليمَة إِسْناد (assignment) وليست تصريح (declaration) أي أنه من غَيْر المُمكن اِستخدَام مُهيِئ المصفوفة -بدون <code>new int[]‎</code>- هنا. تُكتَب تلك الصياغة في العموم على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_39" style="">
<span class="kwd">new</span><span class="pln"> base</span><span class="pun">-</span><span class="pln">type </span><span class="pun">[</span><span class="pln"> </span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">list</span><span class="pun">-</span><span class="pln">of</span><span class="pun">-</span><span class="pln">values </span><span class="pun">}</span></pre>

<p>
	في الواقع، يُعدّ ما سبق تعبيرًا (expression) قيمته عبارة عن مَرجِع (reference) إلى كائن مصفوفة (array object) جديد، ويُمكِنك اِستخدَامه لتمثيل قيمة من ذلك النوع، لهذا يُمكِن عدّه ضِمْن هذا السياق مُصفوفة مُصنَّفة النوع (array literal). يَعنِي ذلك أنه من المُمكن اِستخدَامه أينما أمكن اِستخدَام كائن (object) من النوع <code>base-type[]‎</code>، فيُمكِنك مثلًا أن تُمرِّر المصفوفة الجديدة المنشئة للتو كمُعامل فعلي (actual parameter) لبرنامج فرعي (subroutine). تَستخدِم الشيفرة التالية مصفوفة من السَلاسِل النصية (strings) لإنشاء قائمة (menu):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_41" style="">
<span class="com">/**
 * @param menuName  the name for the Menu that is to be created.
 * @param itemNames  an array holding the text that appears in each
 *     MenuItem.  If a null value appears in the array, the corresponding
 *     item in the menu will be a separator rather than a MenuItem.
 * @return  the menu that has been created and filled with items.
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">Menu</span><span class="pln"> createMenu</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> menuName</span><span class="pun">,</span><span class="pln"> </span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> itemNames </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Menu</span><span class="pln"> menu </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">menuName</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> itemName </span><span class="pun">:</span><span class="pln"> itemNames </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> itemName </span><span class="pun">==</span><span class="pln"> null </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            menu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SeparatorMenuItem</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">MenuItem</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MenuItem</span><span class="pun">(</span><span class="pln">itemName</span><span class="pun">);</span><span class="pln">
            menu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">item</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> menu</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَستقبِل التابع <code>createMenu</code> معاملًا ثانيًا عبارة عن مصفوفة من السَلاسِل النصية. يُمكننا أن نُنشِئ المصفوفة المُمرَّرة كمعامل فعلي (actual parameter) إلى ذلك التابع بمحلها باِستخدَام العامل <code>new</code>. على سبيل المثال، تُنشِئ التَعْليمَة التالية قائمة File كاملة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_43" style="">
<span class="typ">Menu</span><span class="pln"> fileMenu </span><span class="pun">=</span><span class="pln"> createMenu</span><span class="pun">(</span><span class="pln"> </span><span class="str">"File"</span><span class="pun">,</span><span class="pln"> 
              </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="str">"New"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Open"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Close"</span><span class="pun">,</span><span class="pln"> null</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Quit"</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ينبغي أن يُقنعِك المثال السابق بأهمية القدرة على إنشاء مصفوفة واِستخدَامها بنفس المكان، فلربما هي بنفس أهمية القدرة على اِستخدَام الأصناف الداخلية (inner classes) مجهولة الاسم (anonymous). ربما كان من الافضل أيضًا كتابة التابع <code>createMenu()‎</code> بهيئة تابع مُتْغيِّر الرتبية (variable arity method).
</p>

<p>
	تستطيع أيضًا أن تَستخدِم <code>new BaseType[] { ... }‎</code> بدلًا من مُهيِئ المصفوفة (array initializer) أثناء التّصرِيح عن مُتْغيِّر مصفوفة (array variable). فمثلًا، بدلًا من كتابة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_47" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	يُمكِنك أن تَستخدِم ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_49" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">17</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	يُفضِّل الكاتب في الواقع اِستخدَام الصياغة الثانية بدلًا من اِستخدَام صياغة خاصة تَعمَل فقط مع تَعْليمَات التّصرِيح (declaration statement).
</p>

<p>
	ملحوظة أخيرة: يُمكِنك كتابة التصريح عن مصفوفة مثل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_53" style="">
<span class="typ">int</span><span class="pun">[]</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span></pre>

<p>
	على النحو التالي أيضًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8533_55" style="">
<span class="typ">int</span><span class="pln"> </span><span class="typ">list</span><span class="pun">[];</span></pre>

<p>
	وهي صياغة مُستخدَمة بلغتي البرمجة C و C++‎، ولكنها مع ذلك غَيْر مُتِّسقَة مع لغة Java، ومن الأفضل تَجنُبها. فالغرض بالنهاية هو التّصرِيح عن مُتْغيِّر من نوع معين، وهذا النوع هو <code>int[]‎</code>، لذلك من الأفضل اتباع <strong><type-name><variable-name></variable-name></type-name></strong> لمثل تلك التصريحات.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1615724013="1" data-ss1615724214="1" href="http://math.hws.edu/javanotes/c7/s1.html" rel="external nofollow">Section 1: Array Details</a> من فصل Chapter 7: Arrays and ArrayLists من كتاب <a data-ss1615724013="1" data-ss1615724214="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1157</guid><pubDate>Sun, 14 Mar 2021 12:18:43 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x643;&#x627;&#x645;&#x644;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644; &#x645;&#x643;&#x62A;&#x628;&#x629; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX</title><link>https://academy.hsoub.com/programming/java/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%83%D8%A7%D9%85%D9%84%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-r1150/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034ff62b65d3_-.png.ca178774459a6a81ade8078c69b7ce4d.png" /></p>

<p>
	لقد ناقشنا حتى الآن الكثير من الجوانب الأساسية المُستخدَمة ببرمجة واجهات المُستخدِم الرسومية (GUI)، وتَعلَّمنا ما يَكفِي لكتابة بعض البرامج الشيقة، ولهذا سنَفْحَص مثالين كاملين يُطبقان ما درسناه حتى الآن عن برمجة الواجهات الرُسومية (GUI) إلى جانب ما درسناه عن البرمجة في العموم بالفصول السابقة. ما يزال هنالك الكثير من الأمور الآخر لتَعْلُّمها، ولهذا سنعود للحديث عن نفس الموضوع بالفصل ١٣.
</p>

<h2>
	لعبة ورق بسيطة
</h2>

<p>
	برنامجنا الأول عبارة عن نُسخة من برنامج سطر الأوامر <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter5/HighLow.java" rel="external nofollow">HighLow.java</a> من <a data-ss1614085982="1" href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%88%D8%B1%D9%82-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1111/" rel="">القسم الفرعي ٥.٤.٣</a>، ولكنها ستَكُون مُدعَّمة بواجهة مُستخدِم رُسومية (GUI). بنُسخة البرنامج الجديدة <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/HighLowGUI.java" rel="external nofollow">HighLowGUI.java</a>، ينبغي أن تَنظُر إلى ورقة لعب ثم تَتَوقَّع ما إذا كانت قيمة ورقة اللعب التالية ستَكُون أكبر أو أقل من قيمة ورقة اللعب الحالية. إذا كان تَوقُّعك خاطئًا، فستَخسَر المباراة أما إذا كان صحيحًا لثلاث مرات متتالية، فستَفوز بالمباراة. بَعْد انتهاء أية مباراة، يُمِكنك النَقْر على "New Game" لبدء مباراة جديدة. تُوضِح الصورة التالية ما سيَبدُو عليه البرنامج خلال المباراة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085982="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/001High_Low_Gui.png.753baf997e4b85316540d525ac323f78.png" data-fileid="58465" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58465" data-unique="85wqry1hp" src="https://academy.hsoub.com/uploads/monthly_2021_02/001High_Low_Gui.png.753baf997e4b85316540d525ac323f78.png" alt="001High_Low_Gui.png"></a>
</p>

<p>
	شيفرة البرنامج بالكامل مُتاحَة بالملف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/HighLowGUI.java" rel="external nofollow">HighLowGUI.java</a>. حاول تَصْرِيفها وتَشْغِيلها. ملحوظة: يَتَطلَّب البرنامج كُلًا من الملفات <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/Card.java" rel="external nofollow">Card.java</a> و <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/Deck.java" rel="external nofollow">Deck.java</a> و <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/Hand.java" rel="external nofollow">Hand.java</a> من <a data-ss1614085982="1" href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%88%D8%B1%D9%82-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1111/" rel="">القسم ٥.٤</a> حيث تُعرِّف أصنافًا (classes) مُستخدَمة ضِمْن البرنامج كما أنه يَتَطلَّب ملف صورة ورق اللعب <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/cards.png" rel="external nofollow">cards.png</a> الذي اِستخدَمناه بالبرنامج <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/RandomCards.java" rel="external nofollow">RandomCards.java</a> بالقسم الفرعي <a data-ss1614085982="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D8%B9%D8%B6-%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1146/" rel="">٦.٢.٤</a>.
</p>

<p>
	ربما من السهل تَخْمِين التخطيط (layout) المُستخدَم بالبرنامج: يَستخدِم <code>HighLowGUI</code> حاوية من النوع <code>BorderPane</code> كمُكوِّن جذري (root) لمبيان المشهد (scene graph). تَتَضمَّن تلك الحاوية حاوية آخرى من النوع <code>Canvas</code> مرسُوم عليها ورق لعب ورسالة وتَقَع بمنتصف الحاوية الخارجية. يَتَضمَّن المَوْضِع السفلي من الحاوية الخارجية حاوية آخرى من النوع <code>HBox</code> تَحتوِي بدورها على ثلاثة أزرار ضُبِطَت بحيث يَكُون لها جميعًا نفس قيمة العَرْض بغَرْض مَلْئ الحاوية بالكامل كما أَوضَحنا <a data-ss1614085982="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A-%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-r1149/" rel="">بالقسم الفرعي ٦.٥.٣</a>. يُنفِّذ التابع <code>start()‎</code> التالي ما سبق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_7" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> start</span><span class="pun">(</span><span class="typ">Stage</span><span class="pln"> stage</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    cardImages </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">(</span><span class="str">"cards.png"</span><span class="pun">);</span><span class="pln">     </span><span class="com">// حمل صورة ورق اللعب</span><span class="pln">
    board </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Canvas</span><span class="pun">(</span><span class="lit">4</span><span class="pun">*</span><span class="lit">99</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">123</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">80</span><span class="pun">);</span><span class="pln"> </span><span class="com">// مسافة أربعة أوراق</span><span class="pln">

    </span><span class="typ">Button</span><span class="pln"> higher </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Higher"</span><span class="pun">);</span><span class="pln">    </span><span class="com">// أنشئ المفاتيح</span><span class="pln">
    higher</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doHigher</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">   </span><span class="com">// وجهز معالجات الأحداث</span><span class="pln">
    </span><span class="typ">Button</span><span class="pln"> lower </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Lower"</span><span class="pun">);</span><span class="pln">
    lower</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doLower</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="typ">Button</span><span class="pln"> newGame </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"New Game"</span><span class="pun">);</span><span class="pln">
    newGame</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doNewGame</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

    </span><span class="typ">HBox</span><span class="pln"> buttonBar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HBox</span><span class="pun">(</span><span class="pln"> higher</span><span class="pun">,</span><span class="pln"> lower</span><span class="pun">,</span><span class="pln"> newGame </span><span class="pun">);</span><span class="pln">

    </span><span class="com">// اضبط كل زر لكي يحصل على ثلث العرض المتاح</span><span class="pln">
    higher</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="pln">board</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">()/</span><span class="lit">3.0</span><span class="pun">);</span><span class="pln"> 
    lower</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="pln">board</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">()/</span><span class="lit">3.0</span><span class="pun">);</span><span class="pln">  
    newGame</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="pln">board</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">()/</span><span class="lit">3.0</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">BorderPane</span><span class="pln"> root </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BorderPane</span><span class="pun">();</span><span class="pln">  </span><span class="com">// أنشئ المكون الجذري لمبيان المشهد</span><span class="pln">
    root</span><span class="pun">.</span><span class="pln">setCenter</span><span class="pun">(</span><span class="pln">board</span><span class="pun">);</span><span class="pln">
    root</span><span class="pun">.</span><span class="pln">setBottom</span><span class="pun">(</span><span class="pln">buttonBar</span><span class="pun">);</span><span class="pln">

    doNewGame</span><span class="pun">();</span><span class="pln">  </span><span class="com">// جهز المباراة الأولى</span><span class="pln">

    </span><span class="typ">Scene</span><span class="pln"> scene </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Scene</span><span class="pun">(</span><span class="pln">root</span><span class="pun">);</span><span class="pln">  </span><span class="com">// Finish setting up the scene and stage.</span><span class="pln">
    stage</span><span class="pun">.</span><span class="pln">setScene</span><span class="pun">(</span><span class="pln">scene</span><span class="pun">);</span><span class="pln">
    stage</span><span class="pun">.</span><span class="pln">setTitle</span><span class="pun">(</span><span class="str">"High/Low Game"</span><span class="pun">);</span><span class="pln">
    stage</span><span class="pun">.</span><span class="pln">setResizable</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
    stage</span><span class="pun">.</span><span class="pln">show</span><span class="pun">();</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// end start()</span></pre>

<p>
	تَستدعِي مُعالِجات الأحداث (event handlers) بالأعلى توابعًا مُعرَّفة بمكان آخر من البرنامج مثل التابع <code>doNewGame()‎</code>. تُعدّ برمجة تلك التوابع (methods) تمرينًا جيدًا على التفكير وفقًا لما يُعرَف باسم "آلة الحالة (state machine)": ينبغي أن تُفكِر بالحالات (states) التي يُمكِن للمباراة أن تَقَع ضِمْنها، وبالكيفية التي قد تَتَغيَّر بها من حالة لآخرى، وكذلك بالكيفية التي ستَعتمِد بها مُعالجات الأحداث (events) على تلك الحالات. لاحِظ أن الأسلوب الذي اتِبعَناه بالنسخة الأصلية النصية من البرنامج بالقسم الفرعي ٥.٤.٣ غَيْر مُناسِب هنا حيث ستُربكَك محاولة التفكير بالمباراة تفصيليًا خطوة بخطوة من البداية وحتى النهاية أكثر مما ستُساعِدك.
</p>

<p>
	تَتَضمَّن حالة المباراة ورق لعب (cards) مُمثَل بكائن (object) من النوع <code>Hand</code> بالإضافة إلى رسالة مُمثَلة بكائن من النوع <code>String</code>. تُخزَّن تلك القيم ضِمْن مُتْغيِّرات نُسخ (instance variables). هنالك جانب آخر أقل وضوحًا سنُضْمّنه بحالة (state) المباراة: في بعض الأحيان، يَكُون هنالك مباراة قَيْد التّنْفِيذ ونَكُون بانتظار اختيار المُستخدِم لتَوقُّعه عن ورقة اللعب التالية. في بعض الأحيان الآخرى، نَكُون للتو قد انتهينا من مباراة وبانتظار نَقْر المُستخدِم على الزر "New Game". لذلك، ربما من الجيد أن نُضيِف عنصرًا جديدًا إلى حالة المباراة يُميِّز ذلك الاختلاف البسيط. يَستخدِم البرنامج مُتْغيِّر نُسخة (instance variable) من النوع <code>boolean</code> اسمه <code>gameInProgress</code> لذلك الغرض.
</p>

<p>
	عندما يَنقُر المُستخدِم على أحد الأزرار، فقد تَتَغيَّر حالة المباراة. يُعرِّف البرنامج ثلاثة توابع (methods) للاستجابة على أحداث الضَغْط على زر هي: <code>doHigher()‎</code> و <code>doLower()‎</code> و <code>newGame()‎</code>، والتي يُوكَل إليها مُهِمّة مُعالِجة الأحداث (event-handling).
</p>

<p>
	لأننا لا نُريد أن نَسمَح للمُستخدِم ببدء مباراة جديدة طالما كان هنالك مباراة آخرى قَيْد التّنْفِيذ بالفعل، فإن استجابة التابع <code>newGame()‎</code> ستَختلِف بناءً على قيمة مُتْغيِّر الحالة <code>gameInProgress</code>. إذا كانت قيمته تُساوِي <code>true</code> أي أن هنالك مباراة قَيْد التّنْفِيذ، فينبغي أن يُضبَط مُتْغيِّر النُسخة <code>message</code> إلى رسالة خطأ معينة. في المقابل، إذا كانت قيمة المُتْغيِّر تُساوِي <code>false</code> أي ليس هنالك أي مباراة، فينبغي ضَبْط جميع مُْتْغيِّرات الحالة (state variables) إلى القيم المناسبة لبدء مباراة جديدة. بجميع الأحوال، لابُدّ من إعادة رَسْم الرقعة (board) حتى يَرَى المُستخدِم أن الحالة قد تَغيَّرت. يُمكِننا تَعرِيف التابع <code>newGame()‎</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_9" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> doNewGame</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">gameInProgress</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// إذا لم تنته المباراة الحالية بعد، اعرض رسالة خطأ</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"You still have to finish this game!"</span><span class="pun">;</span><span class="pln">
        drawBoard</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    deck </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Deck</span><span class="pun">();</span><span class="pln">   </span><span class="com">// أنشئ مجموعة ورق اللعب واليد</span><span class="pln">
    hand </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Hand</span><span class="pun">();</span><span class="pln">
    deck</span><span class="pun">.</span><span class="pln">shuffle</span><span class="pun">();</span><span class="pln">
    hand</span><span class="pun">.</span><span class="pln">addCard</span><span class="pun">(</span><span class="pln"> deck</span><span class="pun">.</span><span class="pln">dealCard</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">  </span><span class="com">// اسحب ورقة اللعب الأولى</span><span class="pln">

    message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Is the next card higher or lower?"</span><span class="pun">;</span><span class="pln">
    gameInProgress </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    drawBoard</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end doNewGame()</span></pre>

<p>
	التابعان <code>doHigher()‎</code> و <code>doLower()‎</code> مُتطابقان تقريبًا، وربما كان بإِمكَاننا دَمْجهُما ضِمْن تابع واحد يَستقبِل مُعاملًا (parameter) واحدًا. عندما يَنقُر المُستخدِم على الزر "Higher"، يُستدعَى البرنامج <code>doHigher()‎</code>، ولأن ذلك الاستدعاء ليس له أي معنى إلا في حالة وجود مباراة قَيْد التّنْفِيذ، يَفْحَص التابع قيمة مُتْغيِّر الحالة <code>gameInProgress</code> أولًا. إذا كانت قيمته تُساوِي <code>false</code>، فإنه يَعرِض رسالة خطأ أما إذا كانت قيمته تساوي <code>true</code> أي توجد مباراة قَيْد التّنْفِيذ، فإنه يُضيِف ورقة لعب جديدة إلى اليد (hand) ثم يَفْحَص تَوقُّع المُستخدِم. قد تنتهي المباراة في تلك اللحظة بفوز المُستخدِم أو بخسارته، وعندها يََضبُط التابع قيمة مُتْغيِّر الحالة <code>gameInProgress</code> إلى القيمة <code>false</code> لأن المباراة قد انتهت ببساطة. بجميع الأحوال، يُعاد رَسْم الرُقعة (board) لضمان عَرْض الحالة (state) الجديدة. اُنظر التابع <code>doHigher()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_11" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> doHigher</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">gameInProgress </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// إذا كانت المباراة قد انتهت، فمن الخطأ النقر</span><span class="pln">
        </span><span class="com">// ‫على زر "Higher" لذلك اعرض رسالة خطأ وانهي المعالجة</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Click \"New Game\" to start a new game!"</span><span class="pun">;</span><span class="pln">
        drawBoard</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    hand</span><span class="pun">.</span><span class="pln">addCard</span><span class="pun">(</span><span class="pln"> deck</span><span class="pun">.</span><span class="pln">dealCard</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">     </span><span class="com">// اسحب ورقة لعب إلى اليد</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> cardCt </span><span class="pun">=</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">getCardCount</span><span class="pun">();</span><span class="pln">
    </span><span class="typ">Card</span><span class="pln"> thisCard </span><span class="pun">=</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">getCard</span><span class="pun">(</span><span class="pln"> cardCt </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">);</span><span class="pln">  </span><span class="com">// تم سحب ورقة لعب</span><span class="pln">
    </span><span class="typ">Card</span><span class="pln"> prevCard </span><span class="pun">=</span><span class="pln"> hand</span><span class="pun">.</span><span class="pln">getCard</span><span class="pun">(</span><span class="pln"> cardCt </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> 
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> thisCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> prevCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        gameInProgress </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Too bad! You lose."</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> thisCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> prevCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        gameInProgress </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Too bad!  You lose on ties."</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> cardCt </span><span class="pun">==</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        gameInProgress </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"You win!  You made three correct guesses."</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Got it right!  Try for "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> cardCt </span><span class="pun">+</span><span class="pln"> </span><span class="str">"."</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    drawBoard</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end doHigher()</span></pre>

<p>
	يَعتمِد التابع <code>drawBoard()‎</code> على قيم مُتْغيِّرات الحالة (state variables) لتَقْرِير ما ينبغي له أن يَرسِمه ضِمْن الحاوية (canvas)، فيَعرِض السِلسِلة النصية المُخزَّنة بالمُتْغيِّر <code>message</code> ويَرسِم ورق اللعب ضِمْن اليد <code>hand</code>. وأخيرًا، إذا كانت المباراة قَيْد التّنْفِيذ، فإنه يَرسِم ورقة لعب إضافية مقلوبة تُمثِل ورقة اللعب التالية بمجموعة ورق اللعب (deck). ملحوظة: لقد ناقشنا التقنية المُستخدَمة لرَسْم ورقة لعب (card) مُفرّدة بالقسم ٦.٢.
</p>

<h2>
	القوائم <code>Menu</code> وشريط القوائم <code>MenuBar</code>
</h2>

<p>
	سنَعرِض الآن برنامج رَسْم اسمه "MosaicDraw". تَتَوفَّر شيفرة البرنامج بالملف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/MosaicDraw.java" rel="external nofollow">MosaicDraw.java</a> كما يَتَطلَّب الصَنْف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/MosaicCanvas.java" rel="external nofollow">MosaicCanvas.java</a>. تُظهِر الصورة التالية رسمة مَصنُوعة باِستخدام البرنامج:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085982="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/002Mosaic_Draw.png.49c779d2b6eefbc913f7f14b6f193640.png" data-fileid="58466" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58466" data-unique="p27m4ia81" src="https://academy.hsoub.com/uploads/monthly_2021_02/002Mosaic_Draw.png.49c779d2b6eefbc913f7f14b6f193640.png" alt="002Mosaic_Draw.png"></a>
</p>

<p>
	عندما يَنقُر المُستخدِم على الفأرة ويَسحَبها داخل مساحة الرسم (drawing area)، فإن الفأرة تَترُك أثرًا مُكوَّنًا من مربعات صغيرة مُلوَّنة عشوائيًا. بالإضافة إلى ذلك، يُوفِّر البرنامج شريط قوائم (menu bar) أعلى مساحة الرسم. أولًا، تحتوي قائمة "Control" على أوامر لمَلْئ مساحة الرسم وتنظيفها مع خيارات آخرى تُؤثِر على مَظهَر الصورة. ثانيًا، تَسمَح قائمة "Color" بتَخْصِيص اللون المُستخدَم أثناء الرسم. ثالثًا، تُؤثِر قائمة "Tools" على سلوك الفأرة، فعندما نَستخدِم أداة "Draw" الافتراضية، تَترُك الفأرة أثرًا مُكوَّنًا من مُربعات مُفردّة، ولكن عندما نَستخدِم أداة "Draw 3x3"، فإنها تترُك أثرًا يَبلُغ عَرْضه ثلاثة مربعات. تَتَوفَّر أيضًا أداة "Erase" لإعادة ضَبْط المربعات إلى اللون الأسود الافتراضي.
</p>

<p>
	سنُمثِل مساحة الرسم (drawing area) داخل البرنامج باستخدام الصَنْف الفرعي <code>MosaicCanvas</code> المُشتَق من الصَنْف <code>Canvas</code> المُعرَّف بالملف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/MosaicCanvas.java" rel="external nofollow">MosaicCanvas.java</a>. لا يُدعِّم ذلك الصَنْف "رسوم الفسيفساء" مباشرةً، ولكن بإمكاننا اِستخدَامه لمحاكاتها عن طريق ضَبْط لون كل مربع على حدى. أَعدّ البرنامج <code>MosaicDraw</code> معالجين لحدثيّ الفأرة <code>MousePressed</code> و <code>MouseDragged</code> الواقعين ضِمْن الحاوية (canvas) بحيث تَستجِيب تلك المُعالجات بتطبيق الأداة (tool) المُختارة حاليًا على مَوضِع مُؤشر الفأرة عند النَقْر. يُعدّ هذا البرنامج في العموم مثالًا جيدًا على تَطبيق مُستمع حَدَث (event listeners) على كائن (object) مما يَسمَح له بالقيام بأمور لم يَكُن هو ذاته مُبرمَجًا للقيام بها.
</p>

<p>
	ستَجِد شيفرة البرنامج بالكامل بالملف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/MosaicDraw.java" rel="external nofollow">MosaicDraw.java</a>. لن نُناقش جميع تفاصيلها هنا، ولكن يُفْترَض أن تَكُون قادرًا على فهمها بالكامل بَعْد قراءة هذا القسم. في المقابل، تَعتمِد شيفرة الملف <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/source/chapter6/MosaicCanvas.java" rel="external nofollow">MosaicCanvas.java</a> على بعض التقنيات التي لن تَتَمكَّن من استيعابها في الوقت الحالي، ومع ذلك، يُمكِنك قراءة التعليقات الموجودة بالملف لكي تَتَعلَّم المزيد عن واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) الخاصة بالصَنْف <code>MosaicCanvas</code>.
</p>

<p>
	سنَستخدِم مُكوِّن شريط القوائم (menu bar) للمرة الأولى ضِمْن البرنامج <code>MosaicDraw</code>. تَسمَح لك مكتبة جافا إف إكس (JavaFX) بإضافة قائمة إلى البرنامج بسهولة عن طريق استخدام كائنات (objects) تَنتمِي للصَنْف <code>MenuItem</code> أو إلى أي من أصنافها الفرعية (subclasses) الُمعرَّفة بحزمة <code>javafx.scene.control</code> لتَمثيِل عناصر القائمة. تَعمَل عناصر القائمة تقريبًا بنفس الكيفية التي تعمل بها الأزرار (buttons). يُوفِّر الصَنْف <code>MenuItem</code> باني كائن (constructor) بمُعامل واحد عبارة عن نص عنصر القائمة. بإمكانك استخدامه لإنشاء كائن من ذلك الصَنْف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_13" style="">
<span class="typ">MenuItem</span><span class="pln"> fillCommand </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MenuItem</span><span class="pun">(</span><span class="str">"Fill"</span><span class="pun">);</span></pre>

<p>
	نظرًا لأن عناصر القائمة قد تَتَكوَّن من صورة ونص مثل الأزرار، يُوفِّر الصَنْف باني كائن (constructor) آخر بمُعاملين لتَخْصِيصهما. عندما يختار المُستخدِم عنصر قائمة من النوع <code>MenuItem</code> من قائمة، يَقَع حَدَث من النوع <code>ActionEvent</code>. يُمكِنك إضافة مُستمِع حَدَث (event listener) إليه باِستخدَام تابعه <code>setOnAction(handler)‎</code>. بالإضافة إلى ذلك، قد تَستخدِم التابع <code>setDisable(disabled)‎</code> لتَفْعيل عنصر قائمة أو تَعْطِيله، وكذلك التابع <code>setText()‎</code> لتَغْيِير النص المعروض.
</p>

<p>
	يختلف عنصر القائمة (menu item) عن الزر (button) بكَوْنه يَظهَر ضِمْن قائمة (menu). في الواقع، أي عنصر قائمة هو عبارة عن عقدة من النوع <code>Node</code> تَظهَر عادةً ضِمْن قائمة، ولكنها قد تَظهَر أيضًا بأي مكان آخر ضِمْن مبيان المشهد (scene graph). تُمثَل القوائم (menus) بمكتبة جافا إف إكس (JavaFX) باِستخدَام الصَنْف <code>Menu</code>. لمّا كان ذلك الصَنْف صنفًا فرعيًا مُشْتقًا من الصَنْف <code>MenuItem</code>، فإنه من المُمكن إضافة قائمة ضِمْن قائمة آخرى كعنصر، وتَكُون عندها قائمة فرعية (submenu) من القائمة الأصلية المُضافة إليها. أي قائمة من الصَنْف <code>Menu</code> لها اسم يُمكِنك تَخْصِيصه من خلال باني الكائن (constructor). يُعيد تابع النسخة <code>getItems()‎</code> المُعرَّف بالصَنْف <code>Menu</code> قائمة (list) بعناصر القائمة (menu items)، ويُمكِنك أن تُضيِف عنصر قائمة جديد بإضافته إلى تلك القائمة. تُوضِح الشيفرة التالية طريقة إضافة عناصر (items) إلى قائمة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_15" style="">
<span class="typ">Menu</span><span class="pln"> sampleMenu </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="str">"Sample"</span><span class="pun">);</span><span class="pln">
sampleMenu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> menuItem </span><span class="pun">);</span><span class="pln">  </span><span class="com">// اضف عنصر قائمة إلى القائمة</span><span class="pln">
sampleMenu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln"> item1</span><span class="pun">,</span><span class="pln"> item2</span><span class="pun">,</span><span class="pln"> item3 </span><span class="pun">);</span><span class="pln">  </span><span class="com">// أضف عدة عناصر</span></pre>

<p>
	ينبغي أن تضيف القائمة (menu) بَعْد إنشائها إلى شريط قوائم (menu bar) يُمثَل باِستخدَام الصنف <code>MenuBar</code>. لا يملك شريط القوائم اسمًا، فهو يعمل فقط بمثابة حاوي (container) لعدة قوائم. يمكنك استدعاء باني الكائن (constructor) الخاص به بدون أي معاملات أو بقائمة مُعاملات (parameter list) تحتوي على القوائم (menus) المطلوب إضافتها إليه. يعيد تابع النسخة <code>getMenus()‎</code> قائمة من القوائم (menus).
</p>

<p>
	يُعيد تابع النسخة <code>getMenus()‎</code> المُعرَّف بالصَنْف <code>MenuBar</code> قائمة (list) بالقوائم (menus) ضمن الشريط، ويستخدم كُلًا من التابعين <code>add()‎</code> و <code>addAll()‎</code> لإضافة قوائم (menus) جديدة إلى الشريط. يَستخدَم برنامج <code>MosaicDraw</code> ثلاثة قوائم هي <code>controlMenu</code> و <code>colorMenu</code> و <code>toolMenu</code>. تُنشِئ التَعْليمَات التالية شريط قوائم (menu bar) وتُضيِف إليه عدة قوائم (menus):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_17" style="">
<span class="typ">MenuBar</span><span class="pln"> menuBar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MenuBar</span><span class="pun">();</span><span class="pln">
menuBar</span><span class="pun">.</span><span class="pln">getMenus</span><span class="pun">().</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">controlMenu</span><span class="pun">,</span><span class="pln"> colorMenu</span><span class="pun">,</span><span class="pln"> toolMenu</span><span class="pun">);</span></pre>

<p>
	أو قد نُمرِّر القوائم (menu) مباشرةً إلى الباني (constructor) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_19" style="">
<span class="typ">MenuBar</span><span class="pln"> menuBar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MenuBar</span><span class="pun">(</span><span class="pln">controlMenu</span><span class="pun">,</span><span class="pln"> colorMenu</span><span class="pun">,</span><span class="pln"> toolMenu</span><span class="pun">);</span></pre>

<p>
	تَبقَّى لنا إضافة شريط القوائم (menu bar) إلى مبيان المشهد (scene) الخاص بالبرنامج. عادةً ما يَكُون شريط القوائم أعلى النافذة، ولكن من الممكن وَضْعُه بأي مكان آخر. عادةً ما تَستخدِم البرامج -المُحتويّة على شريط قوائم- كائن حاوية من النوع <code>BorderPane</code> يَعمَل كمُكوِّن جذري (root) لمبيان المشهد (scene graph). يَظهَر شريط القوائم (menu bar) بالمُكوِّن الموجود أعلى الحاوية أما المُكوِّنات الرسومية (GUI components) الآخرى فتَظهَر بالمواضع الأربعة الآخرى.
</p>

<p>
	يُمكِنك إذًا إنشاء أي قائمة على النحو التالي: أولًا، اِنشِئ شريط قوائم (menu bar). ثانيًا، اِنشِئ قوائم (menus) وأَضِفها إلى ذلك الشريط. ثالثًا، اِنشِئ عناصر قائمة (menu items) وأَضِفها إلى تلك القوائم. أَضِف مُستمعِي أحداث للأحداث (events) الصادرة عن تلك العناصر لمُعالجتها. أخيرًا، ضَعْ شريط القوائم (menu bar) بأعلى مُكوِّن الحاوية من النوع <code>BorderPane</code>.
</p>

<p>
	تُوفِّر مكتبة جافا إف إكس (JavaFX) أنواعًا آخرى من عناصر القوائم (menu items)، والتي يُمكِن بطبيعة الحال إضافتها إلى أي قائمة. تلك العناصر مُعرَّفة كأصناف فرعية (subclasses) من الصَنْف <code>MenuItem</code>. على سبيل المثال، يَستخدِم المثال التالي عنصر قائمة من النوع <code>SeparatorMenuItem</code> لإضافة خط بين عناصر القائمة الأخرى:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_21" style="">
<span class="pln">menu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SeparatorMenuItem</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يتوفَّر أيضًا الصنفين الفرعيين <code>CheckMenuItem</code> و <code>RadioMenuItem</code>.
</p>

<p>
	يُمثِل الصَنْف <code>CheckMenuItem</code> عنصر قائمة يُمكِنه أن يَكُون بواحدة من حالتين: مُختار (selected) أو غير مُختار (not selected). تَتَبدَّل حالة العنصر عندما يختاره المُستخدِم من القائمة الحاوية له. يَعمَل ذلك العنصر بنفس الطريقة التي تَعمَل بها مُربعات الاختيار من الصَنْف <code>CheckBox</code> (اُنظر القسم الفرعي ٦.٤.٣).
</p>

<p>
	يَستخدِم البرنامج <code>MosaicDraw</code> ثلاثة عناصر من النوع <code>CheckMenuItems</code> بقائمة "Control": يُستخدَم إحداها لتَفْعِيل ميزة الألوان العشوائية للمربعات وإلغاؤها. يُستخدَم آخر لغَلْق خاصية التَماثُل (symmetry) وفَتْحها. عند تَفْعِيل تلك الخاصية، تَنعكِس الرسمة أفقيًا ورأسيًا لإنتاج نمط مُتماثِل. أخيرًا، يَعرِض عنصر القائمة الثالث ما يُعرَف بـ"الحشو (grouting)" ويُخفِيه. يَتَكوَّن ذلك الحشو من خطوط رمادية مرسومة حول كل مربع داخل الرسمة. اُنظر التَعْليمَات التالية المُستخدَمة لإضافة عنصر القائمة المُمثِل للخيار "Use Randomness" إلى قائمة "Control":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_23" style="">
<span class="pln">useRandomness </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckMenuItem</span><span class="pun">(</span><span class="str">"Use Randomness"</span><span class="pun">);</span><span class="pln">
useRandomness</span><span class="pun">.</span><span class="pln">setSelected</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">  </span><span class="com">// شغل العشوائية افتراضيًا</span><span class="pln">
controlMenu</span><span class="pun">.</span><span class="pln">getMenus</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">useRandomness</span><span class="pun">);</span><span class="pln">   </span><span class="com">// أضف عنصر قائمة إلى القائمة</span></pre>

<p>
	لم نُضِف مُعالجًا للحدث <code>ActionEvent</code> الصادر عن عنصر القائمة <code>useRandomness</code> حيث يَفْحَص البرنامج حالته باستدعاء <code>useRandomness.isSelected()‎</code> عند حاجته إلى تلوين أي مربع لكي يُقرِّر ما إذا كان سيُضيف بعض العشوائية للون أم لا. في المقابل، عندما يختار المُستخدِم مربع الاختيار "Use Grouting"من القائمة، فلابُدّ أن يُعِيد البرنامج رَسْم الحاوية (canvas) مباشرةً حتى تَعكِس حالتها الجديدة. تُضيِف الشيفرة التالية معالجًا (handler) إلى عنصر القائمة <code>useGrouting</code> المُمثِل لذلك الخيار لكي يَستدعِي التابع المناسب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_25" style="">
<span class="pln">useGrouting</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doUseGrouting</span><span class="pun">(</span><span class="pln">useGrouting</span><span class="pun">.</span><span class="pln">isSelected</span><span class="pun">())</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تحتوي قائمة "Tools" وقائمة "Color" بالبرنامج على عناصر قائمة من النوع <code>RadioMenuItem</code> تَعمَل بنفس الطريقة التي تَعمَل بها أزرار الانتقاء من الصَنْف <code>RadioButton</code> (اُنظر القسم الفرعي ٦.٤.٣). يُمكِن لأي عنصر قائمة من ذلك النوع أن يَكُون مختارً (selected) أو غير مختار، ولكن إذا أضفت عدة عناصر منها إلى كائن من الصَْنْف <code>ToggleGroup</code>، فلن تَتَمكَّن من اختيار أكثر من عنصر واحد فقط ضِمْن تلك المجموعة.
</p>

<p>
	يستطيع المُستخدِم أن يختار الأداة التي يَرغَب باِستخدَامها من قائمة "Tools"، ولأنه سيختار بطبيعة الحال أداة واحدة بكل مرة، كان من البديهي تَمثيِل تلك الأدوات بهيئة عناصر قائمة من الصَنْف <code>RadioMenuItem</code>، ومن ثَمَّ إضافتها جميعًا إلى كائن من الصَنْف <code>ToggleGroup</code>. يُؤشَر بعلامة على عنصر القائمة المُمثِل للأداة المُختارة حاليًا بالقائمة، وعندما يختار المُستخدِم أداة جديدة، يَتبدَّل مكان ذلك التأشير كاِستجابة مرئية تُوضِح الأداة المُختارَة حاليًا. بالإضافة إلى ذلك، تَملُك الكائنات من الصَنْف <code>ToggleGroup</code> خاصية "قابلة للمراقبة (observable)" تُمثِل الخيار المُحدَّد حاليًا (اُنظر القسم ٦.٣.٧). يُضيِف البرنامج مُستمِعًا (listener) إلى تلك الخاصية ويُخصِّص لها مُعالِج حَدَث (event handler) يُستدعَى تلقائيًا بكل مرة يختار فيها المُستخدِم أداة جديدة. اُنظر الشيفرة المسئولة عن إنشاء قائمة "Tools":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_27" style="">
<span class="typ">Menu</span><span class="pln"> toolMenu </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="str">"Tools"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">ToggleGroup</span><span class="pln"> toolGroup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ToggleGroup</span><span class="pun">();</span><span class="pln">
toolGroup</span><span class="pun">.</span><span class="pln">selectedToggleProperty</span><span class="pun">().</span><span class="pln">addListener</span><span class="pun">(</span><span class="pln"> 
                 e </span><span class="pun">-&gt;</span><span class="pln"> doToolChoice</span><span class="pun">(</span><span class="pln">toolGroup</span><span class="pun">.</span><span class="pln">getSelectedToggle</span><span class="pun">())</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
addRadioMenuItem</span><span class="pun">(</span><span class="pln">toolMenu</span><span class="pun">,</span><span class="str">"Draw"</span><span class="pun">,</span><span class="pln">toolGroup</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
addRadioMenuItem</span><span class="pun">(</span><span class="pln">toolMenu</span><span class="pun">,</span><span class="str">"Erase"</span><span class="pun">,</span><span class="pln">toolGroup</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
addRadioMenuItem</span><span class="pun">(</span><span class="pln">toolMenu</span><span class="pun">,</span><span class="str">"Draw 3x3"</span><span class="pun">,</span><span class="pln">toolGroup</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
addRadioMenuItem</span><span class="pun">(</span><span class="pln">toolMenu</span><span class="pun">,</span><span class="str">"Erase 3x3"</span><span class="pun">,</span><span class="pln">toolGroup</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span></pre>

<p>
	وبحيث يُعرَّف التابع <code>addRadioMenuItem</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_29" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> addRadioMenuItem</span><span class="pun">(</span><span class="typ">Menu</span><span class="pln"> menu</span><span class="pun">,</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> command</span><span class="pun">,</span><span class="pln"> 
                                   </span><span class="typ">ToggleGroup</span><span class="pln"> group</span><span class="pun">,</span><span class="pln"> boolean selected</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">RadioMenuItem</span><span class="pln"> menuItem </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RadioMenuItem</span><span class="pun">(</span><span class="pln">command</span><span class="pun">);</span><span class="pln">
    menuItem</span><span class="pun">.</span><span class="pln">setToggleGroup</span><span class="pun">(</span><span class="pln">group</span><span class="pun">);</span><span class="pln">
    menu</span><span class="pun">.</span><span class="pln">getItems</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">menuItem</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">selected</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        menuItem</span><span class="pun">.</span><span class="pln">setSelected</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الشيفرة المسئولة عن إنشاء شريط القوائم (menua bar) مُعرَّفة بالكامل ضِمْن تابع (method) اسمه هو <code>createMenuBar()‎</code>.
</p>

<h2>
	<code>Scene</code> و <code>Stage</code>
</h2>

<p>
	قبل أن ننتهي من هذه المقدمة المُختصَرة عن برمجة واجهات المُستخدِم الرسومية (GUI)، سنَفْحَص صَنْفين أساسيين على نحو أكثر تفصيلي: <code>Scene</code> من حزمة <code>package javafx.scene</code> و <code>Stage</code> من حزمة <code>package javafx.stage</code>.
</p>

<p>
	يُمثِل أي مشهد من الصَنْف <code>Scene</code> مساحة المحتويات ضِمْن نافذة، ويَحمِل المُكوِّن الجذري لمبيان المشهد (scene graph) الخاص بها. يُعرِّف الصَنْف <code>Scene</code> بواني كائن (constructors) كثيرة تَستقبِل جميعها المُكوِّن الجذري لمبيان المشهد كمُعامل (parameters) إلى جانب عدة معاملات آخرى. على سبيل المثال، يَستقبِل الباني <code>new Scene(root)‎</code> مُعاملًا واحدًا فقط يُمثِل المُكوِّن الجذري، ويُعدّ أكثر بواني الصنف شيوعًا.
</p>

<p>
	يُمكِنك أيضًا أن تُمرِّر عَرْض المشهد وارتفاعه كمُعاملات للباني باِستخدَام <code>Scene(root,width,height)‎</code>. إذا كان المُكوِّن الجذري عبارة عن حاوية من النوع <code>Pane</code>، يُضبَط حجم الحاوية بحيث يُساوِي حجم المشهد وتُعرَض محتوياتها بناءً على ذلك الحجم أما إذا لم يُخصَّص حجم المشهد، فإنه يُضْبَط إلى القيمة المفضلة لحجم المشهد.
</p>

<p>
	في المقابل، إذا كان المُكوِّن الجذري من الصَنْف <code>Group</code> المُعرَّف بحزمة <code>package javafx.scene</code>، فإنه لا يُضْبَط إلى نفس حجم المشهد وإنما يُقْصّ أي لا يَظهَر أي جزء يَقَع خارج المشهد (scene). لا تستطيع البرامج ضَبْط عَرْض أو ارتفاع مشهد من الصَنْف (Scene)، ولكن إذا تَغيَّر حجم المرحلة (stage) -من الصَنْف <code>Stage</code>- الحاوية لمشهد (scene)، فإن حجم المشهد يَتَغيَّر أتوماتيكيًا ليُطابِق الحجم الجديد لمساحة المُحتويات بالمرحلة (stage) كما يَتَغيَّر حجم المُكوِّن الجذري (root) للمشهد (scene) إذا كان من النوع <code>Pane</code>.
</p>

<p>
	يُمكِنك أن تُخصِّص لون مَلْئ (fill) -من النوع <code>Paint</code>- لخلفية مشهد (scene) عن طريق باني الكائن (constructor). مع ذلك، لا تَكُون خلفية المشهد مرئية في العموم لكَوْنها مغطاة بخلفية المُكوِّن الجذري (root). يُضْبَط لون خلفية المُكوِّن الجذري إلى الرمادي الفاتح افتراضيًا، ولكن قد تَضبُطه ليُصبِح شفافًا (transpatent) إذا أردت رؤية خلفية المشهد.
</p>

<p>
	تُمثِل أي مرحلة من الصَنْف <code>Scene</code> -المُعرَّف بحزمة <code>javafx.stage</code>- نافذة على شاشة الحاسوب. يَملُك أي تطبيق جافا إف إكس (JavaFX) مرحلة (stage) واحدة على الأقل يُطلَق عليها اسم "المرحلة الرئيسية (primary stage)". يُنشِئ النظام تلك المرحلة ويُمرِّرها كمُعامل (parameter) إلى التابع <code>start()‎</code> الخاص بالتطبيق. تَستخدِم البرامج في العادة أكثر من مجرد نافذة واحدة كما يُمكِنها أن تُنشِئ كائنات مرحلة جديدة من النوع <code>Stage</code>. سنعُود للحديث عن ذلك بالفصل ١٣.
</p>

<p>
	تحتوي أي مرحلة (stage) على مشهد (scene) يَملَئ مساحة المحتويات (content area) الخاصة بها. يستخدم تابع النسخة <code>stage.setScene(scene)‎</code> لضَبْط المشهد (scene) الخاص بمرحلة (stage). يُمكِنك عَرْض مرحلة لا تحتوي على أي مشهد، وستَظهَر عندها مساحة محتوياتها على هيئة مستطيل فارغ.
</p>

<p>
	علاوة على ذلك، تَحتوِي أي مرحلة (stage) على شريط عنوان (title bar) يَظهَر فوق مساحة محتوياتها (content area). يَتَكوَّن ذلك الشريط من عنوان نافذة إلى جانب مجموعة من أزرار التَحكُّم التي يستطيع المُستخدِم أن يَنقُر عليها للقيام بأشياء مثل غَلْق النافذة أو تكبيرها. يُضيِف نظام التشغيل (operating system) -لا الجافا- ذلك الشريط تلقائيًا، ويَتَحكَّم بتصميمه الخارجي. يُستخدَم تابع النسخة <code>stage.setTitle(string)‎</code> لضَبْط النص المَعرُوض بشريط العنوان، وتستطيع استدعائه بأي وقت.
</p>

<p>
	افتراضيًا، يستطيع المُستخدِم أن يَضبُط حجم أي مرحلة (stage) بسَحْب حوافها أو أركانها. مع ذلك، يُمكِنك استدعاء <code>stage.setResizable(false)‎</code> لكي تَمنَعه من ذلك. لاحِظ أنه حتى بَعْد استدعاء ذلك التابع على مرحلة، فما تزال شيفرة البرنامج ذاتها قادرة على تَغْيِير حجم تلك المرحلة باستدعاء توابع النسخة <code>stage.setWidth(w)‎</code> و <code>stage.setHeight(h)‎</code>. عادةً ما يُحدَّد الحجم المبدئي لأي مرحلة بحجم المشهد (scene) الموجود داخلها، ولكن بإمكانك استدعاء <code>setWidth()‎</code> و <code>setHeight()‎</code> لضَبْط قيمة الحجم المبدئي قَبْل عَرْض النافذة.
</p>

<p>
	عندما يَكُون حجم مرحلة (stage) قابلًا للضَبْط من قِبَل المُستخدِم، فإن بإِمكانه تَصغِير النافذة أو تَكْبيرها إلى أي قيمة عشوائية افتراضيًا. يُمكِنك مع ذلك اِستخدَام توابع النسخ <code>stage.setMinWidth(w)‎</code> و <code>stage.setMaxWidth(w)‎</code> و <code>stage.setMinHeight(h)‎</code> و <code>stage.setMaxHeight(h)‎</code> لوَضْع بعض القيود على القيم المسموح بها. تُطبَق تلك القيود على ما يستطيع المُستخدِم القيام به أثناء سَحْبه لحواف النافذة أو أركانها.
</p>

<p>
	يُمكِنك تَغْيِير موضع مرحلة (stage) على الشاشة -عادةً قبل عَرْضها- باستدعاء توابع النُسخ <code>stage.setX(x)‎</code> و <code>stage.setY(y)‎</code>. تُخصِّص الإحداثيات <code>x</code> و <code>y</code> مَوضِع الركن الأيسر العلوي من النافذة وفقًا لنظام إحداثيات الشاشة.
</p>

<p>
	أخيرًا، تَذكَّر أن أي مرحلة (stage) تَظلّ غَيْر مرئية إلى أن تَعرِضها على الشاشة صراحةً باستدعاء تابع النسخة <code>stage.show()‎</code>. عادةً ما يَكُون إظهار "المرحلة الرئيسية (primary stage)" هو آخر ما يُنفِّذه التابع <code>start()‎</code> الخاص بالتطبيق.
</p>

<h2>
	إنشاء ملفات Jar
</h2>

<p>
	الملف المُنتهِي بالامتداد <code>‎.jar</code> هو عبارة عن ملف جافا أرشيفي (java archive) يَتَضمَّن عددًا من ملفات الأصناف (class files) والموارد (resource files) المُستخدَمة ضِمْن البرنامج. عندما تُنشِئ برنامجًا يَعتمِد على أكثر من ملف واحد، يُنصَح عمومًا بوَضْع جميع الملفات التي يَتَطلَّبها ذلك البرنامج ضِمْن ملف جافا أرشيفي، وعندها لن يحتاج المُستخدِم لأكثر من ذلك الملف حتى يَتَمكَّن من تَشْغِيل البرنامج. يُمكِنك أيضًا أن تُنشِئ ما يُعرَف باسم "ملف جافا أرشيفي تّنْفِيذي (executable jar file)"، والذي يستطيع المُستخدِم أن يُشغِّله بنفس الكيفية التي يُشغِّل بها أي تطبيق آخر أي بالنَقْر المُزدوج على أيقونة الملف، ولكن لابُدّ من وجود نُسخة مُثبتَّة وصحيحة من الجافا على حاسوبه كما ينبغي أن تَكُون إعدادات حاسوبه مَضْبُوطَة بطريقة معينة. عادةً ما تُضبَط تلك الإعدادات تلقائيًا عند تثبيت الجافا بنظامي ويندوز وماك على الأقل.
</p>

<p>
	تَعتمِد طريقة إنشاء ملفات الجافا الأرشيفية (jar file) على بيئة البرمجة (programming environment) المُستخدَمة. لقد ناقشنا بالفعل نوعين أساسين منها هما: بيئة سطر الأوامر (command line) وبيئة التطوير المُتكاملة (IDE) بالقسم ٢.٦. تُوفِّر أي بيئة تطوير مُتكاملة للجافا (Java IDE) أمرًا لإنشاء ملفات الجافا الأرشيفية (jar files). اِتبِع مثلًا الخطوات التالية ببيئة تَطوِير إكلبس (Eclipse): اُنقُر بزر الفأرة الأيمن على المشروع داخل نافذة مُتصفِح الحزم (Package Explorer) ثُمَّ اِختَر "Export" من القائمة. ستَظهَر نافذة، اِختَر منها "JAR file" ثُمَّ اُنقُر على "Next". ستَظهَر نافذة أخرى، أَدْخِل اسمًا للملف الأرشيفي بصندوق الإدخال "JAR file" (يُمكنِك أيضًا النَقْر على زر "Browse" المجاور لذلك الصندوق لاختيار اسم الملف من خلال نافذة). لابُدّ أن يَنتهِي اسم الملف بالامتداد <code>‎.jar</code>. إذا كنت تُنشِئ ملفًا أرشيفيًا (jar file) عاديًا لا ملفًا تنفيذيًا، تستطيع أن تَنقُر على "Finish" لإنشاء الملف. أما إذا كنت تريد أن تُنشِئ ملفًا أرشيفيًا تنفيذيًا (executable)، اُنقُر على زر "Next" مرتين إلى أن تَصِل إلى شاشة "Jar Manifest Specification". ستَجِد صندوق الإدخال "Main class" بنهاية الشاشة. ينبغي أن تُدْخِل به اسم الصَنْف المُتْضمِّن للبرنامج <code>main()‎</code> المُراد تَشْغِيله عند تّنْفِيذ الملف الأرشيفي. إذا ضَغطَت على زر "Browse" المجاور للصندوق "Main class"، ستَظهَر قائمة بالأصناف المُحتويّة على برنامج <code>main()‎</code> والتي يُمكِنك الاختيار من بينها. بَعدّ اختيار الصَنْف، اُنقُر على زر "Finish" لإنشاء الملف التّنْفِيذي.
</p>

<p>
	بالنسبة لبيئة سطر الأوامر، تَتَضمَّن عُدّة تطوير جافا (Java Development Kit - JDK) برنامج سطر الأوامر <code>jar</code> المُستخدَم لإنشاء ملفات جافا أرشيفية (jar files). إذا كانت جميع الأصناف (classes) واقعة ضِمْن الحزمة الافتراضية (default package) كغالبية الأمثلة التي تَعرَّضنا لها، يَكُون اِستخدَام الأمر <code>jar</code> سهلًا نوعًا ما. فمثلًا، لكي تُنشِئ ملفًا أرشيفيًا غَيْر تّنفِيذي بسطر الأوامر، اِنتقِل إلى المجلد الذي يَحتوِي على ملفات الأصناف المطلوب إضافتها إلى الملف الأرشيفي، ثُمَّ اِستخدِم الأمر التالي:
</p>

<pre class="ipsCode" id="ips_uid_8574_36">
jar  cf  JarFileName.jar  *.class</pre>

<p>
	حيث <code>JarFileName</code> هو اسم ملف الجافا الأرشيفي الذي تَرغَب بإنشائه. لاحِظ أن المحرف <code>*</code> بـ <code>‎*.class</code> هو عبارة عن محرف بدل (wildcard) يَجعَل <code>‎*.class</code> تُطابِق جميع أسماء ملفات الأصناف (class files) الموجودة بالمجلد الحالي أي سيَحتوِي الملف الأرشيفي (jar file) على كل ملفات الأصناف ضِمْن المجلد. إذا كان البرنامج يَستخدِم ملفات موارد (resource files) مثل الصور، فلابُد من أن تُضيفها إلى الأمر <code>jar</code> أيضًا. أما إذا كنت تريد أن تُضيِف ملفات أصناف مُحدَّدة فقط، فينبغي أن تَذكُر أسمائها صراحةً بحيث تَفصِل مسافة فارغة بين كل اسم والذي يليه. إذا كانت ملفات الأصناف والموارد مُعرَّفة بحزم غَيْر الحزمة الافتراضية (default package)، تُصبِح الأمور أكثر تعقيدًا، ففي تلك الحالة، لابُدّ أن تَقَع ملفات الأصناف بمجلدات فرعية داخل المجلد الذي تُنفِّذ به الأمر <code>jar</code>. اُنظُر القسم الفرعي ٢.٦.٧.
</p>

<p>
	في المقابل، إذا كنت تريد أن تُنشِئ ملفًا أرشيفيًا تنفيذيًا من خلال سطر الأوامر، فالأمر ربما أعقد قليلًا. فلابُدّ من وجود طريقة لتَخْصِيص الصَنْف المُتضمِّن للبرنامج <code>main()‎</code> مثلًا بإنشاء ملف نصي يحتوي على سطر وحيد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8574_33" style="">
<span class="typ">Main</span><span class="pun">-</span><span class="typ">Class</span><span class="pun">:</span><span class="pln"> </span><span class="typ">ClassName</span></pre>

<p>
	يُشير <code>ClassName</code> -بالأعلى- إلى اسم الملف المُحتوِي على البرنامج <code>main()‎</code>. على سبيل المثال، إذا كان البرنامج <code>main()‎</code> مُعرَّف ضِمْن الصنف <code>MosaicDraw</code>، فينبغي أن يَحتوِي الملف على العبارة "Main-Class: MosaicDraw". يُمكِنك أن تُسمِي الملف بأي اسم تريده، ولكن لابُدّ من أن تَضعُه داخل نفس المجلد الذي تُنفِّذ به الأمر <code>jar</code> كما ينبغي أن تَكتُب الأمر <code>jar</code> بالصياغة التالية:
</p>

<pre class="ipsCode">
jar  cmf  ManifestFileName  JarFileName.jar  *.class
</pre>

<p>
	يستطيع الأمر <code>jar</code> تّنْفِيذ الكثير من العمليات المختلفة. يخصص مُعامله (parameter) الأول <code>cf</code> أو <code>cmf</code> العملية المطلوب تّنْفِيذها.
</p>

<p>
	بالمناسبة، إذا استطعت إنشاء ملف أرشيفي تّنْفِيذي (executable jar file)، يُمكِنك اِستخدَام الأمر <code>java -jar</code> لتَشْغِيله عبر سطر الأوامر كالتالي:
</p>

<pre class="ipsCode">
java  -jar  JarFileName.jar
</pre>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/c6/s6.html" rel="external nofollow">Section 6: Complete Programs</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614085982="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1150</guid><pubDate>Fri, 19 Mar 2021 13:03:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62E;&#x637;&#x64A;&#x637; &#x627;&#x644;&#x623;&#x633;&#x627;&#x633;&#x64A; &#x644;&#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x641;&#x64A; &#x645;&#x643;&#x62A;&#x628;&#x629; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A-%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-r1149/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034fd09dc1a9_-.png.a0732fdeba020d12cf922385b20c7a49.png" /></p>

<p>
	تُعدّ المُكوِّنات (components) اللبنة الأساسية بأي واجهة مُستخدِم رُسومية (graphical user interface). لا يَقْتصِر الأمر على إنشاء مُكوِّنات الواجهة بل تحتاج أيضًا إلى تَخْطِيط الكيفية التي ستَتَموْضَع بها تلك المُكوِّنات على الشاشة وكذلك تَخْصِيص أحجامها. في العموم، تُعدّ عملية حِسَاب إحداثيات المَواضِع معقدة نوعًا ما خاصةً إذا لم تَفْترِض ثبات حجم مساحة الرسم. تُوفِّر الجافا لحسن الحظ حلًا مُبسِّطًا لذلك.
</p>

<p>
	تُشير المُكوِّنات (components) إلى الأشياء المرئية التي تُشكِّل واجهة المُستخدِم الرُسومية (GUI). بعضًا منها عبارة عن مُكوِّنات حاوية (containers) يُمكِنها أن تَتَضمَّن مُكوِّنات آخرى. بمصطلحات جافا إف إكس (JavaFX)، المُكوِّنات الحاوية (container) هي عُقَد مبيان مشهد (scene graph node) يُمكِنها أن تَتَضمَّن عقد مبيان مشهد آخرى كأبناء (children). لابد من ضَبْط مواضع وأحجام أبناء أي مُكوِّن حاوي فيما يُعرَف باسم "التخطيط (layout)"، وهو ما قد تُبرمجه بنفسك، ولكن تُستخدَم عادةً حلول أتوماتيكية بواسطة المُكوِّن الحاوي (container) نفسه. تُنفِّذ أنواع مختلفة من المُكوِّنات الحاوية سياسات تخطيط (layout policies) مختلفة لتَحْديد مواضع العقد الأبناء (child nodes). سنُغطِي في هذا القسم بعض أنواع المُكوِّنات الحاوية (containers) بمنصة جافا إف إكس (JavaFX) مع توضيح سياسات التخطيط الخاصة بكُلًا منها كما سنَفْحَص أمثلة برمجية متعددة.
</p>

<p>
	لأن أي مُكوِّن حاوي (container) هو بالنهاية عُقدَة مبيان مشهد (scene graph node)، فيُمكِنك أن تُضيف مُكوِّن حاوي كابن لمُكوِّن حاوي آخر مما يَسمَح بتداخل مُعقد للمُكوِّنات كما هو مُبيَّن بالصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085382="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/001Panels_inLayout.png.6e347d35cf8338fb409b58b053b84a91.png" data-fileid="58460" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58460" data-unique="vr5ii7g5d" src="https://academy.hsoub.com/uploads/monthly_2021_02/001Panels_inLayout.png.6e347d35cf8338fb409b58b053b84a91.png" alt="001Panels_inLayout.png"></a>
</p>

<p>
	تُوضِح الصورة السابقة مُكوِّن حاوي (container) كبير يَتَكوَّن من مُكوِّنين حاويين أصغر كلًا منهما يَحمِل بدوره ثلاثة مُكوِّنات آخرى.
</p>

<p>
	تُخصِّص كل عقدة مبيان مشهد (scene graph node) قيم صغرى وكبرى ومُفضّلة لكُلًا من عرضها وارتفاعها. يَستعِين أي مُكوِّن حاوي بتلك القيم عند تقريره لطريقة وَضْع أبنائه. من الجهة الآخرى، يُعدّ حجم بعض مُكوِّنات العُقَد ثابتًا -مثل الصنفين <code>Canvas</code> و <code>ImageView</code>-، وتَتَساوَى عندها كُلًا من القيم الصغرى والعظمى والمُفضّلة للعرض والارتفاع مع الحجم الفعليّ للمُكوِّن، ولا يُمكِن لأي مُكوِّن حاوي عندها تَغْيير ذلك الحجم أثناء التخطيط (layout). في العموم، سيَحسِب المُكوِّن الحاوي حجمه المُفضّل اعتمادًا على ما يَتَضمَّّنه من مُكوِّنات وبحيث يَسمَح ذلك الحجم بأن يَحصُل كل مُكوِّن داخله على حجمه المُفضّل على الأقل. سيَحسِب المُكوِّن الحاوي القيم الصغرى والعظمى لحجمه بنفس الطريقة أي وفقًا للأحجام الصغرى والعظمى لأبنائه.
</p>

<p>
	أثناء التخطيط (layout)، تلتزم أغلب المُكوِّنات الحاوية (container) بضَبْط قيمة عَرْض (width) أي مُكوِّن ابن بحيث تتراوح بين القيمة الصغرى والعظمى لعَرْض المُكوِّن الابن ذاته حتى لو أدى ذلك إلى تَداخُله مع مُكوِّنات آخرى أو إلى تَمدُّده إلى خارج المُكوِّن الحاوي (قد يُصبِح ذلك الجزء المُتمدِّد مرئيًا أو لا بالاعتماد على المُكوِّن الحاوي) أو حتى إلى ترك مساحات فارغة بالمُكوِّن الحاوي. يَنطبِق نفس الأمر على ارتفاع المُكوِّنات الأبناء.
</p>

<p>
	تُعرِّف العُقَد مُتغيِّرة الحجم (resizable nodes) -مثل مُكوِّنات التحكُّم (controls) وغالبية المُكوِّنات الحاوية- توابع نسخ (instance methods) لضَبْط قيم العَرْض الصغرى والعظمى والمُفضّلة، هي كالتالي: <code>setMinWidth(w)‎</code> و <code>setPrefWidth(w)‎</code> و <code>setMaxWidth(w)‎</code> حيث المُعامِل <code>w</code> من النوع <code>double</code> بالإضافة إلى توابع نسخ مشابهة لضَبْط قيم الارتفاع. تَتَوفَّر أيضًا توابع مثل <code>setMaxSize(w,h)‎</code> و <code>setPrefSize(w,h)‎</code> لضَبْط قيمتي العرض والارتفاع بنفس الوقت. بالنسبة لمُكوِّن حاوي (container)، ستُبطِل القيم المضبوطة باِستخدَام هذه التوابع تلك القيم التي كانت ستُحسَب بالاعتماد على المُكوِّنات الأبناء.
</p>

<p>
	بمنصة جافا إف إكس (JavaFX)، تُعرَّف المُكوِّنات الحاوية المسئولة عن التخطيط بواسطة الصنف <code>Pane</code> وأصنافه الفرعية (subclasses) الواقعة بحزمة <code>javafx.scene.layout</code>. سنَفْحَص الآن عددًا قليلًا من أصناف التخطيط (layout classes) بدءًا بالصَنْف <code>Pane</code>.
</p>

<h2>
	إعداد تخطيط مخصص
</h2>

<p>
	أحيانًا قد ترغب بتصميم التخطيط (layout) الخاص بك بدلًا من اِستخدَام إحدى الأصناف الفرعية القياسية المُشتقَّة من الصَنْف <code>Pane</code>. سنَستخدِم الصَنْف الأعلى <code>Pane</code> ذاته بالمثال الأول، وهو ما يَعنِي أننا سنتتوَلّي مُهِمّة ضَبْط مواضع العُقد الأبناء (child nodes). بالنسبة لأحجام تلك العُقد، سيتولَّى الصَنْف <code>Pane</code> ضَبْطها إلى أحجامها المُفضّلة افتراضيًا، ومع ذلك يُمكِنك أن تُنجِز تلك المُهِمّة أيضًا إذا أردت.
</p>

<p>
	بفَرْض أن <code>node</code> عُقدة مبيان مشهد، تَضَع التَعْليمَة التالية الركن الأيسر العُلوي للعُقدة بإحداثيات النقطة (x,y) وفقًا لنظام إحداثيات المُكوِّن الحاوي (container) لتلك العُقدة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_7" style="">
<span class="pln">node</span><span class="pun">.</span><span class="pln">relocate</span><span class="pun">(</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">);</span></pre>

<p>
	كما تَضبُط التَعْليمَة التالية حجم العُقدة بشَّرْط أن تَكُون مُتْغيِّرة الحجم (resizable):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_9" style="">
<span class="pln">node</span><span class="pun">.</span><span class="pln">resize</span><span class="pun">(</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">)</span></pre>

<p>
	لن يَكُون لتلك التَعْليمَة أي تأثير إذا كانت العُقدة ثابتة الحجم (non-resizable). ومع ذلك تَملُك بعض العُقد ثابتة الحجم -مثل <code>Canvas</code>- توابعًا مثل <code>setWidth(w)‎</code> و <code>setHeight(h)‎</code> لضَبْط أحجامها.
</p>

<p>
	إذا كانت العُقدة ضِمْن مُكوِّن حاوي (container) مسئول عن التخطيط (layout) كليًا، فلن يَكُون لأي من التابعين <code>relocate()‎</code> و <code>resize()‎</code> أي تأثير. أما إذا كانت ضِمْن مُكوِّن حاوي مثل <code>Pane</code>، فسيُؤثِر التابع <code>resize()‎</code> بالعُقد مُتْغيِّرة الحجم أما التابع <code>relocate()‎</code> فلن يَكُون له أي تأثير. يُعدّ ما سَبَق صحيحًا فقط إذا كانت العُقدة مُدارة (managed). يُمكِن ضَبْط عُقدة معينة لتُصبِح غَيْر مُدارة (unmanaged) باستدعاء التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_11" style="">
<span class="pln">node</span><span class="pun">.</span><span class="pln">setManaged</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span></pre>

<p>
	سنَفْحَص مثالًا يَحتوِي على ٤ مُكوِّنات: زرين (buttons) وعنوان (label) وحاوي (canvas) يَعرِض رقعة شطرنج:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085382="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/002Null_Layout_Demo.png.ded344433a46b3cd3cb6952848f907e9.png" data-fileid="58461" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58461" data-unique="szj1ngbu4" src="https://academy.hsoub.com/uploads/monthly_2021_02/002Null_Layout_Demo.png.ded344433a46b3cd3cb6952848f907e9.png" alt="002Null_Layout_Demo.png"></a>
</p>

<p>
	عندما يَنقُر المُستخدِم على الأزرار، سيَتبدَّل النص المعروض بمُكوِّن العنوان (label). إلى جانب ذلك، لن يُنفِّذ البرنامج أي شيء فعليّ آخر، فهو فقط مُجرّد مثال بسيط على التخطيط (layout). سنَستخدِم هذا المثال بالقسم ٧.٥ كنقطة انطلاق لبرنامج لعبة داما (checkers).
</p>

<p>
	لأن هذا المثال يَستخدِم كائنًا من الصَنْف <code>Pane</code> كعُقدَة جذرية (root node) للمشهد وكمُكوِّن حاوي (container) للمُكوِّنات الأربعة الآخرى، فإن البرنامج سيَكُون مسئولًا عن ضَبْط مَواضِع المُكوِّنات باستدعاء تابعها <code>relocate()‎</code> وإلا سيَقَع الركن الأيسر العُلوي لجميع المُكوِّنات بالمَوضِع الافتراضي (٠،٠). سنَضبُط أيضًا حجم الزرين (buttons) لكي يُصبِحا بنفس الحجم الذي سيَكُون أكبر قليلًا من حجمهما المُفضّل، ولكن لمّا كان الزران مدارين (managed)، فإن استدعاء تابعهما <code>resize()‎</code> لن يَكُون له أي تأثير حيث سيُعيد المُكوِّن الحاوي ضَبْطهما لحجمهما المُفضّل، لذا سنحتاج أولًا لجعلهما غَيْر مُدارين (unmanaged). اُنظر الشيفرة التالية من تابع التطبيق <code>start()‎</code> الذي يُنشِئ المُكوِّنات الأربعة ويَضبُط مَواضِعها وأحجامها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_13" style="">
<span class="com">/* أنشئ العقد الأبناء */</span><span class="pln">

board </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Checkerboard</span><span class="pun">();</span><span class="pln"> </span><span class="com">// صنف فرعي من الصنف‫ Canvas</span><span class="pln">
board</span><span class="pun">.</span><span class="pln">draw</span><span class="pun">();</span><span class="pln">  </span><span class="com">// ارسم محتويات رقعة الشطرنج</span><span class="pln">

newGameButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"New Game"</span><span class="pun">);</span><span class="pln">
newGameButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doNewGame</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

resignButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Resign"</span><span class="pun">);</span><span class="pln">
resignButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doResign</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

message </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="str">"Click \"New Game\" to begin."</span><span class="pun">);</span><span class="pln">
message</span><span class="pun">.</span><span class="pln">setTextFill</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">rgb</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="lit">255</span><span class="pun">,</span><span class="lit">100</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Light green.</span><span class="pln">
message</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="pln">null</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="lit">18</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="com">// ‫اضبط موضع كل ابن باستدعاء تابعه relocate() </span><span class="pln">

board</span><span class="pun">.</span><span class="pln">relocate</span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="lit">20</span><span class="pun">);</span><span class="pln">
newGameButton</span><span class="pun">.</span><span class="pln">relocate</span><span class="pun">(</span><span class="lit">370</span><span class="pun">,</span><span class="pln"> </span><span class="lit">120</span><span class="pun">);</span><span class="pln">
resignButton</span><span class="pun">.</span><span class="pln">relocate</span><span class="pun">(</span><span class="lit">370</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span><span class="pln">
message</span><span class="pun">.</span><span class="pln">relocate</span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">370</span><span class="pun">);</span><span class="pln">

</span><span class="com">/* اضبط حجم الأزرار. لابد أن تجعلها غير مدارة أولًا */</span><span class="pln">

resignButton</span><span class="pun">.</span><span class="pln">setManaged</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
resignButton</span><span class="pun">.</span><span class="pln">resize</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="lit">30</span><span class="pun">);</span><span class="pln">
newGameButton</span><span class="pun">.</span><span class="pln">setManaged</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
newGameButton</span><span class="pun">.</span><span class="pln">resize</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="lit">30</span><span class="pun">);</span></pre>

<p>
	يُمثِل المُكوِّن الحاوي من الصَنْف <code>Pane</code> العُقدَة الجذرية (root node) للمشهد، لذا سيُضبَط حجم نافذة البرنامج بما يتناسب مع الحجم المُفضَّل لذلك المُكوِّن. افتراضيًا، يُحسَب الحجم المُفضَّل للمُكوِّن الحاوي بحيث يَكُون كبيرًا كفاية لعَرْض عُقده الأبناء المُدارة، ولأننا ضَبطَنا الأزرار لتُصبِح غَيْر مُدارة (unmanaged)، فإنها لن تُؤثِر على حجمه المُفضَّل. سنَضبُط عَرْض المُكوِّن الحاوي وارتفاعه بالقيم التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_15" style="">
<span class="typ">Pane</span><span class="pln"> root </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Pane</span><span class="pun">();</span><span class="pln">
root</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="lit">500</span><span class="pun">);</span><span class="pln">
root</span><span class="pun">.</span><span class="pln">setPrefHeight</span><span class="pun">(</span><span class="lit">420</span><span class="pun">);</span></pre>

<p>
	سنضيف الآن كُلًا من الأزرار (buttons) والعنوان (label) والرقعة (board) كأبناء بالمكون الحاوي بخطوة واحدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_19" style="">
<span class="pln">root</span><span class="pun">.</span><span class="pln">getChildren</span><span class="pun">().</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">board</span><span class="pun">,</span><span class="pln"> newGameButton</span><span class="pun">,</span><span class="pln"> resignButton</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">);</span></pre>

<p>
	أو على عدة خطوات بحيث يُضَاف كل مُكوِّن على حدى باِستخدَام التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_23" style="">
<span class="pln">root</span><span class="pun">.</span><span class="pln">getChildren</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">board</span><span class="pun">);</span></pre>

<p>
	أو يُمكِنك أن تُمرِّر العُقد الأبناء (child nodes) كمُعامِلات (parameters) لبَانِي الكائن (constructor) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_25" style="">
<span class="typ">Pane</span><span class="pln"> root </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Pane</span><span class="pun">(</span><span class="pln">board</span><span class="pun">,</span><span class="pln"> newGameButton</span><span class="pun">,</span><span class="pln"> resignButton</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">);</span></pre>

<p>
	لابُدّ من ضَبْط المُكوِّن الحاوي من الصَنْف <code>Pane</code> ليَكُون المُكوِّن الجذري (root) للمشهد (scene). ينبغي أيضًا أن نَضَع ذلك المشهد بالمرحلة (stage) التي لابُدّ من ضَبْطها وعَرْضها على الشاشة. اُنظر كامل الشيفرة المصدرية بالملف <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/source/chapter6/OwnLayoutDemo.java" rel="external nofollow">OwnLayoutDemo.java</a>.
</p>

<p>
	قد يَكُون تَخْصِيص التخطيط (layout) بالمثال السابق أمرًا سهلًا على نحوٍ معقول، ولكنه سيُصبِح أصعب كثيرًا إذا كان حجم النافذة عامًلا مُتْغيِّرًا. في تلك الحالة، يُفضَّل عمومًا الاعتماد على أحد المُكوِّنات الحاوية القياسية لتُنفِّذ التخطيط (layout) بدلًا منك، ولكن إذا أردت تّنْفيذه، فلابُدّ من أن تُعرِّف صنفًا فرعيًا من الصَنْف <code>Pane</code> أو من صَنْفها الأعلى <code>Region</code> ثم ينبغي أن تُعيد تَعرِيف (override) التابع <code>layoutChildren()‎</code> الذي يَستدعِيه النظام لحَث المُكوِّن الحاوي (container) على تطبيق التخطيط (layout) بما في ذلك تَغْيِير حجم المُكوِّن الحاوي.
</p>

<h2>
	<code>BorderPane</code>
</h2>

<p>
	يُعدّ مُكوِّن الحاوية <code>BorderPane</code> صنفًا فرعيًا (subclass) من الصَنْف <code>Pane</code>. قد يَشتمِل ذلك المُكوِّن على ما يَصِل إلى ٥ مُكوِّنات (components) يَقَع أكبرها بالمنتصف بينما يُرتَّب البقية حوله بالأعلى وبالأسفل وعلى اليسار وعلى اليمين كما هو مُبيَّن بالصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085382="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/003Border_Layout.png.f047f93de8d2353e6a70ae0841b4f353.png" data-fileid="58462" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58462" data-unique="oiph6hl9h" src="https://academy.hsoub.com/uploads/monthly_2021_02/003Border_Layout.png.f047f93de8d2353e6a70ae0841b4f353.png" alt="003Border_Layout.png"></a>
</p>

<p>
	قد يَشتمِل مُكوِّن الحاوية <code>BorderPane</code> على عدد أقل من خمسة مُكوِّنات لذا أنت لست مُضطّرًا لأن تُخصِّص مُكوِّن بكل مَوْضِع ولكن عادةً ما يُخصَّص مُكوِّن بمنتصف الحاوية.
</p>

<p>
	يُوفِّر الصَنْف <code>BorderPane</code> بانيين (constructors): لا يَستقبِل أحدهما أية مُعامِلات (parameters) بينما يَستقبِل الآخر مُعامِلًا واحدًا يُخصِّص مُكوِّن ابن (child) يُفْترَض وَضعُه بمنتصف الحاوية. بالإضافة إلى ذلك، يُمكِنك أن تُخصِّص العُقد الأبناء (child nodes) الآخرى لحاوية <code>pane</code> من الصَنْف <code>BorderPane</code> باِستخدَام التوابع (methods) التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_27" style="">
<span class="pln">pane</span><span class="pun">.</span><span class="pln">setCenter</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span><span class="pln">
pane</span><span class="pun">.</span><span class="pln">setTop</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span><span class="pln">
pane</span><span class="pun">.</span><span class="pln">setRight</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span><span class="pln">
pane</span><span class="pun">.</span><span class="pln">setBottom</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span><span class="pln">
pane</span><span class="pun">.</span><span class="pln">setLeft</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span></pre>

<p>
	ملحوظة: إذا مَرَّرت مُعامِلًا (parameter) قيمته تُساوِي <code>null</code> لأي من تلك التوابع، فسيُحذَف المُكوِّن الموجود بذلك المَوْضِع ضِمْن الحاوية
</p>

<p>
	تَضبُط مُكوِّنات الحاوية من الصَنْف <code>BorderPane</code> حجم عُقدها الأبناء (child nodes) على النحو التالي (لابُدّ أن يتراوح كُلًا من عرض أي مُكوّن وارتفاعه بين قيمته العظمى والصغرى المُخصَّصة): تَحتَل المُكوِّنات بالمَوْضِع العلوي والسفلي -إن وجدت- مساحة ارتفاعها يُساوِي القيمة المُفضَّلة لارتفاع المُكوِّن أما عَرْضها فيُساوِي عَرْض الحاوية الكلي. في المقابل، تَحتَل المُكوِّنات على اليمين واليسار مساحة عَرْضها يُساوِي القيمة المُفضَّلة لعَرْض المُكوِّن أما ارتفاعها فيُساوِي قيمة ارتفاع الحاوية مطروحًا منها المسافة التي احتلتها المُكوِّنات بكُلًا من المَوْضِع العلوي والسفلي. أخيرًا، يَمتدّ المُكوِّن بمنتصف الحاوية عَبْر المساحة المُتْبقيّة.
</p>

<p>
	تُضبَط القيمة المُفضَّلة لحجم الحاوية على نحو يتناسب مع الحجم المُفضَّل لأبنائها من العُقد المُدارة (managed). تُحسَب القيمة الصغرى بنفس الطريقة أما القيمة العظمى الافتراضية فهي لا نهائية.
</p>

<p>
	تُتيِح بعض الأصناف الفرعية (subclasses) المُشْتقَّة من الصَنْف <code>Pane</code> تطبيق ما يُعرَف باسم "قيود التَخْطِيط (layout constraint)" لتَعْدِيل تَخْطِيط (layout) العُقد الأبناء. على سبيل المثال، ماذا سيَحدُث بحاوية من النوع <code>BorderPane</code> إذا لم يَكُن مَسمُوحًا بإعادة ضَبْط حجم مُكوِّن ابن لكي يتناسب تمامًا مع المساحة المُتوفرة؟ في تلك الحالة، يَحتَل ذلك المُكوِّن مَوْضعًا افتراضيًا ضِمْن تلك المساحة. يَقَع مكون بمنتصف مساحة الحاوية (pane) بينما يقع آخر بالركن الأيسر السفلي منها وهكذا. يُمكِنك أن تُعدِّل الطريقة الافتراضية باِستخدَام التابع الساكن (static method) التالي المُعرَّف بالصَنْف <code>BorderPane</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_29" style="">
<span class="typ">BorderPane</span><span class="pun">.</span><span class="pln">setAlignment</span><span class="pun">(</span><span class="pln"> child</span><span class="pun">,</span><span class="pln"> position </span><span class="pun">);</span></pre>

<p>
	يُشير <code>child</code> إلى العُقدة التي تَرغَب بتَعْدِيل مَوْضِعها أما <code>position</code> فيَحتوِي على إحدى ثوابت نوع التعداد <code>Pos</code> من حزمة <code>package javafx.geometry</code> مثل <code>Pos.CENTER</code> أو <code>POS.TOP_LEFT</code> أو <code>Pos.BOTTOM_RIGHT</code> أو غيرها.
</p>

<p>
	يَسمَح الصَنْف <code>BorderPane</code> بإضافة هامش (margin) لأي مُكوِّن ابن يَقَع ضِمْن الحاوية. الهامش عبارة عن مسافة فارغة تُحيِط بالمُكوِّن الابن وتُلوَّن بنفس لون خلفية الحاوية، ويُسنَد إليها قيمة من النوع <code>Insets</code> المُعرَّف بحزمة <code>package javafx.geometry</code>. يَملُك أي كائن من النوع <code>Insets</code> أربع خاصيات (properties) من النوع <code>double</code> هي <code>top</code> و <code>right</code> و <code>bottom</code> و <code>left</code> والتي يُمكِنك ضَبْطها باِستخدَام بَانِي الكائن (constructor):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_31" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Insets</span><span class="pun">(</span><span class="pln">top</span><span class="pun">,</span><span class="pln">right</span><span class="pun">,</span><span class="pln">bottom</span><span class="pun">,</span><span class="pln">left</span><span class="pun">)</span></pre>

<p>
	يَتَوفَّر باني كائن (constructor) آخر يَستقبِل مُعاملًا (parameter) واحدًا يُمثِل قيمة الخاصيات (properties) الأربعة. عند اِستخدَامه لتَخْصِيص هامش (margin) حول مُكوِّن ابن معين، تُحدِّد تلك الخاصيات الأربعة عَرْض الهامش (margin) حول جوانبه الأربعة: أعلاه ويمينه وأسفله ويساره.
</p>

<p>
	تستطيع تَخْصِيص الهامش (margin) باِستخدَام التابع الساكن (static method) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_33" style="">
<span class="typ">BorderPane</span><span class="pun">.</span><span class="pln">setMargin</span><span class="pun">(</span><span class="pln"> child</span><span class="pun">,</span><span class="pln"> insets </span><span class="pun">);</span></pre>

<p>
	على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_35" style="">
<span class="typ">BorderPane</span><span class="pun">.</span><span class="pln">setMargin</span><span class="pun">(</span><span class="pln"> topchild</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Insets</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="lit">5</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يُمكِنك أيضًا اِستخدَام لغة أوراق الأنماط المتعاقبة (CSS) لتَعْدِيل مَظهَر حاوية (container)، وهي في الواقع واحدة من أسهل الطرائق لضَبْط خاصيات مثل لون الخلفية.
</p>

<h2>
	<code>HBox</code> و <code>VBox</code>
</h2>

<p>
	إذا أردت ترتيب مجموعة من المُكوِّنات ضِمْن صف أفقي أو عمود رأسي، يُمكِنك اِستخدَام الصنفين <code>HBox</code> و <code>VBox</code>. يُرتِّب الصَنْف الفرعي <code>HBox</code> المُشْتق من الصَنْف <code>Pane</code> أبنائه إلى جوار بعضها البعض ضِمْن صف أفقي بينما يُرتِّب الصَنْف الفرعي <code>VBox</code> المُشتق من الصَنْف <code>Pane</code> أبنائه ضِمْن عمود أفقي. يُمكِنك مثلًا أن تَستخدِم حاوية من الصَنْف <code>HBox</code> بالمَوْضِع السفلي لحاوية من الصَنْف <code>BorderPame</code> مما سيَسمَح باصطفاف عدة مُكوِّنات على طول الحافة السفلية للحاوية. بالمثل، قد تَستخدِم حاوية من الصَنْف <code>VBox</code> بالمَوْضِع الأيسر أو الأيمن لحاوية من الصَنْف <code>BorderPane</code>. سنُناقش الصَنْف <code>HBox</code> فقط ولكن تستطيع اِستخدَام الصَنْف <code>VBox</code> بنفس الكيفية.
</p>

<p>
	بإِمكَانك ضَبْط الصَنْف <code>HBox</code> لكي يَترُك فراغًا بين كل ابن والابن الذي يليه. بفَرْض وجود حاوية <code>hbox</code>، يُمكِنك تَخْصِيص مقدار الفراغ بتمرير قيمة من النوع <code>double</code> للتابع التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_37" style="">
<span class="pln">hbox</span><span class="pun">.</span><span class="pln">setSpacing</span><span class="pun">(</span><span class="pln"> gapAmount </span><span class="pun">);</span></pre>

<p>
	القيمة الافتراضية تُساوِي صفر. تُضَاف الأبناء إلى حاوية <code>hbox</code> من الصَنْف <code>HBox</code> بنفس الكيفية التي تُضَاف بها إلى حاوية من الصَنْف <code>Pane</code> أي باستدعاء <code>hbox.getChildren().add(child)‎</code> أو <code>hbox.getChildren().addAll(child1,child2,...)‎</code>. يُوفِّر الصَنْف <code>HBox</code> بانيين (constructor). لا يَستقبِل الأول أية مُعاملات (parameters) بينما يَستقبِل الثاني مُعاملين هما حجم الفراغ المتروك وأي عُقد أبناء (child nodes) قد ترغب بإضافتها إلى تلك الحاوية. تَضْبُط أي حاوية من النوع <code>HBox</code> حجم كل ابن وفقًا لقيمة العَرْض المُفضَّلة لذلك الابن مع إِمكانية تَرك مسافة فارغة إضافية إلى اليمين. إِذا كان مجموع قيم العَرْض المُفضَّلة للأبناء ضِمْن حاوية معينة أكبر من القيمة الفعلية لعَرْض تلك الحاوية، يُقلَّص عَرْض كل ابن منها في حدود قيمة العَرْض الصغرى الخاصة به. في المقابل، يُضبَط ارتفاع الأبناء إلى الارتفاع المتاح بالحاوية بما يَتَوافَق مع كُلًا من القيم الصغرى والعظمى المُخصَّصة لارتفاع تلك الأبناء.
</p>

<p>
	إذا أردت تَوْسِيع عَرْض عدة أبناء ضِمْن حاوية من النوع <code>HBox</code> إلى ما هو أبعد من قيمها المُفضَّلة بغَرْض مَلْئ مساحة مُتاحَة ضِمْن تلك الحاوية، فينبغي أن تُطبِق "قيد تخطيطي (layout constraint)" على كل ابن تَرغَب بتوسيعه باستدعاء التابع الساكن (static method) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_39" style="">
<span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln"> child</span><span class="pun">,</span><span class="pln"> priority </span><span class="pun">);</span></pre>

<p>
	يَستقبِل التابع بالأعلى مُعاملًا ثانيًا عبارة عن ثابت (constant) من نوع التعداد <code>Priority</code> المُعرَّف بحزمة <code>package javafx.scene.layout</code>. إذا اِستخدَمت القيمة <code>Priority.ALWAYS</code>، فدائمًا ما سيَتَقاسَم ذلك الابن أي مساحة إضافية مُتْبقيّة، ولكنه مع ذلك سيَظَلّ مقيدًا بقيمة العَرْض العظمى الخاصة به لذا قد تَضطّر إلى زيادتها لتَسمَح له بالتَوسُع كما ينبغي.
</p>

<p>
	لنَفْترِض مثلًا وجود حاوية من الصَنْف <code>HBox</code> تَحتوِي على ثلاثة أزرار هي <code>but1</code> و <code>but2</code> و <code>but3</code> والتي تُريِدها أن تَتَوسَّع بما يَكفِي لمَلْئ الحاوية بالكامل. ستحتاج إذًا إلى ضَبْط القيد التخطيطي <code>HGrow</code> لكل زر منها. بالإضافة إلى ذلك، لمّا كانت كُلًا من القيمة العظمى والمُفضَّلة لعَرْض زر مُتساوية، كان من الضروري زيادة القيمة القصوى. بالمثال التالي، ضُبِطَت قيمة العَرْض العُظمى لكل زر إلى قيمة الثابت <code>Double.POSITIVE_INFINITY</code> مما سيُمكِّنها من التَوسُّع بدون أي قيود نهائيًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_41" style="">
<span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">but1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln"> 
</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">but2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln"> 
</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">but3</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln"> 
but1</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln"> 
but2</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln"> 
but3</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span></pre>

<p>
	ستُوزَّع الآن أي مساحة إضافية بالتَساوِي على الأزرار الثلاثة وتُضاف إلى قيمة عَرْضها المُفضَّلة. لا يَعنِي ذلك أن عَرْضها سيُصبِح متساويًا بالضرورة؛ فقد لا تَكُون قيمها المُفضَّلة الأصلية مُتساوية أساسًا. إذا أردت ضَبْط الأزرار الثلاثة لتُصبِح بنفس الحجم، فلابُدّ من ضَبْط قيمة العَرْض المُفضَّلة لكُلًا منها لتَحمِل نفس القيمة كما يَلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_43" style="">
<span class="pln">but1</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
but2</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
but3</span><span class="pun">.</span><span class="pln">setPrefWidth</span><span class="pun">(</span><span class="lit">1000</span><span class="pun">);</span></pre>

<p>
	سيَحتَلّ الآن كل زر ضِمْن تلك الحاوية مساحة عَرْضها مُتساوي.
</p>

<p>
	تَتَوفَّر طرائق آخرى لتَعْدِيل التخطيط (layout) ضِمْن حاوية من النوع <code>HBox</code>. يُمكِنك مثلًا إضافة هامش (margin) حول أي مُكوِّن ابن باِستخدَام تابع ساكن (static method) مُشابه لذلك المُستخدَم لنفس الغرض بالحاويات من النوع <code>BorderPane</code>. تستطيع أيضًا استدعاء <code>hbox.setFillHeight(false)‎</code> لضَبْط ارتفاع حاوية معينة إلى القيمة المُفضَّلة لارتفاع أبنائها بدلًا من توسيعهم ليتلائموا مع ارتفاع تلك الحاوية. قد تَستدعِي <code>hbox.setAlignment(position);‎</code> لضَبْط مَوْضِع ابن ضِمْن حاوية عندما لا يَملَؤها بالكامل. لاحِظ أن مُعاملها (parameter) من النوع <code>Pos</code> بقيمة افتراضية تساوي <code>Pos.TOP_LEFT</code>. يُمكِنك أيضًا أن تُطبِّق عليها خاصيات لغة أوراق الأنماط المتعاقبة (CSS).
</p>

<p>
	سنَفْحَص الآن مثالًا لواجهة مُستخدِم رُسومية (GUI) مبنية بالكامل على الصَنْفين <code>HBox</code> و <code>VBox</code>. يُمكِنك الإطلاع على شيفرة البرنامج بالملف <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/source/chapter6/SimpleCalc.java" rel="external nofollow">SimpleCalc.java</a>. تَتَضمَّن نافذة البرنامج ما يلي: حَقْلين نصيين من الصَنْف <code>TextField</code> تَسمَح للمُستخدِم بكتابة عدد، بالإضافة إلى أربعة أزرار يُمكِن للمُستخدِم النَقْر عليها لإجراء عملية جمع أو طرح أو ضرب أو قسمة على العددين المُدْخَلين، وأخيرًا عنوان نصي من الصَنْف <code>Label</code> يَعرِض نتيجة العملية المُجراة. تُبيِّن الصورة التالية ما تبدو عليه نافذة البرنامج:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085382="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/004Simple_Calc.png.660c8145f5ea4efbf13eefe92faf7304.png" data-fileid="58463" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58463" data-unique="8tpna74dv" src="https://academy.hsoub.com/uploads/monthly_2021_02/004Simple_Calc.png.660c8145f5ea4efbf13eefe92faf7304.png" alt="004Simple_Calc.png"></a>
</p>

<p>
	تُستخدَم حاوية من الصَنْف <code>VBox</code> كعُقدة جذرية لنافذة البرنامج، وتَحتوِي على أربعة عناصر: الثلاثة الأولى منها عبارة عن حاويات من الصَنْف <code>HBox</code>. يَحتوِي أول حاوي منها على عنوان من النوع <code>Label</code> يَعرِض النص "‎x =‎" بالإضافة إلى حقل نصي من الصَنْف <code>TextField</code>. يُنشَئ ذلك باستخدام الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_45" style="">
<span class="pln">xInput </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextField</span><span class="pun">(</span><span class="str">"0"</span><span class="pun">);</span><span class="pln">  </span><span class="com">// Text input box initially containing "0"</span><span class="pln">
</span><span class="typ">HBox</span><span class="pln"> xPane </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HBox</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="str">" x = "</span><span class="pun">),</span><span class="pln"> xInput </span><span class="pun">);</span></pre>

<p>
	يُنشَئ العنوان (label) باستخدام باني (constructor) ثم يُضاف مباشرةً إلى الحاوية لعدم حاجتنا إلى مَرجِع (reference) يُشير إليه فيما بَعْد. ستُضاف هذه الحاوية لاحقًا كابن للحاوية الخارجية من الصنف <code>VBox</code>.
</p>

<p>
	سيَعرِض ثالث حاوي الأزرار الأربعة. ولأن القيمة الافتراضية لعَرْض الزر صغيرة نسبيًا، فسنُطبِّق القيد <code>hgrow</code> عليها كما سنُزيد قيمة العرض العظمى لكُلًا منها لكي نَتَأكَّد من كَوْنها تملئ عَرْض الحاوية بالكامل. يُمكِنك القيام بما سبق باِستخدَام الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_47" style="">
<span class="com">// ‫انشئ الأزرار الأربعة وحاوية HBox لحملها</span><span class="pln">

</span><span class="typ">Button</span><span class="pln"> plus </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"+"</span><span class="pun">);</span><span class="pln">
plus</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doOperation</span><span class="pun">(</span><span class="str">'+'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="typ">Button</span><span class="pln"> minus </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"-"</span><span class="pun">);</span><span class="pln">
minus</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doOperation</span><span class="pun">(</span><span class="str">'-'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="typ">Button</span><span class="pln"> times </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"*"</span><span class="pun">);</span><span class="pln">
times</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doOperation</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="typ">Button</span><span class="pln"> divide </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"/"</span><span class="pun">);</span><span class="pln">
divide</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> doOperation</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

</span><span class="typ">HBox</span><span class="pln"> buttonPane </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HBox</span><span class="pun">(</span><span class="pln"> plus</span><span class="pun">,</span><span class="pln"> minus</span><span class="pun">,</span><span class="pln"> times</span><span class="pun">,</span><span class="pln"> divide </span><span class="pun">);</span><span class="pln">

</span><span class="com">/* ‫ينبغي أن تضبط الأزرار الأربعة قليلًا لكي تملئ الحاوية buttonPane 
 * بإمكانك القيام بذلك بضبط القيمة العظمى لعرض كل زر إلى قيمة أكبر */</span><span class="pln">

</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">plus</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln">
plus</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln">
</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">minus</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln">
minus</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln">
</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">times</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln">
times</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln">
</span><span class="typ">HBox</span><span class="pun">.</span><span class="pln">setHgrow</span><span class="pun">(</span><span class="pln">divide</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Priority</span><span class="pun">.</span><span class="pln">ALWAYS</span><span class="pun">);</span><span class="pln">
divide</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span></pre>

<p>
	يَعرِض القسم الأخير بالحاوية الخارجية من النوع <code>VBox</code> مُكوِّنًا واحدًا عبارة عن عنوان من النوع <code>Label</code>. نظرًا لكَوْنه مُكوِّنًا وحيدًا، يُمكِننا إضافته إلى الحاوية الخارجية مباشرةً دون الحاجة لوَضْعه أولًا ضِمْن حاوية من النوع <code>HBox</code>. لكي نَضْمَن ظهور النص بمنتصف النافذة بدلًا من جانبها الأيسر، سنحتاج إلى زيادة قيمة العَرْض العظمى لمُكوِّن العنوان (label) وكذلك إلى ضَبْط خاصية محاذاة النص (alignment) بمُكوِّن العنوان (label) إلى المنتصف بدلًا من اليسار. اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_49" style="">
<span class="pln">answer</span><span class="pun">.</span><span class="pln">setMaxWidth</span><span class="pun">(</span><span class="typ">Double</span><span class="pun">.</span><span class="pln">POSITIVE_INFINITY</span><span class="pun">);</span><span class="pln">
answer</span><span class="pun">.</span><span class="pln">setAlignment</span><span class="pun">(</span><span class="typ">Pos</span><span class="pun">.</span><span class="pln">CENTER</span><span class="pun">);</span></pre>

<p>
	عندما يَنقُر المُستخدِم على أي من الأزرار الأربعة، يُستدعَى التابع <code>doOperation()‎</code> ليُنفِّذ ما يَلي: يقرأ العَدَدين الذين أَدْخَلهما المُستخدِم بالحقول النصية (text fields) ثم يُجرِي العملية الحسابية المطلوبة ويَعرِض الناتج بنص العنوان (label). نظرًا لأن محتويات الحقول النصية (text fields) تُعاد كسَلاسِل نصية من النوع <code>String</code>، لابُدّ من تَحْوِيلها إلى قيم عددية أولًا. إذا فشلت عملية التَحْوِيل، يَعرِض العنوان (label) رسالة خطأ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_51" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> doOperation</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> op </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">  </span><span class="com">// الأعداد المدخلة</span><span class="pln">

    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// اقرأ x من أول صندوق إدخال</span><span class="pln">
        </span><span class="typ">String</span><span class="pln"> xStr </span><span class="pun">=</span><span class="pln"> xInput</span><span class="pun">.</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">
        x </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Double</span><span class="pun">.</span><span class="pln">parseDouble</span><span class="pun">(</span><span class="pln">xStr</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">NumberFormatException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫لم يكن xStr عدد صالح</span><span class="pln">
        </span><span class="com">// ‫اعرض رسالة خطأ وانقل موضع التركيز إلى xInput</span><span class="pln">
        </span><span class="com">// وحدد محتوياته بالكامل</span><span class="pln">
        answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Illegal data for x."</span><span class="pun">);</span><span class="pln">
        xInput</span><span class="pun">.</span><span class="pln">requestFocus</span><span class="pun">();</span><span class="pln">
        xInput</span><span class="pun">.</span><span class="pln">selectAll</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln"> </span><span class="com">// توقف عن المعالجة عند حدوث خطأ</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// ‫اقرأ y من ثاني صندوق إدخال</span><span class="pln">
        </span><span class="typ">String</span><span class="pln"> yStr </span><span class="pun">=</span><span class="pln"> yInput</span><span class="pun">.</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">
        y </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Double</span><span class="pun">.</span><span class="pln">parseDouble</span><span class="pun">(</span><span class="pln">yStr</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">NumberFormatException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Illegal data for y."</span><span class="pun">);</span><span class="pln">
        yInput</span><span class="pun">.</span><span class="pln">requestFocus</span><span class="pun">();</span><span class="pln">
        yInput</span><span class="pun">.</span><span class="pln">selectAll</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/* نفذ العملية الحسابية بالاعتماد على قيمة المعامل الممررة */</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">op </span><span class="pun">==</span><span class="pln"> </span><span class="str">'+'</span><span class="pun">)</span><span class="pln">
        answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="pln"> </span><span class="str">"x + y = "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">+</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">op </span><span class="pun">==</span><span class="pln"> </span><span class="str">'-'</span><span class="pun">)</span><span class="pln">
        answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="pln"> </span><span class="str">"x - y = "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">-</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">op </span><span class="pun">==</span><span class="pln"> </span><span class="str">'*'</span><span class="pun">)</span><span class="pln">
        answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="pln"> </span><span class="str">"x * y = "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">*</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">op </span><span class="pun">==</span><span class="pln"> </span><span class="str">'/'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">y </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// Can't divide by zero! Show an error message.</span><span class="pln">
            answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Can't divide by zero!"</span><span class="pun">);</span><span class="pln">
            yInput</span><span class="pun">.</span><span class="pln">requestFocus</span><span class="pun">();</span><span class="pln">
            yInput</span><span class="pun">.</span><span class="pln">selectAll</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            answer</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="pln"> </span><span class="str">"x / y = "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">/</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end doOperation()</span></pre>

<p>
	يُمكِنك الإطلاع على شيفرة البرنامج بالكامل بالملف <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/source/chapter6/SimpleCalc.java" rel="external nofollow">SimpleCalc.java</a>.
</p>

<h2>
	<code>GridPane</code> و <code>TilePane</code>
</h2>

<p>
	سنَطلِّع الآن على الصَنْف الفرعي <code>GridPane</code> المُشتَّق من الصَنْف <code>Pane</code>. يُرتِّب ذلك الصَنْف أبنائه ضِمْن شبكة (grid) من الصفوف والأعمدة مُرقَّمة بدءًا من الصفر. تُبيِّن الصورة التالية شبكة مُكوَّنة من ٤ صفوف و ٥ أعمدة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614085382="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/005Grid_Layout.png.12099cfb064c017c8f5cf71f17c5238e.png" data-fileid="58464" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58464" data-unique="en0z5xfa4" src="https://academy.hsoub.com/uploads/monthly_2021_02/005Grid_Layout.png.12099cfb064c017c8f5cf71f17c5238e.png" alt="005Grid_Layout.png"></a>
</p>

<p>
	قد لا تَكُون الصفوف بنفس الارتفاع كما قد لا تَكُون الأعمدة بنفس العرض.
</p>

<p>
	يُمكِنك أن تَتَرُك فراغات بين الصفوف أو بين الأعمدة وستَظهَر خلفية الحاوية بتلك الفراغات. إذا كان <code>grid</code> عبارة عن حاوية من النوع <code>GridPane</code>، يُمكِنك استدعاء التالي لضَبْط حجم تلك الفراغات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_53" style="">
<span class="pln">grid</span><span class="pun">.</span><span class="pln">setHGap</span><span class="pun">(</span><span class="pln"> gapSize </span><span class="pun">);</span><span class="pln">  </span><span class="com">// مسافة بين الأعمدة</span><span class="pln">
gris</span><span class="pun">.</span><span class="pln">setVGap</span><span class="pun">(</span><span class="pln"> gapSize </span><span class="pun">);</span><span class="pln">  </span><span class="com">// مسافة بين االصفوف</span></pre>

<p>
	إذا أردت أن تُضيِف ابنًا (child) إلى حاوية من النوع <code>GridPane</code>، يُمكِنك استدعاء التابع التالي، والذي تستطيع من خلاله تَخْصِيص كُلًا من رقمي الصف والعمود لمَوْضِع ذلك الابن ضِمْن الشبكة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_55" style="">
<span class="pln">grid</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> child</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">,</span><span class="pln"> row </span><span class="pun">);</span></pre>

<p>
	ملحوظة: يُخصَّص رقم العمود أولًا.
</p>

<p>
	قد يَحتَل ابن (child) أكثر من مجرد صف أو عمود واحد ضِمْن الحاوية. يَستقبِل التابع <code>add</code> عدد الأعمدة والصفوف التي ينبغي لابن أن يَحتَلّها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_59" style="">
<span class="pln">grid</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> child</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">,</span><span class="pln"> row</span><span class="pun">,</span><span class="pln"> columnCount</span><span class="pun">,</span><span class="pln"> rowCount </span><span class="pun">);</span></pre>

<p>
	يُحدَّد عدد الصفوف والأعمدة ضِمْن شبكة معينة بناءً على قيم المواضع التي أُضيفت إليها الأبناء.
</p>

<p>
	تَضبُط أي حاوية من الصَنْف <code>GridPane</code> حجم كل ابن داخلها بحيث يَملئ المَوضِع أو مجموعة المَواضِع المُخصَّصة له ضِمْن شبكة الحاوية. تُضبَط القيمة المُفضَّلة لعَرْض عمود ضِمْن الشبكة لتَكُون كبيرة كفاية بما يَتَناسَب مع عَرْض جميع الأبناء ضِمْن ذلك العمود، ويَنطبِق الأمر نفسه على القيمة المُفضَّلة للارتفاع. يتَوفَّر أيضًا عدد من الطرائق لتَعْدِيل ذلك، ولكننا سنَكتفِي بذِكر إمكانية التَحكُّم بالقيمة الفعلية لكُلًا من عَرْض العمود وارتفاع الصف.
</p>

<p>
	يُمكِنك أن تُطبِّق بعض القيود (constraints) على أعمدة وصفوف حاوية من الصَنْف <code>GridPane</code> لكي تُخصِّص الكيفية التي يُحسَب على أساسها كُلًا من عَرْض العمود وارتفاع الصف. بصورة افتراضية، يُحسَب عَرْض أي عمود وفقًا لعَرْض الأبناء المُضمَّنة داخله، ولكن قد تَعيد ضَبْطُه إلى قيمة ثابتة (constant) أو قد تَحسِب قيمته كنسبة من المساحة المُتاحة. يَنطبِق الأمر نفسه على ارتفاع الصفوف. تُسنِد الشيفرة التالية قيم ثابتة (constant) لارتفاع أربعة صفوف ضِمْن حاوية من النوع <code>GridPane</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_61" style="">
<span class="pln">gridpane</span><span class="pun">.</span><span class="pln">getRowConstraints</span><span class="pun">().</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RowConstraints</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln"> </span><span class="com">// ارتفاع الصف رقم 0 يساوي 100</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RowConstraints</span><span class="pun">(</span><span class="lit">150</span><span class="pun">),</span><span class="pln"> </span><span class="com">// ارتفاع الصف رقم 1 يساوي 150</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RowConstraints</span><span class="pun">(</span><span class="lit">100</span><span class="pun">),</span><span class="pln"> </span><span class="com">// ارتفاع الصف رقم 2 يساوي 100</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RowConstraints</span><span class="pun">(</span><span class="lit">200</span><span class="pun">),</span><span class="pln"> </span><span class="com">// ارتفاع الصف رقم 3 يساوي 200</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	بالمثال الأعلى، الارتفاع الكلي للحاوية عبارة عن قيمة ثابتة بغَضْ النظر عن مقدار المساحة المُتوفِّرة.
</p>

<p>
	أما إذا اِستخدَمنا النسب (percentages)، فستتوسَّع الحاوية لتَملئ أي مساحة إضافية مُتوفِّرة، وسيُحسَب ارتفاع الصف وعَرْض العمود وفقًا لقيمة تلك النسب. بفَرْض أن <code>gridpane</code> هي كائن حاوية من الصَنْف <code>GridPane</code> مُكوَّنة من خمسة أعمدة، يُمكِننا اِستخدَام الشيفرة التالية للتأكُّد من أن الحاوية تُغطِي العَرْض المُتاح بالكامل، وكذلك للتأكُّد من أن عَرْض جميع أعمدتها متساوي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_63" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">ColumnConstraints</span><span class="pln"> constraints </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ColumnConstraints</span><span class="pun">();</span><span class="pln">
   constraints</span><span class="pun">.</span><span class="pln">setPercentWidth</span><span class="pun">(</span><span class="lit">20</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لا يوجد باني يقوم بذلكّ</span><span class="pln">
   gridpane</span><span class="pun">.</span><span class="pln">getColumnConstraints</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">constraints</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا تجاوز مجموع النسب المُخصَّصة ١٠٠، فإنها تُقلَّص بشكل مُتناسب أي أن ضَبْط قيم النسب الخمسة إلى ٥٠ له نفس تأثير ضَبْطها جميعًا إلى ٢٠. قد تُصبِح الأمور أكثر تعقيدًا من ذلك ولكن هذه هي الحالات الأكثر شيوعًا في العموم.
</p>

<p>
	يَستخدِم البرنامج <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/source/chapter6/SimpleColorChooser.java" rel="external nofollow">SimpleColorChooser.java</a> من <a data-ss1614085382="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D8%B9%D8%B6-%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1146/" rel="">القسم الفرعي ٦.٢.١</a> حاوية من الصَنْف <code>GridPane</code>. ينبغي الآن أن تَكُون قادرًا على قرائته بالكامل وفهمه.
</p>

<p>
	إذا كنت تحتاج إلى شبكة (grid) مُكوَّنة من مستطيلات مُتساوية الحجم، قد تَستخدِم حاوية من الصَنْف <code>TilePane</code> حيث يُقسَّم سطحها إلى "بلاطات (tiles)" متساوية الحجم ضِمْن صفوف وأعمدة. تُناظِر كل بلاطة (tile) عُقدة واحدة والعكس أي لا يُمكِن تَوزِيع عُقدة على أكثر من بلاطة واحدة.
</p>

<p>
	تَملُك أي حاوية <code>tpane</code> من الصَنْف <code>TilePane</code> عددًا مُفضَّلًا من الصفوف والأعمدة يُمكِنك ضَبْطُه باستدعاء ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_65" style="">
<span class="pln">tpane</span><span class="pun">.</span><span class="pln">setPrefColumns</span><span class="pun">(</span><span class="pln">cols</span><span class="pun">);</span></pre>

<p>
	عادةً ما تُعرَض الحاوية بحجمها المُفضَّل، ويعتمد عدد أعمدتها في تلك الحالة على عدد الأعمدة المفضَّل، ولكنها قد تُعرَض أحيانًا بحجم مختلف، ويَعتمِد عندها عدد الأعمدة على المساحة المتاحة. أما عدد الصفوف فيُحدَّد بناءً على عدد العقد الأبناء بالحاوية حيث تَملَئ تلك العُقد الحاوية بدايةً من صفها الأول من اليسار إلى اليمين ثُمَّ تَملَئ الصف الثاني وهكذا حتى تَنتهِي العقد. لاحظ أنه ليس من الضروري للصف الأخير أن يَكُون مملوءًا بالكامل. يُعدّ ما سبق صحيحًا إذا كان "اتجاه (orientation)" الحاوية أفقيًا أما إذا كان عموديًا، فيُفْترَض بك تَخْصِيص عدد الصفوف المُفضَّل وليس عدد الأعمدة.
</p>

<p>
	من الشائع جدًا اِستخدَام حاويات من الصَنْف <code>TilePane</code> عدد أعمدتها المُفضَّل يُساوِي واحد، وتُشبِه عندها الحاويات من الصَنْف <code>VBox</code> كما يَشيِع اِستخدَام حاويات عدد أعمدتها مُساوِي لعدد العُقد الأبناء (child nodes) داخلها، وتُشبِه في تلك الحالة الحاويات من الصَنْف <code>HBox</code>.
</p>

<p>
	عند عَرْض حاوية بحجمها المُفضَّل، يَكُون عَرْض جميع البلاطات (tiles) مُساوِيًا لأكبر عَرْض مُفضَّل لأي من أبنائها وبالمثل لارتفاع البلاطة. بالإضافة إلى ذلك، تُعيد الحاوية ضَبْط حجم كل ابن لكي يَملَئ البلاطة الخاصة به بالكامل بما هو مُتوافِق بالطبع مع القيمة العُظمى لعَرْض ذلك الابن وارتفاعه.
</p>

<p>
	يُوفِّر الصَنْف <code>TilePane</code> باني كائن (constructor) بدون أية مُعاملات (parameters) وباني آخر يَستقبِل قائمة مُكوَّنة من أي عدد من الأبناء المطلوب إضافتها للحاوية. تستطيع أيضًا إضافة أي أبناء آخرى لاحقًا باِستخدَام أي مما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_67" style="">
<span class="pln">tpane</span><span class="pun">.</span><span class="pln">getChildren</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="pln">child</span><span class="pun">);</span><span class="pln">
tpane</span><span class="pun">.</span><span class="pln">getChildren</span><span class="pun">().</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">child1</span><span class="pun">,</span><span class="pln"> child2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span></pre>

<p>
	قد تَستخدِم باني الكائن (constructor) التالي لتَخْصِيص حجم الفراغ الأفقي بين الأعمدة أو حجم الفراغ الرأسي بين الصفوف، وستَظهَر خلفية الحاوية بتلك المسافات الفارغة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8676_69" style="">
<span class="typ">TilePane</span><span class="pln"> tpane </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TilePane</span><span class="pun">(</span><span class="pln"> hgapAmount</span><span class="pun">,</span><span class="pln"> vgapAmount </span><span class="pun">);</span></pre>

<p>
	أو قد تُخصِّصها لاحقًا باستدعاء التوابع <code>tpane.setHgap(h)‎</code> و <code>tpane.setVgap(v)‎</code>.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/c6/s5.html" rel="external nofollow">Section 5: Basic Layout</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614085382="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1149</guid><pubDate>Tue, 23 Feb 2021 13:07:24 +0000</pubDate></item><item><title>&#x645;&#x643;&#x648;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x62D;&#x643;&#x645; &#x627;&#x644;&#x628;&#x633;&#x64A;&#x637;&#x629; &#x641;&#x64A; &#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x641;&#x64A; &#x645;&#x643;&#x62A;&#x628;&#x629; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX</title><link>https://academy.hsoub.com/programming/java/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%81%D9%8A-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-r1148/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034fb4acf616_--.png.e86436a7c6183a54b4aec7a02e14cbe9.png" /></p>

<p>
	تَعلَّمنا خلال الأقسام السابقة كيف نَستخدِم سياقًا رُسوميًا (graphics context) للرَسْم على الشاشة كما تَعلَّمنا كيف نُعالِج كُلًا من أحداث الفأرة (mouse events) وأحداث لوحة المفاتيح (keyboard events). من ناحية، يُمثِل ذلك كل ما هو مُتعلِّق ببرمجة واجهات المُستخدِم الرسومية (GUI) في العموم، فإذا كنت تريد الآن برمجة الرسوم ومعالجة أحداث لوحة المفاتيح والفأرة، فليس هناك أي شيء آخر تحتاج لتَعلُّمه. من ناحية آخرى، فإنك وبهذه المرحلة ستَضطرّ إما للقيام بعَمَل أكبر بكثير مما ينبغي أو ستضطرّ لبرمجة واجهات مُستخدِم (user interfaces) بسيطة. في الواقع، عادةً ما تَتَكوَّن واجهات المُستخدِم التقليدية من مُكوِّنات واجهة قياسية كالأزرار وشرائط التمرير (scroll bars) وصناديق الإِدْخَال النصية والقوائم، ولقد كُتبَت بالفعل تلك المُكوِّنات بطريقة تُمكِّنها من رَسْم نفسها ومُعالجة أحداث لوحة المفاتيح والفأرة الخاصة بها لكي لا تَضطرّ لتَكْرار ذلك.
</p>

<p>
	على سبيل المثال، أحد أبسط مُكوِّنات واجهة المُستخدِم هو "زر الضَغْط (push button)". الزر مُحاط بإطار (border) ويَعرِض نصًا معينًا يُمكِن تَغْييره. في بعض الأحيان، يَكُون الزر مُعطَّلًا (disabled) أي غَيْر مُفعَّل وعندها لا يُصبِح للنقر عليه أي تأثير كما يختلف مظهره نوعًا ما. عندما يَنقُر المُستخدِم على الزر، فإن مظهره يَتَغيَّر لحظة الضَغْط عليه ثم يَعود لمظهره العادي لحظة تَحْرِيره. لاحِظ أنه في حالة تحريك مُؤشِر الفأرة إلى خارج الزر قبل تَحْريره، سيَعُود عندها إلى مظهره العادي بذات اللحظة، كما أن تَحْريره لن يُمثِل نقرة على الزر. لتَتَمكَّن من تّنْفيذ ما سبَق، فلابُدّ أن تستجيب إما لحَدَث سَحْب الفأرة أو لحَدَث خُروج الفأرة. علاوة على ذلك، قد يُصبِح الزر مَوضِع التركيز (focus) ببعض المنصات، وعندها يَتَغيَّر مظهره، ولو ضَغَطَ المُستخدِم على مسطرة المسافات (space bar) في تلك اللحظة، فستُعدّ تلك الضغطة بمثابة نقرة على الزر. لذا لابُدّ أن يستجيب الزر لكُلًا من أحداث لوحة المفاتيح (keyboard events) والتركيز (focus events).
</p>

<p>
	في الواقع، أنت لست مُضطرًا لبرمجة أي من ذلك بِفَرْض اِستخدَامك لكائن ينتمي إلى الصَنْف القياسي <code>javafx.scene.control.Button</code>.
</p>

<p>
	يَتولَّى كائن الزر من الصَنْف <code>Button</code> مسئولية رَسْم نفسه وكذلك مُعالجة كُلًا من أحداث التركيز والفأرة ولوحة المفاتيح. ستحتاج للتَعامُل مع ذاك الزر فقط عندما يَنقُر المُستخدِم عليه أو عندما يَضغَط على مسطرة المسافات (space bar) بينما هو موضع التركيز (focus). عندما يَحدُث ذلك، سيُنشَئ كائن حَدَث (event object) ينتمي إلى الصنف <code>javafx.event.ActionEvent</code> ثم سيُرسَل إلى أي مُستمِع (listener) مُسجَّل لتبلّيغه بأن هنالك من ضَغَطَ على الزر، وعليه، سيَحصُل برنامجك فقط على المعلومة التي يحتاجها وهي أن الزر قد ضُغِطَ عليه.
</p>

<p>
	تُعرِّف واجهة برمجة تطبيقات منصة جافا إف إكس لواجهات المُستخدِم الرسومية (JavaFX GUI <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) العديد من مُكوِّنات التَحكُّم (controls) القياسية باستخدام أصناف فرعية (subclasses) من الصنف <code>Control</code> المُعرَّف بحزمة <code>package javafx.scene.control</code>. يستطيع مُستخدِم البرنامج أن يَتعامَل مع تلك المُكوِّنات مما يؤدي إلى إنتاج مُدْخَلات أو وقوع أحداث (events). تُعرِّف مُكوِّنات التَحكُّم (controls) العديد من التوابع المفيدة سنَستعرِض منها ثلاثة يُمكِن اِستخدَامها مع أي مُكوِّن تَحكُّم <code>control</code> من الصنف <code>Control</code>:
</p>

<ul>
<li>
		<code>control.setDisable(true)‎</code>: يُعطِّل (disable) مُكوِّن التحكُّم ويُمكِنك استدعاء <code>control.setDisable(false)‎</code> لتَفْعيله مرة آخرى. عندما يَكُون مُكوِّن التَحكُّم مُعطَّلًا (disabled)، يَتَغيَّر مظهره ولا يُمكِن له عندها أن يَكُون مقصدًا (event target) لأحداث المفاتيح أو الفأرة. في الواقع، لا يَقْتصِر استدعاء تلك الدالة (function) على مُكوِّنات التَحكُّم (controls) وإنما يُمكِن استدعائها لأي عقدة (node) بمبيان المشهد (scene graph). لاحِظ أنه عند تعطيل عُقدة (node) معينة، فإن جميع العُقد (nodes) المُضمَّنة داخل تلك العُقدة تُصبِح مُعطَّلة أيضًا. تَتَوفَّر الدالة <code>control.isDisabled()‎</code> والتي تُعيد قيمة منطقية تُحدِّد ما إذا كان مُكوِّن تَحكُّم (control) معين مُعطَّلًا سواء كان ذلك نتيجة لطلب تعطيله صراحةً أو لكَوْنه مُضمَّن بعُقدَة (مُكوِّن حاوي [container]) كانت قد عُطِّلَت.
	</li>
	<li>
		<code>control.setToolTipText(string)‎</code>: يَستقبِل سِلسِلة نصية ويَضبُطها لتُصبِح تلميحًا (tooltip) لمُكوِّن التَحكُّم. عادةً ما يُعطِي ذلك التلميح (tooltip) بعضًا من المعلومات عن مُكوِّن التَحكُّم وطريقة اِستخدَامه ويَظهَر عندما يَقَع مؤشر الفأرة بالمُكوِّن ولا يتحرك لعدة ثواني.
	</li>
	<li>
		<code>control.setStyle(cssString)‎</code>: يَضبُط قواعد الأنماط لمُكوِّن التَحكُّم. تُكْتَب تلك القواعد بلغة أوراق الأنماط المتعاقبة (CSS) التي ناقشناها بالقسم الفرعي ٦.٢.٥.
	</li>
</ul>
<p>
	لكي تَستخدِم مُكوِّن تحكُّم (control) أو أي عقدة آخرى بمبيان المشهد (graph node)، تحتاج عمومًا للقيام بعدة خطوات: لابُدّ أولًا أن تُنشِئ الكائن المُمثِل للمُكوِّن باستخدام باني الكائن (constructor) ثم تُضيفه إلى مُكوِّن حَاوِي (container). غالبًا ما ستحتاج إلى تسجيل مُستمِع (listener) يستجيب إلى الأحداث (events) الصادرة من ذلك المُكوِّن. قد تُخزِّن مَرجِعًا (reference) إلى المُكوِّن بمُتْغيِّر نُسخة (instance variable) في بعض الحالات لكي تَتَمكَّن من إعادة اِستخدَام المُكوِّن بَعْد إنشائه. سنَفْحَص خلال هذا القسم عددًا قليلًا من مُكوِّنات التَحكُّم (controls) القياسية البسيطة المُتاحة بمنصة جافا إف إكس (JavaFX)، والمُعرَّفة باستخدام أصناف بحزمة <code>javafx.scene.control</code>. سنَتَعلَّم بالقسم التالي كيفية وَضْع مُكوِّنات التحكُّم تلك بمُكوِّنات حاوية (containers).
</p>

<h2>
	<code>ImageView</code>
</h2>

<p>
	قبل مناقشة مُكوِّنات التحكُّم (controls)، سنتناول سريعًا نوع عُقدَة (node) آخر هو <code>ImageView</code> مُعرَّف بحزمة <code>javafx.scene.image</code>. كما ذَكَرَنا سابقًا بالقسم الفرعي ٦.٢.٣، تُمثِل الكائنات من الصنف <code>Image</code> صورًا يُمكِن تحميلها من ملفات موارد (resource files)، ولأن كائنات الصَنْف <code>Image</code> لا تُعدّ عُقدًا من النوع <code>Node</code>، لا يُمكِن لها إذًا أن تَكُون جزءًا من مبيان المشهد (scene graph)، ولهذا سنَضطرّ لرسَمها على كائن حاوية من النوع <code>Canvas</code>.
</p>

<p>
	في الواقع، يَسمَح الصنف <code>ImageView</code> بإضافة صورة إلى مبيان مشهد دون الحاجة إلى رَسْمها على حاوية (canvas)، فكائناتها عبارة عن عُقدَ مبيان مشهد (scene graph node) يَعمَل كُلًا منها كمُغلِّف (wrapper) للصورة تمهيدًا لعَرْضها. تُخصَّص الصورة كمُعامِل (parameter) إلى باني الصَنْف <code>ImageView</code>. لنَفْترِض أن مسار ملف صورة معينة هو "icons/tux.png"، يُمكِننا إذًا أن نُنشِئ كائنًا من النوع <code>ImageView</code> لعَرْض الصورة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_7" style="">
<span class="typ">Image</span><span class="pln"> tux </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">(</span><span class="str">"icons/tux.png"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">ImageView</span><span class="pln"> tuxIcon </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ImageView</span><span class="pun">(</span><span class="pln"> tux </span><span class="pun">);</span></pre>

<p>
	نحن هنا نفكر بالصورة كما لو كانت "أيقونة" أي كما لو كانت صورة صغيرة معروضة فوق زر أو ضمن عنوان نصي (label) أو بعنصر قائمة لإضافة لمسة رسومية إلى جانب النص البسيط المعروض، وهو في الواقع ما سنرى كيفية القيام به بمنصة جافا إف إكس (JavaFX).
</p>

<h2>
	العناوين <code>Label</code> والأزرار <code>Button</code>
</h2>

<p>
	سنَفْحَص الآن أربعة من مُكوِّنات التَحكُّم (controls) تتشارك جميعها في أنها تَعرِض سِلسِلة نصية للمُستخدِم يُمكِنه أن يراها لكن دون أن يُجرِي عليها أي تَعْدِيل كما يُمكِنها أيضًا أن تَعرِض عنصرًا رسوميًا (graphical element) إلى جوار تلك السِلسِلة النصية أو كبديل عنها. يُمكِن لأي كائن من النوع <code>Node</code> أن يُمثِل ذلك العنصر الرسومي، ولكن عادةً ما تُعرَض أيقونة صغيرة مُنفَّذة (implement) باستخدام كائن من النوع <code>ImageView</code>. تَرِث مُكوِّنات التحكُّم (controls) الأربعة سُلوكها (behavior) من صَنْف أعلى (superclass) مشترك هو الصنف <code>Labeled</code>. بالقسم الفرعي ٦.٦.٢، سنَفْحَص عناصر القوائم والتي تَرِث سلوكها من نفس الصَنْف. يُعرِّف الصنف <code>Labeled</code> عددًا من توابع النُسخ (instance methods) التي يُمكِننا أن نَستخدِمها مع العناوين النصية (labels) والأزرار وغيرها من مُكوِّنات التحكُّم المُعنونة، نَستعرِض بعضا منها فيما يلي:
</p>

<ul>
<li>
		<p>
			<code>setText(string)‎</code>: يَضبُط السِلسِلة النصية المعروضة بمُكوِّن التحكُّم، والتي قد تَتكوَّن من عدة أسطر. يدل محرف سطر جديد <code>‎\n</code> بسِلسِلة نصية على نهاية السطر.
		</p>
	</li>
	<li>
		<p>
			<code>setGraphic(node)‎</code>: يَضبُط العنصر الرسومي (graphical element) لمُكوِّن التحكُّم.
		</p>
	</li>
	<li>
		<p>
			<code>setFont(font)‎</code>: يَضبُط الخط المُستخدَم لرسم السِلسِلة النصية.
		</p>
	</li>
	<li>
		<p>
			<code>setTextFill(color)‎</code>: يَضبُط لون الملء المُستخدَم لرسم السِلسِلة النصية.
		</p>
	</li>
	<li>
		<p>
			<code>setGraphicTextGap(size)‎</code>: يَضبُط عَرْض المسافة الفارغة بين كُلًا من السِلسِلة النصية والعنصر الرسومي. لاحظ أن المُعامِل <code>size</code> من النوع <code>double</code>.
		</p>
	</li>
	<li>
		<p>
			<code>setContentDisplay(displayCode)‎</code>: يَضبُط موقع العنصر الرسومي بالنسبة للسِلسِلة النصية. لاحظ أن قيمة المُعامِل ستَكُون واحدة من ثوابت التعداد <code>ContentDisplay</code> أي <code>ContentDisplay.LEFT</code> أو <code>ContentDisplay.RIGHT</code> أو <code>ContentDisplay.TOP</code> أو <code>ContentDisplay.BOTTOM</code>.
		</p>
	</li>
</ul>
<p>
	يَتَوفَّر تابع جَلْب (getter methods) لكل تابع من توابع الضَبْط (setter methods) السابقة مثل <code>getText()‎</code> و <code>getFont()‎</code>. يَتَوفَّر أيضًا تابع آخر لضَبْط خاصية لون الخلفية. بفَرْض أن <code>c</code> هو مُكوِّن تحكُّم (control)، يُمكِننا اِستخدَام الشيفرة التالية لضَبْط لون خلفيته إلى الأبيض:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_9" style="">
<span class="pln">c</span><span class="pun">.</span><span class="pln">setBackground</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Background</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BackgroundFill</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">WHITE</span><span class="pun">,</span><span class="pln">null</span><span class="pun">,</span><span class="pln">null</span><span class="pun">)));</span></pre>

<p>
	حيث الأصناف <code>Background</code> و <code>BackgroundFill</code> مُعرَّفة بحزمة <code>javafx.scene.layout</code>. في الواقع، يُمكِنك القيام بنفس الشيء بطريقة أسهل باِستخدَام التابع <code>setStyle()‎</code> لضَبْط قواعد أنماط CSS الخاصة بمُكوِّن التحكُّم (control)، ويُمكِنك أيضًا اِستخدَامها لضَبْط كُلًا من الإطار (border) والحشوة (padding).
</p>

<p>
	تُمثِل العناوين من الصَنْف <code>Label</code> أحد أبسط أنواع مُكوِّنات التحكُّم، فهو لا يُضيف أي شيء تقريبًا إلى الصنف <code>Labeled</code>، ويُستخدَم لعَرْض نص غَيْر قابل للتَعْدِيل مع رسمة أو بدون. يُعرِّف الصنف <code>Label</code> بانيين (constructors)، يَستقبِل الأول مُعامِلًا من النوع <code>String</code> يُحدِّد نصًا للعنوان (label) أما الثاني فيَستقبِل مُعامِلًا إضافيًا من النوع <code>Node</code> يُحدِّد رسمة (graphic) للعنوان. بفَرْض أن <code>tuxIcon</code> هو كائن من النوع <code>ImageView</code> من القسم الفرعي السابق، اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_11" style="">
<span class="typ">Label</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Label</span><span class="pln"> linuxAd </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="str">"Choose Linux First!"</span><span class="pun">,</span><span class="pln"> tuxIcon</span><span class="pun">);</span></pre>

<p>
	لاحِظ أن خلفية العناوين (labels) من النوع <code>Label</code> تَكُون شفافة وبدون إطار (border) أو حشوة (padding) افتراضيًا. غالبًا يُفضَّل إضافة قليل من الحشوة على الأقل. اُنظر المثال التالي لضَبْط الخاصيات الثلاثة باِستخدَام لغة CSS:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_13" style="">
<span class="pln">message</span><span class="pun">.</span><span class="pln">setStyle</span><span class="pun">(</span><span class="str">"-fx-border-color: blue; -fx-border-width: 2px; "</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
                           </span><span class="str">"-fx-background-color: white; -fx-padding: 6px"</span><span class="pun">);</span></pre>

<p>
	لقد تَعرَّضنا للأزرار بالقسم ٦.١. تَعرِض الأزرار من الصنف <code>Button</code> نصًا مع رسمة أو بدون. يُعرِّف الصَنْف <code>Button</code> بانيين (constructors) تمامًا مثل الصَنْف <code>Label</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_15" style="">
<span class="typ">Button</span><span class="pln"> stopButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Stop"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Button</span><span class="pln"> linuxButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Get Linux"</span><span class="pun">,</span><span class="pln"> tuxIcon</span><span class="pun">);</span></pre>

<p>
	عندما يَضغَط المُستخدِم على زر، يَقَع حَدَث (event) من النوع <code>ActionEvent</code>، ويُستخدَم التابع <code>setOnAction</code> لتسجيل مُعالِج حدث (event handler) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_19" style="">
<span class="pln">stopButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> animator</span><span class="pun">.</span><span class="pln">stop</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	بالإضافة إلى التوابع (methods) الموروثة من الصَنْف <code>Labeled</code>، يُعرِّف الصنف <code>Button</code> توابع نُسخ (instance methods) آخرى مثل <code>setDisable(boolean)‎</code> و <code>setToolTip(string)‎</code> والتي ذَكَرناها ببداية هذا القسم. يُمكِننا أيضًا اِستخدَام التابعين <code>setDisable()‎</code> و <code>setText()‎</code> لإعطاء المُستخدِم بعض المعلومات عما يَحدُث بالبرنامج. اِستخدَامك لزر مُعطَّل (disabled) هو أفضل عمومًا من اِستخدَامك لزر يُعطِي رسالة خطأ مثل "عذرًا، لا يُمكِنك الضَغْط على الزر الآن" بَعْد الضَغْط عليه. لنَفْترِض مثلًا أن لدينا زرين لتَشْغِيل تحريكة (animation) مُمثَلة بالكائن <code>animator</code> من الصَنْف <code>AnimationTimer</code> وإيقافها، فينبغي تَعْطيل زر بدء التَشْغِيل عندما تَكُون التحريكة مُشغَّلة أما زر الإيقاف فينبغي أن يُعطَّل عندما تَكُون التحريكة مُتوقِّفة مؤقتًا. اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_21" style="">
<span class="typ">Button</span><span class="pln"> startButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Run Animation"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Button</span><span class="pln"> stopButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Stop Animation"</span><span class="pun">);</span><span class="pln">
stopButton</span><span class="pun">.</span><span class="pln">setDisable</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Stop button is initially disabled.</span><span class="pln">
startButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    animator</span><span class="pun">.</span><span class="pln">start</span><span class="pun">();</span><span class="pln">
    startButton</span><span class="pun">.</span><span class="pln">setDisable</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
    stopButton</span><span class="pun">.</span><span class="pln">setDisable</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
stopButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    animator</span><span class="pun">.</span><span class="pln">stop</span><span class="pun">();</span><span class="pln">
    startButton</span><span class="pun">.</span><span class="pln">setDisable</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
    stopButton</span><span class="pun">.</span><span class="pln">setDisable</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تتأكَّد الشيفرة بالأعلى من أن المُستخدِم لا يُمكِنه أن يحاول بدء تَشْغِيل تحريكة (animation) بينما هي مُشغَّلة بالفعل أو إيقافها عندما تَكُون مُتوقِّفة.
</p>

<p>
	غالبًا ما يُوفِّر البرنامج زرًا يؤدي لحُدوث فِعل (action) افتراضي معين. فمثلًا، قد يُدخِل المُستخدِم مجموعة بيانات بصناديق إِدْخَال نصية ثم يَنقُر على زر "Compute" لمعالجة تلك البيانات. سيَكُون من الرائع لو تَمكَّن المُستخدِم من مُجرّد الضغط على المفتاح "Return" عند انتهاءه من الكتابة بدلًا من النقر على الزر. في الحقيقة، يُمكِنك ضَبْط الكائن <code>button</code> من الصَنْف <code>Button</code> ليُصبِح زر النافذة الافتراضي (default button) باستدعاء التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_23" style="">
<span class="pln">button</span><span class="pun">.</span><span class="pln">setDefaultButton</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span></pre>

<p>
	في حالة وجود زر افتراضي (default button) بنافذة (window) معينة، فإن الضَغْط على المفتاح "Return" أو "Enter" بلوحة المفاتيح يَكُون مكافئًا للنقر على ذلك الزر الافتراضي إلا لو اُستُهلِك حَدَث (event) الضَغْط عليه من قِبَل مُكوِّن آخر.
</p>

<h2>
	مربعات الاختيار <code>CheckBox</code> وأزرار الانتقاء <code>RadioButton</code>
</h2>

<p>
	مربعات الاختيار (check box) من الصَنْف <code>CheckBox</code> هي نوع آخر من مُكوِّنات التَحكُّم. أي مربع اختيار له "حالة (state)"، فإما أن يَكُون مختارًا (selected) أو غَيْر مُختار (unselected)، ويستطيع المُستخدِم عمومًا أن يُغيِّر حالة مربع اختيار (check box) معين بالنقر عليه. تُستخدَم قيمة من النوع <code>boolean</code> لتمثيل حالة مربع الاختيار بحيث تَكُون مُساوية للقيمة المنطقية <code>true</code> إذا كان مختارًا (selected) وللقيمة المنطقية <code>false</code> إذا كان غَيْر مُختار. علاوة على ذلك، يَعرِض أي مربع اختيار (checkbox) عنوانًا (label) يُخصَّص عند إنشائه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_26" style="">
<span class="typ">CheckBox</span><span class="pln"> showTime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CheckBox</span><span class="pun">(</span><span class="str">"Show Current Time"</span><span class="pun">);</span></pre>

<p>
	لأن الصَنْف <code>CheckBox</code> هو صَنْف فرعي (subclass) من الصَنْف <code>Labeled</code>، يُمكِنك بطبيعة الحال اِستخدَام جميع توابع النُسخ (instance methods) المُعرَّفة بالصَنْف <code>Labeled</code> مع كائنات مربعات الاختيار (checkboxes). بالمثل من مُكوِّنات التَحكُّم السابقة (controls)، يُمكِن لمربعات الاختيار أن تَعرِض رسمة (graphic)، ولكنها لا تُوفِّر باني (constructor) لضَبْط تلك الرسمة وإنما لابُدّ من استدعاء <code>setGraphic(node)‎</code> لضَبْطها.
</p>

<p>
	عادةً ما يَكُون المُستخدِم هو المسئول عن ضَبْط حالة مربع اختيار من الصَنْف <code>CheckBox</code> بالنقر عليه، ولكن من الممكن أيضًا ضَبْط حالته (state) برمجيًا باستدعاء التابع <code>setSelected(boolean)‎</code>. على سبيل المثال، إذا كان لديك كائن مربع اختيار <code>showTime</code> من الصَنْف <code>CheckBox</code>، فبإمكانك اختياره (selected) باستدعاء <code>showTime.setSelected(true)‎</code> أو إلغاء الاختيار باستدعاء <code>showTime.setSelected(false)‎</code>. يَتَوفَّر أيضًا التابع <code>isSelected()‎</code> والذي يُعيد قيمة منطقية من النوع <code>boolean</code> تُحدِّد الحالة (state) الحالية لمربع الاختيار.
</p>

<p>
	عندما يُغيِّر مُستخدِم من حالة (state) مربع اختيار (checkbox) من الصَنْف <code>CheckBox</code>، يَقَع حَدَث (event) من النوع <code>ActionEvent</code>، لذا إذا أردت أن تُنفِّذ شيئًا أثناء تَغيُّر حالة مربع اختيار معين، فلابُدّ إذًا من أن تُسجِّل معالجًا (handler) بذلك المربع عن طريق استدعاء تابعه <code>setOnAction()‎</code>. ولكن في العادة، لا تحتاج البرامج لذلك وتَقْتصِر على فَحْص حالة مربعات الاختيار باستدعاء التابع <code>isSelected()‎</code>. لاحِظ أنه في حالة تَغيُّر الحالة برمجيًا أي باستدعاء التابع <code>setSelected()‎</code>، لا يَقَع حَدَث من النوع <code>ActionEvent</code>، ولكن هنالك تابع آخر مُعرَّف بالصنف <code>CheckBox</code> اسمه هو <code>fire()‎</code>، والذي يُحاكِي حُدوث نقرة على مربع الاختيار ويُولِّد حَدَثًا من النوع <code>ActionEvent</code>.
</p>

<p>
	في الواقع، هنالك حالة ثالثة لمربعات الاختيار (checkboxes): "غير مُحدَّد (indeterminate)" على الرغم من كَوْنها غَيْر مُفعَّلة افتراضيًا. اُنظر توثيق واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>) لمزيد من التفاصيل.
</p>

<p>
	تتشابه أزرار الانتقاء (radio buttons) مع مربعات الاختيار (check boxes) نوعًا ما. كما هو الحال مع مربعات الاختيار، يُمكِن لأي زر انتقاء أن يَكُون مُختارًا (selected) أو غَيْر مُختار. يُفْترَض لأزرار الانتقاء (radio buttons) أن تَقَع ضِمْن مجموعات، ويُسمَح باختيار زر انتقاء وحيد على الأكثر ضِمْن المجموعة بأي لحظة، بتعبير آخر، تَسمَح مجموعات أزرار الانتقاء للمُستخدِم باختيار وحيد من عدة بدائل. تُستخدَم كائنات الصَنْف <code>RadioButton</code> بمنصة جافا إف إكس لتمثيل أزرار الانتقاء (radio button). عند اِستخدَامها بمفردها، فإنها تكون شبيهة تمامًا بمربع اختيار من الصَنْف <code>CheckBox</code>، فلديها نفس الباني (constructor)، والتوابع (methods)، والأحداث (events) بما في ذلك التوابع الموروثة من الصنف <code>Labeled</code>. لكنها غالبًا ما تُستخدَم ضِمْن مجموعة، ويُستخدَم عندها كائن من الصنف <code>ToggleGroup</code> لتمثيل المجموعة. لاحِظ أن الصنف <code>ToggleGroup</code> ليس بمُكوِّن ولا يظهر على الشاشة، فهو يعمل فقط خلف الكواليس كمُنظم لمجموعة من أزرار الانتقاء (radio buttons) ليتأكَّد من اختيار زر واحد فقط ضِمْن المجموعة بأي لحظة.
</p>

<p>
	لكي تَستخدِم مجموعة أزرار انتقاء (radio buttons)، لابُدّ أن تُنشِئ كائنًا من الصَنْف <code>RadioButton</code> لكل زر انتقاء ضِمْن المجموعة بالإضافة إلى كائن واحد من النوع <code>ToggleGroup</code> لتنظيمها. لا يلعب الصنف <code>ToggleGroup</code> أي دور فيما يتعلَّق برسم الأزرار على الشاشة، فلابُدّ أن تُضيف كُلًا منها على حدى إلى مبيان المشهد (scene graph) لكي يَظهَر على الشاشة. لابُدّ أيضًا أن تُضيف كُلًا منها إلى كائن الصنف <code>ToggleGroup</code> من خلال استدعاء تابع النُسخة <code>setToggleGroup(group)‎</code> بكائن زر الانتقاء. إذا أردت اختيار أحد الأزرار بصورة افتراضية، يُمكِنك استدعاء التابع <code>setSelected(true)‎</code> لذلك الزر، وإن لم تَفعَل، فلن يَكُون أيًا منها مُختارًا (selected) إلى أن يَنقُر المُستخدِم على إحداها.
</p>

<p>
	على سبيل المثال، تَستعرِض الشيفرة التالية طريقة تهيئة مجموعة من أزرار الانتقاء (radio buttons) التي تَسمَح للمُستخدِم باختيار لون:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_28" style="">
<span class="typ">RadioButton</span><span class="pln"> redRadio</span><span class="pun">,</span><span class="pln"> blueRadio</span><span class="pun">,</span><span class="pln"> greenRadio</span><span class="pun">,</span><span class="pln"> yellowRadio</span><span class="pun">;</span><span class="pln">
         </span><span class="com">// تمثل المتغيرات أزرار انتقاء</span><span class="pln">
         </span><span class="com">// قد تكون تلك متغيرات نسخة لكي تستخدم عبر البرنامج</span><span class="pln">

</span><span class="typ">ToggleGroup</span><span class="pln"> colorGroup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ToggleGroup</span><span class="pun">();</span><span class="pln">

redRadio </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RadioButton</span><span class="pun">(</span><span class="str">"Red"</span><span class="pun">);</span><span class="pln">   </span><span class="com">// اِنشئ زر</span><span class="pln">
redRadio</span><span class="pun">.</span><span class="pln">setToggleGroup</span><span class="pun">(</span><span class="pln">colorGroup</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أضفه إلى مجموعة الأزرار</span><span class="pln">

blueRadio </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RadioButton</span><span class="pun">(</span><span class="str">"Blue"</span><span class="pun">);</span><span class="pln">
blueRadio</span><span class="pun">.</span><span class="pln">setToggleGroup</span><span class="pun">(</span><span class="pln">colorGroup</span><span class="pun">);</span><span class="pln">

greenRadio </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RadioButton</span><span class="pun">(</span><span class="str">"Green"</span><span class="pun">);</span><span class="pln">
greenRadio</span><span class="pun">.</span><span class="pln">setToggleGroup</span><span class="pun">(</span><span class="pln">colorGroup</span><span class="pun">);</span><span class="pln">

yellowRadio </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RadioButton</span><span class="pun">(</span><span class="str">"Yellow"</span><span class="pun">);</span><span class="pln">
yellowRadio</span><span class="pun">.</span><span class="pln">setToggleGroup</span><span class="pun">(</span><span class="pln">colorGroup</span><span class="pun">);</span><span class="pln">

redRadio</span><span class="pun">.</span><span class="pln">setSelected</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">  </span><span class="com">// Make an initial selection.</span></pre>

<p>
	بدلًا من استدعاء التابع <code>redRadio.setSelected(true)‎</code>، يُمكِنك أن تَستدعِي تابع النسخة <code>selectToggle()‎</code> المُعرَّف بالصَنْف <code>ToggleGroup</code> لاختيار زر الانتقاء (radio button) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_32" style="">
<span class="pln">colorGroup</span><span class="pun">.</span><span class="pln">selectToggle</span><span class="pun">(</span><span class="pln"> redRadio </span><span class="pun">);</span></pre>

<p>
	تمامًا كمربعات الاختيار (checkboxes)، لست مضطرًّا إلى أن تُسجِّل مُستمِعًا (listener) لأحداث أزرار الانتقاء (radio buttons). يُمكِنك فَحْص حالة (state) زر انتقاء معين من الصَنْف <code>RadioButton</code> باستدعاء تابعه <code>isSelected()‎</code> أو باستدعاء التابع <code>getSelectedToggle()‎</code> بكائن المجموعة من الصَنْف <code>ToggleGroup</code>. يُعيد ذلك التابع قيمة نوعها <code>Toggle</code> عبارة عن واجهة (interface) يُنفِّذها (implement) الصَنْف <code>RadioButton</code>. اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_34" style="">
<span class="typ">Toggle</span><span class="pln"> selection </span><span class="pun">=</span><span class="pln"> colorGroup</span><span class="pun">.</span><span class="pln">getSelectedToggle</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">selection </span><span class="pun">==</span><span class="pln"> redRadio</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">RED</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">selection </span><span class="pun">==</span><span class="pln"> greenRadio</span><span class="pun">){</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">.</span></pre>

<p>
	تُوضِح الصورة التالية كيف ستبدو أزرار الانتقاء (radio buttons) بالأعلى عند اصطفافها رأسيًا ضِمْن مُكوِّن حاوية (container):
</p>

<p style="text-align: center;">
	<img alt="001_Color_Radio_Buttons.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58457" data-unique="sfda7755x" src="https://academy.hsoub.com/uploads/monthly_2021_02/001_Color_Radio_Buttons.png.31a502e3573cebece652cac5ac1165ba.png"></p>

<h2>
	الحقول النصية <code>TextField</code> والمساحات النصية <code>TextArea</code>
</h2>

<p>
	يُمثِل الصنفان <code>TextField</code> و <code>TextArea</code> مُكوِّنات إِدْخَال نصي (text input component) حيث تَعرِض نصًا يستطيع المُستخدِم أن يُعدّله. يُمكِن لأي حقل نصي من الصنف <code>TextField</code> أن يَحمِل سطرًا واحدًا فقط أما المساحة النصية من الصَنْف <code>TextArea</code> فيُمكِنها أن تَحمِل عدة أسطر. تستطيع أن تضبط حقلًا نصيًا <code>TextField</code> معينًا أو مساحة نصية <code>TextArea</code> معينة بحيث تَكُون للقراءة فقط (read-only) أي سيَتَمكَّن المُستخدِم عندها من قراءة النص الموجود لكنه لن يَتَمكَّن من تَعْديله. في الواقع، الأصناف <code>TextField</code> و <code>TextArea</code> هي أصناف فرعية (subclasses) من صَنْف مُجرّد (abstract class) اسمه <code>TextInputControl</code> يُعرِّف بعضًا من الخاصيات المشتركة بينها.
</p>

<p>
	يشترك الصَنْفان <code>TextField</code> و <code>TextArea</code> بكثير من التوابع (methods) سنَستعرِض بعضًا منها فيما يلي: يُعيد تابع النُسخة <code>getText()</code> قيمة من النوع <code>String</code> تُمثِل محتويات مُكوِّن إِدْخَال معين. يُستخدَم تابع النسخة <code>setText(text)</code> لتَغْيير النص المعروض بمُكوِّن إِدْخَال حيث يَستقبِل مُعامِلًا من النوع <code>String</code> ويستبدله بالنص الحالي لمُكوِّن الإدخال. يُستخدَم تابع النسخة <code>appendText(text)‎</code> لإضافة سِلسِلة نصية من النوع <code>String</code> بنهاية النص الموجود بالفعل بمُكوِّن إِدْخَال. قد يَتَضمَّن النص المُمرَّر إلى التابعين <code>setText()‎</code> و <code>appendText()‎</code> محرف سطر جديد <code>‎\n</code> لتمثيل نهاية السطر، ولكنه لن يُؤثِر بالحقول النصية من الصَنْف <code>TextField</code>. يُمكِنك أيضًا أن تَستخدِم تابع النسخة <code>setFont(font)‎</code> لتَغْيير الخط المُستخدَم بمُكوِّن الإدخال النصي.
</p>

<p>
	يُمكِنك أيضًا استدعاء التابع <code>setEditable(false)‎</code> لمَنْع المُستخدِم من تَعْدِيل نص مُكوِّن إِدْخَال معين. مَرِّر القيمة المنطقية <code>true</code> لنفس التابع لكي تجعله قابل للتَعْدِيل مرة آخرى.
</p>

<p>
	يستطيع المُستخدِم أن يَكْتُب بمُكوِّن إِدْخَال نصي (input component) معين فقط بَعْد جَعَله موضع التركيز (focus) عن طريق النقر عليه بالفأرة. يُمكِنك أيضًا استدعاء التابع <code>requestFocus()‎</code> لمُكوِّن إِدْخَال -مثل الحقول النصية (text fields)- لضبطه برمجيًا ليُصبِح موضع التركيز (focus)، وهو ما قد يَكُون مفيدًا في بعض الأحيان.
</p>

<p>
	يستطيع المُستخدِم أيضًا أن يُحدِّد جزءًا من نص مُكوِّن إدخال ويَظهَر عندها مُظلَّلًا ويُمكِن قَصُه (cut) أو نَسخُه (copy) كما يَتَضمَّن الصَنْف <code>TextInputComponent</code> مجموعة من توابع النسخ (instance methods) لأغراض تَحْدِيد النصوص (text selection) منها التابع <code>selectAll()‎</code> والذي يُحدِّد نص مُكوِّن إِدْخَال بأكمله.
</p>

<p>
	على سبيل المثال، عندما تَكْتشِف وجود خطأ بالقيمة التي أَدْخَلها المُستخدِم بحَقْل نصي <code>input</code> من الصَنْف <code>TextField</code>، يُمكِنك عندها أن تَستدعِي كُلًا من التابعين <code>input.requestFocus()‎</code> و <code>input.selectAll()‎</code>، وهو ما يُساعِد المُستخدِم على اكتشاف مكان حُدوث الخطأ كما يَسمَح له ببدء تصحيحه مباشرة؛ فبمُجرّد أن يبدأ بالكتابة، سيُحذَف النص المُظلَّل.
</p>

<p>
	يُعرِّف الصَنْفين <code>TextField</code> و <code>TextArea</code> بانيين (constructors). لا يَستقبِل الباني الأول أي مُعامِلات (parameter) ويُستخدَم لإنشاء مُكوِّن إِدْخَال نصي فارغ أما الثاني فيَستقبِل مُعامِلًا من النوع <code>String</code> يُخصِّص نصًا افتراضيًا لمُكوِّن الإِدْخَال.
</p>

<p>
	تملك الحقول النصية من الصَنْف <code>TextField</code> خاصية عدد الأعمدة (columns)، والتي تُخصِّص العرض (width) المُفضّل للحقل، وتُساوِي ١٢ بشكل افتراضي. تُستخدَم تلك القيمة المُفضّلة لضَبْط حجم الحقل إذا لم يُعاد ضَبْطه بواسطة البرنامج على سبيل المثال. بفَرْض وجود كائن حقل نصي <code>input</code> من الصَنْف <code>TextField</code>، يُمكِنك استدعاء التابع <code>input.setPrefColumnCount(n)‎</code> لضَبْط عدد الأعمدة حيث <code>n</code> هي عدد صحيح موجب.
</p>

<p>
	على نحو مُشابه، تَملُك المساحات النصية من الصَنْف <code>TextArea</code> عدد أعمدة يُساوِي ٤٠ افتراضيًا وكذلك عدد صفوف يُساوِي ١٠ افتراضيًا. يُستخدَم تابعي النسخة <code>setPrefColumnCount(n)‎</code> و <code>setPrefRowCount(n)‎</code> بالصَنْف <code>TextArea</code> لتَعْدِيل قيمتهما على الترتيب.
</p>

<p>
	إلى جانب التوابع (methods) الموروثة (inherit) من الصَنْف <code>TextInputControl</code>، يُعرِّف الصَنْف <code>TextArea</code> عدة توابع آخرى بما في ذلك تابع لضَبْط مقدار المسافة التي نزلها النص، وآخر لجَلْب ذلك المقدار. مثال آخر هو التابع <code>setWrapText(wrap)‎</code> حيث <code>wrap</code> هو مُعامِل من النوع <code>boolean</code>. يُخصِّص ذلك التابع طريقة عَرْض الأسطر النصية الطويلة التي لا تَتَناسَب مع حجم المساحة النصية (text area). إذا مرَّرنا <code>true</code> كقيمة للمُعامِل <code>wrap</code>، فإنها ستُقسَّم إلى عدة أسطر مع إضافة محرف سطر جديد بين الكلمات إن أمكن. أما إذا مرَّرنا القيمة <code>false</code>، فإنها ستمتد إلى خارج المساحة النصية وسيضطرّ المُستخدِم إلى تمرير المساحة النصية أفقيًا (scroll) ليرى محتوى السطر بالكامل. لاحِظ أن قيمة <code>wrap</code> الافتراضية هي <code>false</code>.
</p>

<p>
	لمّا كانت الحاجة لتَحرِيك مساحة نصية (text area) من الصَنْف <code>TextArea</code> أفقيًا (scroll) ضرورية في أحيان كثيرة لكي نَتَمكَّن من رؤية مُحتواها النصي بالكامل، فإن ذلك الصَنْف يُوفِّر شرائط تمرير (scroll bars) تُصبِح مرئية عند الضرورة فقط أي عندما لا يَتَناسَب النص مع المساحة المتاحة.
</p>

<p>
	يُمكِنك الإطلاع على ملف البرنامج <a data-ss1614084921="1" data-ss1614085226="1" href="http://math.hws.edu/javanotes/source/chapter6/TextInputDemo.java" rel="external nofollow">TextInputDemo.java</a> والذي يُوظِّف حقلًا نصيًا (text field) ومساحة نصية (text area) ضِمْن مثال بسيط. تُبيِّن الصورة التالية نافذة البرنامج بَعْد تَعْدِيل النص وتمريره للأسفل قليلًا:
</p>

<p style="text-align: center;">
	<img alt="002_Text_Input_Demo.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58458" data-unique="r8kvpctim" src="https://academy.hsoub.com/uploads/monthly_2021_02/002_Text_Input_Demo.png.b02492b19e2cbe8a079b049e57350bf5.png"></p>

<h2>
	المنزلق <code>Slider</code>
</h2>

<p>
	يَسمَح مُنزلِق (slider) من الصَنْف <code>Slider</code> للمُستخدِم باختيار قيمة عددية صحيحة ضِمْن نطاق من القيم المُحتملة عن طريق سَحْب عُقدة على طول شريط. يُمكِنك تَزْيِين المُنزلِق بعلامات تجزئة (tick marks) أو بعناوين (labels). تَعرِض نافذة البرنامج <a data-ss1614084921="1" data-ss1614085226="1" href="http://math.hws.edu/javanotes/source/chapter6/SliderDemo.java" rel="external nofollow">SliderDemo.java</a> بالأسفل ثلاثة مُنزلِقات يَسمَح كُلًا منها بنطاق مُختلف من القيم كما أنها مُزيَّنة بشكل مختلف:
</p>

<p style="text-align: center;">
	<img alt="003Slider_Demo.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58459" data-unique="h42tff187" src="https://academy.hsoub.com/uploads/monthly_2021_02/003Slider_Demo.png.2370a251b9708e2bccbd8b568bfa8219.png"></p>

<p>
	اُستخدِمت علامات تجزئة لتَزْيِين المُنزلِق الثاني بينما اُستخدِمت عناوين لتَزْيِين المُنزلِق الثالث. يُمكِنك أيضًا تَزْيِين مُنزلِق واحد بكلتا الطريقتين معًا.
</p>

<p>
	يُخصِّص الباني (constructor) الأكثر شيوعًا بالصَنْف <code>Slider</code> بداية ونهاية نطاق القيم المُحتملة للمُنزلِق (slider) وكذلك قيمته الافتراضية عند عَرْضه على الشاشة لأول مرة. يُكْتَب ذلك الباني كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_36" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">Slider</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> minimum</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> maximum</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> value</span><span class="pun">)</span></pre>

<p>
	إذا لم نُمرِّر أي مُعامِل (parameter) للباني، ستُستخدَم القيم ٠ و ١٠٠ و ٠ افتراضيًا وعلى الترتيب. يَظهَر المُنزلِق أفقيًا بصورة افتراضية، ولكن يُمكِنك استدعاء <code>setOrientation(Orientation.VERTICAL)‎</code> لضَبْطه بحيث يَظهَر رأسيًا. لاحِظ أن <code>Orientation</code> عبارة عن تعداد مُعرَّف بحزمة <code>package javafx.geometry</code>.
</p>

<p>
	يُمكِنك قراءة القيمة الحالية لمُنزلِق (slider) من الصَنْف <code>Slider</code> بأي لحظة باستدعاء تابعه <code>getValue()‎</code> والذي يُعيد قيمة من النوع <code>double</code>. أما إذا أردت ضَبْط قيمته، فيُمكِنك استدعاء التابع <code>setValue(val)‎</code> والذي يَستقبِل مُعاملًا (parameter) من النوع <code>double</code>. إذا كانت القيمة المُمرَّرة لتابع الضَبْط واقعة خارج نطاق القيم المسموح بها، فإنها ستُعدَّل لكي تُصبِح ضِمْن ذلك النطاق.
</p>

<p>
	إذا أردت أن تُنفِّذ شيئًا عندما يُغيِّر المُستخدِم قيمة مُنزلِق (slider) معين، فينبغي أن تُسجِّل مُستمِعًا (listener) بذلك المُنزِلق. بخلاف مُكوِّنات التَحكُّم الآخرى، لا تُولِّد المنزلِقات من الصَنْف <code>Slider</code> أحداثًا من النوع <code>ActionEvent</code>، وإنما تَملُك خاصية قابلة للمُراقبة (observable) من النوع <code>Double</code> تُمثِل قيمته (انظر القيم الفرعي ٦.٣.٧). بفَرْض وجود كائن مُنزلِق <code>slider</code> من الصَنْف <code>Slider</code>، يُمكِنك استدعاء التابع <code>slider.valueProperty()‎</code> لمَعرِفة قيمته. يُمكِنك أيضًا أن تُسجِّل مُستمِعًا (listener) لتلك الخاصية سيُستدعَى بكل مرة تَتَغيَّر فيها قيمة المُنزلِق. تُبيِّن الشيفرة التالية كيفية إضافة مُستمِع (listener) لكائن مُنزلِق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_38" style="">
<span class="pln">slider1</span><span class="pun">.</span><span class="pln">valueProperty</span><span class="pun">().</span><span class="pln">addListener</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> sliderValueChanged</span><span class="pun">(</span><span class="pln">slider1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سيُستدعَى ذلك المُستمِع (listener) كلما تَغيَّرت قيمة المُنزلِق سواء كان ذلك نتيجة لسَحْب العقدة على الشريط أو لاستدعاء البرنامج للتابع <code>setValue()‎</code>. يُمكِنك استدعاء التابع <code>isValueChanging()‎</code> المُعرَّف بكائن المُنزلِق لمَعرِفة ما إذا كان المُستخدِم هو المسئول عن وقوع الحَدَث (event) حيث يُعيد ذلك التابع القيمة المنطقية <code>true</code> إذا كان المُستخدِم يَسحَب العقدة على الشريط.
</p>

<p>
	لعرض علامات التجزئة (tick marks) على مُنزلِق (slider)، ستحتاج للقيام بالخطوتين التاليتين: اُضبط أولًا الفاصل بين علامات التجزئة (tick marks) ثم بلِّغ المُنزلِق برغبتك بعَرْض علامات التجزئة. في الحقيقة، هنالك نوعين من علامات التجزئة: علامات تجزئة رئيسية (major) وأخرى ثانوية (minor)، ويُمكِنك عَرْْض واحدة منهما فقط أو كلتاهما. علامات التجزئة الرئيسية تَكُون أطول قليلًا بالمُوازنة مع علامات التجزئة الثانوية. يَستقبِل التابع <code>setMajorTickSpacing(x)‎</code> مُعامِلًا من النوع <code>double</code> ويُشير إلى أن علامات التجزئة الرئيسية ينبغي أن تُعرَض بفارق <code>x</code> من الوحدات على طول المُنزلِق. لاحِظ أن الفاصل بين تلك العلامات يَكُون على أساس قيم المُنزلِق وليس البكسل. في المقابل، يَستقبِل التابع <code>setMinorTickCount(n)‎</code> مُعامِلًا من النوع <code>int</code>، ويُخصِّص عدد علامات التجزئة الثانوية المُفْترَض عَرْضها بين كل علامتين رئيسيتين متتاليتين وتُساوِي ٤ افتراضيًا. يُمكِنك ضَبْطها إلى صفر إذا لم ترغب بعَرْض أي علامات تجزئة ثانوية. لاحِظ أن استدعاء تلك التوابع (methods) ليس كافيًا لعَرْض علامات التجزئة (tick marks)، بل ستحتاج أيضًا إلى استدعاء <code>setShowTickMarks(true)‎</code>. على سبيل المثال، تُنشِئ التَعْليمَات التالية المُنزلِق (slider) الثاني من البرنامج السابق وتَضبُطه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_41" style="">
<span class="pln">slider2 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Slider</span><span class="pun">();</span><span class="pln">  </span><span class="com">// استخدم القيم الافتراضية ‫(0,100,0)</span><span class="pln">
slider2</span><span class="pun">.</span><span class="pln">setMajorTickUnit</span><span class="pun">(</span><span class="lit">25</span><span class="pun">);</span><span class="pln"> </span><span class="com">// المسافة بين علامات التجزئة الرئيسية</span><span class="pln">
slider2</span><span class="pun">.</span><span class="pln">setMinorTickCount</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫5 علامات تجزئة بين كل علامتي تجزئة</span><span class="pln">
slider2</span><span class="pun">.</span><span class="pln">setShowTickMarks</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span></pre>

<p>
	تُعالَج العناوين (labels) على المنزلق (slider) بنفس الطريقة، فسيُعرَض العنوان بكل علامة تجزئة رئيسية، ولكن قد تُحذَف بعض العناوين إذا تداخلت مع بعضها البعض. ستحتاج إلى استدعاء <code>setShowTickLabels(true)‎</code> لعَرْض العناوين. على سبيل المثال، تُنشِئ التَعْليمَات التالية المُنزلِق (slider) الثالث من البرنامج السابق وتَضبُطه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_43" style="">
<span class="pln">slider3 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Slider</span><span class="pun">(</span><span class="lit">2000</span><span class="pun">,</span><span class="lit">2100</span><span class="pun">,</span><span class="lit">2018</span><span class="pun">);</span><span class="pln">
slider3</span><span class="pun">.</span><span class="pln">setMajorTickUnit</span><span class="pun">(</span><span class="lit">50</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لا تعرض علامات التجزئة</span><span class="pln">
slider3</span><span class="pun">.</span><span class="pln">setShowTickLabels</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span></pre>

<p>
	لاحِظ أن قيمة أي مُنزلِق (slider) تَكُون من النوع <code>double</code>. أحيانا، قد تَرغَب بقَصرِها على الأعداد الصحيحة فقط أو على مضاعفات قيمة معينة. في مثل هذه الحالات، يُمكِنك استدعاء التابع <code>slider.setSnapToTicks(true)‎</code>، وعندها ستُضبَط قيمة المُنزلِق تلقائيًا إلى أقرب علامة تجزئة رئيسية أو ثانوية -حتى لو لم تَكُن مرئية- بعدما ينتهي المُستخدِم من سَحْب العُقدَة. لا يُطبَق ذلك التقييد بينما يسَحَب المُستخدِم العُقدَة وإنما تُضبَط قيمته فقط بَعْد انتهائه. لا يَلتزِم أيضًا التابع <code>setValue(x)‎</code> بتلك القيود، لذا قد تَستخدِم التابع <code>adjustValue(x)‎</code> بدلًا عنه والذي يَضبُط قيمة المُنزِلق لأقرب علامة تجزئة. على سبيل المثال، إذا أردت لمُنزلِق أن يَقْتصِر على الأعداد الصحيحة بنطاق يتراوح من ٠ إلى ١٠، يُمكِنك اِستخدَام التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6548_45" style="">
<span class="typ">Slider</span><span class="pln"> sldr </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Slider</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
sldr</span><span class="pun">.</span><span class="pln">setMajorTickUnit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">  </span><span class="com">// major ticks 1 unit apart</span><span class="pln">
sldr</span><span class="pun">.</span><span class="pln">setMinorTickCount</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لا توجد علامة تجزئة ثانوية</span><span class="pln">
sldr</span><span class="pun">.</span><span class="pln">setSnapToTicks</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span></pre>

<p>
	ضُبِطَ المُنزلِق الثالث بالمثال التوضيحي بحيث يَقْتصِر على قيمة صحيحة بنهاية أي عملية سَحْب.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614084921="1" data-ss1614085226="1" href="http://math.hws.edu/javanotes/c6/s4.html" rel="external nofollow">Section 4: Basic Controls</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614084921="1" data-ss1614085226="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1148</guid><pubDate>Tue, 23 Feb 2021 13:01:06 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x623;&#x647;&#x645; &#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x645;&#x643;&#x62A;&#x628;&#x629; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX</title><link>https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%87%D9%85-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-r1147/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034f55d0885c_-.png.2355e298125439d1ccff52248fce71af.png" /></p>

<p>
	تعتمد برمجة واجهات المُستخدِم الرسومية (graphical user interface) اعتمادًا رئيسيًا على ما يُعرَف باسم الأحداث (events). بخلاف برامج الطرفية، لا تَحتوِي برامج واجهة المُستخدِم الرسومية (GUI) على البرنامج <code>main()‎</code> الذي اعتدنا اِستخدَامه لوَصْف ما ينبغي للبرنامج القيام به منذ لحظة تَشْغِيله خطوة بخطوة وحتى انتهائه، وإنما لابُدّ من إعداد برامج الواجهة بحيث تَتَمكَّن من الاستجابة إلى الأنواع المختلفة من الأحداث (events) التي يُمكِنها أن تَقَع بأوقات غَيْر مُتوقعة وبترتيب لا يُمكِن التَحكُّم به. تَقَع مثلًا بعض من أبسط أنواع الأحداث نتيجة لاِستخدَام الفأرة أو لوحة المفاتيح مثل أن يَضغَط المُستخدِم على مفتاح بلوحة المفاتيح أو يُحرِك الفأرة أو يَضغَط على أحد أزرار الفأرة. يستطيع المُستخدِم عمومًا أن يقوم بأي من تلك الأمور بأي لحظة ولابُدّ للحاسوب عندها من أن يستجيب بشكل مناسب.
</p>

<p>
	تُمثَل الأحداث (events) بالجافا باِستخدَام كائنات (objects)، فعندما يَقَع حَدَث معين، يُجمّع النظام كل المعلومات المُرتبِطة بذلك الحَدَث (event) ثم يُنشِئ كائنًا يَتَضمَّن تلك المعلومات. تَتَوفَّر أصناف (classes) مختلفة لتمثيل الأنواع المختلفة من الأحداث، فمثلًا، يُنشَئ كائن ينتمي للصَنْف <code>MouseEvent</code> عندما يَضغَط المُستخدِم على أحد أزرار الفأرة بينما يُنشَئ كائن ينتمي للصنف <code>KeyEvent</code> عندما يَضغَط المُستخدِم على مفتاح بلوحة المفاتيح. في حين يَحتوِي الكائن الأول على المعلومات المُتعلِّقة بحَدَث الضغط على زر الفأرة والتي تُجيب عن أسئلة مثل: أي مُكوِّن واجهة نَقر عليه المُستخدِم بالتحديد ويُعرَف ذلك المُكوِّن باسم مقصد الحَدَث (target event)؟ أي نقطة (x,y) ضِمْن المُكوِّن قد نقر عليها بالتحديد؟ هل ضَغَطَ على أحد مفاتيح التباديل (modifier keys) كالمفتاح "shift"؟ أي مفتاح تحديدًا بالفأرة ضَغَطَ عليه؟ في المقابل، يَحتوِي الكائن الآخر على المعلومات المُتعلِّقة بحَدَث الضغط على مفتاح بلوحة المفاتيح. تُمرَّر تلك الكائنات المُنشَئة كمُعامِل (parameter) إلى تابع (method) يُعرَف باسم مُعالِج الحَدَث (event handler) ويُكتَب عادةً بهيئة تعبير لامدا (lambda expressions) بمنصة جافا إف إكس (JavaFX). تُعدّ كتابة مُعالِجات الأحداث (event handler) بمثابة تبلّيغ للحاسوب بما ينبغي له القيام به عند وقوع تلك الأحداث.
</p>

<p>
	ينبغي لأي مبرمج جافا أن يَكُون على إطلاع بماهية الأحداث (events) في العموم. تَقَع كثير من الأمور منذ لحظة وقوع حَدَث (event) معين كضَغْط المُستخدِم على مفتاح معين أو كتحريكه للفأرة وحتى لحظة استدعاء التابع (method) الذي يُفْترَض له أن يَستجِيب لذلك الحَدَث. في الواقع، أنت لا تحتاج لمَعرِفة ذلك تفصيليًا، وإنما ينبغي فقط أن تفهم التالي: أثناء زمن التَشْغِيل، هُنالك برنامج (routine) بمكان ما يُنفِّذ حَلْقة التَكْرار (loop) التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_7" style="">
<span class="com">// بينما البرنامج قيد التنفيذ</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> the program is still running</span><span class="pun">:</span><span class="pln">
    </span><span class="com">// انتظر إلى أن يحين موعد الحدث التالي</span><span class="pln">
    </span><span class="typ">Wait</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the next event to occur
    </span><span class="com">// عالج الحدث</span><span class="pln">
    </span><span class="typ">Handle</span><span class="pln"> the event</span></pre>

<p>
	تُسمَى تلك الحَلْقة بحَلْقة الأحداث (event loop)، والتي لابُدّ لأي برنامج واجهة مُستخدِم رسومية (GUI program) من أن يُنفِّذها. لا تحتاج لكتابة تلك الحَلْقة بنفسك ببرامج الواجهة بلغة الجافا لأنها تُعدّ بالفعل جزءًا من النظام، ولكن قد تَضطرّ لذلك بلغة برمجية آخرى.
</p>

<p>
	سنَفْحَص خلال هذا القسم كيفية معالجة حَدَثي الفأرة (mouse event) والمفتاح (key event) بلغة الجافا كما سنُغطِي إِطار عمل معالجة الأحداث (handling events) في العموم. وأخيرًا، سنرى طريقة إنشاء تَحرِيكة (animation).
</p>

<h2>
	معالجة الأحداث
</h2>

<p>
	لكي يُصبِح لحَدَث (event) معين تأثير ضِمْن برنامج، فلابُدّ لذلك البرنامج من أن يَستمِع (listen) إلى ذاك الحَدَث (event) حتى يَتَمكَّن من رصده، ومِن ثَمَّ الإستجابة إليه. تُوكَل مُهِمّة الاستماع إلى الأحداث إلى ما يُعرَف باسم مُستمِع الحَدَث (event listener)، والذي يُعرَّف باِستخدَام واجهة (interface) تَصِف توابع مُعالجات الأحداث (event handler methods) الخاصة به والتي تَكُون مسئولة بشكل فعليّ عن الاستجابة على تلك الأحداث. غالبًا ما تُعرِّف تلك الواجهات (interface) تابع مُعالج حَدَث (event handler method) وحيد فيما يُعرَف باسم واجهات نوع الدالة (functional interface)، وفي تلك الحالة، قد يُخصَّص المُستمِع (listener) بهيئة تعبير لامدا (lambda expression). تُستخدَم واجهات (interfaces) مختلفة لتعريف مُستمعِي الأحداث (events) من الأصناف المختلفة.
</p>

<p>
	يُعرَّف مُستمِع الحدث (event listener) لغالبية الأحداث بمنصة جافا إف إكس (JavaFX) باِستخدَام واجهة نوع الدالة <code>EventHandler</code>. تُعرِّف تلك الواجهة التابع <code>handle(event)‎</code> والذي يَستقبِل كائن حَدَث (event object) من الصنف <code>Event</code> يتضمن معلومات عن ذلك الحدث. عندما تُوفِّر تعريفًا (definition) للتابع <code>handle()</code>‎، فأنت تَكْتُب الشيفرة التي ستُنفَّذ لمعالجة الحدث.
</p>

<p>
	في الواقع، الصَنْف <code>EventHandler</code> هو نوع مُعمَّم أو نوع مُحدَّد بمُعامِلات نوع (parameterized type) أي يُعرِّف عدة أنواع مختلفة: <code>EventHandler&lt;MouseEvent&gt;‎</code> و <code>EventHandler&lt;KeyEvent&gt;‎</code> و <code>EventHandler&lt;ActionEvent&gt;‎</code>. في حين يُعرِّف النوعان <code>EventHandler&lt;MouseEvent&gt;‎</code> و <code>EventHandler&lt;KeyEvent&gt;‎</code> التابع <code>handle(event)‎</code>، فإن مُعامِل ذلك التابع <code>event</code> يَكُون من الصنف <code>MouseEvent</code> بالنوع الأول ومن الصنف <code>KeyEvent</code> بالثاني. في الحقيقة، نحن لم ولن نَتعرَض للأنواع المُعمَّمة (parameterized types) حتى القسم ٧.٣، ولا حاجة لذلك عمومًا فيما هو مُتعلِّق بهذا الفصل، فكل ما أنت بحاجة لمَعرِفته هو فكرة أن كائن الحَدَث (event object) المُستخدَم للمعالجة دائمًا ما سيتناسب مع ذلك الحَدَث، فمثلًا، يَكُون كائن الحَدَث من النوع <code>MouseEvent</code> عند معالجة حَدَث فأرة.
</p>

<p>
	تَرتَبِط غالبية الأحداث (events) بمنصة جافا إف إكس (JavaFX) بإحدى مُكوِّنات واجهة المُستخدِم الرسومية (GUI components). فمثلًا، عندما يَضغَط المُستخدِم على زر بالفأرة، يُعدّ المُكوِّن المُتْضمِّن لمؤشر الفأرة عند وقوع حَدَث الضُغط مُرتبِطًا به، ويُسمَى هذا الكائن بمقصد الحَدَث (event target). لكي تَتَمكَّن من الاستجابة لحدث معين، ستحتاج إلى تسجيل مُستمِع (listener) إِما بمقصد الحَدَث (event target) أو بكائن آخر على علم بذاك الحَدَث. لنَفْحَص مثلًا التَعْليمَة التالية من برنامج <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/HelloWorldFX.java" rel="external nofollow">HelloWorldFX.java</a> من القسم ٦.١:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_9" style="">
<span class="pln">helloButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Hello World!"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	بالأعلى، <code>helloButton</code> هو كائن زر من النوع <code>Button</code>. عندما ينقر المُستخدِم على هذا الزر بالتحديد، سيقع حَدَث (event) من النوع <code>ActionEvent</code> مقصده (target) هو <code>helloButton</code>. يُسجِّل التابع <code>helloButton.setOnAction()‎</code> مُستمِع حَدَث (event listener) يُفْترَض له أن يَستقبِل تنبيهًا بكل مرة يقع فيها حَدَث من النوع <code>ActionEvent</code> من ذلك الزر. عُرِّف المُستمِع بالأعلى بهيئة تعبير لامدا (lambda expression) يُنفَّذ كاستجابة على ذلك الحَدَث عند وقوعه، وبحيث يَستقبِل كائن الحَدَث من الصنف <code>ActionEvent</code> كقيمة للمُعامِل <code>e</code>. سنُعالج غالبية الأحداث (events) ضمن هذا الفصل بنفس الطريقة.
</p>

<p>
	لا تَقْتصِر الاستجابة على بعض أحداث المفاتيح والفأرة على مقصد الحَدَثَ (event target) فقط. فمثلًا، عندما تَضغَط على زر الفأرة أثناء وجود مؤشرها بحاوية (canvas) من الصَنْف <code>Canvas</code> واقعة ضِمْن مُكوِّن حاوية من الصَنْف <code>BorderPane</code> بمشهد من الصَنْف <code>Scene</code>، يَكُون عندها مقصد الحَدَثَ هو كائن الصَنْف <code>Canvas</code>، ولكن يُمكِن أيضًا لكائني الصَنْفين <code>BorderPane</code> و <code>Scene</code> أن يستجيبا لذلك الحَدَث أي يُمكِن تَسْجيل مُستمِع الحدث (event listener) بأي من تلك الكائنات، ويُعرَف عندها هذا الكائن باسم "مصدر الحدث (event source)". يَستقبِل تابع معالجة الحدث (event handler method) مُعامِل كائن الحدث <code>evt</code> والذي يَملُك مصدر ومقصد يُعطَى باستدعاء كُلًا من <code>evt.getSource()‎</code> و <code>evt.getTarget()‎</code> على الترتيب، وغالبًا ما يكونا نفس الكائن ولكنه ليس ضروريًا. لاحِظ أنه يُمكِن إرسال نفس ذات الحدث (event) إلى أكثر من معالج (handler)، ولكن إذا استهلك أي منها الحَدَثَ باستدعاء <code>evt.consume()‎</code>، سيَتَوقَّف إرساله إلى أي معالجات آخرى. مثلًا، عندما تَكْتُب بصندوق إدخال (input box) نصي، فإنه يستهلك أحداث المفاتيح الواقعة أثناء الكتابة، حتى لا يُعطِى أي فرصة للمشهد (scene) المُتْضمِّن للصندوق لمُعالجتها.
</p>

<p>
	الأمر في الحقيقة أكثر تعقيدًا من ذلك بقليل. تنتقل بعض أحداث الفأرة والمفاتيح أولًا عبر المشهد (scene) ثم عبر عُقَد مبيان المشهد (scene graph nodes) الحاضنة لمقصد الحدث (event target)، وهو ما يُعرَف بمرحلة "ترشيح الأحداث (event filtering)" أو " (bubble down)" من معالجة الحدث. بعد وصوله للمقصد (target)، ينتقل الحدث عائدًا عبر مبيان المشهد (scene graph) ثم إلى المشهد (scene)، وهو ما يعرف بمرحلة "معالجة الأحداث (event handling)" أو "(bubble up)". قد يُستهَلك الحدث بأي نقطة وعندها تَتَوقَّف العملية. لن نتعرض لذلك خلال هذا الفصل، ولكن يمكنك الإطلاع على توثيق التابعين <code>addEventFilter()‎</code> و <code>addEventHandler()‎</code> بالصنفين <code>Scene</code> و <code>Node</code> لمزيد من المعلومات.
</p>

<p>
	سنهتم بأحداث المفاتيح (key events) والفأرة (mouse events) بهذا القسم. في الحقيقة، لا تحتاج كثير من برامج واجهات المُستخدِم الرُسومية (GUI programs) للتَعامُل مع تلك الأحداث مباشرةً، وإنما عادةً ما تَتَعامَل مع مُكوِّنات الواجهة المُبرمَجَة بالفعل لمُعالجة تلك الأحداث. فمثلًا، عندما ينقر المُستخدِم على زر من الصَنْف <code>Button</code>، فإن الزر يَكُون مُبرمَجًا بالفعل ليَستمِع لأحداث الفأرة (mouse events) ومِنْ ثَمَّ يَستجيب لها بتوليد كائن حَدَثَ من الصَنْف <code>ActionEvent</code>. لذا عند كتابة تطبيق يحتوي على أزرار، غالبًا ما ستَكْتُب معالج لأحداث الصَنْف <code>ActionEvent</code> وليس لأحداث الفأرة (mouse events). بالمثل، عندما يَكْتُب المُستخدِم داخل صندوق إِدْخَال نصي (input box)، فإن الصندوق يَكُون مُبرمَجًا بالفعل ليَستمِع لأحداث المفاتيح (key events) ومِنْ ثَمَّ يَستجِيب لها. ولكن بالنهاية سيَكُون من المفيد أن تَكُون على دراية بأحداث المفاتيح والفأرة خاصة أنها ستُمكِّنك من القيام ببعض الأمور الشيقة.
</p>

<h2>
	أحداث الفأرة (mouse events)
</h2>

<p>
	تُستخدَم كائنات من النوع <code>MouseEvent</code> لتمثيل أحداث الفأرة (mouse event)، والتي لا تقتصر في الواقع على الفأرة، فقد تقع تلك الأحداث أيضًا نتيجة لاستخدام أجهزة إِدْخَال آخرى كلوحات التتبع (trackpad) وشاشات اللمس (touch screen)، ويُترجِم النظام عندها الأحداث الناتجة عن تلك الأجهزة إلى أحداث من الصَنْف <code>MouseEvent</code>. يَقَع هذا الصَنْف بالإضافة إلى غيره من الأصناف المُتعلِّقة بأحداث المفاتيح والفأرة بحزمة <code>javafx.scene.input</code>. تَقَع أنواع مختلفة من الأحداث عند اِستخدَام الفأرة، فمثلًا، تَقَع الأحداث التالية عند النقر على زر فأرة: "mouse pressed" و "mouse released" و "mouse clicked". في المقابل، تَقَع متتالية من الأحداث عند تحريك مؤشر الفأرة من نقطة إلى أخرى على الشاشة. والآن، لكي نَتَمكَّن من الاستجابة إلى أحداث الفأرة الواقعة داخل مُكوِّن معين، يُمكِننا أن نُسجِّل مُستمِع حَدَثَ (event listener) بذلك المُكوِّن. فمثلًا، إذا كان لدينا المكون <code>c</code>، نستطيع أن نُسجِّل به مُستمِع لكل حدث مختلف باستخدام توابع نسخ (instance methods) مثل <code>c.setOnMousePressed(handler)‎</code> و <code>c.setOnMouseMoved(handler)‎</code>. تَستقبِل تلك التوابع مُعالِج حَدَث (event handler) كمُعامِل، والذي يُمرَّر عادةً بهيئة تعبير لامدا (lambda expression). لنَفْترِض أن لدينا حاوية <code>canvas</code> عبارة عن مُكوِّن من النوع <code>Canvas</code> وأننا نريد استدعاء التابع <code>redraw()‎</code> في كل مرة ينقر فيها المُستخدِم على تلك الحاوية، يُمكِننا إذًا اِستخدَام التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_11" style="">
<span class="pln">canvas</span><span class="pun">.</span><span class="pln">setOnMousePressed</span><span class="pun">(</span><span class="pln"> evt </span><span class="pun">-&gt;</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	في العموم، تُكْتَب تلك التَعْليمَة بمَتْن التابع <code>start()‎</code> المُعرَّف بالصَنْف <code>Application</code> المُمثِل للتطبيق. لاحِظ أنه من المُمكِن أيضًا مُعالجة حَدَث النقر على زر الفأرة عند وقوع المُؤشر داخل الحاوية (canvas) إما عن طريق المشهد (scene) أو عن طريق أي عقدة (node) آخرى بمبيان المشهد (scene graph) بشَّرْط أن تَكُون مُتضمِّنة لتلك الحاوية بصورة مباشرة أو غير مباشرة، ومع ذلك، عادةً ما تُوكَل مُهِمّة معالجة حََدَث (event) معين إلى مقصده (target).
</p>

<p>
	تتضمن أنواع أحداث الفأرة التالي:
</p>

<ul>
<li>
		<code>MouseEntered</code>: يقع عندما يتحرك مُؤشِر الفأرة من خارج مُكوِّن واجهة معين إلى داخل ذلك المُكوِّن.
	</li>
	<li>
		<code>MouseExited</code>: يقع عند خروج مُؤشِر الفأرة من مُكوِّن معين.
	</li>
	<li>
		<code>MousePressed</code>: يقع عندما يَضغَط المُستخدِم على أحد أزرار الفأرة.
	</li>
	<li>
		<code>MouseReleased</code>: يقع عندما يُحرِّر المُستخدِم أحد أزرار الفأرة بَعْد الضَغْط عليه.
	</li>
	<li>
		<code>MouseClicked</code>: يقع بَعْد الحدث <code>MouseReleased</code> إذا كان المُستخدِم قد ضَغَطَ على زر الفأرة وحَرَّرها داخل نفس المُكوِّن.
	</li>
	<li>
		<code>MouseDragged</code>: يقع عندما يُحرِك المُستخدِم مؤشر الفأرة بينما يَضغَط باستمرار على أحد أزرار الفأرة.
	</li>
	<li>
		<code>MouseMoved</code>: يقع عندما يُحرِك المُستخدِم مؤشر الفأرة دون الضَغْط باستمرار على أي زر من أزرار الفأرة.
	</li>
</ul>
<p>
	لاحِظ أن مقصد (target) الأحداث <code>MouseDragged</code> و <code>MouseReleased</code> و <code>MouseClicked</code> هو نفس مُكوِّن الواجهة الذي كان قد ضُغِطَ (press) عليه بالأساس حتى لو كان المُؤشِر قد خرج من ذلك المُكوِّن بينما مقصد الأحداث <code>MousePressed</code> و <code>MouseMoved</code> فهو المُكوِّن الحاوي لمُؤشِر الفأرة عند وقوع الحدث. وأخيرًا، فإن مقصد الأحداث <code>MouseEntered</code> و <code>MouseExited</code> هو المُكوِّن الذي يَدْخُل إليه مُؤشِر الفأرة أو يَخرُج منه على الترتيب.
</p>

<p>
	عند وقوع حَدَث فأرة (mouse event) معين، نحتاج عادةً إلى مَعرِفة موقع مُؤشِر الفأرة. تُتاح تلك المعلومة بكائن حَدَث الفأرة من الصَنْف <code>MouseEvent</code> والمُمرَّر كمُعامِل إلى تابع معالجة الحدث حيث يَتَضمَّن ذلك الكائن توابع نُسخ (instance methods) تُعيد معلومات عن الحَدَث (event). بفَرْض أن المُتْغيِّر <code>evt</code> هو ذلك المُعامِل، فيُمكِننا إذًا استدعاء التابعين <code>evt.getX()‎</code> و <code>evt.getY()‎</code> لمَعرِفة موقع مؤشر الفأرة حيث يعيدان قيم من النوع <code>double</code> تُمثِل كُلًا من الإحداثي <code>x</code> و <code>y</code> لموقع مؤشر الفأرة عند وقوع الحدث. يُعبَر عن نظام الإحداثيات وفقًا لمُكوِّن مصدر الحدث (event source) أي تُمثِل إحداثيات النقطة (٠،٠) ركنه الأيسر العلوي. تَذَكَّر أن مصدر الحدث هو المُكوِّن الذي سُجِّل به مُستمِع الحدث (event listener) أي أنه قد يَكُون مختلفًا عن مقصد الحدث (event target) على الرغم من ندرة حدوث ذلك.
</p>

<p>
	يستطيع المُستخدِم أن يَضغَط باستمرار على مفتاح تبديل (modifier key) واحد أو أكثر بينما يَستخدِم الفأرة. تَتَضمَّن مفاتيح التبديل المفاتيح التالية: المفتاح "Shift"، والمفتاح "Control"، والمفتاح "Alt" (أو "Option" بأجهزة ماك)، والمفتاح "Meta" (أو "Command" بأجهزة ماك). قد تَرغَب أحيانًا بالإستجابة إلى حَدَث فأرة (mouse event) معين بصورة مختلفة في حالة كان المُستخدِم يَضغَط باستمرار على مفتاح تبديل معين. يُمكِنك اختبار ذلك باستدعاء توابع النُسخ التالية <code>evt.isShiftDown()‎</code> و <code>evt.isControlDown()‎</code> و <code>evt.isAltDown()‎</code> و <code>evt.isMetaDown()‎</code> المُعرَّفة بكائن حَدَث الفأرة.
</p>

<p>
	قد ترغب بمعالجة أحداث الفأرة بشكل مختلف اعتمادًا على ما إذا كان المُستخدِم قد ضَغَطَ على زر الفارة الأيسر أم الأوسط أم الأيمن. بالنسبة لأحداث الفأرة الصادرة عن الأزرار، يُمكِنك أن تَستدعِي التابع <code>evt.getButton()‎</code> لمَعرِفة أي زر ضَغَطَ عليه المُستخدِم أو حَرَّره. يُعيد ذلك التابع أحد ثوابت أنواع التعداد (enumerated type constants) التالية: <code>MouseButton.PRIMARY</code> والذي يُمثِل زر الفأرة الأيسر أو <code>MouseButton.MIDDLE</code> أو <code>MouseButton.SECONDARY</code> الذي يُمثِل زر الفأرة الأيمن. أما بالنسبة لأحداث الفأرة غَيْر الصادرة عن الأزرار مثل <code>mouseEntered</code> و <code>mouseExited</code>، يُعيِد التابع <code>evt.getButton()‎</code> الثابت <code>MouseButton.NONE</code>.
</p>

<p>
	قد يَضغَط المُستخدِم باستمرار على أكثر من زر فأرة بنفس الوقت. إذا أردت مَعرِفة أي من أزرار الفأرة كان المُستخدِم يَضغَط عليها باستمرار أثناء وقوع حَدَث معين، يُمكِنك استدعاء الدوال <code>evt.isPrimaryButtonDown()‎</code> و <code>evt.isMiddleButtonDown()‎</code> و <code>evt.isSecondaryButtonDown()‎</code> والتي تُعيد قيم منطقية.
</p>

<p>
	كمثال بسيط، لنَفْترِض أننا نَرغَب برَسْم مستطيل أحمر اللون كلما نقر المُستخدِم على كائن حاوية <code>canvas</code> من الصَنْف <code>Canvas</code>، ولكن إذا كان المُستخدِم ضاغطًا باستمرار على المفتاح "Shift" أيضًا أثناء وقوع الحَدَث، فنُريد أن نرسم شكلًا بيضاويًا أزرق اللون. يُمكننا القيام بذلك بتعريف مُعالِج الحَدَث (event handler) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_13" style="">
<span class="pln">canvas</span><span class="pun">.</span><span class="pln">setOnMousePressed</span><span class="pun">(</span><span class="pln"> evt </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">GraphicsContext</span><span class="pln"> g </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getGraphicsContext2D</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">isShiftDown</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLUE </span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">fillOval</span><span class="pun">(</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getX</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">30</span><span class="pun">,</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getY</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">15</span><span class="pun">,</span><span class="pln"> </span><span class="lit">60</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">RED </span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getX</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">30</span><span class="pun">,</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getY</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">15</span><span class="pun">,</span><span class="pln"> </span><span class="lit">60</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سيُساعدك البرنامج <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/SimpleTrackMouse.java" rel="external nofollow">SimpleTrackMouse.java</a> على فهم كيفية عَمَل أحداث الفأرة (mouse events) بصورة أفضل. يَستمِع البرنامج لجميع أنواع أحداث الفأرة (mouse events) السبعة، ويستجيب لها بعَرْض كُلًا من إحداثيات مُؤشِر الفأرة، ونوع الحَدَث، بالإضافة إلى قائمة بمفاتيح التعديل (modifier keys) وأزرار الفأرة المضغوط عليها. يُمكِنك أيضًا أن تُجرِّب البرنامج لترى ما يَحدُث أثناء استخدامك للفأرة. يفضَّل أيضًا أن تَطَّلِع على شيفرة البرنامج المصدرية.
</p>

<h2>
	السحب (dragging)
</h2>

<p>
	يُقصَد بعملية السحب (dragging) أن يُحرِك المُستخدِم الفأرة بينما يَضغَط باستمرار على أحد أزرار تلك الفأرة. سنناقش الآن كيف يُمكِن لبرنامج معين أن يستجيب لعملية السحب (dragging). تبدأ عملية السحب عندما يَضغَط المُستخدِم على أحد أزرار الفأرة، وتستمر بينما يَسحَب تلك الفأرة، وأخيرًا تنتهي عندما يُحرِّر (release) ذاك الزر. تَتَضمَّن عملية السحب إذًا الأحداث التالية: <code>MousePressed</code> و <code>MouseDragged</code> -قد يُستدعَى أكثر من مرة أثناء تَحرِيك الفأرة- و <code>MouseReleased</code>، لذا تُقسَّم الاستجابة لأحداث السحب على ثلاثة مُعالِجات أحداث (event handlers). في مثل تلك الحالات، عادةً ما نحتاج إلى ضَبْط بعض مُتْغيِّرات النُسخ (instance variables) لنتابع ما يَحدُث بين كل استدعاء لتابع والاستدعاء الذي يليه. فمثلًا، عادةً ما يحتاج مُعالِج الحدث <code>MouseDragged</code> إلى الاحتفاظ بإحداثيات المُؤشِر من الاستدعاء السابق، ولهذا قد نُخزِّن تلك المعلومة بمُتْغيِّرات النُسخ <code>prevX</code> و <code>prevY</code> من النوع <code>double</code> كما قد نُخزِّن إحداثيات المُؤشِر المبدئية -أي عند وقوع الحدث <code>MousePressed</code>- بمُتْغيِّرات نُسخ آخرى. بالإضافة إلى ذلك، قد نُعرِّف مُتْغيِّرًا من النوع <code>boolean</code> يَحمِل الاسم <code>dragging</code> بحيث يُضبَط إلى القيمة المنطقية <code>true</code> إذا كان هنالك عملية سحب (dragging) قيد التّنْفيذ. يُعدّ ذلك ضروريًا لأنه سيُمكِّن توابع معالجة الأحداث من اختبار ما إذا كان هنالك عملية سحب قيد التّنْفيذ أم لا؛ لأنه لا ينبغي لكل حدث <code>MousePressed</code> أن يبدأ عملية سحب جديدة، وكذلك في حالة ضَغَطَ المُستخدِم على زر فأرة آخر بدون أن يُحرِّر الأول، فسيَقَع حدثان <code>MousePressed</code> قبل وقوع الحَدَث <code>MouseReleased</code>، وعندها لا ينبغي عادةً بدء عملية سحب جديدة عند وقوع الحدث <code>MousePressed</code> بالمرة الثانية. سنُعرِّف توابع نسخ (instance methods) لمعالجة الأحداث (events) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_15" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> startX</span><span class="pun">,</span><span class="pln"> startY</span><span class="pun">;</span><span class="pln"> </span><span class="com">// المَوضع الأصلي الذي نقر عليه المستخدم</span><span class="pln">
</span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> prevX</span><span class="pun">,</span><span class="pln"> prevY</span><span class="pun">;</span><span class="pln">   </span><span class="com">// إحداثيات الفأرة الأحدث</span><span class="pln">
</span><span class="kwd">private</span><span class="pln"> boolean dragging</span><span class="pun">;</span><span class="pln">      </span><span class="com">// ‫اضبطه إلى true إذا كانت هناك عملية سحب</span><span class="pln">
</span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// متغيرات نسخ أخرى</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> mousePressed</span><span class="pun">(</span><span class="typ">MouseEvent</span><span class="pln"> evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">dragging</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// يضغط المستخدم على زر الفأرة الآخر قبل أن يحرر الأول</span><span class="pln">
         </span><span class="com">// تجاهل الضغط على الزر الثاني</span><span class="pln">
       </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> we</span><span class="pun">-</span><span class="pln">want</span><span class="pun">-</span><span class="pln">to</span><span class="pun">-</span><span class="pln">start</span><span class="pun">-</span><span class="pln">dragging </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       dragging </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
       startX </span><span class="pun">=</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getX</span><span class="pun">();</span><span class="pln">  </span><span class="com">// تذكر موضع البداية</span><span class="pln">
       startY </span><span class="pun">=</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getY</span><span class="pun">();</span><span class="pln">
       prevX </span><span class="pun">=</span><span class="pln"> startX</span><span class="pun">;</span><span class="pln">       </span><span class="com">// أحدث مَوضِع</span><span class="pln">
       prevY </span><span class="pun">=</span><span class="pln"> startY</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">.</span><span class="pln"> 
          </span><span class="pun">.</span><span class="pln"> </span><span class="com">// أجري أي معالجات أخرى</span><span class="pln">
          </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> mouseDragged</span><span class="pun">(</span><span class="typ">MouseEvent</span><span class="pln"> evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// افحص أولًا إذا ما كنا نعالج عملية سحب</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> dragging </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> 
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">               
    </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getX</span><span class="pun">();</span><span class="pln"> </span><span class="com">// موضع الفأرة الحالي</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getY</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">  
      </span><span class="pun">.</span><span class="pln">  </span><span class="com">// ‫عالج حركة الفأرة من (prevX, prevY) إلى (x,y).</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">
    prevX </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تذكر الموضع الحالي للاستدعاء التالي</span><span class="pln">
    prevY </span><span class="pun">=</span><span class="pln"> y</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> mouseReleased</span><span class="pun">(</span><span class="typ">MouseEvent</span><span class="pln"> evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="com">// افحص أولًا إذا ما كنا نعالج عملية سحب</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> dragging </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">)</span><span class="pln">  
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">               
    dragging </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">  </span><span class="com">// .انهي عملية السحب</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">  </span><span class="com">// أي معالجات أخرى</span><span class="pln">
     </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنُسجِّل الآن معالجات أحداث (event handlers) بالمُكوِّنات ذات الصلة بحيث يَقْتصِر دورها على استدعاء تلك التوابع الثلاثة المُعرَّفة بالأعلى:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_17" style="">
<span class="pln">c</span><span class="pun">.</span><span class="pln">setOnMousePressed</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> mousePressed</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
c</span><span class="pun">.</span><span class="pln">setOnMouseDragged</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> mouseDragged</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
c</span><span class="pun">.</span><span class="pln">setOnMouseReleased</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> mouseReleased</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يَقْتصِر دور مُعالجات الأحداث (event handlers) بالتَعْليمَات السابقة على استدعاء تابع (method) آخر مُعرَّف ضِمْن نفس الصَنْف، والذي يَستقبِل نفس مُعامِل (parameter) مُعالج الحدث. يُمكِننا إذًا استخدام مَراجِع التوابع (method reference) -اُنظر القسم الفرعي ٤.٥.٤- لكتابة تعبيرات لامدا (lambda expressions) السابقة، ولمّا كانت التوابع المُستدعَاة عبارة عن توابع نُسخ (instance methods) مُعرَّفة بالكائن <code>this</code>، فستُستخدَم أسماء مَراجِع مثل <code>this::mousePressed</code> للإشارة إليها. يُمكِننا الآن تَسْجيل مُعالِجات الأحداث (event handlers) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_19" style="">
<span class="pln">c</span><span class="pun">.</span><span class="pln">setOnMousePressed</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">::</span><span class="pln">mousePressed </span><span class="pun">);</span><span class="pln">
c</span><span class="pun">.</span><span class="pln">setOnMouseDragged</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">::</span><span class="pln">mouseDragged </span><span class="pun">);</span><span class="pln">
c</span><span class="pun">.</span><span class="pln">setOnMouseReleased</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">::</span><span class="pln">mouseReleased </span><span class="pun">);</span></pre>

<p>
	سنَفْحَص الآن برنامجًا يَتَضمَّن واحدة من الاِستخدَامات التقليدية عملية السحب (dragging): سنَسمَح للمُستخدِم برسم منحنى عن طريق سحب الفأرة بمساحة رسم (drawing area) بيضاء كبيرة، وسنُمكِّنه أيضًا من اختيار اللون المُستخدَم للرسم من خلال النقر على أحد المستطيلات المُلوَّنة على يمين مساحة الرسم. يُمكِنك الإطلاع على شيفرة البرنامج بالكامل بالملف <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/SimplePaint.java" rel="external nofollow">SimplePaint.java</a>. تَعرِض الصورة التالية نافذة البرنامج بَعْد القيام ببعض الرسم:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614083416="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/001Simple_Paint.png.ad1197a38e298be0c295434892fad1ce.png" data-fileid="58455" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58455" data-unique="e1abqe3v0" src="https://academy.hsoub.com/uploads/monthly_2021_02/001Simple_Paint.png.ad1197a38e298be0c295434892fad1ce.png" alt="001Simple_Paint.png"></a>
</p>

<p>
	سنُناقش فقط بعضًا من أجزاء الشيفرة المصدرية للبرنامج وليس كلها، ولكن يُفضَّل أن تقرأها كاملة بتأني وستَجِد الكثير من التعليقات (comments) التوضيحية بالشيفرة.
</p>

<p>
	بهذا البرنامج، تَقَع عملية الرسم ضِمْن حاوية (canvas) واحدة تُغطِي النافذة بالكامل. صُمّم البرنامج ليتناسب مع أي حجم للحاوية بشَّرْط ألا تَكُون صغيرة أكثر من اللازم. في الحقيقة، يُصعِب ذلك الأمور قليلًا عما لو كنا قد اِفترَضنا حجمًا ثابتًا للحاوية (canvas)؛ فلابُدّ لنا الآن من حساب قيم الإحداثيات وفقًا لعَرْض الحاوية وارتفاعها. يُمكِننا مَعرِفة عَرْض الحاوية وارتفاعها باستدعاء التابعين <code>canvas.getWidth()‎</code> و <code>canvas.getHeight()‎</code> على الترتيب. سنَفْحَص الآن بعضًا من تلك الحسابات: تمتد مساحة الرسم (drawing area) البيضاء الكبيرة من <code>y = 3</code> إلى <code>y = height - 3</code> رأسيًا، ومن <code>x = 3</code> إلى <code>x = width - 56</code> أفقيًا. تُراعِي تلك الحسابات وجود إطار رمادي حول الحاوية (canvas) بعَرْض يَبلُغ ٣ بكسل، وكذلك وجود لوحة الألوان على طول الحافة اليُمنَى للحاوية، والتي يَبلُغ عَرْضها ٥٠ بكسل مع ٣ بكسل للإطار و٣ بكسل للحاجز الفاصل بين مساحة الرسم ولوحة الألوان. وفقًا لذلك، تَبعُد الحافة اليُمنى لمساحة الرسم (drawing area) مسافة قدرها ٥٦ بكسل عن الحافة اليمنى للحاوية (canvas).
</p>

<p>
	يوجد مربع أبيض مكتوب عليه كلمة "CLEAR" أسفل لوحة الألوان الواقعة على طول الحافة اليُمنَى للحاوية (canvas). باستبعاد ذلك المربع، نستطيع أن نَحسِب المسافة الرأسية المُتاحة لجميع المستطيلات المُلوَّنة ثم نُقسِّمها على ٧ لنَحصُل على قيمة المسافة الرأسية المُتاحة لكل مستطيل على حدى بحيث تُخزَّن تلك القيمة بالمُتْغيِّر <code>colorSpace</code>. بالإضافة إلى ذلك، يَفصِل فراغ عَرْضه ٣ بكسل بين كل مستطيل وآخر، ولهذا فإن ارتفاع كل مستطيل يُساوِي <code>colorSpacing - 3</code>. بفَرْض بِدء عدّ المستطيلات من الصفر، ستَبعُد إذًا الحافة العلوية لمستطيل <code>N</code> مسافة قدرها <code>N*colorSpacing + 3</code> بكسل عن الحافة العُلوية للحاوية (canvas)؛ وذلك لوجود عدد <code>N</code> من المستطيلات أعلى المستطيل <code>N</code> يَبلُغ ارتفاع كُلًا منها قيمة تُساوِي <code>colorSpace</code> بكسل بالإضافة إلى ٣ بكسل لإطار الحافة العلوية للحاوية (canvas). نستطيع الآن كتابة الأمر اللازم لرسم مستطيل <code>N</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_21" style="">
<span class="pln">g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="pln">width </span><span class="pun">-</span><span class="pln"> </span><span class="lit">53</span><span class="pun">,</span><span class="pln"> N</span><span class="pun">*</span><span class="pln">colorSpace </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> colorSpace </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	لم يكن ذلك سهلًا! ولكنه يُوضِح نوعية التفكير والدقة المطلوبين في بعض الأحيان للحصول على نتائج جيدة.
</p>

<p>
	تُستخدَم الفأرة بهذا البرنامج لثلاثة أغراض مختلفة هي: اختيار اللون، ومسح الرسوم، ورسم المنحنى. تَشتمِل الأخيرة فقط على عملية سحب (dragging) أي لا تُؤدي كل نقرة على الفأرة إلى بدء عملية سحب. سيَفْحَص التابع <code>mousePressed()‎</code> إحداثيات النقطة (x,y) التي نقر عليها المُستخدِم ليُقرِّر كيفية الاستجابة. فمثلًا، إذا نقر المُستخدِم على المستطيل "CLEAR"، فستُفرَّغ مساحة الرسم (drawing area) باستدعاء التابع <code>clearAndDrawPalette()‎</code> والذي يُعيد رَسْم الحاوية (canvas) بالكامل. أما إذا نقر المُستخدِم بموضع آخر داخل لوحة الألوان، فلابُدّ للمعالج من أن يُحدِّد اللون الذي نقر عليه المُستخدِم وذلك بقسمة الإحداثي <code>y</code> على قيمة المُتغيِّر <code>colorSpacing</code>، ثم يُخصِّص ذلك اللون لعملية الرسم. أخيرًا، إذا نقر المُستخدِم على مساحة الرسم (drawing area)، فستبدأ عملية سحب (drag)، وسيُضبَط المُتْغيِّر المنطقي <code>dragging</code> في تلك الحالة إلى القيمة <code>true</code> مما يُمكِّن التابعين <code>mouseDragged</code> و <code>mouseReleased</code> من مَعرِفة أن هنالك عملية رسم منحنى قيد التّنْفيذ. تُوكَل مُهِمّة الرسم الفعليّ للمنحنى للتابع <code>mouseDragged()‎</code> والذي يَرسِم خطًا من الموضع السابق لمُؤشِر الفأرة وحتى موضعه الحالي. سنحتاج أيضًا إلى التأكُّد من أن الخط المرسوم لن يمتد إلى ما بَعْد مساحة الرسم (drawing area) البيضاء داخل الحاوية (canvas)، وهو ما لا يَحدُث تلقائيًا؛ لأن الحاسوب يَنظُر للإطار وللوحة الألوان على أساس كَوْنهما جزءًا من الحاوية (canvas). سيَضطرّ التابع <code>mouseDragged()‎</code> إذًا إلى تَعْديل قيم كُلًا من الإحداثي <code>x</code> والإحداثي <code>y</code> بحيث يَقعَا ضِمْن نطاق مساحة الرسم إذا سحب المُستخدِم مؤشر الفأرة إلى خارج مساحة الرسم البيضاء أثناء رسمه لمنحنى.
</p>

<h2>
	أحداث المفاتيح (key events)
</h2>

<p>
	تُصبِح أفعال (actions) المُستخدِم في العموم أحداثًا (events) بالبرنامج وبحيث يَكُون مُكوِّن واجهة (GUI component) معين هو مقصد ذلك الحدث (event target). فمثلًا، عندما يَضغَط المُستخدِم على أي من أزرار الفأرة، يُعدّ المُكوِّن المُتَضمِّن لمؤشر الفأرة هو مقصد الحدث. والآن، ماذا عن أحداث لوحة المفاتيح (key events)؟ فمثلًا، عندما يَضغَط المُستخدِم على مفتاح، فأي مُكوِّن واجهة يَكُون مقصدًا للحدث <code>KeyEvent</code>؟
</p>

<p>
	تَعتمِد واجهة المُستخدِم الرُسومية (GUI) على فكرة التركيز (input focus) لتَحْديد مقصد أحداث (event target) لوحة المفاتيح. بأي لحظة، يُمكِن أن يَقَع التركيز على عنصر واجهة (interface element) واحد فقط على الشاشة وبحيث تُوجَّه إليه أحداث لوحة المفاتيح (keyboard events). إذا كان ذلك العنصر مُكوِّن بمنصة جافا إف إكس (JavaFX component)، يُنشَئ كائن من النوع <code>KeyEvent</code> يَتَضمَّن معلومات عن حدث لوحة المفاتيح ثم يُرسَل لأي من مُعالِجات أحداث المفاتيح (key event handlers) المُسجَّلة بذلك المُكوِّن والمُستمعِة للأحداث من الصَنْف <code>KeyEvent</code>. نظرًا للكيفية التي تُعالَج بها أحداث المفاتيح (key events)، يَحصُل كائن المشهد من الصنف <code>Scene</code> بالنافذة المُتْضمِّنة للمُكوِّن الواقع عليه التركيز على فرصة لمعالجة أحداث المفاتيح بل قد يُصبِح مقصدًا (event) لها إذا لم يَكُن هناك أي مُكوِّن آخر واقع عليه التركيز بالنافذة. سنُضيف خلال البرامج التالية مُعالِجات أحداث المفاتيح (key event handlers) إلى كائن المشهد.
</p>

<p>
	عادةً ما يُعطِي البرنامج ملحوظة مرئية للمُستخدِم عن مُكوِّن الواجهة الواقع عليه التركيز (input focus). فعلى سبيل المثال، إذا كان المُكوِّن عبارة عن صندوق إدخال نصي، قد تكون تلك الملحوظة بهيئة مُؤشِر وامض أو برَسْم إطار مُلوَّن حول حافة ذلك المُكوِّن، وهو ما قد تلاحظه بمُكوِّنات الأزرار، ويُكافئ عندها الضغط على مفتاح مسطرة المسافات (space bar) النقر على الزر.
</p>

<p>
	لنَفْترِض أن <code>comp</code> هو مُكوِّن واجهة ترغب بأن يقع عليه التركيز (input focus)، يُمكِنك عندها أن تَستدعِي <code>comp.requestFocus()‎</code>. بأي واجهة مُستخدِم (user interface) تقليدية، عندما يَنقُر المُستخدِم على مُكوِّن معين باستخدام الفأرة، يقع التركيز على ذلك المُكوِّن تلقائيًا كما ينتقل التركيز من مُكوِّن واجهة إلى آخر بكل مرة يَضغَط فيها المُستخدِم على المفتاح "tab". يَحدُث ذلك تلقائيًا في غالبية الأحوال ودون الحاجة لإجراء أي برمجة، ولكن لا تَطلُب بعض المُكوِّنات أن يقع عليها التركيز تلقائيًا بعد نقر المُستخدِم عليها مثل الصَنْف <code>Canvas</code>، وعندها لابُدّ من استدعاء التابع <code>requestFocus()‎</code> بكائن ذلك الصنف حتى يُصبِح مَوضِع التركيز وكذلك لابُد من استدعاء <code>comp.setFocusTraversable(true)‎</code> لتفعيل خاصية انتقال التركيز التلقائي للمُكوِّن عند الضغط على المفتاح "tab". تستطيع أيضًا اِستخدَام التابع <code>comp.isFocused</code> لاختبار ما إذا كان التركيز واقعًا على ذلك المُكوِّن.
</p>

<p>
	تُعدّ النافذة التي تَتَضمَّن المُكوِّن الواقع عليه التركيز نافذة "مركزة (focused)" أو "نشطة (active)" وعادةً ما تَكُون النافذة الأمامية بالشاشة. بمنصة جافا إف إكس (JavaFX)، يُمثِل كائن المرحلة من الصَنْف <code>Stage</code> نافذة، ويُمكِنك أن تَستدعِي <code>stage.requestFocus()‎</code> لتَطلُب نقلها إلى مقدمة الشاشة لكي تُصبِح "نشطة (active)" كما يُمكِنك أن تَستدعِي <code>stage.isFocused()‎</code> لاختبار ما إذا كانت نشطة.
</p>

<p>
	تُفرِّق الجافا بين المفاتيح التي تَضغَط عليها والمحارف (characters) التي تَكْتُبها بصورة واضحة. هنالك الكثير من المفاتيح بأي لوحة مفاتيح: مفاتيح الحروف، ومفاتيح الأعداد، ومفاتيح التَعْدِيل (modifier keys) مثل "Control" و "Shift"، ومفاتيح الأسهم، ومفتاحي "صفحة لأعلى" و"صفحة لأسفل"، ومفاتيح الوظائف (function keys)، وغيره. أحيانًا لا يؤدي الضَغْط على مفتاح معين إلى كتابة محرف كالمفتاح "shift". في المقابل، أحيانًا ما تَتَطلَّب كتابة محرف واحد الضَغْط على أكثر من مفتاح. فمثلًا، تَتَطلَّب كتابة الحالة الكبيرة من الحرف "A" الضغط باستمرار على المفتاح "Shift" ثُمَّ الضَغْط على المفتاح "A" وأخيرًا تحرير المفتاح "Shift". كذلك تَتَطلَّب كتابة "é" بأجهزة ماك الضَغْط باستمرار على المفتاح "Option" ثم الضَغْط على المفتاح "E" ثم تحرير المفتاح "Option" وأخيرًا الضغط على "E" مجددًا. كُتِبَ بالنهاية محرف واحد فقط على الرغم من الحاجة للضغط على ثلاثة مفاتيح مع تحرير إحداها بالوقت المناسب.
</p>

<p>
	بمنصة جافا إف إكس، هنالك ثلاثة أنواع من أحداث المفاتيح (key event):‏ <code>KeyPressed</code> و <code>KeyReleased</code> و <code>KeyTyped</code>. يَقَع الأول عندما يَضغَط المُستخدِم على أي مفتاح بلوحة المفاتيح أما الثاني فيَقَع عندما يُحرِّر مفتاحًا كان قد ضَغَطَ عليه أما الثالث فيَقَع عندما يَكْتُب محرفًا سواء كان ذلك نتيجة لضَغْطه على مفتاح واحد أو أكثر. قد يُؤدي فِعْل (action) واحد كالضَغْط على المفتاح "E" إلى وقوع الحَدَثَين <code>keyPressed</code> و <code>keyTyped</code>. على سبيل المثال، تُؤدي كتابة الحالة الكبيرة من الحرف "A" إلى وقوع حدثين <code>keyPressed</code> وآخرين <code>keyReleased</code> بالإضافة إلى حَدَث <code>keyTyped</code>.
</p>

<p>
	في العموم، يُفضَّل أن تُفكِّر بوجود مجريين منفصلين من الأحداث (events) الأول يحتوي على أحداث <code>keyPressed</code> و <code>keyReleased</code> والآخر يَتَضمَّن أحداث <code>keyTyped</code> بحيث تستمع بعض أنواع التطبيقات إلى أحداث المجرى الأول بينما تستمع تطبيقات آخرى إلى أحداث المجرى الثاني. يُمكِنك استخراج نفس المعلومات الموجودة بمجرى أحداث <code>keyTyped</code> من مجرى أحداث <code>keyPressed/keyReleased</code> ولكنها عملية صعبة نوعًا ما كما أنها تعتمد على النظام إلى درجة معينة. لاحظ أن بعض أفعال المُستخدِم (user actions) -مثل الضغط على المفتاح "Shift"- يُمكِن فقط رصدها بهيئة أحداث <code>keyPressed</code>. على سبيل المثال، تُميِّز لعبة الحاسوب Solitaire ورق اللعب الذي يُمكِن نقله عند الضَغْط باستمرار على المفتاح "Shift". يُمكِنك القيام بذلك بالجافا عن طريق تَمْييز ورق اللعب عند الضَغْط على المفتاح "Shift" ثم إلغاء ذلك التَمْييز عند تحرير المفتاح.
</p>

<p>
	لاحِظ أيضًا أنه عند الضَغْط باستمرار على مفتاح معين، فقد يتكرَّر ذلك المفتاح تلقائيًا أي ستَقَع متتالية من أحداث <code>KeyPressed</code> متبوعة بحَدَث <code>KeyReleased</code> وحيد بالنهاية كما قد تَقَع أيضًا متتالية من أحداث <code>KeyTyped</code>. لن يُؤثِر ذلك في الغالب على أسلوب كتابة البرامج ولكن لا تَفْترِض أن كل حَدَث <code>KeyPressed</code> لابُدّ وأن يُقابله حَدَث <code>KeyReleased</code>.
</p>

<p>
	كل مفتاح بلوحة المفاتيح له ترميز (key code) يُميِّزه والتي تُمثَل بمنصة جافا إف إكس (JavaFX) باستخدام ثوابت التعداد <code>KeyCode</code>. عندما يُستدعَى مُعالِج الحَدَث <code>KeyPressed</code> أو <code>KeyReleased</code>، فإن المُعامِل <code>evt</code> يحتوي على ترميز المفتاح المضغوط عليه أو المُحرَّر على الترتيب، ويُمكِننا مَعرِفته باستدعاء الدالة <code>evt.getCode()‎</code>. على سبيل المثال، عندما يَضغَط المُستخدِم على المفتاح "shift"، تُعيد تلك الدالة القيمة <code>KeyCode.SHIFT</code>. يُمكِنك الإطلاع على توثيق التعداد <code>KeyCode</code> لمَعرِفة اسم الثابت لترميز مفتاح معين مع أنه من السهل تَخْمِين أسمائها عمومًا. فمثلًا، مفاتيح الحروف لها أسماء مثل <code>KeyCode.A</code> و <code>KeyCode.Q</code> بينما مفاتيح الأسهم لها أسماء مثل <code>KeyCode.LEFT</code> و <code>KeyCode.RIGHT</code> و <code>KeyCode.UP</code> و <code>KeyCode.DOWN</code> أما مفتاح مسطرة المسافات (space bar) هو <code>KeyCode.SPACE</code> وأخيرًا مفاتيح الوظائف لها أسماء مثل <code>KeyCode.F7</code>.
</p>

<p>
	في حالة الحَدَث <code>KeyTyped</code>، سترغب عادةً بمَعرِفة المحرف المَكْتُوب لذا يُمكِنك استدعاء الدالة <code>evt.getCharacter()‎</code> لمَعرِفة ذلك حيث تُعيد قيمة من النوع <code>String</code> تَحتوِي على المحرف المَكْتُوب.
</p>

<p>
	كأول مثال، يَرسِم البرنامج <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/KeyboardEventDemo.java" rel="external nofollow">KeyboardEventDemo.java</a> مربعًا صغيرًا داخل حاوية (canvas). يستطيع المُستخدِم أن يُحرِك ذلك المربع إلى اليسار أو إلى اليمين أو إلى أعلى أو إلى أسفل عن طريق الضَغْط على مفاتيح الأسهم (arrow keys). نُفِذَ (implement) ذلك بالتابع التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_23" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> keyPressed</span><span class="pun">(</span><span class="pln"> </span><span class="typ">KeyEvent</span><span class="pln"> evt </span><span class="pun">)</span></pre>

<p>
	سيُستدعَى التابع بالأعلى بواسطة مُعالِج حَدَث (event handler) يَستمِع لأحداث <code>KeyPressed</code>. تُسجِّل التَعْليمَة التالية ذلك المُعالِج بكائن المشهد من الصَنْف <code>Scene</code> بمَتْن التابع <code>start()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_25" style="">
<span class="pln">scene</span><span class="pun">.</span><span class="pln">setOnKeyPressed</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> keyPressed</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يَفْحَص التابع <code>keyPressed()‎</code> قيمة الدالة <code>evt.getCode()‎</code>، فإذا كان المفتاح المضغوط عليه واحدًا من مفاتيح الأسهم (arrow keys)، يُعاد رسم الحاوية (canvas) بحيث تُظهِر المربع بمَوضِع مختلف.
</p>

<p>
	يُسجِّل البرنامج أيضًا مُعالِجات (handlers) للأحداث <code>KeyReleased</code> و <code>KeyTyped</code> بنفس الكيفية. فمثلًا، سيُغيِّر مُعالِج الحَدَث <code>KeyTyped</code> لون المربع عندما يَكْتُب المُستخدِم الحرف "r" أو "g" أو "b" أو "k". اِحرص على قراءة الشيفرة الكاملة للبرنامج بتأني وجرِّب تَشْغِيله.
</p>

<h2>
	الصنف <code>AnimationTimer</code>
</h2>

<p>
	سنناقش الآن نوعًا آخر من الأحداث البسيطة المستخدمة بالتحريك الحاسوبي (animation). تقع الأحداث (events) في هذه الحالة بالخلفية لذا أنت لا تكون مضطرًا لتسجيل مستمع (listener) ليستجيب لها، ولكن ستحتاج إلى كتابة تابع (method) يستدعيه النظام عند وقوع تلك الأحداث.
</p>

<p>
	يَعتمِد التَحرِيك الحاسوبي (computer animation) على متتالية من الصور الثابتة المُنفصلة، يُطلَق على كُل منها اسم الإطار (frame). تُعرَض هذه الصور بشكل سريع واحدة تلو الآخرى، فإذا كان التَغْيِير بين كل صورة والصورة التي تَليها طفيفًا، ستبدو متتالية الصور وكأنها تَحرِيكة مُستمرة (continuous animation). بمنصة جافا إف إكس (JavaFX)، تُستخدَم كائنات من النوع <code>AnimationTimer</code> المُعرَّف بحزمة <code>javafx.animation</code> لبرمجة تحريكة (animation). بفَرْض أن الكائن <code>animator</code> من ذلك الصنف، فيُمكِننا استدعاء التابع <code>animator.start()‎</code> لبدء التحريكة إذا كانت مُتوقِّفة أو لإعادة تَشْغِيلها إذا كانت مشغَّلة. يُمكِننا أيضًا استدعاء التابع <code>animator.stop()‎</code> لإيقاف التحريكة. يَتَضمَّن الصنف التابع <code>handle(time)‎</code> والذي لا يُمكِنك استدعائه وإنما ينبغي أن تَكْتُب تّنْفيذه لتُحدِّد ما ينبغي أن يَحدُث بالتَحرِيكة. سيَستدعِي النظام ذلك التابع مرة واحدة لكل إطار (frame) بالتحريكة لكي يُنفِّذ كل ما هو ضروري لتّنْفيذ الإطار.
</p>

<p>
	يَستدعِى النظام التابع <code>handle()‎</code> بخيط تطبيق جافا إفا إكس (JavaFX application thread) مما يَعنِي إمكانية الرسم على حاوية (canvas) أو مُعالجة مُكوِّن واجهة (GUI component) أو القيام بأي أشياء آخرى مشابهة بشَّرْط ألا تَستغرِق وقتًا طويلًا؛ لأن التحريكات بمنصة جافا إف إكس (JavaFX) تُنفَّذ بمُعدل ٦٠ إطار لكل ثانية أي يُستدعَى التابع <code>handle()‎</code> كل ١\٦٠ ثانية.
</p>

<p>
	ملحوظة: بُلِّغ عن وجود خطأ برمجي (bug) قد يؤدي أحيانًا إلى استدعاء التابع <code>handle()‎</code> أكثر من ٦٠ مرة بالثانية، ولذلك أضفنا بعض الشيفرة إلى تّنْفيذ (implementation) الصَنْف <code>AnimationTimer</code> لنتجنَّب ذلك الخطأ البرمجي وأرفقنا معها تعليقات توضيحية.
</p>

<p>
	لاحِظ أن الصَنْف <code>AnimationTimer</code> هو صَنْف مُجرّد (abstract class) كما أن التابع <code>handle()‎</code> هو تابع مُجرّد (abstract method) لذا ستحتاج إلى كتابة صَنْف فرعي (subclass) من <code>AnimationTimer</code> يُعرِّف التابع <code>handle()‎</code> لتَتَمكَّن من برمجة تَحرِيكة (animation). لنَفْترِض أنك ترغب باستدعاء تابع اسمه <code>draw()‎</code> بكل إطار بالتحريكة، يُمكِنك القيام بذلك باِستخدَام صَنْف فرعي مجهول الاسم (anonymous subclass) من الصَنْف <code>AnimationTimer</code> كالتالي (اُنظر القسم الفرعي ٥.٨.٣):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_27" style="">
<span class="typ">AnimationTimer</span><span class="pln"> animator </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AnimationTimer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> handle</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> time </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        draw</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	سنحتاج الآن إلى استدعاء <code>animator.start()‎</code> لتَشْغِيل التَحرِيكة، وهو ما يُمكِنك القيام به بمَتْن تابع التطبيق <code>start()‎</code>.
</p>

<p>
	يُمثِل المُعامِل <code>time</code> -بالأعلى- الوقت الحالي مُقاس بحساب الفارق بين الوقت الحالي ووقت آخر عشوائي بالماضي بوحدة النانوثانية. يُمكِنك اِستخدَام <code>time</code> ضِمْن الحسابات المطلوبة لرسم الإطار (frame) كطريقة لتّمْيِيز كل إطار عما يليه.
</p>

<h2>
	آلات الحالة (state machines)
</h2>

<p>
	نحن الآن جاهزين لفحص برنامج يوظف كلا من التحريك (animation) وأحداث المفاتيح (key events) لتنفيذ لعبة بسيطة. يستخدم البرنامج الصنف <code>AnimationTimer</code> لإدارة التحريك كما يستخدم عددًا من متغيرات النسخ (instance variables) لمتابعة "الحالة (state)" الحالية للعبة.
</p>

<p>
	تُعبِر القيم المُخزَّنة بمُتْغيِّرات نُسخ (instance variables) كائن معين عن "حالة (state)" ذلك الكائن. عند استدعاء إحدى توابعه، قد يَعتمِد ما يُنفِّذه الكائن حينها على حالته، أو بتعبير آخر، يُمكِن للتابع أن يَفْحَص قيم مُتْغيِّرات النُسخ (instance variables) ليُقرِّر ما ينبغي له القيام به. قد تَتَغيَّر حالة الكائن، أو بتعبير آخر، يُمكِن للتابع أن يُسنِد (assign) قيم جديدة لمُتْغيِّرات النُسخ. هنالك فكرة بعلوم الحاسوب (computer science) تُعرَف باسم "آلة الحالة (state machine)"، وهي تُعبِر عن شيء ذات حالة (state)، والتي قد تَتغيَّر استجابة لبعض الأحداث (event) أو المُدْخَلات (input)، وبحيث تَعتمِد كيفية استجابة الآلة لحَدَث معين على حالتها عند وقوع ذلك الحَدَث. في الواقع، يُعدّ الكائن بمثابة آلة حالة (state machine)، وأحيانًا ما تَكُون وجهة النظر تلك مفيدة أثناء تصميم الأصناف.
</p>

<p>
	تبرز أهمية وجهة النظر تلك ببرمجة واجهات المُستخدِم الرُسومية (graphical user interfaces) والتي تعتمد على فكرة الأحداث (events) اعتمادًا كليًا. عند تصميم برنامج واجهة، ينبغي أن تُفكِّر بالأسئلة التالية: أي معلومات ينبغي تَضْمِينها ضِمْن حالة (state) البرنامج؟ ما هي الأحداث التي يُمكِنها أن تُغيِّر من حالة البرنامج؟ كيف ستعتمد استجابة حَدَث معين على حالة البرنامج الحالية؟ هل ينبغي تَغْيير مَظهَر الواجهة ليَعكِس تَغْييرًا بحالة البرنامج؟ كيف ستُؤثِر حالة البرنامج على رسم محتويات الحاوية (canvas)؟ يُعدّ ذلك كله بمثابة بديل عن نهج التصميم المتدرج (step-wise-refinement) الذي يقع ضِمْن "الاستراتيجيات من أعلى لأسفل (top-down)"، وهو ما لا يُطبَق على كيفية تصميم برنامج حَدَثي التوجه (event-oriented).
</p>

<p>
	بالبرنامج <code>KeyboardEventDemo</code> -بالأعلى-، تُخزَّن حالة (state) البرنامج بمُتْغيِّرات نُسخ (instance variables) مثل <code>squareColor</code> و <code>squareLeft</code> و <code>squareTop</code> تُمثِل كُلًا من لون المربع ومَوضِعه. يَستخدِم التابع <code>draw()‎</code> مُتْغيِّرات الحالة (state variables) تلك لرَسْم المربع على الحاوية (canvas) بينما تُغيِّر توابع مُعالجة أحداث المفاتيح (key events) من قيمها.
</p>

<p>
	سنَفْحَص ببقية هذا القسم مثالًا آخر تَلعَب فيه حالة (state) البرنامج دورًا أكبر حيث سيَلعَب المُستخدِم لعبة بسيطة من خلال الضَغْط على مفاتيح الأسهم (arrow keys). البرنامج متاح بملف الشيفرة <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/SubKiller.java" rel="external nofollow">SubKiller.java</a>، وكالعادة، سيَكُون من الأفضل لو تَمَكَّنت من تصريف (compile) البرنامج وتَشْغِيله وكذلك قراءة شيفرته المصدرية بالكامل. اُنظر الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1614083416="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/002Sub_Killer.png.f0ada5a4ba9e8dea6055e734506a4fcd.png" data-fileid="58456" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="58456" data-unique="b56eanv7h" src="https://academy.hsoub.com/uploads/monthly_2021_02/002Sub_Killer.png.f0ada5a4ba9e8dea6055e734506a4fcd.png" alt="002Sub_Killer.png"></a>
</p>

<p>
	تُغطِى حاوية (canvas) نافذة التطبيق بالكامل. بأسفل تلك الحاوية، يَعرِض البرنامج "غواصة" سوداء تتحرك عشوائيًا ذهابًا وإيابًا بالقرب من أسفل النافذة. بأعلى النافذة، هناك "قارب" أزرق يُمكِنك تَحرِيكه ذهابًا وإيابًا بالضَغْط على مفتاحي "سهم يسار" و"سهم يمين". رُبِط بالقارب "قنبلة" حمراء يُمكِنك قذفها بالضَغْط على مفتاح "سهم لأسفل". الهدف من اللعبة هو تفجير الغواصة عن طريق ضربها بقنبلة. إذا سَقَطَت القنبلة إلى خارج أسفل الشاشة، فستَحصُل على قنبلة جديدة أما إذا انفجرت الغواصة، فستُنشَئ غواصة جديدة وستَحصُل أيضًا على قنبلة جديدة. جرِّب اللعبة وتَأكَّد من ضرب الغواصة مرة واحدة على الأقل حتى تَتَمكَّن من رؤية الانفجار.
</p>

<p>
	سنُفكِّر الآن بالكيفية التي ينبغي لنا بها برمجة تلك اللعبة. أولًا، لأننا نَعتمِد على البرمجة كائنية التوجه (object-oriented programming)، فسنُمثِل كُلًا من القارب والقنبلة والغواصة ككائنات. كل كائن منها مُعرَّف باِستخدَام صَنْف مُتداخِل (nested class) منفصل ضِمْن صَنْف التطبيق الأساسي كما أن كُلًا منها لديه حالته (state) الخاصة والمُمثَلة باِستخدَام مُتْغيِّرات النُسخ (instance variables) المُعرَّفة بصنفه. سنَستخدِم المُتْغيِّرات <code>boat</code> و <code>bomb</code> و <code>sub</code> للإشارة إلى كُلًا من كائنات القارب والقنبلة والغواصة على الترتيب.
</p>

<p>
	الآن، ما هي حالة (state) البرنامج؟ أو ما الأشياء التي ستَتَغيَّر من وقت لآخر بحيث تُؤثِر على مَظهَر أو سُلوك البرنامج؟ بدايةً، تَتَكوَّن الحالة من مَوضِع كُلًا من القارب والقنبلة والغواصة، ولذلك ستَتَضمَّن كائناتها مُتْغيِّرات نُسخ (instance variables) لتَخْزِين مَواضِعها. ثانيًا، من المحتمل أن تَسقُط القنبلة إلى أسفل الشاشة، وهو ما يُشكِّل فارقًا بالحالة، لذلك سنُمثِل ذلك الجانب من الحالة ضِمْن الكائن <code>bomb</code> باِستخدَام مُتْغيِّر من النوع <code>boolean</code> لوجود احتمالين وحيدين، وسيَكُون اسمه هو <code>bomb.isFalling</code>. ثالثًا، قد تتحرك الغواصة لليسار أو لليمين، لذا سنُمثِل ذلك الفرق باِستخدَام مُتْغيِّر آخر من النوع <code>boolean</code> هو <code>sub.isMovingLeft</code>. رابعًا، قد تنفجر الغواصة، وهو ما يُعدّ جزءًا من الحالة، لذا سنُمثِله باِستخدَام مُتْغيِّر من النوع <code>boolean</code> هو <code>sub.isExploding</code>. يَقَع الانفجار عبر متتالية من الأُطر (frames) يزداد حجمه خلالها بصورة تؤثر على مَظهَر الغواصة بكل إطار. بالإضافة إلى ذلك، سنحتاج أيضًا إلى مَعرِفة وقت انتهاء الانفجار حتى نَتَمكَّن من العودة إلى تَحرِيك الغواصة ورسمها بالطريقة العادية، لذا سنُعرِّف مُتْغيِّرًا من النوع <code>int</code> هو <code>sub.explosionFrameNumber</code> للاحتفاظ بعدد الأُطر المرسومة منذ بداية الانفجار، وستُستخدَم قيمته فقط أثناء وقوع الانفجار.
</p>

<p>
	كيف ستَتَغيَّر قيم تلك المُتْغيِّرات؟ ومتى؟ في الواقع سيَتَغيَّر بعضها من تلقاء نفسه. على سبيل المثال، تَتَغيَّر مُتْغيِّرات الحالة (state variables) المُمثِلة لمَوضِع الغواصة أثناء تحركها لليسار ولليمين نتيجة للتحريكة (animation) المُدارة باِستخدَام الصَنْف <code>AnimationTimer</code>. بكل مرة يُستدعَى فيها التابع <code>handle()‎</code>، فإنه سيُعدِّل بعض مُتْغيِّرات الحالة لكي تُصبح جاهزة لرسم الإطار (frame) التالي من التحريكة. لمّا كانت كُلًا من الكائنات <code>boat</code> و <code>bomb</code> و <code>sub</code> تُعرِّف التابع <code>updateForNextFrame()‎</code> المسئول عن تحديث مُتْغيِّراتها لتُصبِح جاهزة للرسم بإطار (frame) التحريكة التالي، فإن التابع <code>handle()‎</code> فقط يَستدعِي تلك التوابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_29" style="">
<span class="pln">boat</span><span class="pun">.</span><span class="pln">updateForNewFrame</span><span class="pun">();</span><span class="pln">
bomb</span><span class="pun">.</span><span class="pln">updateForNewFrame</span><span class="pun">();</span><span class="pln">
sub</span><span class="pun">.</span><span class="pln">updateForNewFrame</span><span class="pun">();</span></pre>

<p>
	بالإضافة إلى مَوضِع الغواصة، تُعدِّل توابع التحديث -بالأعلى- قيم بعض مُتْغيِّرات الحالة (state variables) الآخرى: أولًا، ستزداد قيمة الإحداثي <code>y</code> أثناء سقوط القنبلة. ثانيًا، إذا تَمكَّنت القنبلة من ضَرْب الغواصة، فسيُضبَط المُتْغيِّر <code>isExploding</code> بالكائن المُمثِل للغواصة إلى القيمة <code>true</code> كما سيُضبَط المُتْغيِّر <code>isFalling</code> بالكائن المُمثِل للقنبلة إلى <code>false</code>. ثالثًا، عندما تَسقُط القنبلة إلى أسفل الشاشة، سيُضبَط المُتْغيِّر <code>isFalling</code> إلى <code>false</code>. رابعًا، أثناء انفجار الغواصة، ستزداد قيمة المُتْغيِّر <code>explosionFrameNumber</code> بمقدار الواحد مع كل إطار إلى أن تَصِل إلى قيمة مُحدّدة، سينتهي عندها الانفجار وسيُضبَط المُتْغيِّر <code>isExploding</code> إلى القيمة <code>false</code>. أخيرًا، تُبدِّل الغواصة بين الحركة لليمين والحركة لليسار من وقت لآخر، ولمّا كان اتجاه حركتها مُخزَّنًا بالمُتْغيِّر <code>isMovingLeft</code> بالكائن المُمثِل للغواصة، فإن التابع <code>updateForNewFrame()‎</code> المُعرَّف بكائن الغواصة سيُغيِّر قيمة <code>isMovingLeft</code> عشوائيًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_31" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.02</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    isMovingLeft </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln"> isMovingLeft</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هنالك احتمالية واحد من الخمسين أن يُعيد الاستدعاء <code>Math.random()‎</code> قيمة أقل من ٠,٠٢ أي ستُنفَّذ التَعْليمَة <code>isMovingLeft = ! isMovingLeft</code> بمتوسط إطار واحد لكل خمسين إطار. تَعكِس تلك التَعْليمَة قيمة <code>isMovingLeft</code> من <code>false</code> إلى <code>true</code> أو من <code>true</code> إلى <code>false</code> أي أنها تَعكِس اتجاه حركة الغواصة.
</p>

<p>
	بالإضافة إلى تَغْييرات الحالة (state) الواقعة بين كل إطار (frame) والإطار الذي يليه، فهنالك تَغْييرات آخرى بمُتْغيِّرات الحالة ولكنها تَقَع عندما يَضغَط المُستخدِم على مفاتيح معينة. يَفْحَص مُعالِج الحَدَث <code>KeyPressed</code> المفتاح الذي ضَغَطَ عليه المُستخدِم، فإذا كان أي من المفتاحين "سهم يسار" أو "سهم يمين"، فإنه سيُعدِّل من مَوضِع القارب. أما إذا كان مفتاح "سهم لأسفل"، فإنه سيُغيِّر حالة القنبلة من عدم السقوط إلى السقوط. تَستعرِض الشيفرة التالية جزءًا من مَتْن تابع التطبيق <code>start()‎</code> يُعرِّف خلالها المُعالِج بهيئة تعبير لامدا (lambda expression) مُسجَّل بالكائن <code>scene</code> المُمثِل للمشهد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_33" style="">
<span class="pln">scene</span><span class="pun">.</span><span class="pln">setOnKeyPressed</span><span class="pun">(</span><span class="pln"> evt </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫يستجيب مستمع الحدث إلى أحداث KeyPressed على الحاوية</span><span class="pln">
        </span><span class="com">// تحرك مفاتيح سهم يمين و سهم يسار القارب </span><span class="pln">
        </span><span class="com">// بينما يحرر مفتاح سهم لأسفل القنبلة</span><span class="pln">
       </span><span class="typ">KeyCode</span><span class="pln"> code </span><span class="pun">=</span><span class="pln"> evt</span><span class="pun">.</span><span class="pln">getCode</span><span class="pun">();</span><span class="pln">  </span><span class="com">// أي مفتاح ضُغط عليه</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">code </span><span class="pun">==</span><span class="pln"> </span><span class="typ">KeyCode</span><span class="pun">.</span><span class="pln">LEFT</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           boat</span><span class="pun">.</span><span class="pln">centerX </span><span class="pun">-=</span><span class="pln"> </span><span class="lit">15</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">code </span><span class="pun">==</span><span class="pln"> </span><span class="typ">KeyCode</span><span class="pun">.</span><span class="pln">RIGHT</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
           boat</span><span class="pun">.</span><span class="pln">centerX </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">15</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">code </span><span class="pun">==</span><span class="pln"> </span><span class="typ">KeyCode</span><span class="pun">.</span><span class="pln">DOWN</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> bomb</span><span class="pun">.</span><span class="pln">isFalling </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
               bomb</span><span class="pun">.</span><span class="pln">isFalling </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	لاحِظ أنه ليس من الضروري إعادة رَسْم الحاوية (canvas) بذلك التابع؛ لأنها في الواقع تَعرِض تحريكة (animation) يُعَاد رسمها باستمرار على أية حال أي ستُصبِح أي تغييرات بالحالة (state) مرئية للمُستخدِم بمُجرّد رسم الإطار التالي. لابُدّ لنا من أن نَتَأكَّد من أن المُستخدِم لا يُحاوِل تحريك القارب إلى خارج الشاشة، وهو ما كان بإمكاننا القيام به بمُعالِج الحدث (event handler)، ولكننا سنقوم به ببرنامج (routine) آخر مُعرَّف بالكائن المُمثِل للقارب.
</p>

<p>
	سيَكُون من الأفضل لو تَمكَّنت من قراءة الشيفرة المصدرية للبرنامج بالملف <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/source/chapter6/SubKiller.java" rel="external nofollow">SubKiller.java</a>. قد تَكُون بعض أجزاء تلك الشيفرة صعبة نوعًا ما، ولكن بقليل من الجهد، ينبغي أن تَكُون قادرًا على قراءة البرنامج بالكامل وفهمه. حَاوِل التَفْكير بالبرنامج من وجهة نظر "آلات الحالة (state machines)"، ولاحِظ كيفية تَغيُّر حالة كُلًا من الكائنات الثلاثة نتيجة للأحداث الواقعة من المؤقت والمُستخدِم.
</p>

<p>
	على الرغم من أن تلك اللعبة ليست متقدمة بالموازنة مع لعب آخرى إلا أنها تُبيِّن كيفية تطبيق فكرة آلة الحالة (state-machine) بالبرمجة المبنية على الأحداث (event-oriented programming).
</p>

<h2>
	القيم القابلة للمراقبة (observable)
</h2>

<p>
	هنالك نوع آخر من الأحداث البسيطة تلعب دورًا مُهِمًّا بمنصة جافا إف إكس (JavaFX) هي الأحداث الواقعة عند تَعْدِيل قيمة "قابلة للمراقبة (observable)". فمثلًا، يَستخدِم البرنامج <code>SubKiller</code> من القسم السابق كائن <code>stage</code> من الصَنْف <code>Stage</code> لديه خاصية (property) من النوع <code>ObservableBooleanValue</code> تُحدِّد ما إذا كان الكائن يُمثِل النافذة الواقع عليها التركيز (focused window)، والتي تستطيع جَلْب قيمتها باستدعاء <code>stage.focusedProperty()‎</code>. عندما تَتَغيَّر قيمة خاصية من النوع <code>ObservableBooleanProperty</code>، يَقَع حدث (event). يُمكِنك أن تُسجِّل مُستمِع تَغْيير (change listener) من الصَنْف <code>ChangeListener</code> بتلك الخاصية، والذي يَتَضمَّن تابع معالج حدث (event handler) يُستدعَى عند وقوع الحدث. في تلك الحالة، سيَستقبِل ذلك التابع ثلاثة مُعامِلات: الخاصية القابلة للمراقبة (observable) المسئولة عن توليد الحَدَث، والقيمة السابقة للخاصية، والقيمة الجديدة. نوع القيمة القديمة والجديدة لخاصية من النوع <code>ObservableBooleanValue</code> هو <code>boolean</code>. هنالك أنواع قيمة آخرى "قابلة للمراقبة (observable)" مثل <code>ObservableIntegerValue</code> و <code>ObservableStringValue</code> و <code>ObservableObjectValue</code>.
</p>

<p>
	ستبقى التحريكة (animation) مُشغَّلة حتى لو لم يَعُدْ التركيز واقعًا على نافذة <code>SubKiller</code>، وهو ما قد يَكُون مزعجًا عند محاولة العمل على نافذة آخرى، لهذا سنُوقِف التحريكة (animation) مؤقتًا عندما تفقد النافذة التركيز وسنُعيد تَشْغِيلها عندما تَكْتسِبه مرة آخرى. عندما تفقد النافذة التركيز أو تَكْتسِبه، ستَتَغيَّر قيمة الخاصية <code>stage.focusedProperty()‎</code> المنطقية والقابلة للمراقبة (observable). للاستجابة لذلك التَغْيير، سنُضيف مُستمِع تَغْيير (change listener) لتلك الخاصية بحيث يُوقِف التحريكة عن العمل عندما تَتَغيَّر قيمة الخاصية إلى <code>false</code> ويُشغِّلها عندما تَتَغيَّر قيمتها إلى <code>true</code>. سنُضيف الشيفرة التالية إلى التابع <code>start()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5487_35" style="">
<span class="pln">stage</span><span class="pun">.</span><span class="pln">focusedProperty</span><span class="pun">().</span><span class="pln">addListener</span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln">oldVal</span><span class="pun">,</span><span class="pln">newVal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">//  يوقف المستمع التحريكة إذا لم تعد نافذة البرنامج </span><span class="pln">
        </span><span class="com">// محل التركيز</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newVal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// أصبحت النافذة محل التركيز</span><span class="pln">
            timer</span><span class="pun">.</span><span class="pln">start</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// لم تعد النافذة محل التركيز</span><span class="pln">
            timer</span><span class="pun">.</span><span class="pln">stop</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        draw</span><span class="pun">();</span><span class="pln"> </span><span class="com">// أعد رسم الحاوية</span><span class="pln">
    </span><span class="pun">});</span></pre>

<p>
	يُسجِّل التابع <code>addListener()‎</code> لخاصية "قابلة للمراقبة" (observable) مُستمِع تَغْيير (change listener) بتلك الخاصية. يَستقبِل تعبير لامدا (lambda expression) لمُعالِج الحَدَث (event handler) ثلاثة مُعامِلات (parameters). اِستخدَمنا فقط المُعامِل <code>newVal</code> بالأعلى، والذي يُمثِل القيمة الحالية للخاصية <code>focused</code> بالكائن المُمثِل للمرحلة (stage).
</p>

<p>
	تتضمن مكونات واجهة المستخدم الرسومية (GUI) بمنصة جافا إف إكس (JavaFX) خاصيات عديدة قابلة للمراقبة (observable) من أنواع مختلفة. على سبيل المثال، نص الزر هو خاصية من النوع <code>ObservableStringProperty</code> وكذلك عَرْض حاوية (canvas) وارتفاعها عبارة عن قيم من النوع <code>ObservableDoubleProperty</code>. سنَتَعرَّض لمزيد من الأمثلة بالقسم التالي.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/c6/s3.html" rel="external nofollow">Section 3: Basic Events</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614083416="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1147</guid><pubDate>Tue, 23 Feb 2021 12:33:05 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x628;&#x639;&#x636; &#x623;&#x635;&#x646;&#x627;&#x641; &#x645;&#x643;&#x62A;&#x628;&#x629; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX  &#x627;&#x644;&#x628;&#x633;&#x64A;&#x637;&#x629;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D8%B9%D8%B6-%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-%D8%A7%D9%84%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1146/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034f1e7654fe_--.png.c1c459a942f868c12af7f695c5142e25.png" /></p>

<p>
	سنُناقش خلال هذا القسم بعضًا من الأصناف البسيطة المُستخدَمة لتَمثيِل كُلًا من الألوان والخطوط والصور، كما سنرى طريقة اِستخدَامها مع كائن السياق الرسومي <code>GraphicsContext</code> الذي تَعرَّضنا له مبدئيًا <a data-ss1614082532="1" data-ss1614083009="1" href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-gui-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1070/" rel="">بالقسم ٣.٩</a>. إلى جانب ذلك، تستطيع أيضًا اِستخدَام تلك الأصناف بأجزاء آخرى من مكتبة جافا إف إكس (JavaFX). وأخيرًا، سنتناول مقدمة مُختصَرة عن أوراق الأنماط المُتعاقبة (CSS style sheet) والتي يُمكِنك أن تَستخدِمها للتَحكُّم بالكثير من الجوانب المرئية لمُكوِّنات واجهة المُستخدِم الرسومية (GUI).
</p>

<h2>
	الأصناف <code>Color</code> و <code>Paint</code>
</h2>

<p>
	يشيع اِستخدَام نظام الألوان RGB لتَخْصِيص الألوان عمومًا. يَتَألف كل لون وفقًا لهذا النظام من ثلاثة أعداد يُطلق عليها اسم "مُكوِّنات اللون (color components)" والتي تُعطِي درجات الأحمر والأخضر والأزرق باللون. بمكتبة جافا إف إكس (JavaFX)، يُستخدَم كائن من النوع <code>Color</code> المُعرَّف بحزمة <code>javafx.scene.paint</code> لتمثيل لون معين. كل مُكوِّن لون من المُكوِّنات الثلاثة هو قيمة من النوع <code>double</code> تَقَع بنطاق يتراوح بين ٠ و ١. تَملُك الكائنات من النوع <code>Color</code> مُكوِّنًا رابعًا أيضًا تتراوح قيمته بين ٠ و ١، ويُشار إليه باسم مكون اللون "ألفا (alpha)"، ويُستخدَم لتَمثيِل درجة شفافية (transparency) أو عتمة (opaqueness) اللون عند اِستخدَامه للرَسْم. إذا اِستخدَمت لونًا مُعتِمًا تمامًا (مُكوِّن اللون ألفا يُساوِي ١) للرَسْم، فسيَحلّ ذلك اللون مَحلّ اللون الحالي لسطح الرسم أما إذا اِستخدَمت لونًا شفافًا تمامًا (مُكوِّن اللون ألفا يُساوِي ٠)، فلن يَكُون له أي تأثير نهائيًا. إذا وَقَع مُكوِّن اللون ألفا بين ٠ و ١، يُخلَط لون الرسم مع اللون الحالي ليُعطِي لونًا جديدًا لسطح الرسم، ويبدو عندها المحتوي الأصلي لسطح الرسم كما لو كان مرئيًا من خلف نظارة مُلوَّنة شفافة. يُمكِنك إنشاء كائن من النوع <code>Color</code> بإعطاء قيمة مُكوِّنات اللون الأربعة (الأحمر والأخضر والأزرق وألفا) على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_8" style="">
<span class="typ">Color</span><span class="pln"> myColor </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="pln"> r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> a </span><span class="pun">);</span></pre>

<p>
	حيث <code>r</code> و <code>g</code> و <code>b</code> و <code>a</code> واقعة بنطاق يَتَراوح من ٠ إلى ١. يَملُك الصَنْف <code>Color</code> أيضًا عددًا من التوابع الساكنة (static methods) لإنشاء كائنات.
</p>

<p>
	ملحوظة: يُطلق اسم "التوابع المُنتِجة (factory methods)" على التوابع الساكنة التي يَقْتصِر دورها على إنشاء كائنات. تستطيع إذًا أن تَستخدِم تابعًا منتجًا بدلًا من باني الكائن (constructor) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_10" style="">
<span class="typ">Color</span><span class="pln"> myColor </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">color</span><span class="pun">(</span><span class="pln"> r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> a </span><span class="pun">);</span></pre>

<p>
	كما تستطيع اِستخدَام ما يلي إذا كنت تَرغَب بلون مُعتِم تمامًا، أي بقيمة <code>a</code> تُساوِي ١:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_12" style="">
<span class="typ">Color</span><span class="pln"> myColor </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">color</span><span class="pun">(</span><span class="pln"> r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b </span><span class="pun">);</span></pre>

<p>
	تُفضَّل التوابع المُنتِجة (factory methods) الساكنة على البواني (constructor) عمومًا لأنها تُمكِّنك من إعادة اِستخدَام نفس كائنات الألوان من الصَنْف <code>Color</code>. على سبيل المثال، إذا استدعيت التعبير <code>Color.color(0.2,0.3,1.0)‎</code> أكثر من مرة، فسيُعاد دائمًا نفس الكائن من الصَنْف <code>Color</code>، وهو ما يُعدّ مناسبًا لأن كائنات الألوان غَيْر قابلة للتَعْدِيل (immutable) أي ليس هناك طريقة لتَغْيِير لون بَعْد إِنشائه، لذلك لا حاجة لاِستخدَام كائنات مختلفة لتمثيل نفس اللون.
</p>

<p>
	عادةً ما تَستخدِم شاشات الحاسوب لونًا حجمه ٣٢ بت أي تُستخدَم مساحة قدرها ٨ بت لتمثيل كل مُكوِّن من مُكوِّنات اللون (color components) الأربعة، والتي يُمكِنها أن تُمثِل ٢٥٦ عدد صحيح بنطاق يتراوح من ٠ إلى ٢٥٥، ولهذا تُخصَّص ألوان الحاسوب باِستخدَام مُكوِّنات لون قيمتها تتراوح بين ٠ و ٢٥٥. يمتلك الصَنْف <code>Color</code> التابع الساكن (static method) التالي لإنشاء ألوان بتلك الطريقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_14" style="">
<span class="typ">Color</span><span class="pun">.</span><span class="pln">rgb</span><span class="pun">(</span><span class="pln"> r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b </span><span class="pun">)</span></pre>

<p>
	حيث <code>r</code> و <code>g</code> و <code>b</code> هي أعداد صحيحة واقعة بنِطاق يَتَراوح من ٠ إلى ٢٥٥. يَتَوفَّر أيضًا <code>Color.rgb(r,g,b,a)‎</code> حيث <code>r</code> و <code>g</code> و <code>b</code> هي أعداد صحيحة واقعة بنطاق يَتَراوح من ٠ إلى ٢٥٥ بينما <code>a</code> هي قيمة من النوع <code>double</code> تتراوح من ٠ إلى ١.
</p>

<p>
	يَتَألف كل لون وفقًا لهذا النظام من ثلاثة أعداد يُطلق عليها اسم "مُكوِّنات اللون (color components)" والتي تُعطِي درجات الأحمر والأخضر والأزرق باللون.
</p>

<p>
	بالإضافة إلى نظام الألوان RGB، يَشيِع أيضًا اِستخدَام نظام الألوان HSB. يَتَألف كل لون وفقًا لهذا النظام من ثلاثة أعداد تُحدِّد كُلًا من: درجة اللون (hue) والإشباع (saturation) والسطوع (brightness). درجة اللون (hue) هي اللون الأساسي بدايةً من الأحمر مرورًا بالبرتقالي وغيرها من ألوان الطيف الأخرى أما السطوع (brightness) فمعناها مفهوم ضمنيًا. بالنسبة للإشباع (saturation)، فاللون المُشبع (saturated) تمامًا هو لون نقي، ويُمكِننا تَقْليِل درجة إشباع لون معين بخلطه مع لون أبيض أو رمادي. بمكتبة جافا إف إكس (JavaFX)، تُعطَى درجة اللون (hue) كقيمة من النوع <code>double</code> تَتَراوح من ٠ إلى ٣٦٠ أما الإشباع (saturation) والسطوع (brightness) فهي قيم من النوع <code>double</code> تتراوح من ٠ إلى ١. تُخصَّص قيمة درجة اللون بوحدة الدرجة (degrees) كما لو كانت جميع الألوان مُمدَّدة عبر دائرة بحيث تُمثِل الدرجتان ٠ و ٣٦٠ لونًا أحمرًا نقيًا. يُعرِّف الصَنْف <code>Color</code> التابعين الساكنين <code>Color.hsb(h,s,b)‎</code> و <code>Color.hsb(h,s,b,a)‎</code> لإنشاء ألوان بنظام HSB. على سبيل المثال، يُمكِنك اِستخدَام الشيفرة التالية لإنشاء لون مُشِع (bright) ومُشبِع (saturated) بأقصى ما يُمكِن، وبدرجة لون (hue) عشوائية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_16" style="">
<span class="typ">Color</span><span class="pln"> randomColor </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">hsb</span><span class="pun">(</span><span class="pln"> </span><span class="lit">360</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يُعدّ نظامي اللون RGB و HSB طريقتين مختلفين لوصف نفس مجموعة الألوان، ويُمكِننا في الواقع أن نُحوِّل بينهما. الطريقة الأفضل عمومًا لفهم أنظمة الألوان هو تجريبها، وهو ما تستطيع القيام به باِستخدَام المثال التوضيحي <code><a data-ss1614082532="1" data-ss1614083009="1" href="http://math.hws.edu/javanotes/source/chapter6/SimpleColorChooser.java" rel="external nofollow">SimpleColorChooser.java</a></code>. لن تَفهَم شيفرة البرنامج بالكامل، ولكن يُمكِنك أن تُشغِّله وتُجرِّبه.
</p>

<p>
	يحتوي الصنف <code>Color</code> على عدد كبير من الثوابت (constants) الممثلة للألوان مثل <code>Color.RED</code> و <code>Color.BLACK</code> و <code>Color.LIGHTGRAY</code> و <code>Color.GOLDENROD</code>. لاحظ أن اللون <code>Color.GREEN</code> يمثل اللون الأخضر الداكن المعطى باستخدام <code>Color.rgb(0,128,0)‎</code> أما ذلك المعطى باستخدام <code>Color.rgb(0,255,0)‎</code> فيُمثِل <code>Color.LIME</code>. يتوفر أيضًا اللون <code>Color.TRANSPARENT</code> والذي يمثل لونًا شفافًا تمامًا حيث جميع مكونات اللون RGBA الأربعة تُساوي صفر.
</p>

<p>
	بفَرْض وجود مُتْغيِّر <code>c</code> يُشير إلى كائن من النوع <code>Color</code>، يُمكِنك استدعاء دوال مثل <code>c.getRed()‎</code> و <code>c.getHue()‎</code> و <code>c.getOpacity()‎</code> لجَلْب قيم مُكوِّنات اللون المختلفة. تُعيد تلك التوابع (methods) قيمًا من النوع <code>double</code> بنطاق يَتَراوح من ٠ إلى ١ باستثناء التابع <code>c.getHue()‎</code> والذي يُعيد قيمًا من النوع <code>double</code> تَقَع بنطاق يَتَراوح من ٠ إلى ٣٦٠.
</p>

<p>
	لاحِظ أن <code>Color</code> هو صَنْف فرعي (subclass) من الصَنْف <code>Paint</code> والذي يُمثِل الفكرة الأعم لشيء يُمكِن اِستخدَامه لمَلْئ (fill) الأشكال ورَسْم حوافها (stroke). إلى جانب الألوان، يُمكِنك أيضًا اِستخدَام طلاءً مُكوَّنًا من صور (image paints) أو طلاءً مُتدرجًا (gradient paints). سنُعود للحديث عن ذلك بالقسم الفرعي ١٣.٢.٢ أما الآن فينبغي أن تَعلَم فقط أنه بإِمكانك أن تَستخدِم كائنًا من النوع <code>Color</code> كقيمة لمُعامل (parameter) من النوع <code>Paint</code>.
</p>

<h2>
	الخطوط <code>Font</code>
</h2>

<p>
	يُعبِر الخط (font) عن كُلًا من هيئة النص وحجمه، فيختلف مَظهَر المحرف (character) ذاته بتَغَيُّر الخط. تُستخدَم كائنات من النوع <code>Font</code> المُعرَّف بحزمة <code>javafx.scene.text</code> لتمثيل الخطوط بمنصة جافا إف إكس (JavaFX). يُوفِّر الصَنْف <code>Font</code> عددًا من البواني (constructors) وكذلك بعضًا من التوابع المُنتِجة (factory methods) الساكنة لإنشاء كائن خط من الصَنْف <code>Font</code>، ويُفضَّل عمومًا استخدام التوابع المُنتِجة عن التوابع.
</p>

<p>
	كل خط له اسم عبارة عن سِلسِلة نصية تُخصِّص نوع الخط (font family) مثل "Times New Roman". عادة ما تَتَوفَّر نسخ مختلفة لنفس نوع الخط، مثلًا نُسخة عريضة (bold) وآخرى مائلة (italic). أخيرًا، يُقاس حجم الخط (size) بوحدة النقاط (points)، والتي تُعادِل حوالي ١\٧٢ بوصة، وتُساوِي عمليًا حجم بكسل واحد. يُمكِنك اِستخدَام الدالة (function) التالية لإنشاء خط:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_18" style="">
<span class="typ">Font</span><span class="pln"> myFont </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="pln"> family</span><span class="pun">,</span><span class="pln"> weight</span><span class="pun">,</span><span class="pln"> posture</span><span class="pun">,</span><span class="pln"> size </span><span class="pun">);</span></pre>

<p>
	إذا لَمْ يَجِد النظام خطًا تَتَطابَق خاصياته مع قيم المُعامِلات (parameters) المُمرَّرة تطابقًا كليًا، فإنه يُعيِد خطًا أقرب ما يَكُون إليها. أولًا، المُعامِل <code>family</code> هو عبارة عن سِلسِلة نصية من الصنف <code>String</code> تُخصِّص إحدى أنواع الخطوط المتاحة للبرنامج. في الواقع، ليس هنالك مجموعة خطوط لابُدّ أن تَكُون متاحة في العموم، ومع ذلك من المُتوقَّع دومًا أن تَعمَل خطوط مثل "Times New Roman" و "Arial" و "Verdana"، والتي صنعتها مايكروسوفت (Microsoft) ونشرتها للاستخدام الحر، وهي مُثبَّتة بالكثير من الأنظمة. يُمكِنك أن تُمرِّر <code>null</code> كقيمة لهذا المُعامِل، وسيَستخدِم النظام عندها نوع الخط الافتراضي.
</p>

<p>
	ثانيًا: يَستقبِل المُعامِل <code>weight</code> إحدى قيم نوع التعداد <code>FontWeight</code> لتَخْصِيص وزن الخط. عادة ما تَكُون القيمة المُمرَّرة هي <code>FontWeight.BOLD</code> أو <code>FontWeight.NORMAL</code> على الرغم من وجود عدة قيم آخرى مثل <code>FontWeight.EXTRA_BOLD</code>. بالمثل، يُمرَّر للمُعامِل <code>posture</code> أي من الثابتين <code>FontPosture.ITALIC</code> و <code>FontPosture.REGULAR</code> لتَخْصيص وَضْع الخط. لاحِظ أن التعدادين <code>FontWeight</code> و <code>FontPosture</code> مُعرَّفان بحزمة <code>javafx.scene.text</code>.
</p>

<p>
	يَتَضمَّن الصَنْف <code>Font</code> بعض الدوال (functions) الساكنة الآخرى المُستخدَمة لإنشاء خطوط، وتَستقبِل كل واحدة منها مجموعة معينة من الخاصيات الأربعة: <code>family</code> و <code>weight</code> و <code>posture</code> و <code>size</code> مثل الدوال <code>Font.font(size)‎</code> و <code>Font.font(family)‎</code> و <code>Font.font(family,weight,size)‎</code> وغيرها. تَتَوفَّر قيم افتراضية للخاصيات غَيْر المُمرَّرة تعتمد عادةً على الحاسوب المُشغَّل عليه البرنامج. تُعيد الدالة الساكنة <code>Font.getDefault()‎</code> خطًا تَستخدِم جميع خاصياته قيمها الافتراضية، ويُمكِنك مثلًا أن تَستدعِي <code>Font.getDefault().getSize()‎</code> لمَعرِفة الحجم الافتراضي للنقطة (point). اُنظر الأمثلة التالية لإنشاء خطوط:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_20" style="">
<span class="typ">Font</span><span class="pln"> font1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="lit">40</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Font</span><span class="pln"> font2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Times New Roman"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="lit">24</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Font</span><span class="pln"> font3 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="pln">null</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontPosture</span><span class="pun">.</span><span class="pln">ITALIC</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14</span><span class="pun">);</span></pre>

<h2>
	الصور <code>Image</code>
</h2>

<p>
	تُوفِّر منصة جافا إف إكس (JavaFX) الصَنْف <code>Image</code> بحزمة <code>javafx.scene.image</code> لتمثيل "الصور (images)"، والتي قد تَكُون بهيئة صورة فوتوغرافية أو رسم أو أي شيء آخر يُمكِن تمثيله بهيئة شبكة مستطيلة من البكسلات الملونة. تُخزَّن تلك الصور بملفات يُمكِن تحميلها بسهولة تمهيدًا لعَرْضها ضِمْن البرنامج. يُكْتَب الباني (constructor) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_22" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">(</span><span class="pln"> path </span><span class="pun">)</span></pre>

<p>
	يُحمِّل الباني بالأعلى صورة من ملف. يَستقبِل ذلك الباني سِلسِلة نصية (string) كقيمة للمُعامِل <code>path</code> تُخصِّص موقع (location) الملف. قد يَقَع الملف بشبكة الإنترنت أو بجهاز المُستخدِم، وسنُركِّز هنا فقط على تلكم الواقعة ضِمْن ملفات الموارد (resource). تُسمَى أجزاء البرنامج من غير ملفات الشيفرة باسم الموارد (resource)، والتي قد تَتَضمَّن أنواع مختلفة من الملفات كملفات الصوت والبيانات والخطوط والصور. بإمكان النظام أن يُحمِّل تلك الموارد (resources) من نفس المسارات التي يبحث فيها عن ملفات <code>‎.class</code>. مثلًا، إذا كان لدينا ملف مورد (resource file) بمجلد البرنامج الرئيسي، فإن مساره يَقْتصِر على اسمه أما إذا كان الملف موجودًا بمجلد فرعي داخل المجلد الرئيسي، فلابُدّ أن يَتَضمَّن مساره اسم ذلك المجلد الفرعي. مثلًا، يُشير المسار "images/cards.png" إلى ملف اسمه "cards.png" بمجلد فرعي اسمه "images"، أما المسار "resources/sounds/beep.aiff" فيُشير إلى ملف اسمه "beep.aiff" بمجلد اسمه "sounds" والذي بدوره يَقَع بمجلد اسمه "resources".
</p>

<p>
	تَتَوفَّر صيغ (formats) مختلفة لتَخْزين الصور، وتستطيع الكائنات من الصَنْف <code>Image</code> أن تتعامل في العموم مع ملفات الصور التي تنتهي أسمائها بإحدى الامتدادات التالية: <code>‎.gif</code> و <code>‎.jpeg</code> و <code>‎.jpg</code> و <code>‎.png</code> و <code>‎.bmp</code>. فمثلًا، إذا كان الملف "cards.png" واقعًا بمجلد البرنامج الرئيسي، يُمكِننا أن نُنشِئ كائنًا من الصنف <code>Image</code> لتمثيله كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_24" style="">
<span class="typ">Image</span><span class="pln"> cards </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">(</span><span class="pln"> </span><span class="str">"cards.png"</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	سنرى بعد قليل طريقة عَرْض الصورة ضِمْن سياق رسومي (من الصنف <code>GraphicsContext</code>)، كما سنناقش بعض الاستخدامات الأخرى للصور لاحقًا.
</p>

<h2>
	الحاوية <code>Canvas</code> والسياق الرسومي <code>GraphicsContext</code>
</h2>

<p>
	تَتَكوَّن أي شاشة حاسوب من شبكة من المربعات الصغيرة يُطلَق على كُلًا منها اسم "بكسل (pixel)". نستطيع أن نَضبُط لون كل بكسل منها على حدى، وبالتالي يُمكِننا أن نَرسِم أي شيء على الشاشة مثل أي من مُكوِّنات الواجهة (GUI component). تستطيع غالبية مُكوِّنات الواجهة أن تَرسِم نفسها كما تَملُك نظامًا إحداثيًا لتَحْوِيل الإحداثيات (x,y) إلى نقط واقعة عليها. بالإضافة إلى ذلك، يتوفَّر مُكوِّن وحيد عبارة عن "مساحة رسم (drawing surface)" تستطيع أن تَرسِم عليه أي شيء فقط باستدعاء بعض التوابع (methods) المناسبة. مُكوِّنات مساحة الرسم تلك هي من نوع الحاوية <code>Canvas</code> المُعرَّف بحزمة <code>javafx.scene.canvas</code>، والتي تُعدّ عُقدًا من الصَنْف <code>Node</code> أي يُمكِنها أن تَكُون جُزءًا من مبيان مَشهَد (scene graph)، ولكنها ليست من الصَنْف <code>Parent</code> لذا لا يُمكِنها أن تَعمَل كحاوي (container) لعُقَد آخرى كما لا يُمكِنها أن تَكُون المُكوِّن الجذري (root) لمبيان مَشهَد. يَعنِي ذلك أنه حتى لو رغبت برسم كائن حاوية (canvas) فقط على نافذة، فلابُدّ من أن تَضَعُه أولًا ضِمْن حاوي (container) يَعمَل كمُكوِّن جذري لمبيان مشهد (scene graph) تلك النافذة.
</p>

<p>
	تبدو الحاوية من الصَنْف <code>Canvas</code> بهيئة مستطيل من البكسلات (pixels) على الشاشة. يُستخدَم زوج من الإحداثيات (x,y) للإشارة إلى مَوضِع معين ضِمْن ذلك المستطيل، والذي يُمثِل رُكنه الأيسر العلوي إحداثيات النقطة (٠،٠)، وبحيث تزداد قيمة الإحداثي <code>x</code> من اليسار إلى اليمين بينما تزداد قيمة الإحداثي <code>y</code> من الأعلى إلى الأسفل. تُوضِح الصورة التالية الكيفية التي تُلوَّن بها البكسلات لرَسْم شكل معين مثل خط صغير أو مستطيل أو شكل بيضاوي على حاوية (canvas) بمساحة ١٢ * ١٢ بكسل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58449" data-ss1614082532="1" data-ss1614083009="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/001Pixel_Coordinates.png.b794ac62ec78c31ae83d01aa63e3f246.png" rel=""><img alt="001Pixel_Coordinates.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58449" data-unique="bftt1xitj" src="https://academy.hsoub.com/uploads/monthly_2021_02/001Pixel_Coordinates.png.b794ac62ec78c31ae83d01aa63e3f246.png"></a>
</p>

<p>
	لا تَقَع إحداثيات النقط داخل البكسلات ذاتها وإنما على خطوط الشبكة (grid) الفاصلة بينها، ولمّا كانت تلك الإحداثيات أعدادً من النوع <code>double</code>، فإنها قد تُضبَط لكي تُشيِر إلى موضع داخل بكسل. على سبيل المثال، تُمثِل الإحداثيات (٠.٥، ٠.٥) مركز البكسل الأيسر العلوي بحاوية (canvas) معينة. ملحوظة: تُرسَم كل الأشكال عمومًا باستخدام إحداثيات من النوع <code>double</code>.
</p>

<p>
	يُوفِّر الصَنْف <code>Canvas</code> باني كائن (constructor) يَستقبِل كُلًا من عرض الحاوية (canvas) وارتفاعها. يُمكِننا مثلًا إنشاء حاوية صغيرة جدًا بحجم ٢٠ * ١٢ بكسل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_26" style="">
<span class="typ">Canvas</span><span class="pln"> canvas </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Canvas</span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="lit">12</span><span class="pun">)</span></pre>

<p>
	نستطيع مَعرِفة حجم حاوية (canvas) معينة باستدعاء التابعين <code>canvas.getWidth()‎</code> و <code>canvas.getHeight()‎</code> ويُعيد كُلًا منهما قيمة من النوع <code>double</code>. لا نحتاج عادةً إلى إعادة ضَبْط حجم حاوية بَعْد إنشائها، ومع ذلك يُوفِّر الصَنْف التابعين <code>canvas.setWidth(w)‎</code> و <code>canvas.setHeight(h)‎</code> لإعادة ضَبْط حجم حاوية إن اقتضت الضرورة.
</p>

<p>
	لاحِظ أن الحاوية (canvas) تَكُون شفافة (transparent) بمُجرّد إنشائها، وذلك لأنها تُملأ افتراضيًا بلون أسود شفاف (transparent black) جميع مُكوِّناته RGBA مُساوية للصفر، ولهذا يُمكِن رؤية ما هو موجود خلفها ضِمْن المشهد (scene).
</p>

<p>
	الآن لكي نَتَمكَّّن من الرسم على أي حاوية (canvas)، سنحتاج إلى كائن سياق رسومي (graphics context) من النوع <code>GraphicsContext</code>. يُقابِل أي كائن حاوية من الصَنْف <code>Canvas</code> كائن سياق رسومي من الصَنْف <code>GraphicsContext</code> خاص به. بتعبير آخر، يَرسِم كل كائن سياق رسومي مختلف من الصَنْف <code>GraphicsContexts</code> على كائن حاوية مختلف من الصَنْف <code>Canvas</code>. بفَرْض أن لدينا مُتْغيِّر كائن حاوية اسمه <code>canvas</code> من الصنف <code>Canvas</code>، يُمكِننا استدعاء التابع <code>canvas.getGraphicsContext2D()‎</code> لمَعرِفة كائن السياق الرسومي (graphics context) للحاوية. سيُعيد ذلك التابع (method) نفس كائن السياق الرسومي دومًا لنفس كائن الحاوية. خلال القسم ٣.٩، تَعامَلنا مع كائن سياق رسومي (graphics context)، وتَعلَّمنا عمومًا كيفية رَسْم حواف (stroke) شكل معين أو ملؤه (fill) في حالة كان لديه جزءًا داخليًا. نَستعرِض الآن التوابع التي يُمكِننا اِستخدَامها بكائن سياق رسومي <code>g</code> من الصَنْف <code>GraphicsContext</code> مع مراعاة كَوْن جميع المُعامِلات (parameters) العَدَدية لتلك التوابع من النوع <code>double</code>:
</p>

<ul>
<li>
		التابعان <code>g.strokeRect(x,y,w,h)‎</code> و <code>g.fillRect(x,y,w,h)‎</code>: يَرسِم كلاهما مستطيلًا بعَرْض <code>w</code> وارتفاع <code>h</code> وبحيث يَقَع ركنه الأيسر العلوي بإحداثيات النقطة (x,y). لن يُرسَم أي شيء إذا كانت قيمة أي من المُعامِلين <code>w</code> أو <code>h</code> أقل من أو تُساوِي الصفر.
	</li>
	<li>
		التابع <code>g.clearRect(x,y,w,h)‎</code>: يَملأ المستطيل بلون شفاف (transparent) تمامًا، وبالتالي يُمكن رؤية ما كان موجودًا خلفه عبر الحاوية (canvas)، وهو ما يختلف عن استدعاء التابع <code>g.fillRect(x,y,w,h)‎</code> باِستخدَام لون مَلْئ (fill color) شفاف، ففي تلك الحالة، لا يَكُون هناك أي تأثير على محتويات المستطيل.
	</li>
	<li>
		التابعين <code>g.strokeOval(x,y,w,h)‎</code> و <code>g.fillOval(x,y,w,h)‎</code>: يَرسِم كلاهما شكلًا بيضاويًا يَقَع داخل مستطيل عَرْضه هو <code>w</code> وارتفاعه هو <code>h</code> وبحيث يَقَع ركنه الأيسر العلوي بإحداثيات النقطة (x,y).
	</li>
	<li>
		التابعان <code>g.strokeRoundRect(x,y,w,h,rh,rv)‎</code> و <code>g.fillRoundRect(x,y,w,h,rh,rv)‎</code>: يَرسِم كلاهما مستطيلًا دائري الأركان بعَرْض <code>w</code> وارتفاع <code>h</code> وبحيث يَقَع ركنه الأيسر العلوي بإحداثيات النقطة (x,y). يُقطَّع من كل ركن ربع بيضاوي نصفي قطره الأفقي والرأسي مُساوي للقيم <code>rh</code> و <code>rv</code> على الترتيب.
	</li>
	<li>
		التابعان <code>g.strokeText(str,x,y)‎</code> و <code>g.fillText(str,x,y)‎</code>: يَرسِم كلاهما سِلسِلة نصية من النوع <code>String</code> وبحيث يَقَع الطرف الأيسر من خط النص الأساسي (text baseline) بنقطة الإحداثيات (x,y). تُرسَم أي سِلسِلة نصية (string) عمومًا فوق خط أساسي (baseline) مع إمكانية لتدلّي بعض الأجزاء كذيل الحرف "y" الذي يَمتد إلى ما بَعْد الخط الأساسي بقليل. إذا اِمتدَّت سِلسِلة نصية معينة عَبْر عدة أسطر مفصولة بمحرف سطر جديد <code>‎\n</code>، ستُشير عندها نقطة الإحداثيات (x,y) إلى طرف الخط الأساسي (baseline) الأيسر لأول سطر بالسِلسِلة النصية. لاحِظ أن رَسْم حواف (stroking) نص معين يعني مُجرّد رَسْم حواف محارفه (characters) الخارجية.
	</li>
	<li>
		التابعان <code>g.strokePolygon(xcoords,ycoords,n)‎</code> و <code>g.fillPolygon(xcoords,ycoords,n)‎</code>: يَرسِم كلاهما مُضلعًا (polygon) مُكوَّنًا من عدة خطوط تَربُط مجموعة من النقط بعضها ببعض، والتي تُحدَّد إحداثياتها بالقيم المُمرَّرة للمُعامِلين الأول والثاني، وتَكُون بهيئة مصفوفة من النوع <code>double[]‎</code>. يَستقبِل التابعان أيضًا مُعامِلًا ثالثًا <code>n</code> يُشير إلى عدد تلك النقاط. يَصِل المُضلع النقط التالية مع بعضها البعض: <code>(xcoords[0],ycoords[0]‎)</code> و <code>(xcoords[1],ycoords[1]‎)</code> و … و <code>(xcoords[n-1],ycoords[n-1]‎)</code> و <code>(xcoords[0],ycoords[0]‎)</code> أي يَصِل تلقائيًا خط إضافي أخير بين النقطتين الأولى والأخيرة.
	</li>
	<li>
		التابع <code>g.strokeLine(x1,y1,x2,y2)‎</code>: يَرسِم خطًا يبدأ من إِحداثيَّات نقطة البداية (x1,y1) وحتى إِحداثيَّات نقطة النهاية (x2,y2). ولأن الخط لا يَملُك جزءًا داخليًا، فليس هنالك أي مَعنَى لمحاولة ملؤه (fill).
	</li>
</ul>
<p>
	يَملُك أي كائن سياق رُسومي <code>g</code> من الصَنْف <code>GraphicsContext</code> عددًا من الخاصيات التي تُؤثِر على عملية الرَسْم حيث تُستخدَم القيم الحالية لخاصياته عند اِستخدَامه لرَسْم أي شيء. يُناظِر كل خاصية منها تابعي ضَبْط (setter method) وجَلْب (getter method)، ولكن لاحِظ أن أي تَغْيِير يُجرَى على أي من تلك الخاصيات لن يُؤثِر على ما قد رُسِم بالفعل وإنما يُطبَق فقط على ما سيُرسَم مستقبلًا. على سبيل المثال، تُعدّ الطريقة المُستخدَمة للمَلْئ (filling) واحدة من تلك الخاصيات ويُسنَد إليها كائن من الصَنْف <code>Paint</code>. يَتَوفَّر التابعان <code>g.setFill(paint)‎</code> و <code>g.getFill()‎</code> لضَبْط قيمة تلك الخاصية وجَلْبها على الترتيب. سنَستخدِم كائنًا من الصَنْف <code>Color</code> لهذه الخاصية دائمًا خلال هذا الفصل. الطريقة المُستخدمَة لرَسْم حواف الأشكال (stroking) هي خاصية آخرى بكائن السياق الرسومي ويُسنَد إليها أيضًا كائن من الصَنْف <code>Paint</code> يُمكِن ضَبْطه وجَلْبه باِستخدَام التابعين <code>g.setStroke(paint)‎</code> و <code>g.getStroke()‎</code> على الترتيب. يُمكِن أيضًا ضَبْط خاصية حجم الحواف (strokes) وجَلْب قيمتها باستدعاء التابعين <code>g.setLineWidth(w)‎</code> و <code>g.getLineWidth()‎</code> على الترتيب حيث <code>w</code> قيمة من النوع <code>double</code>. يَتَوفَّر أيضًا التابعان <code>g.setFont(font)‎</code> و <code>g.getFont()‎</code> لضَبْط قيمة خاصية الخط (font) المُستخدَم لرسم النصوص وجَلْب قيمتها على الترتيب.
</p>

<p>
	إن عملية رَسْم حواف شكل معين (stroking) هي أَشْبه ما تَكُون بعملية سَحب قلم على طول حواف ذلك الشكل، وبحيث تُرسَم تلك الحواف على جانبي المسار الفعلي للقلم بعَرْض يُساوِي نصف عَرْض (width/size) القلم على كل جانب. يُحدَّد عَرْض القلم باِستخدَام الخاصية <code>linewidth</code>. على سبيل المثال، إذا رسمنا خطًا أفقيًا بين النقطتين (١٠٠ ،١٠٠) و (٣٠٠، ١٠٠) بعَرْض يُساوِي ١، فسيَقَع نصف الحافة (stroke) أعلى الخط الأفقي بينما سيقع النصف الآخر أسفله، لذلك لا يُغطَى البكسل بالكامل ويضطرّ الحاسوب عندها لخَلْط كُلًا من اللون المُستخدَم لرسم الحافة واللون الحالي للبكسل. في المقابل، إذا رسمت خطًا بين النقطتين (١٠٠.٥، ١٠٠.٥) و (٣٠٠.٥، ١٠٠.٥)، فسيُغطَي البكسل بالكامل. في العموم، عندما تَرسِم شيئًا يُغطِي بكسلًا معينًا تغطية جزئية، فإن اللون المُستخدَم للرسم يُمزَج مع اللون الحالي للبكسل بدلًا من أن يستبدله، وذلك لتقليل خشونة الأشكال المُكوَّنة من بكسلات كاملة مثل الخط والشكل البيضاوي بالصورة السابقة، وهو ما يُعرَف بإجراء "التنعيم (anti-aliasing)".
</p>

<p>
	يُمكِننا أيضًا أن نرسم كائن صورة من النوع <code>Image</code> داخل حاوية (canvas)، ونَستعرِض فيما يلي بعضًا من التوابع (methods) المُستخدَمة لهذا الغرض:
</p>

<ul>
<li>
		التابع <code>g.drawImage(image,x,y)‎</code>: يَستقبِل صورة من خلال المُعامِل <code>image</code> من النوع <code>Image</code>، ويَرسمها بحيث يَقَع ركنها الأيسر العلوي بإحداثيات النقطة (x,y)، وتَظَهَر الصورة بحجمها الفعليّ.
	</li>
	<li>
		التابع <code>g.drawImage(image,x,y,w,h)‎</code>: يَرسِم الصورة المُمثَلة بالمُعامِل <code>image</code> داخل مستطيل يَقَع ركنه الأيسر العلوي بإحداثيات النقطة (x,y) بعَرْض يُساوِي <code>w</code> وارتفاع يُساوِي <code>h</code>. قد تَمتدّ الصورة أو تنكمش بحيث تتلائم مع المستطيل الحاضن لها.
	</li>
	<li>
		التابع <code>g.drawImage(image,sx,sy,sw,sh, dx,dy,dh,dw)‎</code>: يَرسِم المحتويات الموجودة "بمستطيل مصدر (source)" مُحدَّد ضِمْن الصورة المُمرَّرة <code>image</code> إلى "مستطيل مقصد (destination)" مُحدَّد ضِمْن الحاوية (canvas) مما يَسمَح برسم جزء معين من صورة. يُحدِّد المُعامِلان <code>sx</code> و <code>sy</code> إحداثيات النقطة التي يَقَع بها الركن الأيسر العلوي "لمستطيل المصدر" بينما يُحدِّد المُعامِلان <code>sw</code> و <code>sh</code> كُلًا من عَرْض المستطيل وارتفاعه. بالمثل، تُحدِّد المُعامِلات (parameters) الأربعة الأخيرة المُمرَّرة مَوضِع "مستطيل المقصد" وأبعاده.
	</li>
</ul>
<p>
	لنأخُذ بعض الأمثلة الفعلية. يَستخدِم المثال التالي تشكيلة مختلفة من الخطوط (fonts) لرَسْم نص (text). تحديدًا، يَرسِم البرنامج السِلسِلة النصية "Hello JavaFX" بخطوط مختلفة كما يَملْئ (fill) النص بلون عشوائي مع حواف (stroke) سوداء نحيفة، وأخيرًا يضعها بمكان عشوائي.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58450" data-ss1614082532="1" data-ss1614083009="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/002Random_Strings.png.ff253c5b467f2a120a775104d8fb9ebb.png" rel=""><img alt="002Random_Strings.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58450" data-unique="q6t5iyiaz" src="https://academy.hsoub.com/uploads/monthly_2021_02/002Random_Strings.png.ff253c5b467f2a120a775104d8fb9ebb.png"></a>
</p>

<p>
	يَستخدِم التابع <code>start()‎</code> بعض التوابع المُنتجة (factory methods) الساكنة المُعرَّفة بالصنف <code>Font</code> لإنشاء خمسة خطوط (fonts):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_28" style="">
<span class="pln">font1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Times New Roman"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
font2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Arial"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="pln"> </span><span class="typ">FontPosture</span><span class="pun">.</span><span class="pln">ITALIC</span><span class="pun">,</span><span class="pln"> </span><span class="lit">28</span><span class="pun">);</span><span class="pln">
font3 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Verdana"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">);</span><span class="pln">
font4 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="lit">40</span><span class="pun">);</span><span class="pln">
font5 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">.</span><span class="pln">font</span><span class="pun">(</span><span class="str">"Times New Roman"</span><span class="pun">,</span><span class="typ">FontWeight</span><span class="pun">.</span><span class="pln">BOLD</span><span class="pun">,</span><span class="typ">FontPosture</span><span class="pun">.</span><span class="pln">ITALIC</span><span class="pun">,</span><span class="lit">60</span><span class="pun">);</span></pre>

<p>
	يُعرِّف البرنامج تابعًا اسمه <code>draw()‎</code> ويُوكِل إليه مُهِمّة إعادة رَسْم محتويات حاوية (canvas). يُستدعَى ذلك التابع عند إنشاء حاوية (canvas) لأول مرة، وكذلك بكل مرة يَنقُر فيها المُستخدِم على زر "أَعِد الرسم (redraw)". يَملأ التابع أولًا تلك الحاوية (canvas) بخلفية بيضاء لمسح محتوياتها السابقة، ثم يَرسِم ٢٥ نسخة من السِلسِلة النصية "Hello JavaFX" بأماكن عشوائية ضِمْن الحاوية، وبحيث تَكُون كل نُسخة منها مملوءة بلون (fill color) عشوائي ومُحاطة بحواف سوداء وكذلك مرسومة بخط (font) عشوائي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_30" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">GraphicsContext</span><span class="pln"> g </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getGraphicsContext2D</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">double</span><span class="pln"> width </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getHeight</span><span class="pun">();</span><span class="pln">

    g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">WHITE </span><span class="pun">);</span><span class="pln">  </span><span class="com">// fill with white background</span><span class="pln">
    g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">25</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

        </span><span class="com">// اضبط الخط لأي من الخطوط الخمسة المتاحة</span><span class="pln">

        </span><span class="typ">int</span><span class="pln"> fontNum </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="lit">5</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">())</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fontNum</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
            g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln">font1</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2</span><span class="pun">:</span><span class="pln">
            g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln">font2</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">3</span><span class="pun">:</span><span class="pln">
            g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln">font3</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">4</span><span class="pun">:</span><span class="pln">
            g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln">font4</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">5</span><span class="pun">:</span><span class="pln">
            g</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln">font5</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="com">// end switch</span><span class="pln">

        </span><span class="com">// اضبط اللون إلى درجة لون عشوائية ساطعة ومشبعة</span><span class="pln">

        </span><span class="kwd">double</span><span class="pln"> hue </span><span class="pun">=</span><span class="pln"> </span><span class="lit">360</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">hsb</span><span class="pun">(</span><span class="pln">hue</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

        </span><span class="com">// اضبط مَوضِع السلسلة النصية عشوائيًا</span><span class="pln">

        </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">;</span><span class="pln">
        x </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">50</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*(</span><span class="pln">width</span><span class="pun">+</span><span class="lit">40</span><span class="pun">);</span><span class="pln">
        y </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*(</span><span class="pln">height</span><span class="pun">+</span><span class="lit">20</span><span class="pun">);</span><span class="pln">

        </span><span class="com">// ارسم الرسالة النصية</span><span class="pln">

        g</span><span class="pun">.</span><span class="pln">fillText</span><span class="pun">(</span><span class="str">"Hello JavaFX"</span><span class="pun">,</span><span class="pln">x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">);</span><span class="pln">

        </span><span class="com">// ارسم حواف النص باللون الأسود</span><span class="pln">

        g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLACK</span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">strokeText</span><span class="pun">(</span><span class="str">"Hello JavaFX"</span><span class="pun">,</span><span class="pln">x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">);</span><span class="pln">

    </span><span class="pun">}</span><span class="pln"> </span><span class="com">// end for</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end draw()</span></pre>

<p>
	تَتَوفَّر شيفرة البرنامج كاملة بالملف <code><a data-ss1614082532="1" data-ss1614083009="1" href="http://math.hws.edu/javanotes/source/chapter6/RandomStrings.java" rel="external nofollow">RandomStrings.java</a></code>.
</p>

<p>
	يَرسِم البرنامج التالي ٥ ورق لعب سُحبَت عشوائيًا من مجموعة ورق لعب (deck) كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58451" data-ss1614082532="1" data-ss1614083009="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/003Random_Cards.png.df9792b88d4becb497c5aedc95829323.png" rel=""><img alt="003Random_Cards.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58451" data-unique="pymuuj0kl" src="https://academy.hsoub.com/uploads/monthly_2021_02/003Random_Cards.png.df9792b88d4becb497c5aedc95829323.png"></a>
</p>

<p>
	اِستخدَمنا الصَنْفين <code>Card</code> و <code>Deck</code> من القسم ٥.٤ لتمثيل كُلًا من ورقة اللعب ومجموعة ورق اللعب على الترتيب. أُخذَت صور ورق اللعب بالأعلى من ملف المورد <code>cards.png</code> الموجود بالبرنامج، والمأخوذ من <a data-ss1614082532="1" data-ss1614083009="1" href="http://www.gnome.org." rel="external nofollow">مشروع جينوم لسطح المكتب</a>. يَتَضمَّن ذلك الملف صورًا لجميع ورق اللعب مُرتَّبة ضِمْن عدد من الصفوف والأعمدة. تَعرِض الصورة التالية نُسخة مُصغرة من ذلك الملف:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58452" data-ss1614082532="1" data-ss1614083009="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/004Cards.png.53bf7e02b96fe21b910e715907101a88.png" rel=""><img alt="004Cards.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58452" data-unique="onsruv3jl" src="https://academy.hsoub.com/uploads/monthly_2021_02/004Cards.thumb.png.3097cad30ef541f08b6002d553016a1e.png"></a>
</p>

<p>
	يُمكِننا تحميل ملف الصورة إلى البرنامج بكتابة التالي داخل التابع <code>start()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_32" style="">
<span class="pln">cardImages </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">(</span><span class="str">"cards.png"</span><span class="pun">);</span></pre>

<p>
	حيث <code>cardImages</code> هو مُتْغيِّر نُسخة (instance variable) من النوع <code>Image</code>. لنَفْترِض الآن أننا نريد رَسْم ورقة اللعب الواقعة برقمي الصف والعمود <code>R</code> و <code>C</code> على الترتيب ضِمْن كائن سياق رُسومي <code>g</code> من النوع <code>GraphicsContext</code> مع مراعاة أن الصفوف والأعمدة مُرقَّّمة بدءًا من الصفر. لمّا كان حجم أي ورقة لعب بالصورة هو ٧٩ * ١٢٣ بكسل، فإن الركن الأيسر العلوي لأي ورقة لعب يقع بإحداثيات النقطة <code>(‎79*C,123*R)</code> . إذًا، لكي نَضَع ورقة لعب داخل حاوية (canvas) بحيث يقع ركنها الأيسر العلوي بإحداثيات النقطة (x,y)، يُمكِننا أن نَستخدِم النسخة الثالثة من التابع <code>drawImage()‎</code>، والتي تَستقبِل "مستطيل مصدر" ضِمْن صورة بالإضافة إلى "مستطيل مقصد" ضِمْن حاوية (canvas) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_34" style="">
<span class="pln">g</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln"> cardImages</span><span class="pun">,</span><span class="pln"> </span><span class="lit">79</span><span class="pun">*</span><span class="pln">C</span><span class="pun">,</span><span class="lit">123</span><span class="pun">*</span><span class="pln">R</span><span class="pun">,</span><span class="lit">79</span><span class="pun">,</span><span class="lit">123</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">,</span><span class="lit">79</span><span class="pun">,</span><span class="lit">123</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	بفَرْض أن المُتْغيِّر <code>card</code> -من النوع <code>Card</code>- يُمثِل ورقة اللعب التي نَرغَب برسمها، يُمكِننا إذًا أن نَستخدِم التابعين <code>card.getValue()‎</code> و <code>card.getSuit()‎</code> لمَعرِفة كُلًا من قيمة (value) ورقة اللعب ورمزها (suit) على الترتيب. بعد ذلك، سنحتاج إلى تَحْوِيل هاتين القيمتين بطريقة ما إلى رقمي الصف والعمود المُناظرين لتلك الورقة. لمّا كنا ننوي ترك مسافة قدرها ٣٠ بكسل بين كل ورقة لعب والورقة التي تليها بالحاوية (canvas)، فلابُدّ من مراعاة ذلك عند حِسَاب مَوضِع ورقة اللعب. بالشيفرة التالية، يَسحَب التابع <code>draw()‎</code> خمسة أوراق من مجموعة ورقة اللعب (deck) سحبًا عشوائيًا ثم يَرسِمها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_36" style="">
<span class="kwd">private</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">GraphicsContext</span><span class="pln"> g </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getGraphicsContext2D</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">Deck</span><span class="pln"> deck </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Deck</span><span class="pun">();</span><span class="pln">
    deck</span><span class="pun">.</span><span class="pln">shuffle</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// الركن الأيسر العلوي لمستطيل المصدر بصورة ورق اللعب</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> sx</span><span class="pun">,</span><span class="pln">sy</span><span class="pun">;</span><span class="pln">  
    </span><span class="com">// الركن الأيسر العلوي لمستطيل المقصد بالحاوية</span><span class="pln">
    </span><span class="kwd">double</span><span class="pln"> dx</span><span class="pun">,</span><span class="pln">dy</span><span class="pun">;</span><span class="pln">  

    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Card</span><span class="pln"> card </span><span class="pun">=</span><span class="pln"> deck</span><span class="pun">.</span><span class="pln">dealCard</span><span class="pun">();</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">card</span><span class="pun">);</span><span class="pln"> </span><span class="com">// for testing</span><span class="pln">
        sx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">79</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">card</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()-</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        sy </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> card</span><span class="pun">.</span><span class="pln">getSuit</span><span class="pun">());</span><span class="pln">
        dx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="lit">79</span><span class="pun">+</span><span class="lit">20</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        dy </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln"> cardImages</span><span class="pun">,</span><span class="pln"> sx</span><span class="pun">,</span><span class="pln">sy</span><span class="pun">,</span><span class="lit">79</span><span class="pun">,</span><span class="lit">123</span><span class="pun">,</span><span class="pln"> dx</span><span class="pun">,</span><span class="pln">dy</span><span class="pun">,</span><span class="lit">79</span><span class="pun">,</span><span class="lit">123</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end draw()</span></pre>

<p>
	يُمكِنك الإطلاع على النسخة الكاملة من البرنامج بالملف <code><a data-ss1614082532="1" data-ss1614083009="1" href="http://math.hws.edu/javanotes/source/chapter6/RandomCards.java" rel="external nofollow">RandomCards.java</a></code>.
</p>

<h2>
	التنسيق عبر CSS
</h2>

<p>
	تُعدّ لغة أوراق الأنماط المتعاقبة (Cascading Style Sheets) -وتُختصَر إلى CSS-، واحدة من ضِمْن عدة لغات يَشيِع اِستخدَامها لإنشاء صفحات الإنترنت، فبإمكانها أن تَتَحكَّم بالألوان والخطوط وكذلك بحواف العناصر ضِمْن الصفحة. تُدعِّم منصة جافا إف إكس (JavaFX) تلك اللغة بغرض ضَبْط مظهر مُكوِّنات واجهة المُستخدِم الرسومية (GUI). في الواقع، يُمكِن للغة الجافا أن تُنفِّذ أي شيء قد تقوم به تلك اللغة، ولكن هنالك بعض الأمور التي يَكُون من الأسهل تّنْفيذها باِستخدَام لغة CSS، وهو ما سنَتعرَض لبعض منه خلال هذا القسم. إذا كنت على دراية بلغة CSS، يُمكِنك الإطلاع على <a data-ss1614082532="1" data-ss1614083009="1" href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html" rel="external nofollow">دليل استخدامها بجافا إف إكس</a>.
</p>

<p>
	تَتَكوَّن أي قاعدة نمط (style rule) بلغة CSS من خاصية (property) وقيمة، وتبدأ أسماء جميع تلك الخاصيات بمنصة جافا إف إكس (JavaFX) بـالمحارف "-fx-" لتفريقها عن خاصيات CSS العادية. على سبيل المثال، تَتَوفَّر الخاصيات <code>‎-fx-border-color</code> و <code>‎-fx-border-width</code> لوَضْع حافة (border) حول كثير من مُكوِّنات الواجهة. يُمكِنك أن تَستخدِم اسم لون مثل <code>red</code> أو <code>lightblue</code> كقيمة للخاصية <code>‎-fx-border-color</code> أو قد تختار أن تَستخدِم الصياغة <code>‎#RRGGBB</code> حيث <code>R</code> و <code>G</code> و <code>B</code> هي عبارة عن أرقام ست عشريّة (hexadecimal digits). يُمكِن للأعداد الست عشريّة (hexadecimal number) المُكوَّنة من رقمين (digit) أن تُمثِل الأعداد من ٠ وحتى ٢٥٥، لذا تستطيع <code>RR</code> و <code>GG</code> و <code>BB</code> تمثيل كُلًا من المُكوِّن الأحمر والأخضر والأزرق للون على الترتيب، وتتراوح قيم جميعها من ٠ وحتى ٢٥٥. على سبيل المثال، يُمثِل <code>‎#FF0000</code> لونًا أحمرًا نقيًا بينما يُمثِل <code>‎#004444</code> لونًا أخضر-أزرق داكنًا.
</p>

<p>
	أما بالنسبة لخاصية عَرْض الحافة (border width)، فيُمكِنها أن تُخصَّص بقيمة واحدة مُفردة مُكوَّنة من عدد متبوع بوحدة قياس وبدون أي فراغ بينهما، وفي تلك الحالة، تُطبَق تلك القيمة على جميع جوانب الحافة الأربعة. على سبيل المثال، قد تُستخدَم القيمة "3px" أو "0.2cm". تُشير "px" إلى "بكسل (pixel)" وبالتالي "3px" تَعنِي ثلاثة بكسل. في المقابل، يُمكِن لقيمة الخاصية أن تُخصَّص بأربع قيم يَفصِل بين كُلًا منها فراغ، وذلك لتَحْديد عَرْض كل جانب من جوانب الحافة على حدى وفقًا لهذا الترتيب: أعلى، يمين، أسفل، يسار. يُمكِننا مثلًا تَخْصِيص حافة زرقاء سميكة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2079_56" style="">
<span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> blue</span><span class="pun">;</span><span class="pln"> </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">border</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span></pre>

<p>
	أو يُمكِننا اِستخدَام التالي للحصول على حافة حمراء داكنة أكثر سماكة من الأعلى الموازنة مع الجوانب الآخرى:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2079_54" style="">
<span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#550000; -fx-border-width: 3px 1px 1px 1px</span></pre>

<p>
	عندما تُستخدَم عدة قواعد CSS معًا، لابُدّ أن يَفصِل بينها فاصلة منقوطة.
</p>

<p>
	يُمكِننا أيضًا أن نَضبُط لون الخلفية لمُكوِّن واجهة باِستخدَام الخاصية <code>‎-fx-background-color</code>، وتُخصَّص قيمتها بنفس طريقة تَخْصِيص اللون للخاصية <code>‎-fx-border-color</code>.
</p>

<p>
	في المقابل، يُشار إلى الفراغ المتروك بين محتويات المُكوِّن وحافته الخارجية باسم "الحشوة (padding)"، وتُستخدَم الخاصية <code>‎-fx-padding</code> لتَحْدِيد عَرْضها. بالمثل من خاصية عَرْض الحافة (border width)، تُخصَّص قيمة <code>‎-fx-padding</code> إما كقيمة مُفردة مثل <code>‎-fx-padding: 8px</code> أو كقائمة من أربع قيم.
</p>

<p>
	يَستقبِل التابع <code>setStyle()‎</code> مُعامِلًا (parameter) من النوع <code>String</code> يَتَضمَّن قاعدة نمط (style rule) واحدة أو أكثر، ويُطبِقها على مُكوِّن واجهة معين. على سبيل المثال، لنَفْترِض أن المُتْغيِّر <code>message</code> عبارة عن عنوان نصي (label) من النوع <code>Label</code>. لا تَشتمِل العناوين النصية (labels) على حافة (border) أو حشوة (padding) افتراضيًا، ولكن يُمكِن تَخْصِيص أيًا منها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2079_52" style="">
<span class="pln">message</span><span class="pun">.</span><span class="pln">setStyle</span><span class="pun">(</span><span class="pln">
   </span><span class="str">"-fx-padding: 5px; -fx-border-color: black; -fx-border-width: 1px"</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تَضبُط الخاصية <code>‎-fx-font</code> الخط المُستخدَم لعَرْض النصوص ضِمْن مُكوِّن واجهة. تُخصِّص قيمة تلك الخاصية كُلًا من نوع الخط (font family) وحجمه، كما يُمكِنها أن تُخصِّص وزن الخط (font weight) مثل "bold" و طريقة تَموضُعه (font posture) مثل "italic" أو كليهما. اُنظر الأمثلة التالية:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2079_58" style="">
<span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30pt</span><span class="pln"> </span><span class="str">"Times New Roman"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">:</span><span class="pln"> bold italic </span><span class="lit">18pt</span><span class="pln"> serif</span><span class="pun">;</span><span class="pln">
</span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">:</span><span class="pln"> bold </span><span class="lit">42pt</span><span class="pln"> monospace</span><span class="pun">;</span></pre>

<p>
	لابُدّ من إحاطة اسم نوع الخط (font family) بعلامتي اقتباس (quotes) في حالة احتوائه على أي فراغات. تُعدّ أنواع الخطوط "serif" و "monospace" المُخصَّصة بالمثالين الأخيرين وكذلك الأنواع "sans-serif" و "cursive" و "fantasy" أسماءً عامة (generic) أي أنها تُحدِّد نمط عام للخطوط. على سبيل المثال، يُضَاف إلى المحارف (characters) بالنمط "serif" خطوطًا قصيرة لأغراض جمالية أعلى الحرف "I" وأسفله. في المقابل، لا يُضَاف ذلك إلى المحارف بالنمط "sans-serif". أخيرًا، تَحتَل جميع المحارف بالنمط "monospace" نفس المساحة الأفقية، ولذلك فإنها تُستخدَم عادةً عند الحاجة إلى طباعة المحارف بصورة مُصطفّة ضِمْن أعمدة.
</p>

<p>
	يُمكِنك ضَبْط خاصيات أخرى كثيرة باِستخدَام CSS، ولكننا سنكتفي بهذا القدر كما أننا سنَستخدِم CSS فقط لضَبْط الحواف (borders) والحشوات (paddings) وألوان الخلفية والخطوط.
</p>

<p>
	عادةً ما تَكُون عملية ضبط الأنماط (style) مملة خاصة عند وجود عدد كبير من مُكوِّنات الواجهة. في المقابل تُسهِل أوراق الأنماط المتعاقبة (CSS style sheet) من تلك العملية حيث يُمكِنها أن تُطبِق نمطًا معينًا (style) على مُكوِّن مُفرد وحيد أو على مجموعة معينة من المُكوِّنات أو على جميع مُكوِّنات صَنْف معين. ورقة النمط (style sheet) هي عبارة عن ملف بامتداد <code>‎.css</code>. نَستعرِض خلال المثال التالي ورقة نمط (style sheet) تُطبِق بعضًا من قواعد النمط (style rules) على جميع المكونات من الصَنْفين <code>Label</code> و <code>Button</code>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2079_48" style="">
<span class="typ">Button</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">:</span><span class="pln"> bold </span><span class="lit">16pt</span><span class="pln"> </span><span class="str">"Times New Roman"</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">text</span><span class="pun">-</span><span class="pln">fill</span><span class="pun">:</span><span class="pln"> darkblue</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">Label</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">15pt</span><span class="pln"> sans</span><span class="pun">-</span><span class="pln">serif</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7px</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> darkred</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">border</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">text</span><span class="pun">-</span><span class="pln">fill</span><span class="pun">:</span><span class="pln"> darkred</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">-</span><span class="pln">fx</span><span class="pun">-</span><span class="pln">background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> pink</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كأي ملف صورة، يُمكِن لأي ملف ورقة نمط (style sheet) أن يَكُون ملف مورد (resource) ضِمْن البرنامج أي تستطيع تَخْزينه بنفس أماكن تَخْزِين الملفات بامتداد <code>‎.class</code>. بفَرْض وجود ملف ورقة نمط اسمه "mystyle.css" بالمجلد الرئيسي للبرنامج، نستطيع إذًا أن نُطبِقه على جميع المُكوِّنات الموجودة ضِمْن مشهد (scene) باِستخدَام التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2079_60" style="">
<span class="pln">scene</span><span class="pun">.</span><span class="pln">getStylesheets</span><span class="pun">().</span><span class="pln">add</span><span class="pun">(</span><span class="str">"mystyle.css"</span><span class="pun">);</span></pre>

<p>
	يُمكِننا إضافة مجموعة من أوراق النمط (style sheets) إلى مشهد (scene) من الصَنْف <code>Scene</code> أو إلى حاويات (containers) مفردة.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614082532="1" data-ss1614083009="1" href="http://math.hws.edu/javanotes/c6/s2.html" rel="external nofollow">Section 2: Some Basic Classes</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614082532="1" data-ss1614083009="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1146</guid><pubDate>Tue, 23 Feb 2021 12:23:49 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x62C;&#x627;&#x641;&#x627; &#x625;&#x641; &#x625;&#x643;&#x633; JavaFX &#x628;&#x633;&#x64A;&#x637;</title><link>https://academy.hsoub.com/programming/java/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A5%D9%81-%D8%A5%D9%83%D8%B3-javafx-%D8%A8%D8%B3%D9%8A%D8%B7-r1145/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_02/6034eeeae76b8_---.png.68f6edb092f80be94c71c5083eea6353.png" /></p>

<p>
	غالبية مُستخدمِي الحاسوب غَيْر مُعتَادون في العموم على برامج سطر الأوامر (command-line programs)، حيث يَجدُون أسلوب التَفاعُل القائم على تَناوُب الحاسوب والمُستخدِم على كتابة النصوص غَيْر مألوف. منذ منتصف الثمانينات، أُتيحَت الحواسيب المنزلية المُدعِّمة لواجهات المُستخدِم الرُسومية (graphical user interfaces)، والتي تُوفِّر للمُستخدِم واجهة أكثر ثراءً يَتَمكَّن من خلالها من اِستخدَام أدوات إِدْخَال مُتعدِّدة مثل الفأرة ولوحة المفاتيح وغيرها للتَفاعُل مع مجموعة من مُكوِّنات الواجهة مثل النوافذ والقوائم والأزرار وصناديق الإِدْخَال النصية وشرائط التمرير وغيرها.
</p>

<p>
	سنتناول خلال هذا القسم بعضًا من أساسيات برمجة الواجهات باِستخدَام مكتبة جافا إف إكس (JavaFX) من خلال دراسة تطبيق بسيط يَعرِض وَاجهة مُكوَّنة من نافذة (window) تَحتوِي على رسالة نصية وثلاثة أزرار كما هو مُبيَّن بالصورة التالية. لاحِظ أننا قد اِستخدَمنا مصطلح تطبيق (application) وليس برنامج (program) ضِمْن هذا السياق.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58446" data-ss1614081757="1" data-ss1614083036="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/001HelloWorldFX_Screenshot.png.e5d6ed8484f0001665c9212923d648ab.png" rel=""><img alt="001HelloWorldFX_Screenshot.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58446" data-unique="reu8kcenh" src="https://academy.hsoub.com/uploads/monthly_2021_02/001HelloWorldFX_Screenshot.png.e5d6ed8484f0001665c9212923d648ab.png"></a>
</p>

<p>
	إذا ضَغَطت على زر "Say Hello"، سيَرُد الحاسوب بالرسالة "Hello World!" أما إذا ضَغَطت على زر "Say Goodbye"، فسيَتَغيَّر نص الرسالة إلى "Goodbye". تستطيع إغلاق التطبيق إما بالنَقْر على زر "Quit" أو على زر غَلْق النافذة.
</p>

<h2>
	تطبيقات جافا إف إكس (JavaFX)
</h2>

<p>
	تَحتوِي حزمة <code>javafx.application</code> على الصَنْف المُجرّد (abstract class)‏ <code>Application</code>، والذي يَحتوِي على تابع النُسخة (instance method) المُجرّد <code>start()‎</code> من بين أشياء أخرى. لكي تُنشِئ تطبيق جافا إف إكس (JavaFX application)، ينبغي أن تُعرِّف صنفًا جديدًا مُمثِلًا للتطبيق. لابُدّ أن يتوسَّع (extend) الصَنْف الجديد من الصَنْف <code>Application</code> كما لابُدّ أن يُوفِّر تعريفًا (definition) للتابع <code>start()‎</code>. (اُنظر كُلًا من القسمين الفرعيين "توسيع (extend) الأصناف الموجودة"، و "الأصناف المجردة (abstract classes)" من الفصل "<a data-ss1614081757="1" data-ss1614083036="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D9%83%D9%84%D9%8A%D8%A9-polymorphism-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D9%85%D8%AC%D8%B1%D8%AF%D8%A9-abstract-classes-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1112/" rel="">الوراثة والتعددية الشكلية (Polymorphism) والأصناف المجردة (Abstract Classes) في جافا</a>").
</p>

<p>
	سيَحتوِي الصَنْف أيضًا على التابع <code>main()‎</code>، والذي سيتولَّى مُهِمّة تّنْفيذ التطبيق، ويُكْتَب عمومًا على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_7" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    launch</span><span class="pun">(</span><span class="pln">args</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما يُنفِّذ الحاسوب البرنامج <code>main()‎</code>، سيُنشِئ التابع <code>launch()‎</code> خيطًا (thread) جديدًا يُعرَف باسم خيط تطبيق جافا إف إكس (JavaFX application thread). كما ذَكَرَنا <a data-ss1614081757="1" data-ss1614083036="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D8%B3-polling-loops-%D9%88%D8%A7%D9%84%D9%85%D9%82%D8%A7%D8%B7%D8%B9%D8%A7%D8%AA-interrupts-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-r963/" rel="">بالقسم ١.٢</a>، يَضُمّ كل خيط متتالية من التَعْليمَات يُمكِن تّنْفيذها بالتوازي مع خيوط أخرى. ينبغي عمومًا لأي شيفرة تَتَعامَل مع واجهة مُستخدِم رسومية (GUI) أن تَقَع ضِمْن خيط تطبيق جافا إف إكس، وهو ما سيَحدُث أتوماتيكيًا فيما يَتعلَّق بما سنقوم به ضِمْن هذا الفصل، ولكن عندما ننتقل إلى الحديث عن الخيوط (threads) تفصيليًا بالفصل ١٢، سنُطوِّر تطبيقات واجهة مُستخدِم رسومية (GUI) تَستخدِم أكثر من خيط، وعندها ستَّضِح أهمية ذلك. سيُنشِئ التابع <code>launch()‎</code> الكائن المُمثِل للتطبيق، وهو في الواقع من نفس صنف التابع، ثم سيَستدعِي تابعه <code>start()‎</code>، والذي تُوكَل له مُهِمّة تجهيز واجهة المُستخدِم الرسومية (GUI)، ومن ثَمَّ فَتْح نافذة (window) بالشاشة.
</p>

<p>
	تستطيع إصدارات معينة من الجافا أن تُشغِّل تطبيقات جافا إف إكس (JavaFX) حتى لو لم تَتَضمَّن التابع <code>main()‎</code>، ولكن لا تَعتمِد على ذلك، واِحرِص دومًا على كتابة التابع <code>main()‎</code> ضِمْن التطبيقات الخاصة بك، وهو ما سنلتزم به.
</p>

<p>
	ها هي شيفرة التطبيق التي سنُناقِشها تفصيليًا بهذا القسم:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_9" style="">
<span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">application</span><span class="pun">.</span><span class="typ">Application</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="typ">Scene</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">stage</span><span class="pun">.</span><span class="typ">Stage</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">application</span><span class="pun">.</span><span class="typ">Platform</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">layout</span><span class="pun">.</span><span class="typ">BorderPane</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">layout</span><span class="pun">.</span><span class="typ">HBox</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">.</span><span class="typ">Pos</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">control</span><span class="pun">.</span><span class="typ">Label</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">control</span><span class="pun">.</span><span class="typ">Button</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">text</span><span class="pun">.</span><span class="typ">Font</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloWorldFX</span><span class="pln"> extends </span><span class="typ">Application</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> start</span><span class="pun">(</span><span class="typ">Stage</span><span class="pln"> stage</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

        </span><span class="typ">Label</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="str">"First FX Application!"</span><span class="pun">);</span><span class="pln">
        message</span><span class="pun">.</span><span class="pln">setFont</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Font</span><span class="pun">(</span><span class="lit">40</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

        </span><span class="typ">Button</span><span class="pln"> helloButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Say Hello"</span><span class="pun">);</span><span class="pln">
        helloButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Hello World!"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
        </span><span class="typ">Button</span><span class="pln"> goodbyeButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Say Goodbye"</span><span class="pun">);</span><span class="pln">
        goodbyeButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Goodbye!!"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
        </span><span class="typ">Button</span><span class="pln"> quitButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="str">"Quit"</span><span class="pun">);</span><span class="pln">
        quitButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Platform</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">

        </span><span class="typ">HBox</span><span class="pln"> buttonBar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HBox</span><span class="pun">(</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> helloButton</span><span class="pun">,</span><span class="pln"> goodbyeButton</span><span class="pun">,</span><span class="pln"> quitButton </span><span class="pun">);</span><span class="pln">
        buttonBar</span><span class="pun">.</span><span class="pln">setAlignment</span><span class="pun">(</span><span class="typ">Pos</span><span class="pun">.</span><span class="pln">CENTER</span><span class="pun">);</span><span class="pln">
        </span><span class="typ">BorderPane</span><span class="pln"> root </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BorderPane</span><span class="pun">();</span><span class="pln">
        root</span><span class="pun">.</span><span class="pln">setCenter</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
        root</span><span class="pun">.</span><span class="pln">setBottom</span><span class="pun">(</span><span class="pln">buttonBar</span><span class="pun">);</span><span class="pln">

        </span><span class="typ">Scene</span><span class="pln"> scene </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Scene</span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> </span><span class="lit">450</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span><span class="pln">
        stage</span><span class="pun">.</span><span class="pln">setScene</span><span class="pun">(</span><span class="pln">scene</span><span class="pun">);</span><span class="pln">
        stage</span><span class="pun">.</span><span class="pln">setTitle</span><span class="pun">(</span><span class="str">"JavaFX Test"</span><span class="pun">);</span><span class="pln">
        stage</span><span class="pun">.</span><span class="pln">show</span><span class="pun">();</span><span class="pln">

    </span><span class="pun">}</span><span class="pln"> </span><span class="com">// end start();</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        launch</span><span class="pun">(</span><span class="pln">args</span><span class="pun">);</span><span class="pln">  </span><span class="com">// Run this Application.</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class HelloWorldFX</span></pre>

<p>
	عادةً ما تَستخدِم تطبيقات جافا إف إكس (JavaFX) أصنافًا عديدة، غالبيتها مُعرَّف بحزم <code>javafx</code> الفرعية (subpackages)، ولهذا ستُلاحِظ وجود عدد كبير من تَعْليمَات الاستيراد <code>import</code> ببداية أي من تطبيقاتها كالمثال بالأعلى. سنَذكُر دومًا الحزمة التي يَقَع بها صَنْف معين عندما نَتَعرَّض له لأول مرة كما تستطيع البحث عن أي صَنْف منها بـ <a data-ss1614081757="1" data-ss1614083036="1" href="https://docs.oracle.com/javase/8/javafx/api/toc.htm" rel="external nofollow">توثيق واجهة برمجة تطبيقات جافا إف إكس (JavaFX <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>)</a>.
</p>

<p>
	يحتوي الصنف المُمثِل للتطبيق <code>HelloWorldFX</code> -بالأعلى- على التابع <code>main</code> المسئول عن بدء تّنْفيذ التطبيق، بالإضافة إلى التابع <code>start()‎</code>. نُضيف عادةً توابعًا آخرى لذلك الصنف بحيث يَستدعِيها التابع <code>start()‎</code> عند تّنْفيذه. علاوة على ذلك، يَحتوِي الصنف المُجرّد <code>Application</code> على عددًا من التوابع (methods) الآخرى، التي يُمكِنك إعادة تعريفها (override) كالتابعين <code>init()‎</code> و <code>stop()‎</code>. يَستدعِي النظام <code>init()‎</code> قبل استدعاء <code>start()‎</code>، بينما يَستدعِى <code>stop()‎</code> عندما يَكُون التطبيق على وشك الإغلاق. في حين أن تعريفهما ضِمْن الصَنْف <code>Application</code> لا يَتَضمَّن أي شيء فعليّ، تستطيع أن تُعيد تعريفهما لإجراء بعض التهيئة المبدئية (initialization) أو لإجراء أي تنظيف (cleanup) ضروري. ومع ذلك، يُمكِن تّنْفيذ أي تهيئة مطلوبة ضِمْن التابع <code>start()‎</code>، لذا نادرًا ما سنلجأ إليهما.
</p>

<h2>
	المرحلة <code>Stage</code> والمشهد <code>Scene</code> ومبيان المشهد <code>SceneGraph</code>
</h2>

<p>
	تُوفِّر حزمة <code>javafx.stage</code> الصَنْف <code>Stage</code>، والذي يُمثِل أي كائن منه نافذةً (window) على شاشة الحاسوب. يُنشِئ النظام كائن مرحلة (stage) من ذلك الصَنْف، ويُمرِّره كمُعامِل (parameter) إلى التابع <code>start()‎</code>. يُمثِل كائن المرحلة، في تلك الحالة تحديدًا، نافذة البرنامج الرئيسية، ويُشار إليه عادةً باسم "المرحلة الرئيسية (primary stage)". قد يُنشِئ البرنامج كائنات مرحلة (stage) آخرى من نفس الصَنْف إذا أراد فَتْح نوافذ آخرى.
</p>

<p>
	تُعدّ أي نافذة (window) بمثابة مساحة ضِمْن شاشة الحاسوب، والتي يُمكِن مَلْؤها بمحتوى معين، فمثلًا قد نَملْؤها ببعض مما يُعرَف باسم مُكونات واجهة المُستخدِم الرُسومية (GUI components) كالقوائم، والأزرار، وصناديق الإِدْخَال النصية، ومساحات الرسم (drawing areas) التي كنا قد اِستخدَمناها ببعض <a data-ss1614081757="1" data-ss1614083036="1" href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-gui-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1070/" rel="">البرامج الرسومية بالقسم ٣.٩</a>. كما ذَكَرَنا مُسْبَقًا، يُنشِئ النظام كائنًا من الصنف <code>Stage</code>، ليُمثِل النافذة الرئيسية -ويُعرَف باسم "المرحلة الرئيسية (primary stage)"- قبل استدعاء التابع <code>start()‎</code>، ومع ذلك تَكُون تلك النافذة (window) ما تزال فارغة وغَيْر مرئية إلى حين استدعائه، وتُوكَل إليه في الحقيقة مُهِمّة مَلْؤها بالمحتوى المناسب، ومن ثَمَّ إظهارها على الشاشة. فمثلًا، بتطبيق <code>HelloWorldFX</code> بالأعلى، يُظهِر السطر الأخير من التابع <code>start()‎</code> -أيّ التعبير <code>stage.show()‎</code>- النافذة على الشاشة بينما تُنشِئ أسطر التابع الآخرى المحتوى، وتُضيفه إلى النافذة إلى جانب ضَبْط بعض الخيارات المُتعلِّقة بكُلًا من المحتوى والنافذة ذاتها، فمثلًا، تَضبُط الشيفرة التالية نص شريط عنوان النافذة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_11" style="">
<span class="pln">stage</span><span class="pun">.</span><span class="pln">setTitle</span><span class="pun">(</span><span class="str">"JavaFX Test"</span><span class="pun">);</span></pre>

<p>
	تتكوَّن أي مرحلة (stage) -تُمثِل نافذة- من مساحة لعَرْض المحتوى (content area)، والتي يُمكِن مَلْئها بـ"مشهد (scene)" يَضُمّ مُكونات واجهة المُستخدِم الرسومية (GUI components) المُفْترَض عَرْضها بالنافذة. تُوفِّر حزمة <code>javafx</code> الصَنْف <code>Scene</code>، والذي يُمثِل أي كائن منه مشهدًا (scene). يُمكِننا ضَبْط المشهد (scene) المُفْترَض عَرْضه بمساحة المحتوى (content area) الخاصة بمرحلة معينة (stage) باِستخدَام التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_15" style="">
<span class="pln">stage</span><span class="pun">.</span><span class="pln">setScene</span><span class="pun">(</span><span class="pln">scene</span><span class="pun">);</span></pre>

<p>
	يُمكِنك أن تمَلْئ أي مشهد (scene) بمُكوِّنات واجهة المُستخدِم الرسومية (GUI components) كالأزرار وأشرطة القوائم وغيرها. تُوفِّر منصة جافا إف إكس (JavaFX) صَنْفًا لكل مُكوِّن منها، فمثلًا، يُمكِنك أن تَستخدِم كائنًا من الصنف <code>Button</code> من حزمة <code>javafx.scene.control</code> لتمثيل "زر ضغط (push button)" مثل الزر "Say Hello" بالتطبيق السابق. بالإضافة إلى ذلك، تُوفِّر منصة جافا إف إكس (JavaFX) أصنافًا لمُكوِّنات تَعمَل بمثابة حاويات (containers) تُمثِل قسمًا من النافذة (window) مثل الكائن <code>buttonBar</code> من النوع <code>HBox</code>. قد يَشتمِل ذلك القسم من النافذة على مُكوِّنات واجهة آخرى بما في ذلك أي مُكوِّن حَاوِي آخر. بتعبير آخر، قد تحتوي نافذة معينة على عدد من مُكوِّنات واجهة المُستخدِم الرسومية داخل حاويات (containers) تَقَع بدورها ضِمْن حاويات أكبر، وجميعها مُمثَل بواسطة كائن. تُشكِّل كل تلك الكائنات ما يُعرَف باسم "مبيان المشهد (scene graph)"، والذي يُبيِّن علاقات الاحتواء بين جميع مُكوِّنات المشهد (scene). تُبيِّن الصورة التالية "مبيان المشهد (scene graph)" للتطبيق السابق:
</p>

<p style="text-align: center;">
	<img alt="002Scene_graph.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58447" data-unique="bpjwhl5a7" src="https://academy.hsoub.com/uploads/monthly_2021_02/002Scene_graph.png.e2e30079af2ed7e22b3e4919fcb145ae.png"></p>

<p>
	لا تُمثِل الصورة بالأعلى سلالة أصناف (class hierarchy) كما قد تَظُنّ، فهي لا تُبيِّن العلاقات بين أصناف تلك الكائنات، وإنما هي بمثابة تَسَلسُل هرمي لعلاقات الاحتواء (containment hierarchy) بين تلك الكائنات، أي تُبيِّن الكيفية التي تَتَضمَّن بها بعض مُكوِّنات المشهد البعض الآخر. فمثلًا، يَتَّضِح من مبيان المشهد (scene graph) أن كُلًا من <code>root</code> و <code>buttonBar</code> عبارة عن مُكوِّنات حاوية (containers) أما <code>message</code> بالإضافة إلى الأزرار الثلاثة فهي مُجرّد مُكوِّنات بسيطة.
</p>

<p>
	يَحتوِي أي مشهد (scene) على مُكوِّن جذري (root component) وحيد. هذا المُكوِّن هو عبارة عن حَاوِي (container) لجميع المُكوِّنات الآخرى الموجودة بالمشهد. أطلقنا الاسم <code>root</code> على المُكوِّن الجذري بالتطبيق السابق، ويُمكِنك بالطبع أن تختار أي اسم آخر تُفضِّله. يُمكِنك ضَبْط المُكوِّن الجذري لمشهد معين بينما تُنشِئ كائن الصنف <code>Scene</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_740_17" style="">
<span class="typ">Scene</span><span class="pln"> scene </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Scene</span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> </span><span class="lit">450</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span></pre>

<p>
	تُحدِّد الأعداد المُمرَّرة لهذا الباني (constructor) كُلًا من عرض المشهد وارتفاعه بوحدة البكسل (pixel). يُمكِنك حَذْف تلك الأعداد، وفي تلك الحالة، ستُحسَب مساحة المشهد بما يتناسب مع محتوياتها.
</p>

<h2>
	العقد (nodes) والتخطيط (layout)
</h2>

<p>
	يتكوَّن أي مبيان مشهد (scene graph) من مجموعة من الكائنات، تُعرَف باسم "العُقَد (nodes)". لابُدّ أن تنتمي تلك الكائنات إلى إحدى الأصناف الفرعية (subclasses) المُشتقَّة من الصَنْف <code>javafx.scene.Node</code>، وقد يَعمَل بعضها كمُكوِّنات حاوية (container)، وفي تلك الحالة، لابُدّ لها من أن تنتمي إلى إحدى الأصناف الفرعية المُشتقَّة من الصَنْف <code>javafx.scene.Parent</code>، والذي هو بدوره صنف فرعي من نفس الصنف <code>Node</code>. لمّا كانت العُقْدة الجذرية (root node) تَعمَل كمُكوِّن حاوي، فلابُدّ أن تَكُون من الصنف <code>Parent</code>. تَشتمِل العُقَد المُمثلة لمُكوِّنات حاوية (containers) على مجموعة من العُقَد (nodes) الآخرى، والتي تُعدّ أبناءً (children) لها.
</p>

<p>
	بتطبيق <code>HelloWorldFX</code> السابق، اِستخدَمنا كائنات من النوع <code>Button</code> لتمثيل الأزرار. في الواقع، هذا الصنف هو صنف فرعي (subclass) من الصنف <code>Parent</code>، وسنرى لاحقًا أن بإمكانه أن يَتَضمَّن عُقَدًا (nodes) آخرى. يَستقبِل باني (constructor) الصنف <code>Button</code> مُعامِلًا لتَخْصِيص النص المكتوب على الزر. بالمثل، <code>message</code> هو عبارة عن كائن عُقْدة (node) من النوع <code>Label</code> المُعرَّف بحزمة <code>javafx.scene.control</code>، ويُستخدَم لعَرْض سِلسِلة نصية من النوع <code>String</code>. يَتَضمَّن أي كائن عُقْدة من الصنف <code>Label</code> على خاصية نوع الخط، والتي تَتَحكَّم بحجم محارف السِلسِلة النصية، وهيئتها، ويُمكِن ضَبْطها باِستخدَام التابع <code>setFont()‎</code> المُعرَّف بنفس الصنف. بالتطبيق السابق، اِستخدَمنا الباني <code>new Font(40)‎</code>، والذي يُحدِّد مُعامِله (parameter) الوحيد حجم الخط المطلوب.
</p>

<p>
	المُكوِّنات الحاوية (containers) هي أيضًا عُقَد (nodes) من النوع <code>Node</code>، ولكن يُمكِنها أن تَشتمِل على عُقَد آخرى كأبناء (children)، ويُشار إلى الكيفية التي يُرتَّب بها هؤلاء الأبناء على الشاشة باسم "التخطيط (layout)". يعني التخطيط (layout) عمومًا ضَبْط كُلًا من حجم المُكوِّنات الواقعة ضِمْن الحاوي ومَوضِعها. على الرغم من تَوفُّر إمكانية لضَبْط تلك القيم بصورة مباشرة، فلربما تُفضِّل الاعتماد على ضَبْطها أتوماتيكيًا بالاستعانة بالمُكوِّن الحاوي (container) نفسه، وفي الواقع، تُعدّ تلك الطريقة أكثر شيوعًا. عمومًا، يُطبِق كل مُكوِّن حاوي سياسة تخطيط (layout policy) مختلفة، فمثلًا، تُرتِّب المُكوِّنات الحاوية من الصنف <code>HBox</code> مُكوِّناتها ضِمْن صف أفقي. اُنظر الباني (constructor) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_23" style="">
<span class="typ">HBox</span><span class="pln"> buttonBar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HBox</span><span class="pun">(</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> helloButton</span><span class="pun">,</span><span class="pln"> goodbyeButton</span><span class="pun">,</span><span class="pln"> quitButton </span><span class="pun">);</span></pre>

<p>
	تُمثِل جميع مُعامِلات (parameter) ذلك الباني فيما عدا الأول عُقَدًا (nodes) ينبغي إضافتها كأبناء (children) للمُكوِّن الحاوي (container)، بحيث يَفصِل بينها فراغًا تُحدِّد مساحته القيمة المُمرَّرة لمُعامِل الباني الأول.
</p>

<p>
	في المقابل، تُطبِق المُكوِّنات الحاوية من الصنف <code>BorderPane</code> سياسة تخطيط (layout policy) مختلفة تمامًا. يستطيع أي مُكوِّن حاوي منها أن يحتوى على ما يَصِل إلى ٥ مُكوِّنات (components)، واحدة بالمنتصف، أما البقية فبالأعلى وبالأسفل وعلى اليسار وعلى اليمين. بالتطبيق السابق، كان المُكوِّن الجذري للمشهد عبارة عن مُكوِّن حاوي من الصنف <code>BorderPane</code>، واستخدمنا التَعْليمَات التالية لإضافة مُكوّنات آخرى إلى منتصفه والجزء السفلي منه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_25" style="">
<span class="pln">root</span><span class="pun">.</span><span class="pln">setCenter</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
root</span><span class="pun">.</span><span class="pln">setBottom</span><span class="pun">(</span><span class="pln">buttonBar</span><span class="pun">);</span></pre>

<p>
	تتوفَّر أيضًا الكثير من الخيارات لضَبْط التخطيط (layout)، فمثلًا يَستخدِم التطبيق السابق إحداها بالتَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_27" style="">
<span class="pln">buttonBar</span><span class="pun">.</span><span class="pln">setAlignment</span><span class="pun">(</span><span class="typ">Pos</span><span class="pun">.</span><span class="pln">CENTER</span><span class="pun">);</span></pre>

<p>
	تُستخدَم تلك التَعْليمَة لوَضْع الأزرار بمنتصف مُكوّن حاوي من الصنف <code>HBox</code>؛ فبدونه، ستَقَع الأزرار على حافة النافذة اليسرى. تعتمد منصة جافا إف إكس (JavaFX) على أنواع التعداد (enumerated type) بكثرة، والتي كنا قد ناقشناها بالقسم الفرعي ٢.٣.٤، وذلك لتخصيص الخيارات المختلفة، مثل نوع التعداد <code>Pos</code> بالشيفرة السابقة.
</p>

<h2>
	الأحداث (event) ومعالجاتها (handlers)
</h2>

<p>
	لا يَقْتصِر دور التابع <code>start()‎</code> على ضَبْط تخطيط (layout) النافذة، وإنما يُستخدَم أيضًا لضَبْط ما يُعرَف باسم "معالجة الحَدَث (event handling)". مثلًا، بتطبيق <code>HelloWorldFX</code> بالأعلى، عندما يَنقُر المُستخدِم على زر معين، يَقَع ما يُعرَف باسم "الحَدَث (event)"، ويُمكِن عندها للتطبيق أن يُعالِج (handle) ذلك الحَدَث من خلال ما يُعرَف باسم "مُعالِجات الأحداث (event handlers)". تَشتمِل معالجة أي حَدَث على كائنين، يُعبِر الأول عن الحَدَث نفسه ويَحمِل معلومات عنه، فمثلًا، عند النقر على زر، يَكُون كائن الحَدَث من النوع <code>ActionEvent</code> ويَحمِل معلومات عن الزر المنقور عليه. أما الكائن الآخر فيُعبِر عن مُعالِج الحدث، ويَكُون من نوع واجهة نوع الدالة <code>EventHandler</code>، والتي تُعرِّف التابع <code>handle(e)‎</code> حيث <code>e</code> هو كائن الحَدَث. والآن لكي تُعالِج حَدَثًا معينًا، ينبغي أن تُنشِئ صنفًا يُنفِّذ (implements) الواجهة <code>EventHandler</code>، ويُوفِّر تعريفًا للتابع <code>handle()‎</code>. ولكن لمّا كانت <code>EventHandler</code> عبارة عن واجهة نوع دالة (functional interface)، فيُمكِن كتابة المُعالِج (handler) بصورة تعبير لامدا (lambda expression). كنا قد ناقشنا تعبيرات لامدا تفصيليًا بالقسم ٤.٥، وهي في العموم شائعة الاِستخدَام بتطبيقات جافا إف إكس (JavaFX)، فتُستخدَم لكتابة مُعالِجات الأحداث (event handlers) من ضِمْن عدة استخدامات آخرى. اُنظر تعبير لامدا التالي على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_29" style="">
<span class="pln">e </span><span class="pun">-&gt;</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Hello World!"</span><span class="pun">)</span></pre>

<p>
	يُمثِل ذلك التعبير مُعالِج حَدَث (event handler) يَستجيب لحَدَث (event) ما بتغيير نص الرسالة إلى "Hello World". يَستقبِل ذلك المُعالِج مُعامِلًا <code>e</code>، يُمثِل الحَدَث، ويَكُون من النوع <code>ActionEvent</code>، والذي لابُدّ من كتابته دائمًا حتى لو لم يَستخدِمه المُعالِج كالمثال بالأعلى؛ وذلك ليَستوفِي صيغة تعبير اللامدا (lambda expression).
</p>

<p>
	ينبغي الآن أن نُسجِّل (register) مُعالِج الحَدَث (event handler) بَعْد كتابته بالكائن المُنتج للحَدَث. فمثلًا بنفس المثال، كان الكائن هو <code>helloButton</code>، ونستطيع أن نُسجِّل المُعالِج (handler) باستدعاء تابع الكائن <code>setOnAction()‎</code>، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_740_31" style="">
<span class="pln">helloButton</span><span class="pun">.</span><span class="pln">setOnAction</span><span class="pun">(</span><span class="pln"> e </span><span class="pun">-&gt;</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">setText</span><span class="pun">(</span><span class="str">"Hello World!"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ضَبطَنا أيضًا مُعالجي (handlers) الزرين الآخرين بنفس الطريقة. لاحِظ أن لدينا ثلاثة كائنات: كائن مُنتج للحَدَث (event) نتيجة لفعل (action) قام به المُستخدِم، وكائن يُمثِل الحَدَث ذاته، وأخيرًا كائن يُمثِل مُعالِج الحَدَث (event handler) كما يَحتوِي على الشيفرة المطلوب تّنْفيذها استجابةً للحَدَث (event)، كما هو مُوضَح بالصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58448" data-ss1614081757="1" data-ss1614083036="1" href="https://academy.hsoub.com/uploads/monthly_2021_02/003Event_Handling.png.39f969fc7b4a2f0d4258e83e95909b2f.png" rel=""><img alt="003Event_Handling.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58448" data-unique="zd8pambf0" src="https://academy.hsoub.com/uploads/monthly_2021_02/003Event_Handling.png.39f969fc7b4a2f0d4258e83e95909b2f.png"></a>
</p>

<p>
	يَتبقَّى لنا فقط توضيح الاستجابة على فعل (action) النقر على زر "Quit"، اِستخدَمنا التعبير <code>Platform.exit()‎</code> لاستدعاء التابع الساكن <code>exit()‎</code> المُعرَّف بالصنف <code>Platform</code>. يُفضَّل عمومًا اِستخدَام تلك الطريقة للإنهاء البرمجي لتطبيقات جافا إف إكس (JavaFX)؛ لأنها تُغلِق خيط التطبيق (application thread)، كما تَستدعِي التابع <code>stop()‎</code> المُعرَّف بالصنف المُمثِل للتطبيق، مما يُعطِي فرصة للمبرمج لإجراء أي تنظيف (clean up) قد يَرغَب به قَبْل الإغلاق، وذلك بخلاف التابع <code>System.exit()‎</code> مثلًا.
</p>

<p>
	يُعدّ هذا القسم بمثابة نظرة عامة ومُختصرة لتطبيقات جافا إف إكس (JavaFX)، ومع ذلك فقد تَعرَّضنا خلالها للكثير من المفاهيم الأساسية. بالأقسام التالية، سنتناول كل ذلك على نحو تفصيلي.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a data-ss1614081757="1" data-ss1614083036="1" href="http://math.hws.edu/javanotes/c6/s1.html" rel="external nofollow">Section 1: A Basic JavaFX Application</a> من فصل Chapter 6: Introduction to GUI Programming من كتاب <a data-ss1614081757="1" data-ss1614083036="1" href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1145</guid><pubDate>Sun, 28 Feb 2021 13:02:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x635;&#x646;&#x627;&#x641; &#x627;&#x644;&#x645;&#x62A;&#x62F;&#x627;&#x62E;&#x644;&#x629; Nested Classes &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D9%85%D8%AA%D8%AF%D8%A7%D8%AE%D9%84%D8%A9-nested-classes-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1115/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/38.png.94c849cb72ea28cf5ee713546869cd15.png" /></p>

<p>
	تُعدّ الأصناف (classes) اللَبِنة الأساسية عالية المستوى (high-level) بالبرنامج، حيث تُستخدَم لتمثيل الأفكار والكيانات المُعقدة ضِمْن البرنامج وما يَرتبِط بها من بيانات (data) وسلوكيات (behavior). يَدفَع ذلك البعض إلى وَضْع الأصناف بمكانة خاصة، فيُحاولون تَجنُّب كتابة الأصناف الصغيرة جدًا والموجودة فقط لتجميع مجموعة من البيانات معًا، ويَرَونَها تافهة مع أنها قد تَكُون مفيدة أحيانًا أو حتى ضرورية في بعض الأحيان الآخرى. لحسن الحظ، تَرفَع الجافا هذا الحرج؛ حيث تَسمَح بكتابة تعريف صَنْف (class definition) ضِمْن تعريف صَنْف آخر، وبذلك لَمْ تَعُدْ تلك الأصناف الصغيرة جدًا موجودة بمُفردها، وإنما أَصبحَت جُزءًا من أصناف أكبر ذات هيبة. إلى جانب ذلك، هنالك العديد من الأسباب الآخرى التي قد تَدفَعك لتَضْمِين تعريف صنف (class definition) داخل صنف آخر.
</p>

<p>
	الصَنْف المُتداخِل (nested class) هو ببساطة أيّ صنف يَقَع تعريفه (definition) داخل تعريف صنف آخر. وفي الواقع، يُمكِن حتى كتابة تعريف صَنْف داخل تابع (method) والذي بدوره يَقَع ضِمْن صنف. الأصناف المُتداخِلة إِما أن تَكُون مُسمَاة (named) أو مجهولة الاسم (anonymous). سنعود لاحقًا إلى الأصناف مجهولة الاسم (anonymous classes) أما بالنسبة للأصناف المُتداخِلة المُسمَاة (named nested class)، فيُمكِنها أن تَكُون ساكنة (static) أو غَيْر ساكنة (non-static) وذلك كأي شيء مُعرَّف ضِمْن صَنْف. بالمثل من الأصناف، يُمكِن للواجهات (interfaces) أيضًا أن تَقَع ضِمْن تعريفات الأصناف (class definitions) وقد تَكُون ساكنة (static) أو غَيْر ساكنة (non-static)، كما قد تَحتوِي تعريفات الواجهات (interface definitions) على أصناف مُتداخِلة ساكنة (static nested classes) أو واجهات آخرى، ولكننا لن نَتَعرَّض لذلك.
</p>

<h2>
	الأصناف المتداخلة (nested) الساكنة
</h2>

<p>
	تُعرَّف الأصناف المُتداخِلة (nested class) الساكنة كأي تعريف لصَنْف عادي باستثناء أمرين: أولهما وقوع التعريف ضِمْن صنف آخر، والآخر اِستخدَام المُبدِّل <code>static</code> ضِمْن التّصْريح. يُعدّ أي صنف مُتداخِل ساكن جزءًا من البنية الساكنة (static) للصَنْف الحاضن له والذي قد يَستخدِمه لإنشاء كائنات بالطريقة العادية. في المقابل، يُمكِن اِستخدَامه أيضًا خارج صَنْفه الحاضن، ولكن ينبغي أن يُشير اسمه في تلك الحالة إلى كَوْنه عضوًا بصَنْفه الحاضن، أي ينبغي أن يُستخدَم الاسم الكامل للصنف والذي يَتَكوَّن من اسم صَنْفه الحاضن متبوعًا بنقطة ثم باسمه. كأيّ مُكوِّن ساكن آخر ضِمْن صَنْف، تُعدّ الأصناف المُتداخِلة (nested class) الساكنة جزءًا من الصَنْف ذاته بنفس الطريقة التي يُعدّ بها أي مُتْغيِّر عضو (member variable) ساكن جزءًا من الصَنْف ذاته.
</p>

<p>
	لنَفْترِض مثلًا وجود صَنْف، اسمه <code>WireFrameModel</code> يُمثِل مجموعة من الخطوط بفضاء ثلاثي الأبعاد. يَحتوِي ذلك الصنف على صَنْف مُتداخِل ساكن <code>Line</code> يُمثِل خطًا واحدًا. يُمكِننا الآن كتابة تعريف الصنف <code>WireFrameModel</code> والصنف المُتداخِل <code>Line</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_7" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WireFrameModel</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// أعضاء آخرى ضمن الصنف</span><span class="pln">

   </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Line</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// ‫يمثل خطًا من النقطة (x1,y1,z1) إلى النقطة (x2,y2,z2) بفضاء </span><span class="pln">
       </span><span class="com">// ثلاثي الأبعاد</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> x1</span><span class="pun">,</span><span class="pln"> y1</span><span class="pun">,</span><span class="pln"> z1</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> x2</span><span class="pun">,</span><span class="pln"> y2</span><span class="pun">,</span><span class="pln"> z2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف Line</span><span class="pln">

   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// أعضاء آخرى ضمن الصنف</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف WireFrameModel</span></pre>

<p>
	لاحِظ أن الاسم الكامل للصَنْف المُتداخِل هو <code>WireFrameModel.Line</code> ويُمكِنك اِستخدَامه للتّصْريح عن مُتْغيِّر مثلًا. تستطيع تَحْديدًا إنشاء كائن من الصَنْف <code>Line</code> باِستخدَام البَانِي <code>new Line()‎</code> من داخل الصَنْف <code>WireFrameModel</code>، بينما قد تَستخدِم التعبير <code>new WireFrameModel.Line()‎</code> لإنشائه من خارج الصَنْف.
</p>

<p>
	يستطيع أي صَنْف مُتداخِل (nested) ساكن الوصول إلى أي عُضو ساكن (static members) مُعرَّف بالصنف الحاضن له حتى وإن كان عضوًا خاصًا (private). بالمثل، يستطيع الصَنْف الحاضن لصَنْف مُتداخِل (nested) ساكن الوصول إلى أعضاء ذلك الصنف المُتداخِل (nested) حتى وإن كانت خاصة (private). يُمثِل ذلك دافعًا قويًا لاِستخدَام الأصناف المُتداخِلة (nested)؛ نظرًا لأنها تَسمَح لصَنْف معين بالوصول إلى الأعضاء الخاصة (private) المُعرَّفة بصَنْف آخر دون الحاجة إلى إِتاحة تلك الأعضاء بصورة أعم لجميع الأصناف الآخرى. وأخيرًا، يُمكِن لأي صَنْف مُتداخِل (nested) أن يَكُون خاصًا (private) وعندها يُمكِن اِستخدَامه من داخل الصَنْف الحاضن له فقط.
</p>

<p>
	على الرغم من أن تعريف الصَنْف <code>Line</code> يَقَع ضِمْن الصَنْف <code>WireFrameModel</code>، ستُخزَّن النسخة المُصرَّفة (compiled) من الصَنْف <code>Line</code> بملف مُنفصل، اسمه هو <code>WireFrameModel$Line.class</code>، أي أنه عند تصريف تعريف الصَنْف (class definition) -بالأعلى-، سيُنشِئ المُصرِّف ملفًا منفصلًا لكل صَنْف.
</p>

<h2>
	الأصناف الداخلية (inner)
</h2>

<p>
	يُطلَق على الأصناف المُتداخِلة (nested) غَيْر الساكنة (non-static) اسم "الأصناف الداخلية (inner classes)". من الناحية العملية، هي لا تَختلِف كثيرًا عن الأصناف المُتداخِلة الساكنة (static)، مع أنها تُعدّ جزءًا من كائنات الأصَنْاف الحاضنة لها لا الأصناف ذاتها.
</p>

<p>
	لا تُعدّ الأعضاء غَيْر الساكنة (non-static) المُعرَّفة بأي صنف جزءًا فعليًا من الصنف ذاته على الرغم من أن شيفرتها مُتضمَّنة بتعريف ذلك الصنف (class definition)، وهو ما يَنطبِق على الأصناف الداخلية (inner classes). تُحدِّد تلك الأعضاء ما ستَحتوِيه كائنات ذلك الصنف عند إنشائها، وهو ما يَنطبِق أيضًا على الأصناف الداخلية على الأقل منطقيًا أي كما لو كان كل كائن من الصنف الحاضن يَتَضمَّن نسخة خاصة من الصَنْف المُتداخِل (nested) -لا تَأخُذ ذلك بالمعنى الحرفي-. بإمكان تلك النُسخة الوصول لجميع توابع النُسخ (instance methods) ومُتْغيَّرات النُسخ (instance variables) المُعرَّفة بالكائن حتى وإن كانت خاصة (private). مثلًا، إذا كان لدينا كائنين من صَنْف يَتَضمَّن صنفًا داخليًا (inner class)، فإن نُسختي الصَنْف الداخلي بهذين الكائنين مختلفتان؛ لأنهما يُشيران إلى مُتْغيَّرات نُسخ وتوابع نُسخ تَقَع ضِمْن كائنات مختلفة. كيف تُقرِّر ما إذا كان ينبغي لصنف مُتداخِل (nested) معين أن يَكُون ساكنًا أو غَيْر ساكن؟ الأمر بسيط للغاية: إذا اِستخدَم الصنف المُتداخِل مُتْغيِّر نسخة (instance variable) أو تابع نسخة (instance method) من الصَنْف الحاضن، فلابُدّ أن يَكُون غَيْر ساكن (non-static)، أما إن لم يَستخدِم أيًا منها، فيُمكِنه أن يَكُون ساكنًا (static).
</p>

<p>
	يُستخدَم الصنف الداخلي (inner class) في غالبية الأحوال ضِمْن الصَنْف الحاضن له فقط. وفي تلك الحالة، لا يَختلِف اِستخدَامه كثيرًا عن اِستخدَام أيّ صنف آخر؛ فيُمكِنك أن تُصرِّح عن مُتْغيِّر أو أن تُنشِئ كائنًًا باِستخدَام الاسم البسيط للصَنْف الداخلي، ولكن فقط ضِمْن الأجزاء غَيْر الساكنة (non-static) من الصَنْف.
</p>

<p>
	أما إذا أردت اِستخدَامه من خارج صَنْفه الحاضن له، فلابُدّ من الإشارة إليه باِستخدَام اسمه الكامل والذي يُكْتَب بالصياغة <strong><variablename>.<nestedclassname></nestedclassname></variablename></strong>، حيث أن <strong><variablename></variablename></strong> هو مُتْغيِّر يُشير إلى كائن مُتضمِّن لصَنْف داخلي (inner class). لاحِظ أنه لكي تَتَمكَّن من إنشاء كائن ينتمي لصَنْف داخلي، لابُدّ أولًا أن تُنشِئ كائنًا من صَنْفه الحاضن.
</p>

<p>
	لنَفْترِض أننا نريد كتابة صنف يُمثِل مباراة بوكر، بحيث يَتَضمَّن صنفًا داخليًا يُمثِل لاعبي المباراة. يُمكِننا كتابة تعريف الصنف <code>PokerGame</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_9" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PokerGame</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// يمثل مباراة بوكر</span><span class="pln">

    </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Player</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// يمثل أحد لاعبي المباراة</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف Player</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">Deck</span><span class="pln"> deck</span><span class="pun">;</span><span class="pln">      </span><span class="com">// مجموعة ورق اللعب</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> pot</span><span class="pun">;</span><span class="pln">        </span><span class="com">// قيمة الرهان</span><span class="pln">

    </span><span class="pun">.</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف PokerGame</span></pre>

<p>
	إذا كان <code>game</code> مُتْغيِّرًا من النوع <code>PokerGame</code>، فإنه سيَتَضمَّن نسخة خاصة به من الصَنْف <code>Player</code> على نحو منطقي. كأي صَنْف عادي آخر، تستطيع اِستخدَام التعبير <code>new Player()‎</code> لإنشاء كائن جديد من الصَنْف <code>Player</code> بشَّرْط أن يَقَع ذلك داخل تابع نسخة (instance method) بكائن الصَنْف <code>PokerGame</code>. في المقابل، تستطيع اِستخدَام تعبير مثل <code>game.new Player()‎</code> لإنشاء كائن من الصَنْف <code>Player</code> من خارج الصَنْف <code>PokerGame</code>، ولكنه في الواقع أمر نادر الحدوث. يستطيع أي كائن من الصَنْف <code>Player</code> الوصول إلى مُتْغيِّرات النُسخ <code>deck</code> و <code>pot</code> المُعرَّفة بكائن الصَنْف <code>PokerGame</code>. من الجهة الأخرى، يَمتلك أي كائن من الصَنْف <code>PokerGame</code> نسخة خاصة به من كُلًا من <code>deck</code> و <code>pot</code> و <code>Players</code>. فمثلًا، يَستخدِم لاعبو مباراة بوكر معينة المُتْغيِّرين <code>deck</code> و <code>pot</code> الخاصين بتلك المباراة تَحْديدًا، بينما يَستخدِم لاعبو مباراة بوكر آخرى المُتْغيِّرين <code>deck</code> و <code>pot</code> الخاصين بتلك المباراة الآخرى. ذلك بالتحديد هو تأثير كَوْن الصَنْف <code>Player</code> غير ساكن، وهو في الواقع الطريقة الطبيعية التي ينبغي للاعبين التصرُّف على أساسها. يُمثِل كل كائن من الصَنْف <code>Player</code> لاعبًا ضِمْن مباراة بوكر مُحدَّدة، أما إذا كان الصَنْف <code>Player</code> صَنْْفًا مُستقلًا أو صنفًا مُتداخِلًا (nested) ساكنًا، فسيُمثِل عندها الفكرة العامة للاعب البوكر بصورة مُستقلة عن أي مباراة بوكر مُحدَّدة.
</p>

<h2>
	الأصناف الداخلية مجهولة الاسم (anonymous)
</h2>

<p>
	قد تُعرِّف صنفًا داخليًا (inner class)، ثم تَجِد نفسك تَستخدِمه بسطر وحيد بالبرنامج، فهل ينبغي حقًا في تلك الحالة تعريف مثل ذلك الصنف؟ ربما، ولكن قد تُفضِّل أيضًا أن تَستخدِم صنفًا داخليًا مجهول الاسم (anonymous) في مثل هذه الحالات. يُكْتَب أي صَنْف داخلي مجهول الاسم بالصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_11" style="">
<span class="kwd">new</span><span class="pln">  </span><span class="pun">&lt;</span><span class="pln">superclass</span><span class="pun">-</span><span class="pln">or</span><span class="pun">-</span><span class="pln">interface</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">parameter</span><span class="pun">-</span><span class="typ">list</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">methods</span><span class="pun">-</span><span class="pln">and</span><span class="pun">-</span><span class="pln">variables</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعرِّف الباني -بالأعلى- صَنْفًا جديدًا بدون أن يُعطيه أي اسم، ويُنشِئ كائنًا يَنتمِي إلى ذلك الَصَنْف عند تَشْغِيل البرنامج. في العموم، يُنشِئ هذا الشكل من العَامِل <code>new</code> -والذي تستطيع اِستخدَامه أينما استطعت اِستخدَام العامل <code>new</code> العادي- كائنًا جديدًا ينتمي لصَنْف مجهول الاسم يُشبه الصَنْف أو الواجهة <strong><superclass-or-interface></superclass-or-interface></strong>، والتي تَعمَل كأساس لصنف ذلك الكائن مع إضافة المُتْغيرِّات والتوابع <strong><methods-and-variables></methods-and-variables></strong> المُعرَّفة، أي أنه يُنشِئ كائنًا فريدًا. الأهم من ذلك هو أنه يُنشئِه فقط بذلك الجزء من البرنامج حيث تحتاج إليه. لا يُشترَط أن يَكُون ذلك الأساس صنفًا، فيُمكِن أن يَكُون واجهة (interface) أيضًا، ولكن في تلك الحالة -أي اِستخدَام واجهة كأساس-، يَكُون الصَنْف مجهول الاسم مُلزَمًا بتّنْفيذ (implement) الواجهة، أي بتعريف (define) جميع التوابع المُصرَّح (declare) عنها ضِمْن تلك الواجهة كما أن قائمة المُعامِلات <strong><parameter-list></parameter-list></strong> لابُدّ أن تَكُون فارغة. في المقابل، إذا اِستخدَمت صنفًا كأساس، فبإمكانك تمرير مُعامِلات (parameters) بحيث يَستقبِلها أحد البواني (constructor) المُعرَّفة بالصَنْف الأعلى <strong><superclass></superclass></strong>.
</p>

<p>
	إذا كان لدينا الواجهة <code>Drawable</code> المُعرَّفة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_13" style="">
<span class="kwd">public</span><span class="pln"> interface </span><span class="typ">Drawable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنَفْترِض الآن أننا نحتاج إلى كائن من النوع <code>Drawable</code> لكي يَرسِم مربعًا أحمر اللون طوله يُساوِي ١٠٠ بكسل. قد تختار أن تُعرِّف صنفًا جديدًا يُنفِّذ الواجهة <code>Drawable</code>، ثم تُنشِئ كائنًا منه. في المقابل، قد تَستخدِم صنفًا مجهول الاسم (anonymous class) لإنشاء الكائن بتَعْليمَة واحدة فقط، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_15" style="">
<span class="typ">Drawable</span><span class="pln"> redSquare </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Drawable</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">RED</span><span class="pun">);</span><span class="pln">
          g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="lit">100</span><span class="pun">,</span><span class="lit">100</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">};</span></pre>

<p>
	يُشير <code>redSquare</code> -بالأعلى- إلى كائن يُنفِّذ الواجهة <code>Drawable</code>، ويَرسِم مربعًا أحمر اللون عند استدعاء تابعه <code>draw()‎</code>. لاحِظ أن الفاصلة المنقوطة بنهاية التَعْليمَة ليست جزءًا من تعريف الصنف (class definition)، وإنما هي تلك التي تُوجد بنهاية أي تَعْليمَة تّصْريح (declaration).
</p>

<p>
	يَشيع تمرير الأصناف مجهولة الاسم (anonymous class) كمُعامِلات فعليّة (actual parameters) للتوابع. فمثلًا، يَستقبِل التابع التالي كائنًا من النوع <code>Drawable</code>، ثم يَرسمه بسياقين رُسوميين (graphics contexts) مختلفين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_17" style="">
<span class="kwd">void</span><span class="pln"> drawTwice</span><span class="pun">(</span><span class="pln"> </span><span class="typ">GraphicsContext</span><span class="pln"> g1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">GraphicsContext</span><span class="pln"> g2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Drawable</span><span class="pln"> figure </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    figure</span><span class="pun">.</span><span class="pln">draw</span><span class="pun">(</span><span class="pln">g1</span><span class="pun">);</span><span class="pln">
    figure</span><span class="pun">.</span><span class="pln">draw</span><span class="pun">(</span><span class="pln">g2</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما تَستدعِي ذلك التابع، يُمكِنك أن تُمرِّر صنفًا داخليًا (inner class) مجهول الاسم (anonymous) كقيمة للمُعامِل (parameter) الثالث، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_19" style="">
<span class="pln">drawTwice</span><span class="pun">(</span><span class="pln"> firstG</span><span class="pun">,</span><span class="pln"> secondG</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Drawable</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
             g</span><span class="pun">.</span><span class="pln">fillOval</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="lit">100</span><span class="pun">,</span><span class="lit">100</span><span class="pun">);</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
     </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يُنشِئ المُصرِّف ملف صنف <code>‎.class</code> منفصل لكل صنف مُتداخِل (nested) مجهول الاسم ضِمْن الصَنْف. إذا كان اسم الصنف الأساسي هو <code>MainClass</code>، فإن أسماء ملفات الأصناف المتداخلة مجهولة الاسم تكون كالتالي: <code>MainClass$1.class</code> و <code>MainClass$2.class</code> و <code>MainClass$3.class</code> وهكذا.
</p>

<p>
	تُمثِل الواجهة <code>Drawable</code> المُعرَّفة بالأعلى واجهة نوع دالة (functional interface)، وبالتالي نستطيع في هذه الحالة اِستخدَام تعبيرات لامدا (lambda expressions) بدلًا من الأصناف مجهولة الاسم. يُمكِن إعادة كتابة المثال الأخير كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_21" style="">
<span class="pln">drawTwice</span><span class="pun">(</span><span class="pln"> firstG</span><span class="pun">,</span><span class="pln"> secondG</span><span class="pun">,</span><span class="pln"> g </span><span class="pun">-&gt;</span><span class="pln"> g</span><span class="pun">.</span><span class="pln">fillOval</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="lit">100</span><span class="pun">,</span><span class="lit">100</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	كما يُمكِن إعادة تعريف المُتْغيِّر <code>redSquare</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_23" style="">
<span class="typ">Drawable</span><span class="pln"> redSquare </span><span class="pun">=</span><span class="pln"> g </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">.</span><span class="pln">RED</span><span class="pun">);</span><span class="pln">
          g</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="lit">100</span><span class="pun">,</span><span class="lit">100</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">};</span></pre>

<p>
	لا يُنشِئ المُصرِّف أي ملفات جديدة لتعبيرات لامدا (lambda expressions) وهو ما يُحسَب لها، ولكن لاحِظ أنها تُستخدَم فقط مع واجهات نوع الدالة (functional interfaces). في المقابل، تستطيع اِستخدَام الأصناف مجهولة الاسم (anonymous classes) مع أي واجهة أو صنف. قبل الإصدار ٨ من الجافا، كانت الأصناف مجهولة الاسم تُستخدَم لمعالجة الأحداث (events) ببرامج واجهات المُستخدِم الرُسومية (GUI)، ولكن مع الإصدار ٨ ومنصة جافا إف إكس (JavaFX)، أصبحت تعبيرات لامدا مُستخدَمة بكثرة ضِمْن هذا السياق بدلًا من الأصناف مجهولة الاسم.
</p>

<h2>
	الأصناف المحلية (local) وتعبيرات لامدا
</h2>

<p>
	تستطيع أن تُعرِّف صنفًا ضِمْن تعريف برنامج فرعي (subroutine definition). تُعرَف تلك النوعية من الأصناف باسم "الأصناف المحلية (local classes)". يَقْتصِر اِستخدَام أي صنف محلي على البرنامج الفرعي (subroutine) المُعرَّف بداخله، ولكن يُمكِن لكائنات ذلك الصنف أن تُستخدَم خارج البرنامج الفرعي، فمثلًا، قد يُعاد كائن ينتمي لصنف محلي من برنامج فرعي أو قد يُمرَّر كمُعامِل (parameter) لبرنامج فرعي آخر. هذا ممكن لأننا نستطيع عمومًا إِسْناد كائن ينتمي لصنف معين <code>B</code> إلى مُتْغيِّر من النوع <code>A</code> طالما كان <code>B</code> صنفًا فرعيًا من <code>A</code> في حالة كان <code>A</code> عبارة عن صنف، أما إذا كان واجهة فطالما كان <code>B</code> يُنفِّذ <code>A</code>. لنَفْترِض مثلًا أن لدينا برنامج فرعي (subroutine) يَستقبِل مُعامِلًا من النوع <code>Drawable</code> -الواجهة المُعرَّفة بالأعلى-، فنستطيع ببساطة أن نُمرِّر إليه أي كائن طالما كان يُنفِّذ (implements) تلك الواجهة، وعليه، قد ينتمي الكائن لصنف محلي (local class) يُنفِّذ تلك الواجهة.
</p>

<p>
	بمثال سابق بهذا القسم، مَرَّرنا كائنًا من النوع <code>Drawable</code> إلى التابع <code>drawTwice()‎</code>، والذي يَستقبِل مُعاملًا (parameter) من النوع <code>Drawable</code>. في ذلك المثال، كان الصنف المُمرَّر صنفًا داخليًا مجهول الاسم (anonymous inner class). لاحِظ أن الأصناف المحلية عادة ما تَكُون مجهولة الاسم (anonymous) والعكس، لكنه مع ذلك ليس أمرًا ضروريًا. فمثلًا، قد تَستخدِم صنفًا مجهول الاسم لتعريف القيمة المبدئية لمُتْغيِّر عام (global variable). في تلك الحالة، لا يَقع الصَنْف ضِمْن أي برنامج فرعي (subroutine)، وبالتالي لا يُعدّ محليًا (local).
</p>

<p>
	يُمكِن لأي صَنْف محلي (local class) أن يُشير إلى أي مُتْغيِّر محلي (local variables) ضِمْن برنامجه الفرعي أو إلى أي من مُعامِلاته (parameters) المُمرَّرة، ولكن وفقًا لعدة شروط: لابُدّ أن يُصرَّح عن ذلك المُتْغيِّر المحلي (local variable) أو المُعامِل (parameter) باستخدام المُبدِّل <code>final</code> أو أن يَكُون على الأقل "نهائيًا على نحو فعال". يُعدّ المُعامِل (parameter) "نهائيًا على نحو فعال" إذا لم تَتغيّر قيمته داخل البرنامج الفرعي (subroutine) بما في ذلك أصنافه المحلية (local class) التي أشارت إلى ذلك المُعامِل (parameter) بينما يُعدّ المُتْغيِّر المحلي (local variable) "نهائيًا على نحو فعال" إذا لم تَتغيّر قيمته أبدًا بعد التهيئة المبدئية (initialize). في المقابل، تستطيع الأصناف المحلية الإشارة إلى أي مُتْغيِّر عام (global variables) ودون أي شروط.
</p>

<p>
	تَنطبِق نفس شروط اِستخدَام المُتْغيِّرات المحلية (local variables) على تعبيرات لامدا (lambda expressions)، والتي هي كثيرة الشبه بالأصناف مجهولة الاسم (anonymous classes). تَستعرِض الشيفرة التالية برنامجًا فرعيًا يَستخدِم كُلًا من واجهة برمجة التطبيقات <code>stream</code> والواجهة <code>Runnable</code> -ناقشناها بالقسم ٤.٥-؛ لطباعة الأعداد من ١ إلى ١٠ بترتيب غير مُحدَّد يَعتمِد على مجرى مُتوازي (parallel stream):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7385_25" style="">
<span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> print1to10</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Runnable</span><span class="pun">&gt;</span><span class="pln"> printers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;&gt;();</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
        printers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    printers</span><span class="pun">.</span><span class="pln">parallelStream</span><span class="pun">().</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln"> r </span><span class="pun">-&gt;</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">run</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لمّا كان المُتْغيِّر المحلي <code>x</code> "نهائيًا على نحو فعال"، تَمكَّنا من اِستخدَامه ضِمْن تعبير لامدا (lambda expression). في المقابل، لا يُمكِن اِستخدَام المُتْغيِّر <code>i</code> ضِمْن تعبير لامدا؛ لأنه غَيْر نهائي حيث تَتغيّر قيمته عند تّنْفيذ التعبير <code>i++‎</code>.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s8.html" rel="external nofollow">Section 8: Nested Classes</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1115</guid><pubDate>Tue, 02 Feb 2021 13:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; Interfaces &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-interfaces-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1114/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/37.png.ccce6c1d65c7c07f8f80079233f01109.png" /></p>

<p>
	تَسمَح بعض اللغات البرمجية كائنية التوجه (object-oriented programming)، مثل C++‎، للصَنْف بأن يَتمدَّد (extend) من أكثر من مُجرّد صَنْف أعلى (superclass) واحد، وهو ما يُعرَف باسم الوراثة المُتعدّدة (multiple inheritance). بالرسم التالي مثلًا، يَتمدَّد الصنف <code>E</code> من صنفين أعليين (superclasses) مباشرةً، هما الصنفين <code>A</code> و <code>B</code>، بينما يَتمدَّد الصنف <code>F</code> من ثلاثة أصناف أعلين (superclasses) مباشرةً:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="56462" href="https://academy.hsoub.com/uploads/monthly_2021_01/001Multiple_Inheritance.png.4b09a6ece3a5a49024102149b7c2a8a3.png" rel=""><img alt="001Multiple_Inheritance.png" class="ipsImage ipsImage_thumbnailed" data-fileid="56462" data-unique="d9omtldk9" src="https://academy.hsoub.com/uploads/monthly_2021_01/001Multiple_Inheritance.png.4b09a6ece3a5a49024102149b7c2a8a3.png"></a>
</p>

<p>
	أراد مُصمِّمي الجافا أن يجعلوا اللغة بسيطة على نحو معقول، ولمّا وجدوا أن مزايا الوراثة المُتعدّدة (multiple inheritance) لا تَستحِقّ ما يُقابِلها من تعقيد مُتزايد، فإنهم لم يُدعِّموها باللغة. ومع هذا، تُوفِّر الجافا ما يُعرَف باسم الواجهات (interfaces) والتي يُمكِن اِستخدَامها لتحقيق الكثير من أهداف الوراثة المُتعدّدة. لقد تَعرَّضنا -بالقسم ٤.٥- لواجهات نوع الدالة (functional interfaces) وعلاقتها بتعبيرات لامدا (lambda expressions)، ورأينا أنها تُخصِّص تابعًا (method) وحيدًا. في المقابل، يُمكِن للواجهات (interfaces) أن تَكُون أكثر تعقيدًا بمراحل كما أن لها استخدامات آخرى كثيرة.
</p>

<p>
	من غَيْر المُحتمَل أن تحتاج إلى كتابة واجهات (interfaces) خاصة بك حاليًا؛ فهي ضرورية فقط للبرامج المُعقَّدة نسبيًا، ولكن هنالك عدة واجهات (interfaces) مُستخدَمة بحزم جافا القياسية (Java's standard packages) بطرائق مُهِمّة وتحتاج إلى تَعلُّم طريقة اِستخدَامها.
</p>

<h2>
	تعريف الواجهات (interfaces) وتنفيذها (implementation)
</h2>

<p>
	لقد تَعرَّضنا لمصطلح "الواجهة (interface)" ضِمْن أكثر من سياق، سواء فيما يَتَعلَّق بالصناديق السوداء (black boxes) في العموم أو فيما يَتَعلَّق بالبرامج الفرعية (subroutines) على وجه الخصوص. تَتكوَّن واجهة أي برنامج فرعي (subroutine interface) من اسمه، ونوعه المُعاد (return type)، وعدد مُعامِلاته (parameters) وأنواعها. تُمثِل تلك المعلومات كل ما أنت بحاجة إلى مَعرِفته لكي تَتَمكَّن من استدعاء البرنامج الفرعي. بالإضافة إلى ذلك، يَمتلك أي برنامج فرعي جزءًا تّنْفيذيًا (implementation)، هو كتلة الشيفرة المُعرِّفة له (defines) والتي تُنفَّذ عند استدعاءه.
</p>

<p>
	بلغة الجافا، كلمة <code>interface</code> هي كلمة محجوزة تَحمِل معنًى تقنيًا إضافيًا. وفقًا لهذا المعنى، تَتكوَّن الواجهة من مجموعة من واجهات توابع النُسخ (instance method interfaces) بدون أجزائها التّنفيذية (implementations). يستطيع أي صنف أن يُنفِّذ (implement) واجهة معينة بتوفير الأجزاء التّنْفيذية (implementation) لجميع التوابع المُخصَّصة ضِمْن تلك الواجهة. اُنظر المثال التالي لواجهة (interface) بسيطة جدًا بلغة الجافا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_7" style="">
<span class="kwd">public</span><span class="pln"> interface </span><span class="typ">Strokeable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> stroke</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تبدو الشيفرة بالأعلى مشابهة لتعريف صنف (class definition) باستثناء حَذْف الجزء التّنْفيذي (implementation) للتابع <code>stroke()‎</code>. إذا أراد صنف معين أن يُنفِّذ تلك الواجهة <code>Strokeable</code>، فلابُدّ له من أن يُوفِّر جزءًا تّنْفيذيًا للتابع <code>stroke()‎</code> كما قد يَتَضمَّن أي توابع أو متغيرات آخرى. اُنظر الشيفرة التالية على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_9" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Line</span><span class="pln"> implements </span><span class="typ">Strokeable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> stroke</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// ارسم خطًا</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// توابع ومتغيرات وبواني آخرى</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكي يُنفِّذ صنف واجهةً (interface) معينةً، ينبغي عليه أن يَفعَل أكثر من مُجرّد توفير الأجزاء التّنْفيذية (implementation) لجميع التوابع ضِمْن تلك الواجهة، فعليه تحديدًا أن يُعلن صراحةً عن تّنفيذه (implements) لتلك الواجهة باستخدام الكلمة المحجوزة <code>implements</code> كالمثال بالأعلى. لابُدّ لأي صنف حقيقي (concrete class) يَرغَب بتّنْفيذ الواجهة <code>Strokeable</code> من أن يُعرِّف تابع نسخة اسمه <code>stroke()‎</code>، لذا سيَتضمَّن أي كائن (object) مُنشَئ من هذا الصنف التابع <code>stroke()‎</code>. يُعدّ الكائن مُنفِّذًا (implement) لواجهة معينة إذا كان ينتمي لصنف يُنفِّذ (implements) تلك الواجهة، فمثلًا، يُنفِّذ أي كائن من النوع <code>Line</code> الواجهة <code>Strokeable</code>.
</p>

<p>
	في حين يستطيع الصنف أن يَتمدَّد (extend) من صنف واحد فقط، فإنه في المقابل يستطيع أن يُنفِّذ (implements) أي عدد من الواجهات (interfaces). وفي الواقع، يُمكِن للصنف أن يَتمدَّد (extend) من صنف آخر، وأن يُنفِّذ واجهة واحدة أو أكثر بنفس ذات الوقت، لذلك نستطيع كتابة التالي مثلًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_11" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">FilledCircle</span><span class="pln"> extends </span><span class="typ">Circle</span><span class="pln"> 
                        implements </span><span class="typ">Strokeable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Fillable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	على الرغم من أن الواجهات (interfaces) ليست أصنافًا (classes)، فإنها تُشبهها إلى حد كبير. في الواقع، أي واجهة (interface) هي أَشْبه ما تَكُون بصنف مُجرّد (abstract class) لا يُستخدَم لإنشاء كائنات، وإنما كقاعدة لإنشاء أصناف فرعية (subclasses). تُعدّ البرامج الفرعية (subroutines) ضِمْن أي واجهة توابعًا مجردةً (abstract methods)، والتي لابُدّ لأيّ صنف حقيقي (concrete class) يَرغَب بتّنْفيذ تلك الواجهة من أن يُنفِّذها (implement). تستطيع الموازنة بين الواجهة <code>Strokeable</code> والصَنْف المُجرّد (abstract class) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_13" style="">
<span class="kwd">public</span><span class="pln"> abstract </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AbstractStrokeable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> abstract </span><span class="kwd">void</span><span class="pln"> stroke</span><span class="pun">(</span><span class="typ">GraphicsContext</span><span class="pln"> g</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَكمُن الفرق بينهما في أن الصنف الذي يَتمدَّد (extend) من الصنف <code>AbstractStrokeable</code> لا يُمكِنه أن يَتَمدَّد من أي صنف آخر. أما الصنف الذي يُنفِّذ الواجهة <code>Strokeable</code> يستطيع أن يَتَمدَّد من أي صنف آخر كما يستطيع أن يُنفِّذ (implement) أي واجهات (interfaces) آخرى. بالإضافة إلى ذلك، يُمكِن لأي صنف مُجرّد (abstract class) أن يَتَضمَّن توابعًا غير مُجرّدة (non-abstract) وآخرى مُجرّدة (abstract). في المقابل، تستطيع أي واجهة (interface) أن تَتَضمَّن توابعًا مُجرّدة فقط، لذا فهي أَشْبه ما تَكُون بصنف مُجرّد نقي (pure).
</p>

<p>
	ينبغي أن تُصرِّح عن التوابع ضِمْن أي واجهة (interface) على أساس كَوْنها -أي التوابع- عامة <code>public</code> ومُجردّة <code>abstract</code>. ولمّا كان هذا هو الخيار الوحيد المُتاح، فإن تَخْصِيص هذين المُبدِّلين (modifiers) ضِمْن التّصْريح (declaration) ليس ضروريًا.
</p>

<p>
	إلى جانب التّصريح (method declarations) عن التوابع، يُمكِن لأي واجهة (interface) أن تُصرِّح عن وجود مُتْغيِّرات (variable declarations)، وينبغي عندها أن تُصرِّح عنها على أساس كَوْنها عامة <code>public</code>، وساكنة <code>static</code>، ونهائية <code>final</code>، ولذا فإنها تَصيِر عامة وساكنة ونهائية بأي صنف يُنفِّذ (implements) تلك الواجهة. ولمّا كان هذا هو الخيار الوحيد المُتاح للتّصْريح عنها، فإن تَخْصِيص تلك المُبدِّلات (modifiers) ضِمْن التّصْريح (declaration) ليس ضروريًا. اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_15" style="">
<span class="kwd">public</span><span class="pln"> interface </span><span class="typ">ConversionFactors</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> INCHES_PER_FOOT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> FEET_PER_YARD </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">int</span><span class="pln"> YARDS_PER_MILE </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1760</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذه هي الطريقة المناسبة لتعريف (define) ثوابت مُسماة (named constants) يُمكِن اِستخدَامها بعدة أصناف. يُمكِن لأي صنف يُنفِّذ (implements) الواجهة <code>ConversionFactors</code> أن يَستخدِم الثوابت المُعرَّفة بتلك الواجهة (interface) كما لو كانت مُعرَّفة بالصنف.
</p>

<p>
	لاحِظ أن أي مُتْغيِّر مُعرَّف ضِمْن واجهة (interface) هو بالنهاية ثابت (constant) وليس مُتْغيِّرًا على الإطلاق. وفي العموم، لا يُمكِن لأي واجهة (interface) أن تُضيف مُتْغيِّرات نُسخ (instance variables) إلى الأصناف التي تُنفِّذها (implement).
</p>

<p>
	يُمكِن لأي واجهة (interface) أن تَتَمدَّد (extend) من واجهة واحدة أو أكثر. على سبيل المثال، إذا كان لدينا الواجهة <code>Strokeable</code> المُعطاة بالأعلى، بالإضافة إلى الواجهة <code>Fillable</code> والتي تُعرِّف التابع <code>fill(g)‎</code>، نستطيع عندها تعريف الواجهة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_17" style="">
<span class="kwd">public</span><span class="pln"> interface </span><span class="typ">Drawable</span><span class="pln"> extends </span><span class="typ">Strokeable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Fillable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// المزيد من التوابع أو الثوابت</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ينبغي لأي صَنْف حقيقي (concrete class) يُنفِّذ الواجهة <code>Drawable</code> من أن يُوفِّر الأجزاء التّنْفيذية (implementations) لكُلًا من التابع <code>stroke()‎</code> من الواجهة <code>Strokeable</code>، والتابع <code>draw()‎</code> من الواجهة <code>Fillable</code>، بالإضافة إلى أي توابع مُجرّدة (abstract methods) آخرى قد تُخصِّصها الواجهة <code>Drawable</code> مباشرة. عادة ما تُعرَّف (define) الواجهة (interface) ضِمْن ملف <code>‎.java</code> الخاص بها، والذي لابُدّ أن يَكُون له نفس اسم الواجهة. فمثلًا، تُعرَّف الواجهة <code>Strokeable</code> بملف اسمه <code>Strokeable.java</code>. وبالمثل من الأصناف (classes)، يُمكِن للواجهة (interface) أن تقع ضِمْن حزمة (package)، كما يُمكِنها أن تَستورِد (import) أشياءً من حزم آخرى.
</p>

<h2>
	التوابع الافتراضية (default methods)
</h2>

<p>
	بداية من الإصدار الثامن من الجافا، تستطيع الواجهات (interfaces) أن تَتَضمَّن ما يعرف باسم "التوابع الافتراضية (default methods)"، والتي تَملُك جزءًا تّنْفيذيًا (implementation) بعكس التوابع المُجرّدة (abstract methods) المُعتادة. تُورَث التوابع الافتراضية من الواجهات (interfaces) إلى أصنافها المُنفِّذة (implement) بنفس الطريقة التي تُورَث بها التوابع العادية من الأصناف إلى أصنافها الفرعية. لذا عندما يُنفِّذ (implement) صنف معين واجهةً تَحتوِي على توابع افتراضية، فإنه لا يَكُون مضطرًا لأن يُوفِّر جزءًا تّنْفيذيًا (implementation) لأي تابع افتراضي (default method) ضِمْن الواجهة، مع أن بإمكانه القيام بذلك إذا كان لديه تّنْفيذًا (implementation) مُختلفًا. تَدفَع التوابع الافتراضية (default methods) لغة الجافا خطوة للأمام بطريق دَعْم الوراثة المُتعدّدة (multiple inheritance)، ولكنها مع ذلك ليست وراثة مُتعدّدة بحق؛ لأن الواجهات لا تستطيع تعريف مُتْغيِّرات نُسخ (instance variables). تستطيع التوابع الافتراضية (default methods) استدعاء التوابع المجردة (abstract methods) المُعرَّفة بنفس الواجهة، لكنها لا تستطيع الإشارة إلى أي مُتْغيِّر نسخة (instance variable).
</p>

<p>
	ملحوظة: تستطيع واجهات نوع الدالة (functional interfaces) أيضًا أن تَحتوِي على توابع افتراضية (default methods) بالإضافة إلى التابع المُجرّد (abstract method) الوحيد الذي بإمكانها تَخْصِيصه.
</p>

<p>
	ينبغي أن تُصرِّح عن التوابع الافتراضية (default methods) على أساس كَوْنها عامة <code>public</code>، ولمّا كان ذلك هو الخيار الوحيد المُتاح، فإن تَخْصِيص المبدل <code>public</code> ضِمْن التّصْريح (declaration) ليس ضروريًا. في المقابل، لابُدّ من كتابة المُبدِّل <code>default</code> بشكل صريح أثناء التّصْريح عن أي تابع افتراضي (default method). اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_19" style="">
<span class="kwd">public</span><span class="pln"> interface </span><span class="typ">Readable</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// تمثل مصدر إدخال</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> readChar</span><span class="pun">();</span><span class="pln">  </span><span class="com">// اقرأ المحرف التالي المُدْخَل</span><span class="pln">

    </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> readLine</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">//اقرأ حتى نهاية السطر</span><span class="pln">

        </span><span class="typ">StringBuilder</span><span class="pln"> line </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StringBuilder</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">char</span><span class="pln"> ch </span><span class="pun">=</span><span class="pln"> readChar</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ch </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            line</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">ch</span><span class="pun">);</span><span class="pln">
            ch </span><span class="pun">=</span><span class="pln"> readChar</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	لابُدّ لأي صنف حقيقي (concrete class) يُنفِّذ الواجهة (interface) -بالأعلى- من أن يُوفِّر تّنْفيذًا (implementation) للتابع <code>readChar()‎</code>. في المقابل، سيَرِث ذلك الصنف تعريف <code>readLine()‎</code> من الواجهة، ولكنه قد يُوفِّر تعريفًا (definition) جديدًا إذا كان ذلك ضروريًا. عندما يَتَضمَّن صنف معين تّنفيذًا (implementation) لتابع افتراضي (default method)، فإن ذلك التّنْفيذ الجديد يُعيد تعريف (overrides) التابع الافتراضي الموجود بالواجهة (interface).
</p>

<p>
	بالمثال السابق، يَستدعِي التابع الافتراضي <code>readLine()‎</code> التابع المُجرّد <code>readChar()‎</code>، والذي يَتوفَّر تعريفه (definition) فقط من خلال الأصناف المُنفِّذة للواجهة، ولهذا تُعدّ الإشارة إلى <code>readChar()‎</code> مُتعدِّدة الأشكال (polymorphic). كُتب التّنْفيذ الافتراضي للتابع <code>readLine()‎</code> بحيث يَكُون ملائمًا لأي صنف يُنفِّذ الواجهة <code>Readable</code>. اُنظر الشيفرة التالية والتي تَتَضمَّن صنفًا يُنفِّذ الواجهة <code>Readable</code> كما يَحتوِي على البرنامج <code>main()‎</code> لاختبار الصَنْف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_21" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Stars</span><span class="pln"> implements </span><span class="typ">Readable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> readChar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0.02</span><span class="pun">)</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'*'</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Stars</span><span class="pln"> stars </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stars</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// اِستدعي التابع  الافتراضي</span><span class="pln">
            </span><span class="typ">String</span><span class="pln"> line </span><span class="pun">=</span><span class="pln"> stars</span><span class="pun">.</span><span class="pln">readLine</span><span class="pun">();</span><span class="pln">  
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> line </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	تُوفِّر التوابع الافتراضية (default methods) إمكانية شبيهة لما يُعرَف باسم "المخلوط (mixin)" المُدعَّم ببعض اللغات البرمجية الآخرى، والتي تَعنِي المقدرة على خَلْط وظائف مصادر آخرى إلى داخل الصنف. لمّا كان بإمكان أي صنف أن يُنفِّذ أي عدد من الواجهات (interfaces)، فإنه يستطيع خلط وظائف عدة مصادر آخرى مختلفة.
</p>

<h2>
	الواجهات كأنواع
</h2>

<p>
	كما هو الحال مع الأصناف المُجرّدة (abstract classes)، لا يُمكِنك إنشاء كائن فعليّ من واجهة (interface)، ولكن تستطيع التّصْريح (declare) عن مُتْغيِّر نوعه عبارة عن واجهة. لنَفْترِض مثلًا أن لدينا الواجهة <code>Strokeable</code> المُعرَّفة بالأعلى، ويُنفِّذها كُلًا من الصنفين <code>Line</code> و <code>Circle</code>، يُمكِنك عندها كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_23" style="">
<span class="com">// صرح عن متغير من النوع‫ Strokeable والذي يمكنه الإشارة إلى أي</span><span class="pln">
</span><span class="com">// كائن ينفذ تلك الواجهة</span><span class="pln">
</span><span class="typ">Strokeable</span><span class="pln"> figure</span><span class="pun">;</span><span class="pln">  

figure </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Line</span><span class="pun">();</span><span class="pln">  </span><span class="com">// ‫يشير إلى كائن من الصنف Line </span><span class="pln">
figure</span><span class="pun">.</span><span class="pln">stroke</span><span class="pun">(</span><span class="pln">g</span><span class="pun">);</span><span class="pln">   </span><span class="com">// ‫اِستدعي التابع stroke() من الصنف Line</span><span class="pln">

figure </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Circle</span><span class="pun">();</span><span class="pln">   </span><span class="com">// ‫يشير الآن إلى كائن من الصنف Circle</span><span class="pln">
figure</span><span class="pun">.</span><span class="pln">stroke</span><span class="pun">(</span><span class="pln">g</span><span class="pun">);</span><span class="pln">   </span><span class="com">// ‫اِستدعي التابع stroke() من الصنف Circle</span></pre>

<p>
	يُمكِن لأي مُتْغيِّر من النوع <code>Strokeable</code> أن يُشير إلى أي كائن طالما كان صَنْفه يُنفِّذ الواجهة <code>Strokeable</code>. لمّا كان <code>figure</code> مُتْغيِّرًا من النوع <code>Strokeable</code>، ولأن أي كائن من النوع <code>Strokeable</code> يَحتوِي على التابع <code>stroke()‎</code>، فحتمًا سيَحتوِي الكائن الذي يُشير إليه المُتْغيِّر <code>figure</code> على التابع <code>stroke()‎</code>، ولهذا فإن التَعْليمَة <code>figure.stroke(g)‎</code> صالحة تمامًا.
</p>

<p>
	تُستخدَم الأنواع (types) في العموم إما للتّصْريح (declare) عن مُتْغيِّر، أو لتَخْصِيص نوع معامل برنامج فرعي (routine)، أو لتَخْصِيص النوع المُعاد (return type) من دالة (function). النوع في العموم إما أن يَكُون صنفًا أو واجهة (interface) أو أحد الأنواع البسيطة (primitive) الثمانية المَبنية مُسْبَقًا (built-in). ليس هنالك من أيّ احتمال آخر، ربما باستثناء بعض الحالات الخاصة كأنواع التعداد (enum) والتي هي بمثابة نوع خاص من الأصناف. من بين كل تلك الأنواع، الأصناف هي الوحيدة التي يُمكِن اِستخدَامها لإنشاء كائنات (objects).
</p>

<p>
	يُمكِنك أيضًا اِستخدَام الواجهات (interface) لتَخْصِيص النوع الأساسي (base type) لمصفوفة. فمثلًا، تستطيع أن تُصرِّح عن مُتْغيِّر أو أن تُنشِئ مصفوفة باستخدام نوع المصفوفة <code>Strokeable[]‎</code>، وفي تلك الحالة، يُمكِن لعناصر تلك المصفوفة الإشارة إلى أي كائن طالما كان يُنفِّذ الواجهة <code>Strokeable</code>. اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6374_25" style="">
<span class="typ">Strokeable</span><span class="pun">[]</span><span class="pln"> listOfFigures</span><span class="pun">;</span><span class="pln">
listOfFigures </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Strokeable</span><span class="pun">[</span><span class="lit">10</span><span class="pun">];</span><span class="pln">
listOfFigures</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Line</span><span class="pun">();</span><span class="pln">
listOfFigures</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Circle</span><span class="pun">();</span><span class="pln">
listOfFigures</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Line</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">
  </span><span class="pun">.</span></pre>

<p>
	تَملُك جميع عناصر تلك المصفوفة التابع <code>stroke()‎</code>، مما يَعنِي إمكانية كتابة تعبيرات مثل <code>listOfFigures<em>.stroke(g)‎</em></code><em>.
</em></p>
<em>

</em><p><em>
	ترجمة -بتصرّف- للقسم </em><a href="http://math.hws.edu/javanotes/c5/s7.html" rel="external nofollow"><em>Section 7: Interfaces</em></a><em> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب </em><a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow"><em>Introduction to Programming Using Java</em></a><em>.
</em></p>
]]></description><guid isPermaLink="false">1114</guid><pubDate>Sun, 31 Jan 2021 13:09:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x646; &#x627;&#x644;&#x62E;&#x627;&#x635;&#x627;&#x646; this &#x648; super &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D9%86-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D8%A7%D9%86-this-%D9%88-super-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1113/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/36.png.9f2201f14d6ea6d8b01f5f35238569f9.png" /></p>

<p>
	تُعدّ الأفكار الأساسية التي تَرتَكِز عليها البرمجة كائنية التوجه (object-oriented programming) واضحة وبسيطة على نحو معقول، ومع ذلك فإنها ستَأخُذ منك بعض الوقت لكي تَعتَاد عليها تمامًا. بالإضافة إلى ذلك، هنالك الكثير من التفاصيل الدقيقة وراء تلك الأفكار الأساسية والتي قد تَكُون مزعجة في بعض الأحيان. سنُحاوِل أن نُغطِي بعضًا منها بالجزء المُتبقِي من هذا الفصل، وتَذَكَّر أنه ليس ضروريًا أن تَتَمكَّن من إِتقان كل تلك التفاصيل خاصة عند قرائتها لأول مرة. سنَفْحَص بهذا القسم تحديدًا المُتْغيِّرين <code>this</code> و <code>super</code> المُعرَّفين تلقائيًا بأي تابع نسخة (instance method) أو بَانِي كائن (constructor).
</p>

<h2>
	المتغير الخاص <code>this</code>
</h2>

<p>
	ما الذي يعنيه اِستخدَام مُعرِّف بسيط مثل <code>amount</code> أو <code>process()‎</code> للإشارة إلى مُتْغيِّر أو تابع؟ تعتمد الإجابة بشكل أساسي على قواعد النطاق (scope rules)، والتي تُوضِح الأماكن التي يُمكِنها الوصول إلى أيّ مُتْغيِّر أو تابع قد صَرَّح عنه البرنامج وكيفية القيام بذلك. فمثلًا، قد يُشير اسم بسيط لمُتْغيِّر بتعريف تابع (method definition) إلى مُتْغيِّر محليّ (local variable) أو مُعامِل (parameter) في حالة وجود أي منهما ضِمْن النطاق (scope)، أيّ إن كان التَّصْريح (declaration) عن أيّ منهما ما يزال فعالًا بمكان حُدوث تلك الإشارة بالشيفرة، فإذا لم يَكُن كذلك، فإن ذلك الاسم لابُدّ وأنه يُشير إلى مُتْغيِّر عضو (member variable) مُعرَّف بنفس الصنف الذي حدثت فيه تلك الإشارة. بالمثل، أي اسم بسيط لتابع لابُدّ وأنه يُشير إلى تابع (method) مُعرَّف بنفس الصنف.
</p>

<p>
	يَملُك أي عضو ساكن (static member) بصنف اسمًا بسيطًا يُمكِن اِستخدَامه داخل تعريف الصَنْف (class definition) فقط. في المقابل، يُستخدَم الاسم الكامل للعضو <strong><class-name>.<simple-name></simple-name></class-name></strong> للإشارة إليه من خارج الصَنْف. فمثلًا، <code>Math.PI</code> هو مُتْغيِّر عضو ساكن (static member variable) اسمه البسيط هو <code>PI</code> داخل الصنف <code>Math</code>. لاحِظ أنه من المُمكن دومًا الإشارة إلى أي عضو ساكن باِستخدَام اسمه الكامل حتى من داخل الصنف المُعرَّف بداخله بل قد يَكُون ذلك ضروريًا في بعض الأحيان، مثلًا عندما يَكُون الاسم البسيط للمُتغير الساكن مخفيًا نتيجة وجود مُتْغيِّر محليّ (local variable) أو مُعامِل (parameter) بنفس الاسم.
</p>

<p>
	بالمثل، يَملُك أي عضو نسخة (instance member)، سواء كان مُتْغيِّر نسخة (instance variable) أو تابع نسخة (instance method)، اسمًا بسيطًا يُمكِن اِستخدَامه بتوابع النسخ (instance methods) -لا التوابع الساكنة (static methods)- بالصنف المُعرَّف بداخله. تَذَكَر أن أي مُتْغيِّر نسخة أو تابع نسخة هو جزء من الكائن ذاته وليس صَنْفه، فكل كائن يَمتلك نسخة خاصة به من ذلك المُتْغيِّر أو التابع. بالإضافة إلى الاسم البسيط، يَملُك أي عضو نسخة (instance member) اسمًا كاملًا، شطره الأول عبارة عن مَرجِع (reference) يُشير إلى الكائن المُتضمِّن لذاك العضو. مثلًا، إذا كان <code>std</code> مُتْغيِّر يُشير إلى كائن من النوع <code>Student</code>، فقد تُمثِل <code>std.test1</code> الاسم الكامل لمُتْغيِّر نسخة (instance variable) اسمه <code>test1</code> مُعرَّف ضِمْن الكائن.
</p>

<p>
	إذًا، عندما تَستخدِم الاسم البسيط لمُتْغيِّر نُسخة (instance variable) مثل <code>test1</code> بداخل صنف معين، أين الكائن الذي يَتَضمَّن ذلك المُتْغيِّر؟ في الواقع، الأمر بسيط نوعًا ما. لنَفْترِض أنك أَشرت إلى الاسم <code>test1</code> بتعريف تابع نسخة (instance method) معين، والذي هو بالتأكيد جزء من كائن من النوع <code>Student</code>. عندما يُنفَّذ ذلك التابع، فإن الاسم البسيط <code>test1</code> يُشير عندها إلى المُتْغيِّر <code>test1</code> المُعرَّف بذلك الكائن. في الواقع، هذا هو السبب وراء عدم إمكانية استخدام الأسماء البسيطة لأعضاء النسخ (instance members) ضِمْن أي تابع ساكن (static methods)؛ لأنه وبينما يُنفَّذ ذلك التابع الساكن، لا يَكُون هناك أي كائن، ومن ثم لا يَكُون هناك أي أعضاء نُسخ (instance members).
</p>

<p>
	يَضَعنا هذا أمام السؤال التالي: كيف نَستخدِم الاسم الكامل لعضو نسخة (instance members) داخل نفس الصَنْف المُعرَّف بداخله؟ نُريد ببساطة طريقة تُمكِّنا من الإشارة إلى "نفس ذات الكائن الحاضن للتابع". في الواقع، تُوفِّر الجافا المُتْغيِّر الخاص <code>this</code> لهذا الغرض. تستطيع اِستخدَام المُتْغيِّر <code>this</code> بتابع نسخة معين (instance method)؛ للإشارة إلى نفس ذات الكائن الحاضن لنفس ذات التابع. فمثلًا، إذا كان <code>var</code> هو مُتْغيِّر نسخة (instance variable) مُعرَّف بنفس ذات الكائن الحاضن لنفس ذات التابع، فإن <code>this.var</code> هو الاسم الكامل لذلك المُتْغيِّر. وبالمثل، إذا كان <code>otherMethod()‎</code> هو تابع نسخة (instance method) بنفس ذات الكائن، فيُمكِن اِستخدَام <code>this.otherMethod()‎</code> لاستدعاء ذلك التابع. عندما يُنفِّذ الحاسوب تابع نسخة (instance method) معين، فإنه تلقائيًا يَضَع المُتْغيِّر <code>this</code> بحيث يُشير إلى نفس ذات الكائن الحاضن للتابع.
</p>

<p>
	تَستخدِم بعض اللغات البرمجية كائنية التوجه (object oriented) الاسم <code>self</code> بدلًا من <code>this</code>. يُرَى عندها الكائن ككيان يَستقبِل الرسائل، ويَرُد عليها بتَّنْفيذ أمر معين. وفقًا لذلك الكيان، يُشير مُتْغيِّر نسخة (instance variable) مثل <code>self.name</code> إلى نسخة <code>name</code> الخاصة بالكيان، بينما يَكُون استدعاء تابع نسخة (instance method) مثل <code>self.redraw()‎</code> بمثابة قوله "اِرسِل إلى نفسي رسالة redraw".
</p>

<p>
	يَشيع استخدام المُتْغيِّر الخاص <code>this</code> ببواني الكائنات (constructors)، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4292_7" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Student</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">  </span><span class="com">// اسم الطالب</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">   </span><span class="com">// المزيد من المتغيرات والتوابع</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بباني الكائن (constructor) -بالأعلى-، أَصبَح مُتْغيِّر النسخة (instance variable)‏ <code>name</code> مَخفيًّا نتيجة اِستخدَام مُعامِل صُّوريّ (formal parameter) يَحمِل نفس الاسم <code>name</code>. مع ذلك، تستطيع اِستخدَام اسمه الكامل <code>this.name</code> للإشارة إليه. يُشير الاسم <code>name</code> على يمين تَعْليمَة الإِسْناد <code>this.name = name</code> إلى المُعامِل الصُّوريّ (formal parameter)، وتُسنَد قيمته إلى مُتْغيِّر النسخة <code>this.name</code>. يَشيِع اِستخدَام تلك الصياغة، وهي مقبولة في العموم؛ فليس هناك أي حاجة لاختيار أسماء جديدة للمُعامِلات الصُّوريّة المُستخدَمة فقط لتهيئة مُتْغيِّرات النُسخ (instance variables)، لذا اِستخدِم نفس الاسم لكليهما ببساطة.
</p>

<p>
	يُستخدَم المُتْغيِّر الخاص <code>this</code> لأغراض آخرى أيضًا. على سبيل المثال، بينما تَكْتُب تابع نسخة (instance method)، قد تحتاج إلى تمرير الكائن الحاضن للتابع إلى برنامج فرعي (subroutine) كمُعامِل فعليّ (actual parameter). في تلك الحالة، تستطيع ببساطة تمرير <code>this</code>. مثلًا، يُستخدَم <code>System.out.println(this);‎</code> لطباعة تمثيل نصي (string representation) للكائن. بالإضافة إلى ذلك، قد تُسنِد <code>this</code> إلى مُتْغيِّر آخر ضِمْن تَعْليمَة إِسْناد (assignment statement) أو تُخزِّنه بمصفوفة. وفي العموم، تستطيع أن تَفعَل به أي شيء يُمكِنك فعله مع أي مُتْغيِّر آخر باستثناء تَعْدِيل قيمته (اِفْترِض أنه مُتْغيِّر نهائي مُعرَّف باِستخدَام المُبدِّل <code>final</code>).
</p>

<h2>
	المتغير الخاص <code>super</code>
</h2>

<p>
	تُعرِّف الجافا المُتْغيِّر الخاص <code>super</code> للاِستخدَام بتعريفات توابع النُسخ (instance methods) بالأصناف الفرعية (subclasses) تحديدًا. بالمثل من <code>this</code>، يُشير المُتْغيِّر الخاص <code>super</code> إلى نفس ذات الكائن الحاضن للتابع، ولكنه يَنسَى -إذا جاز التعبير- أن ذلك الكائن ينتمي إلى صَنْفه الفرعي (subclass)، ويَتَذكَّر فقط انتماءه إلى صَنْفه الأعلى (superclass). الفكرة ببساطة كالتالي: قد يَتَضمَّن الصَنْف الفرعي بعض الإضافات إلى الصَنْف الأعلى (superclass) أو حتى بعض التَعْديلات عليه، فكانت الحاجة إلى وجود طريقة للإشارة إلى التوابع (methods) والمُتْغيِّرات المُعرَّفة بالصَنْف الأعلى (superclass)، وهذا ما يُوفِّره المُتْغيِّر <code>super</code> فهو ببساطة لا يَعلَم أي شيء عن تلك الإضافات والتعديلات.
</p>

<p>
	لنَفْترِض أن لديك صَنْف فرعي يَحتوِي على تابع نسخة (instance method) اسمه <code>doSomething()‎</code>، ثُمَّ كَتبَت بأحد توابعه تَعْليمَة استدعاء البرنامج الفرعي التالية <code>super.doSomething()‎</code>. لمّا كان المُتْغيِّر <code>super</code> لا يَعلَم أي شيء عن التابع <code>doSomething()‎</code> المُعرَّف بالصنف الفرعي، فهو فقط يَعلَم الأشياء المُعرَّفة بالصنف الأعلى (superclass)، فإنه سيُحاوِل تَّنْفيذ تابع اسمه <code>doSomething()‎</code> من الصَنْف الأعلى (superclass)، فإذا لم يَجِدْه، أيّ في حالة كان التابع <code>doSomething()‎</code> إضافةً وليس تعديلًا، فستَحصُل على خطأ في بناء الجملة (syntax error).
</p>

<p>
	وفَّرت الجافا المُتْغيِّر الخاص <code>super</code> لكي تَتَمكَّن من الوصول إلى أشياء كانت قد عُرِّفت بالصنف الأعلى (superclass)، ولكنها أصبحت مَخفيّة بواسطة بعض الأشياء المُعرَّفة بالأصناف الفرعية (subclasses). مثلًا، دائمًا ما سيُشير الاسم <code>super.var</code> إلى مُتْغيِّر النسخة (instance variable)‏ <code>var</code> المُعرَّف بالصنف الأعلى (superclass). يَكُون ذلك مفيدًا في بعض الحالات كالتالي: إذا اِحتوَى صنف معين على مُتْغيِّر نسخة (instance variable) له نفس اسم مُتْغيِّر نسخة بصنفه الأعلى (superclass)، فعندها سيَحتوِي أي كائن من الصَنْف الفرعي على مُتْغيِّرين لهما نفس الاسم، الأول مُعرَّف كجزء من الصنف نفسه، والآخر مُعرَّف كجزء من الصنف الأعلى، أيّ لا يَحلّ المُتْغيِّر بالصنف الفرعي (subclass) مَحلّ المُتْغيِّر بالصَنْف الأعلى حتى لو كان لهما نفس الاسم وإنما يُخفيه فقط، وعليه ما يزال المُتْغيِّر من الصنف الأعلى قابلًا للوصول باِستخدَام <code>super</code>.
</p>

<p>
	بالمثل، عندما يَتَضمَّن صنف فرعي (subclass) تابع نسخة (instance method) له نفس بصمة (signature) تابع مُعرَّف بالصنف الأعلى (superclass)، فإن التابع من الصنف الأعلى يُصبِح مَخفيًّا بنفس الطريقة، ويُقال عندها أن التابع من الصنف الفرعي أعاد تعريف (override) التابع من الصنف الأعلى. ومع ذلك، يُمكِن اِستخدَام المُتْغيِّر الخاص <code>super</code> للوصول إلى التابع من الصنف الأعلى (superclass).
</p>

<p>
	غالبًا ما يُستخدَم المُتْغيِّر <code>super</code> لتمديد (extend) سلوك تابع موروث بدلًا من أن يستبدله بالكامل، وذلك بكتابة تابع جديد يُعيد تعريف (override) التابع الأصلي. يَتَضمَّن التابع الجديد استدعاءً لتابع الصنف الأعلى (superclass) من خلال المُتْغيِّر <code>super</code> إلى جانب الشيفرة الجديدة المطلوب إضافتها بالأساس. لنَفْترِض مثلًا أن لدينا الصنف <code>PairOfDice</code> والذي يَتَضمَّن تابعًا اسمه <code>roll()‎</code>، والآن نريد إنشاء صنف فرعي <code>GraphicalDice</code> ليُمثِل حجري نَّرد مرسومين على الشاشة. ينبغي للتابع <code>roll()‎</code> المُعرَّف بالصنف الفرعي <code>GraphicalDice</code> أن يُنفِّذ كل شيء يقوم به التابع <code>roll()‎</code> المُعرَّف بالصنف الأعلى <code>PairOfDice</code>، وهو ما يُمكِننا التعبير عنه بالاستدعاء <code>super.roll()‎</code>، والذي سيَستدعِي نسخة التابع بالصنف الأعلى (superclass). بالإضافة إلى ذلك، ينبغي للتابع <code>roll()‎</code> المُعرَّف بالصنف الفرعي <code>GraphicalDice</code> أن يُعيد رسم حجرى النَّرد لإظهار القيم الجديدة. يُمكِننا تعريف الصنف <code>GraphicalDice</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4292_9" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">GraphicalDice</span><span class="pln"> extends </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> roll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         super</span><span class="pun">.</span><span class="pln">roll</span><span class="pun">();</span><span class="pln">  </span><span class="com">// ‫استدعي roll من الصنف PairOfDice</span><span class="pln">
         redraw</span><span class="pun">();</span><span class="pln">      </span><span class="com">// استدعي تابع لرسم حجري النرد</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">  </span><span class="com">// ‫المزيد بما يتضمن تعريف redraw()</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَسمَح ذلك عمومًا بتمديد (extend) سلوك التابع <code>roll()‎</code> حتى لو لم تَكُن على علم بطريقة تَّنْفيذه (implementation) بالصنف الأعلى.
</p>

<h2>
	<code>super</code> و <code>this</code> كبواني كائن
</h2>

<p>
	لا تُورَث بواني الكائن (constructors) عمومًا، أي أنك إذا أنشأت صنفًا فرعيًا (subclass) من صنف موجود، فإن بواني الكائن المُعرَّفة بالصنف الأعلى (superclass) لا تُصبِح جزءًا من الصنف الفرعي. يُمكِنك إضافة بواني جديدة بالصنف الفرعي إذا أردت، وفي حالة عدم إضافتها، سيُنشِئ الحاسوب بَانِي كائن افتراضي (default constructor) بدون أي مُعامِلات (parameters).
</p>

<p>
	إذا كان الصنف الأعلى (superclass) يَحتوِي على باني كائن (constructor) يُنفِّذ الكثير من التهيئة الضرورية، فيبدو أنك ستَضطرّ إلى تَكْرار كل ذلك العمل بالصنف الفرعي. بالإضافة إلى ذلك، قد لا تَملُك شيفرة الصنف الأعلى (superclass)، ولا تَعلَم حتى طريقة تَّنْفيذها (implementation)، وهو ما قد يُمثِل مشكلة حقيقية، والتي قد تُصبِح مستحيلة إذا كان الباني المُعرَّف بالصنف الأعلى يَستخدِم مُتْغيِّرات أعضاء خاصة (private member variables)، ففي تلك الحالة، أنت لا تَملُك حتى صلاحية للوصول إليها من الصنف الفرعي.
</p>

<p>
	لابُدّ إذًا من وجود طريقة لإصلاح ذلك. في الواقع، قد يُستخدَم المُتْغيِّر الخاص <code>super</code> كأول تَعْليمَة بالباني (constructor) المُعرَّف بالصنف الفرعي؛ لكي يَستدعِي الباني (constructor) المُعرَّف بالصنف الأعلى (superclass). قد تَكُون طريقة كتابة ذلك مُضللة نوعًا ما؛ فهي تبدو كما لو كنت تَستدعِي <code>super</code> كبرنامج فرعي (subroutine) على الرغم من أنه ليس كذلك، وفي العموم لا يُمكِنك استدعاء البواني (constructors) بنفس طريقة استدعاء البرامج الفرعية. والآن، لنَفْترِض أن الصنف <code>PairOfDice</code> يَتَضمَّن باني كائن (constructor) يَستقبِل مُعامِلين (parameters) من النوع <code>int</code>، يُمكِننا إذًا أن نُعرِّف صنفًا فرعيًا (subclass) منه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4292_11" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">GraphicalDice</span><span class="pln"> extends </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

     </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">GraphicalDice</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// باني كائن لهذا الصنف</span><span class="pln">

         </span><span class="com">// استدعي الباني من الصنف الأساسي بقيم المعاملات 3 و 4</span><span class="pln">
         super</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="lit">4</span><span class="pun">);</span><span class="pln">  

         initializeGraphics</span><span class="pun">();</span><span class="pln">  </span><span class="com">// نفذ التهيئة الخاصة بالصنف الفرعي</span><span class="pln">

     </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">   </span><span class="com">// المزيد من البواني والمتغيرات والتوابع</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تَستدعِي التَعْليمَة <code>super(3,4);‎</code> الباني (constructor) المُعرَّف بالصنف الأعلى. لابُدّ من كتابته بالسطر الأول من الباني المُعرَّف بالصنف الفرعي (subclass)، وفي حالة عدم استدعاء أي باني من الصنف الأعلى بصورة صريحة، يُستدعَى تلقائيًا بَانِي الصنف الأعلى الافتراضي (default constructor) أي ذلك الذي لا يَستقبِل أي مُعامِلات (parameters)، وإذا لم يَكُن موجودًا، سيُبلِّغ المُصرِّف (compiler) عن وجود خطأ في بناء الجملة (syntax error).
</p>

<p>
	يُستخدَم المُتْغيِّر الخاص <code>this</code> بنفس الطريقة تمامًا، ولكن لاستدعاء باني (constructor) آخر ضِمْن نفس الصنف. سيَبدُو عندها السطر الأول من الباني كما لو كان استدعاءً لبرنامج فرعي اسمه هو <code>this</code>، وسيُنفَّذ عندها مَتْن الباني المُستدعَى مما يُجنّبك تكرار نفس الشيفرة ضِمْن عدة بواني مختلفة. على سبيل المثال، يُستخدَم الصنف <code>MosaicCanvas</code> -من القسم ٤.٧- لتمثيل شبكة من المستطيلات الملونة، ويَتَضمَّن باني يَستقبِل أربعة مُعامِلات (parameters) كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4292_13" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">MosaicCanvas</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> rows</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> columns</span><span class="pun">,</span><span class="pln"> 
                 </span><span class="typ">int</span><span class="pln"> preferredBlockWidth</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> preferredBlockHeight</span><span class="pun">)</span></pre>

<p>
	يَستقبِل ذلك الباني مجموعة من الخيارات يُنفِّذ على أساسها الكثير من التهيئة (initialization). قد تحتاج إلى إضافة بواني (constructors) آخرى بعدد أقل من الخيارات لتَكُون أسهل في الاِستخدَام، ولكن ما يزال تَّنْفيذ التهيئة كاملة أمرًا ضروريًا. يَحتوِي الصنف على البواني التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4292_15" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">MosaicCanvas</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">(</span><span class="lit">42</span><span class="pun">,</span><span class="lit">42</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MosaicCanvas</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> rows</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> columns</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">(</span><span class="pln">rows</span><span class="pun">,</span><span class="pln">columns</span><span class="pun">,</span><span class="lit">16</span><span class="pun">,</span><span class="lit">16</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَستدعِى كل باني كائن (constructor) منهما باني كائن آخر بالإضافة إلى توفير قيم ثابتة للمُعامِلات الآخرى التي لم يَستقبِلها. على سبيل المثال، يَستدعِي التعبير <code>this(42,42)‎</code> الباني الثاني، والذي بدوره يَستدعِي الباني الأساسي ذو المُعامِلات الأربعة. في الواقع، يُستدعَى الباني الأساسي بجميع الحالات وذلك لضمان تَّنْفيذ التهيئة (initialization) الضرورية كاملةً.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s6.html" rel="external nofollow">Section 6: this and super</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1113</guid><pubDate>Tue, 26 Jan 2021 12:50:32 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x631;&#x627;&#x62B;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x62F;&#x62F;&#x64A;&#x629; &#x627;&#x644;&#x634;&#x643;&#x644;&#x64A;&#x629; (Polymorphism) &#x648;&#x627;&#x644;&#x623;&#x635;&#x646;&#x627;&#x641; &#x627;&#x644;&#x645;&#x62C;&#x631;&#x62F;&#x629; (Abstract Classes) &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%88%D8%B1%D8%A7%D8%AB%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D9%83%D9%84%D9%8A%D8%A9-polymorphism-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D9%85%D8%AC%D8%B1%D8%AF%D8%A9-abstract-classes-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1112/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/35.png.c8ec9251b0f2ec97c5f69e6b0e91400a.png" /></p>

<p>
	تُستخدَم الأصناف (classes) لتمثيل مجموعة كائنات (objects) تَتَشارَك نفس البِنْية (structure) والسلوك (behavior). يُحدِّد الصَنْف بِنْية تلك الكائنات عن طريق تَخْصِيصه للمُتْغيِّرات الموجودة ضِمْن أي نسخة (instance) من الصَنْف، كما يُحدِّد سلوكها بتَعْريفه لتوابع نُسخ (instance methods) تُعبر عن سلوك (behavior) تلك الكائنات. هذه الفكرة قوية للغاية، ولكنها مع ذلك مُمكِنة بغالبية اللغات البرمجية التقليدية، فما هي إذًا الفكرة الجديدة التي تُقدِّمها البرمجة كائنية التوجه (object-oriented programming)، وتُفرِّقها عن البرمجة التقليدية؟ في الواقع، تُدعِّم البرمجة كائنية التوجه كُلًا من الوراثة (inheritance) والتعددية الشكلية (polymorphism)، والتي تسمح للأصناف (classes) بتمثيل التشابهات بين مجموعة كائنات تَتَشارَك بعضًا من بِنْيتها وسُلوكها، وليس كلها.
</p>

<h2>
	توسيع (extend) الأصناف الموجودة
</h2>

<p>
	لابُدّ لأي مبرمج أن يفهم المقصود بكُلًا من المفاهيم التالية: الصَنْف الفرعي (subclass)، والوراثة (inheritance)، والتعددية الشكلية (polymorphism). قد تَستغرِق بعض الوقت حتى تستطيع تَوْظِيف مواضيع مثل الوراثة (inheritance) تَوْظِيفًا فعليًا بعيدًا عن مُجرَّد تَمْديد (extending) بعض الأصناف الموجودة، وهو ما سنُناقشه بالجزء الأول من هذا القسم.
</p>

<p>
	ببداية تَعامُل المبرمجين مع الكائنات، عادةً ما يَقْتصر اِستخدَامهم للأصناف الفرعية على موقف واحد فقط: "التَعْديل على صَنْف موجود بالفعل أو الإضافة إليه بحيث يَتلائَم مع ما يُريدونه". يُعدّ ذلك أكثر شيوعًا من تصميم مجموعة كاملة من الأصناف الأعلى والأصناف الفرعية (subclasses). تستطيع أن تُمدِّد (extend) صَنْف فرعي من صَنْف آخر باستخدام الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_13" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">subclass</span><span class="pun">-</span><span class="pln">name</span><span class="pun">&gt;</span><span class="pln"> extends </span><span class="pun">&lt;</span><span class="pln">existing</span><span class="pun">-</span><span class="kwd">class</span><span class="pun">-</span><span class="pln">name</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">   </span><span class="com">// التعديلات والإضافات</span><span class="pln">
   </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنَفْترِض أنك تُريد كتابة برنامج للعبة الورق "بلاك جاك (Blackjack)" تَستخدِم فيه الأصناف <code>Card</code> و <code>Hand</code> و <code>Deck</code> من القسم ٥.٤. أولًا، تَختلِف اليد (hand) بلعبة بلاك جاك نوعًا ما عن اليد في العموم؛ حيث تُحسَب قيمة اليد بلعبة بلاك جاك وِفقًا لقواعد اللعبة، والتي يُمكِن تَلخَّيصها كالتالي: تُحسَب قيمة اليد بجَمْع قيم ورق اللعب باليد، بحيث تَكُون قيمة ورقة اللعب العددية (numeric card)، مثل "ثلاثة" أو "عشرة"، مُساوِية لنفس قيمتها العددية، أما ورقة اللعب المُصورة، مثل "الرجل" أو "الملكة" أو "الملك"، فقيمتها تُساوِي عشرة. وأخيرًا، قيمة ورقة "الآص" قد تُساوِي ١ أو ١١. إنها في العموم تُساوِي ١١ إلا لو أدى ذلك إلى قيمة كلية أكبر من ٢١، مما يَعنِي أن ورقة "الآص" الثانية والثالثة والرابعة قيمها بالضرورة ستُساوِي ١.
</p>

<p>
	أحد الطرائق لمعالجة ما سبق هو توسيع (extend) الصنف <code>Hand</code> الموجود بإضافة تابع (method) يَحسِب قيمة اليد وفقًا للعبة بلاك جاك. اُنظر تعريف (definition) الصنف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_15" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BlackjackHand</span><span class="pln"> extends </span><span class="typ">Hand</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="com">/**
     * Computes and returns the value of this hand in the game
     * of Blackjack.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getBlackjackValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

        </span><span class="typ">int</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">      </span><span class="com">// قيمة اليد</span><span class="pln">
        boolean ace</span><span class="pun">;</span><span class="pln">  
        </span><span class="typ">int</span><span class="pln"> cards</span><span class="pun">;</span><span class="pln">    </span><span class="com">// عدد ورق اللعب باليد</span><span class="pln">

        val </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        ace </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        cards </span><span class="pun">=</span><span class="pln"> getCardCount</span><span class="pun">();</span><span class="pln">  </span><span class="com">// تابع معرف بالصنف الأعلى</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">  i </span><span class="pun">&lt;</span><span class="pln"> cards</span><span class="pun">;</span><span class="pln">  i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="com">// أضف قيمة ورقة اللعب الحالية</span><span class="pln">
            </span><span class="typ">Card</span><span class="pln"> card</span><span class="pun">;</span><span class="pln">    </span><span class="com">// ورقة اللعب الحالية</span><span class="pln">
            </span><span class="typ">int</span><span class="pln"> cardVal</span><span class="pun">;</span><span class="pln">  </span><span class="com">// قيمة بلاك جاك لورقة اللعب الحالية</span><span class="pln">
            card </span><span class="pun">=</span><span class="pln"> getCard</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
            cardVal </span><span class="pun">=</span><span class="pln"> card</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">();</span><span class="pln">  </span><span class="com">// قيمة ورقة اللعب</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cardVal </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                cardVal </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">   
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cardVal </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                ace </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">     </span><span class="com">// هناك على الأقل ورقة آص </span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">+</span><span class="pln"> cardVal</span><span class="pun">;</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">

         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> ace </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">  </span><span class="pun">&amp;&amp;</span><span class="pln">  val </span><span class="pun">+</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">21</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
             val </span><span class="pun">=</span><span class="pln"> val </span><span class="pun">+</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

         </span><span class="kwd">return</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">

    </span><span class="pun">}</span><span class="pln">  </span><span class="com">// نهاية‫ getBlackjackValue</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// نهاية الصنف‫ BlackjackHand</span></pre>

<p>
	لمّا كان الصَنْف <code>BlackjackHand</code> هو صَنْف فرعي (subclass) من الصَنْف الأعلى <code>Hand</code>، فإن أيّ كائن من الصَنْف الفرعي <code>BlackjackHand</code> سيَتَضمَّن، إلى جانب تابع النسخة <code>getBlackjackValue()‎</code> المُعرَّف ضِمْن صَنْفه الفرعي، جميع مُتْغيِّرات النسخ (instance variables) وتوابع النسخ (instance methods) المُعرَّفة بالصَنْف الأعلى <code>Hand</code>. فمثلًا، إذا كان <code>bjh</code> مُتْغيِّرًا من النوع <code>BlackjackHand</code>، يَصِِح اِستخدَام أي من الاستدعاءات التالية: <code>bjh.getCardCount()‎</code> و <code>bjh.removeCard(0)‎</code> و <code>bjh.getBlackjackValue()‎</code>. على الرغم من أن التابعين الأول والثاني مُعرَّفين بالصَنْف <code>Hand</code>، إلا أنهما قد وُرِثَا إلى الصَنْف <code>BlackjackHand</code>.
</p>

<p>
	تُورَث المُتْغيِّرات والتوابع من الصَنْف الأعلى <code>Hand</code> إلى الصَنْف الفرعي <code>BlackjackHand</code>، ولهذا تستطيع اِستخدَامها بتعريف الصَنْف الفرعي <code>BlackjackHand</code> كما لو كانت مُعرَّفة فعليًا بذلك الصَنْف، باستثناء تلكم المُعرَّفة باِستخدَام المُبدِّل <code>private</code> ضِمْن الصَنْف الأعلى؛ حيث يَمنَع ذلك المُبدِّل الوصول إليها حتى من الأصناف الفرعية (subclasses). مثلًا، بتعريف التابع <code>getBlackjackValue()‎</code> -بالأعلى-، تَمَكَّنت التَعْليمَة <code>cards = getCardCount();‎</code> من استدعاء تابع النسخة <code>getCardCount()‎</code> المُعرَّف بالصَنْف <code>Hand</code>.
</p>

<p>
	يُساعد تَمْديد (extend) الأصناف على الاعتماد على أعمالك السابقة، وفي الواقع لقد كُتبَت الكثير من الأصناف القياسية (standard classes) خصيصًا لتَكُون قاعدةً وأساسًا لإنشاء أصناف فرعية.
</p>

<p>
	تُستخدَم مُبدِّلات الوصول (access modifiers) مثل <code>public</code> و <code>private</code> للتَحكُّم بالوصول إلى أعضاء (members) الصَنْف. عندما نَأخُذ الأصناف الفرعية (subclasses) بالحُسبان، يُصبِح من المناسب الحديث عن مُبدِّل وصول آخر تُوفِّره الجافا، هو <code>protected</code>. عند اِستخدَام ذلك المُبدِّل أثناء التَّصْريح عن تابع (method) أو مُتْغيِّر عضو (member variable) بصَنْف، يُصبِح اِستخدَام ذلك العضو مُمكنًا ضِمْن الأصناف الفرعية لذلك الصَنْف وليس بأي مكان آخر. هنالك استثناء: عندما تَستخدِم المُبدِّل <code>protected</code> للتّصريح عن عضو (member)، فإنه يَكُون قابلًا للوصول من أي صَنْف بنفس الحزمة (package). ذَكَرَنا من قَبْل أنه في حالة عدم تخصيص أي مُبدِّل وصول (access modifier) لعضو معين، فإنه يُصبِح قابلًا للوصول من جميع الأصناف الواقعة ضِمْن نفس الحزمة وليس بأي مكان آخر. يُعدّ المُبدَّل <code>protected</code> أكثر تحررًا من ذلك بقليل؛ فبالإضافة إلى الأصناف الواقعة ضِمْن نفس الحزمة، فإنه يجعل العضو أيضًا قابلًا للوصول من الأصناف الفرعية (subclasses) التي لا تقع ضِمْن نفس الحزمة.
</p>

<p>
	عندما تُصرِّح عن تابع أو مُتْغيِّر عضو باستخدام المُبدِّل <code>protected</code>، يُصبِح ذلك العضو جزءًا من تّنْفيذ (implementation) الصنف لا واجهته (interface) العامة، كما يُسمَح للأصناف الفرعية (subclasses) باِستخدَام ذلك الجزء من التّنْفيذ (implementation) أو تَعْدِيله.
</p>

<p>
	على سبيل المثال، يَتَضمَّن الصَنْف <code>PairOfDice</code> مُتْغيِّري النسخة <code>die1</code> و <code>die2</code> لتمثيل الأعداد الظاهرة على حجرى نَّرد. قد نُصرِّح عنهما باِستخدَام المُبدِّل <code>private</code> لكي يُصبِح تَعْديل قيمتهما من خارج الصنف مستحيلًا، ثُمَّ نُعرِّف توابع جَلْب (getter methods) للسماح بقراءة قيمتهما من خارج الصنف، لكن لو تَبيَّن لك إمكانية الحاجة لاِستخدَام الصنف <code>PairOfDice</code> لإنشاء أصناف فرعية (subclasses)، فقد يَكُون من الأفضل حينها السماح لتلك الأصناف الفرعية (subclasses) بتَعْديل قيم تلك الأعداد، أي تعديل قيم تلك المُتْغيِّرات. فمثلًا، يَرسم الصنف الفرعي <code>GraphicalDice</code> حجري النَّرد، وقد يَضطرّ إلى تعديل قيم تلك الأعداد بتوقيت غَيْر ذلك الذي يُرمَي فيه حجري النَّرد. بدلًا من اِستخدَام المُبدِّل <code>public</code> في تلك الحالة، قد نَستخدِم المُبدِّل <code>protected</code> للتَّصْريح عن كُلًا من <code>die1</code> و <code>die2</code>، مما سيَسمَح للأصناف الفرعية فقط -دون العالم الخارجي- بتَعْديل قيمتهما. كبديل، قد تُفضِّل تعريف توابع ضَبْط (setter methods) لتلك المُتْغيِّرات ضِمْن الصنف الأعلى، بحيث يُصرَّح عنها باِستخدَام المُبدِّل <code>protected</code>، وذلك لكي تَتَمكَّن من التَأكُّد من أن القيمة المطلوب إِسْنادها للمُتْغيِّر تقع ضِمْن نطاق يتراوح بين ١ و ٦.
</p>

<h2>
	الوراثة (inheritance) وسلالة أصناف (class hierarchy)
</h2>

<p>
	يُمكِن لصَنْف معين أن يَرِث جزء أو كل بِنيته (structure)، وسلوكه (behavior) من صَنْف آخر، وهو ما يُعرَف باسم الوراثة (inheritance). يُقال عن الصنف الوَارِث أنه صنفًا فرعيًا (subclass) من الصنف المَورُوث. إذا كان الصنف <code>B</code> هو صنف فرعي من الصنف <code>A</code>، فإننا نستطيع أيضًا أن نقول أن الصنف <code>A</code> هو صنف أعلى (superclass) من الصنف <code>B</code>. قد يُضيف الصنف الفرعي إلى ما وَرِثه من بنية وسلوك، كما قد يُعدِّل أو يَستبدِل ما وَرِثه من سلوك. تَستخدِم بعض اللغات البرمجية الآخرى، مثل لغة C++‎، مصطلحات أخرى كالصنف المُشتقّ (derived class) والصنف الأساسي (base class) بدلًا من الصنف الفرعي (subclass) والصنف الأعلى (superclass). عادةً ما تُوضَح العلاقة بين الصنف الفرعي (subclass) والصنف الأعلى (superclass) برسم توضيحي، يقع فيه الصنف الفرعي (subclass) أسفل صنفه الأعلى (superclass) ويَكُون مُتصلًا به، تمامًا كما هو مُوضَح بيسار الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2021_01/001Subclass_Superclass.png.791f12f1d9dc3ddcb42da2a8a6f6ae06.png" data-fileid="56456" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="56456" data-unique="1onrvy66z" src="https://academy.hsoub.com/uploads/monthly_2021_01/001Subclass_Superclass.png.791f12f1d9dc3ddcb42da2a8a6f6ae06.png" alt="001Subclass_Superclass.png"></a>
</p>

<p>
	تُستخدَم الشيفرة التالية لإنشاء صَنْف اسمه <code>B</code> بحيث يكون صنفًا فرعيًا من صنف آخر اسمه <code>A</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_17" style="">
<span class="kwd">class</span><span class="pln"> B extends A </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">  </span><span class="com">// إضافات أو تعديلات على الموروث من الصنف‫ A</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُمكِنك أن تُصرِّح عن عدة أصناف على أساس كَوْنها أصناف فرعية (subclasses) من نفس الصنف الأعلى (superclass)، وتُعدّ الأصناف الفرعية في تلك الحالة "أصناف أخوة (sibling classes)" تَتَشارَك بعضًا من بِنيتها (structure) وسلوكها (behavior)، تَحْديدًا تلك المُوروثة من الصَنْف الأعلى المشترك، فمثلًا، الأصناف <code>B</code> و <code>C</code> و <code>D</code> بيمين الصورة السابقة هي أصناف أخوة (sibling classes). يُمكِن للوراثة (inheritance) أن تتمدَّد أيضًا عَبْر أجيال من الأصناف، فمثلًا، الصنف <code>E</code> -بالصورة السابقة- هو صنف فرعي من الصنف <code>D</code>، والذي هو بدوره صنف فرعي من الصنف <code>A</code>، ويُعدّ الصنف <code>E</code> عندها صنفًا فرعيًا من الصنف <code>A</code> حتى وإن لَمْ يَكُن صنفًا فرعيًا مباشرًا. تُشكِّل مجموعة الأصناف تلك سُلالة أصناف (class hierarchy) صغيرة.
</p>

<h2>
	مثال
</h2>

<p>
	والآن، لنَفْترِض أننا نريد كتابة برنامج ينبغي له التَعامُل مع المركبات المتحركة كالسيارات، والشاحنات، والدراجات النارية (قد يَستخدِمه قسم المركبات المتحركة لأغراض تَعقُّب التسجيلات). يُمكِننا أن نُعرِّف صَنْفًا اسمه <code>Vehicle</code> لتمثيل جميع أنواع المركبات، ولأن السيارات والشاحنات والدراجات النارية هي أنواع من المركبات، يُمكِننا تمثيلها باستخدام أصناف فرعية (subclasses) من الصنف <code>Vehicle</code>، كما هو مُوضَح بسُلالة الأصناف (class hierarchy) التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2021_01/002Vehicle_Hierarchy.png.4b4f2520d2cf641b2f05944abbc5b365.png" data-fileid="56457" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="56457" data-unique="lny3elicy" src="https://academy.hsoub.com/uploads/monthly_2021_01/002Vehicle_Hierarchy.png.4b4f2520d2cf641b2f05944abbc5b365.png" alt="002Vehicle_Hierarchy.png"></a>
</p>

<p>
	قد يَتَضمَّن الصنف <code>Vehicle</code> مُتْغيِّرات نُسخ (instance variables) مثل <code>registrationNumber</code> و <code>owner</code>، بالإضافة إلى توابع نُسخ (instance methods) مثل <code>transferOwnership()‎</code>. ستَملُك جميع المركبات تلك المُتْغيِّرات والتوابع. نستطيع الآن تعريف الأصناف الفرعية (subclasses) الثلاثة <code>Car</code> و <code>Truck</code> و <code>Motorcycle</code> المُشتقّة من الصَنْف <code>Vehicle</code> بحيث يَحمِل كُلًا منها مُتْغيِّرات وتوابع تَخُصّ ذلك النوع المُحدَّد من المركبات، فمثلًا، قد يُعرَّف الصنف <code>Car</code> مُتْغيِّر نسخة <code>numberOfDoors</code>، بينما قد يُعرَّف الصنف <code>Truck</code> مُتْغيِّر نسخة <code>numberOfAxles</code>، وقد يُضيف الصنف <code>Motorcycle</code> مُتْغيِّرًا منطقيًا اسمه <code>hasSidecar</code>. يُمكِننا التَّصْريح عن تلك الأصناف كالتالي (ببرنامج حقيقي، ستُعرَّف تلك الأصناف عادةً بملفات مُنفصلة وعلى أساس كَوْنها عامة):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_19" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Vehicle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> registrationNumber</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">Person</span><span class="pln"> owner</span><span class="pun">;</span><span class="pln">  </span><span class="com">// (بفرض تعريف الصنف Person)</span><span class="pln">
   </span><span class="kwd">void</span><span class="pln"> transferOwnership</span><span class="pun">(</span><span class="typ">Person</span><span class="pln"> newOwner</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Car</span><span class="pln"> extends </span><span class="typ">Vehicle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> numberOfDoors</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Truck</span><span class="pln"> extends </span><span class="typ">Vehicle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> numberOfAxles</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Motorcycle</span><span class="pln"> extends </span><span class="typ">Vehicle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   boolean hasSidecar</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنَفْترِض أن <code>myCar</code> هو مُتْغيِّر من النوع <code>Car</code> صُرِّح عنه وهُيئ باِستخدَام التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_21" style="">
<span class="typ">Car</span><span class="pln"> myCar </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Car</span><span class="pun">();</span></pre>

<p>
	الآن، نستطيع الإشارة إلى <code>myCar.numberOfDoors</code>؛ لأن <code>numberOfDoors</code> مُتْغيِّر نسخة بالصنف <code>Car</code>. بالإضافة إلى ذلك، لمّا كان الصنف <code>Car</code> مُشتقًّا من الصنف <code>Vehicle</code>، فإن أي كائن من الصنف <code>Car</code> يَملُك بنية وسلوك الكائنات من الصنف <code>Vehicle</code>، مما يعني إمكانية الإشارة إلى كُلًا من <code>myCar.registrationNumber</code> و <code>myCar.owner</code> و <code>myCar.transferOwnership()‎</code>.
</p>

<p>
	بالعالم الحقيقي، تُعدّ كُلًا من السيارات والشاحنات والدراجات النارية مركبات، وهو ما أَصبَح مُتحقِّقًا بالبرنامج أيضًا، فأيّ كائن من النوع <code>Car</code> أو <code>Truck</code> أو <code>Motorcycle</code> يُعدّ كذلك كائنًا من النوع <code>Vehicle</code> تلقائيًا. يَقُودنا ذلك إلى الحقيقة الهامة التالية:
</p>

<p>
	إذا أمكَّن لمُتْغيِّر حَمْل مَرجِع إلى كائن من الصَنْف <code>A</code>، فحتمًا بإِمكانه حَمْل مَرجِع إلى الكائنات المُنتمية لأيّ من أصناف <code>A</code> الفرعية.
</p>

<p>
	عمليًا، يَعنِي ذلك إمكانية إِسْناد كائن من الصنف <code>Car</code> إلى مُتْغيِّر من النوع <code>Vehicle</code>، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_23" style="">
<span class="typ">Vehicle</span><span class="pln"> myVehicle </span><span class="pun">=</span><span class="pln"> myCar</span><span class="pun">;</span></pre>

<p>
	أو كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_25" style="">
<span class="typ">Vehicle</span><span class="pln"> myVehicle </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Car</span><span class="pun">();</span></pre>

<p>
	بَعْد إجراء أيّ من التَعْليمَتين -بالأعلى-، سيَحمِل المُتْغيِّر <code>myVehicle</code> مَرجِعًا (reference) إلى كائن من النوع <code>Vehicle</code>، والذي هو في حقيقته نُسخة (instance) من الصَنْف الفرعي <code>Car</code>. يُدرك الكائن حقيقة انتماءه للصنف <code>Car</code> وليس فقط للصنف <code>Vehicle</code>، كما تُخزَّن تلك المعلومة -أيّ صنف الكائن الفعليّ- كجزء من الكائن نفسه، وتستطيع حتى اختبار ما إذا كان كائن معين ينتمي إلى أحد الأصناف عن طريق العَامِل <code>instanceof</code>، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_27" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myVehicle instanceof </span><span class="typ">Car</span><span class="pun">)</span><span class="pln"> </span><span class="pun">...</span></pre>

<p>
	تَفْحَص التَعْليمَة -بالأعلى- ما إذا كان الكائن المُشار إليه باستخدام المُتْغيِّر <code>myVehicle</code> هو بالأساس من النوع <code>Car</code>.
</p>

<p>
	في المقابل، لا تَصِح تَعْليمَة الإِسْناد (assignment) التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_29" style="">
<span class="pln">myCar </span><span class="pun">=</span><span class="pln"> myVehicle</span><span class="pun">;</span><span class="pln">  </span><span class="com">// خطأ</span></pre>

<p>
	لأن <code>myVehicle</code> قد يُشير إلى أنواع آخرى غَيْر النوع <code>Car</code>. يُشبه ذلك المشكلة التي تَعرَّضنا لها بالقسم الفرعي ٢.٥.٦: لن يَسمَح الحاسوب بإِسْناد قيمة من النوع <code>int</code> إلى مُتْغيِّر من النوع <code>short</code>؛ لأن ليس كل <code>int</code> هو <code>short</code> بالضرورة. بالمثل، فإنه لن يَسمَح بإِسْناد قيمة من النوع <code>Vehicle</code> إلى مُتْغيِّر من النوع <code>Car</code>؛ لأن ليس كل كائن من النوع <code>Vehicle</code> هو كائن من النوع <code>Car</code> بالضرورة. بإِمكانك تَجاوُز تلك المشكلة عن طريق إجراء عملية التَحْوِيل بين الأنواع (type-casting)، فمثلًا، إذا كنت تَعلَم -بطريقة ما- أن <code>myVehicle</code> يُشير فعليًا إلى كائن من النوع <code>Car</code>، تستطيع اِستخدَام <code>‎(Car)myVehicle</code> لإجبار الحاسوب على التَعامُل مع <code>myVehicle</code> كما لو كان من النوع <code>Car</code>. لذا تستطيع كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_31" style="">
<span class="pln">myCar </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Car</span><span class="pun">)</span><span class="pln">myVehicle</span><span class="pun">;</span></pre>

<p>
	تستطيع حتى الإشارة إلى <code>‎((Car)myVehicle).numberOfDoors</code>. لاحِظ أن الأقواس ضرورية لأسباب تَتَعلَّق بالأولوية (precedence)؛ فالعَامِل <code>.</code> لديه أولوية أكبر من عملية إجراء التَحْوِيل بين الأنواع (type-cast) أيّ أن التعبير <code>‎(Car)myVehicle.numberOfDoors</code> سيُقرأ كما لو كان مَكْتُوبًا على الصورة <code>(Car)(myVehicle.numberOfDoors)</code> وستُجرَى عندها محاولة للتَحْوِيل من النوع <code>int</code> إلى <code>Vehicle</code> وهو أمر مستحيل.
</p>

<p>
	سنَفْحَص الآن مثالًا لكيفية اِستخدَام ما سبق ضِمْن برنامج. لنَفْترِض أننا نُريد طباعة البيانات المُتعلّقة بالكائن المُشار إليه باِستخدَام المُتْغيِّر <code>myVehicle</code>، فمثلًا، نَطبَع <code>numberOfDoors</code> إذا كان الكائن من النوع <code>Car</code>. لا نستطيع ببساطة الإشارة إليه باستخدام <code>myVehicle.numberOfDoors</code>؛ لأن الصَنْف <code>Vehicle</code> لا يَتَضمَّن أي <code>numberOfDoors</code>، وإنما نستطيع كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_33" style="">
<span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Vehicle Data:"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Registration number:  "</span><span class="pln"> 
                              </span><span class="pun">+</span><span class="pln"> myVehicle</span><span class="pun">.</span><span class="pln">registrationNumber</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myVehicle instanceof </span><span class="typ">Car</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Type of vehicle:  Car"</span><span class="pun">);</span><span class="pln">
   </span><span class="typ">Car</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
   c </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Car</span><span class="pun">)</span><span class="pln">myVehicle</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تحويل للنوع للوصول إلى numberOfDoors</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Number of doors:  "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">numberOfDoors</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myVehicle instanceof </span><span class="typ">Truck</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Type of vehicle:  Truck"</span><span class="pun">);</span><span class="pln">
   </span><span class="typ">Truck</span><span class="pln"> t</span><span class="pun">;</span><span class="pln">
   t </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Truck</span><span class="pun">)</span><span class="pln">myVehicle</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تحويل للنوع للوصول إلى numberOfAxles</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Number of axles:  "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> t</span><span class="pun">.</span><span class="pln">numberOfAxles</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myVehicle instanceof </span><span class="typ">Motorcycle</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Type of vehicle:  Motorcycle"</span><span class="pun">);</span><span class="pln">
   </span><span class="typ">Motorcycle</span><span class="pln"> m</span><span class="pun">;</span><span class="pln">
   m </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Motorcycle</span><span class="pun">)</span><span class="pln">myVehicle</span><span class="pun">;</span><span class="pln">  </span><span class="com">// تحويل للنوع للوصول إلى hasSidecar</span><span class="pln">
   </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Has a sidecar:    "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> m</span><span class="pun">.</span><span class="pln">hasSidecar</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فيما هو مُتعلِّق بالأنواع الكائنية (object type)، عندما يُجرِي الحاسوب عملية التَحْويل بين الأنواع (type cast)، فإنه يَفْحص أولًا ما إذا كانت تلك العملية صالحة أم لا، فمثلًا، إذا كان <code>myVehicle</code> يُشير إلى كائن من النوع <code>Truck</code>، فلا تَصِح العملية <code>‎(Car)myVehicle</code>، وعليه، سيُبلَّغ عن اعتراض (exception) من النوع <code>ClassCastException</code>، ولهذا تَستخدِم الشيفرة -بالأعلى- العَامِل <code>instanceof</code> لاختبار نوع المُتْغيِّر قَبْل إجراء عملية التحويل بين الأنواع (type cast)؛ لتتجنَّب حُدوث الاعتراض <code>ClassCastException</code>. لاحِظ أن هذا الفَحْص يَحدُث وقت التنفيذ (run time) وليس وقت التصريف (compile time)؛ لأن النوع الفعليّ للكائن الذي يُشير إليه المُتْغيِّر <code>myVehicle</code> لا يَكُون معروفًا وقت تصريف البرنامج.
</p>

<h2>
	التعددية الشكلية (polymorphism)
</h2>

<p>
	لنَفْحص برنامجًا يَتعامَل مع أشكال تُرسَم على الشاشة، والتي قد تَكُون مستطيلة (rectangles)، أو بيضاوية (ovals)، أو مستطيلة بأركان دائرية (roundrect)، ومُلوَّنة جميعًا بألوان مختلفة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2021_01/003Various_Shapes.png.e77ca87b10f821a14e639a1f33e2ad8c.png" data-fileid="56458" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="56458" data-unique="fizehc1oz" src="https://academy.hsoub.com/uploads/monthly_2021_01/003Various_Shapes.png.e77ca87b10f821a14e639a1f33e2ad8c.png" alt="003Various_Shapes.png"></a>
</p>

<p>
	سنَستخدِم الأصناف الثلاثة <code>Rectangle</code> و <code>Oval</code> و <code>RoundRect</code> لتمثيل الأشكال المختلفة بحيث تَرِث تلك الأصناف من صَنْف أعلى (superclass) مُشترك <code>Shape</code>، وذلك لتمثيل السمات المشتركة بين جميع الأشكال. فمثلًا، قد يَتَضمَّن الصنف <code>Shape</code> مُتْغيِّرات نُسخ (instance variables) لتمثيل كُلًا من لون الشكل، وموضعه، وحجمه، كما قد يَتَضمَّن توابع نُسخ (instance methods) لتَعْديل قيم تلك السمات. مثلًا، لتَعْديل لون أحد الأشكال، فإننا سنُعدِّل قيمة مُتْغيِّر نُسخة ثم نُعيد رسم الشكل بلونه الجديد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_35" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">Color</span><span class="pln"> color</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لابد من استيرادها من حزمة‫ javafx.scene.paint</span><span class="pln">

    </span><span class="kwd">void</span><span class="pln"> setColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pln"> newColor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// تابع لتغيير لون الشكل</span><span class="pln">
       color </span><span class="pun">=</span><span class="pln"> newColor</span><span class="pun">;</span><span class="pln"> </span><span class="com">// غير قيمة متغير النسخة</span><span class="pln">
       redraw</span><span class="pun">();</span><span class="pln"> </span><span class="com">// أعد رسم الشكل ليظهر باللون الجديد</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// تابع لرسم الشكل</span><span class="pln">
       </span><span class="pun">?</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="pun">?</span><span class="pln">  </span><span class="com">// ما الذي ينبغي كتابته هنا؟</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">          </span><span class="com">// المزيد من متغيرات النسخ والتوابع</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// نهاية الصنف‫ Shape</span></pre>

<p>
	يُرسَم كل نوع من تلك الأشكال بطريقة مختلفة، ولهذا ستُواجهنا مشكلة بخصوص التابع <code>redraw()‎</code>. نستطيع عمومًا استدعاء التابع <code>setColor()‎</code> لأي نوع من الأشكال، وبالتالي سيَحتَاج الحاسوب إلى تَّنْفيذ الاستدعاء <code>redraw()‎</code>، فكيف له إذًا أن يَعرِف الشكل الذي ينبغي عليه رَسْمه؟ نظريًا، يَطلُب الحاسوب من الشكل نفسه أن يقوم بعملية الرسم، فبالنهاية، يُفْترَض أن يَكُون كل كائن من النوع <code>Shape</code> على علم بما عليه القيام به لكي يَرسِم نفسه.
</p>

<p>
	عمليًا، يَعنِي ذلك أن كل صنف من الأصناف الثلاثة ينبغي له أن يُعرِّف نسخته الخاصة من التابع <code>redraw()‎</code>، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_37" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> extends </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">  </span><span class="com">// تعليمات رسم مستطيل</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// المزيد من توابع ومتغيرات النسخ</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Oval</span><span class="pln"> extends </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">  </span><span class="com">// تعليمات رسم شكل بيضاوي</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// المزيد من توابع ومتغيرات النسخ</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">RoundRect</span><span class="pln"> extends </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">  </span><span class="com">// تعليمات رسم مستطيل دائري الأركان</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="com">// المزيد من توابع ومتغيرات النسخ</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنَفْترِض أن <code>someShape</code> هو مُتْغيِّر من النوع <code>Shape</code>، أيّ أنه يستطيع الإشارة إلى أيّ كائن ينتمي لأيّ من الأنواع <code>Rectangle</code> و <code>Oval</code> و <code>RoundRect</code>. أثناء تَّنْفيذ البرنامج، قد تَتغيَّر قيمة المُتْغيِّر <code>someShape</code>، مما يَعنِي أنه قد يُشير إلى كائنات من أنواع مختلفة بأوقات مختلفة! لذا، عندما تُنفَّذ التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_39" style="">
<span class="pln">someShape</span><span class="pun">.</span><span class="pln">redraw</span><span class="pun">();</span></pre>

<p>
	فإن نسخة التابع <code>redraw</code> المُستدعَاة فعليًا تَكُون تلك المُتناسبة مع النوع الفعليّ للكائن الذي يُشير إليه المُتْغيِّر <code>someShape</code>. بالنظر إلى نص الشيفرة فقط، قد لا تَتَمكَّن حتى من مَعرِفة الشكل الذي ستَرسِمه تلك التَعْليمَة حيث يعتمد ذلك بالأساس على القيمة التي تَصادَف أن احتواها <code>someShape</code> أثناء تَّنْفيذ البرنامج. علاوة على ذلك، لنَفْترِض أن تلك التَعْليمَة مُضمَّنة بحَلْقة تَكْرار (loop) وتُنفَّذ عدة مرات. إذا كانت قيمة <code>someShape</code> تتغيَّر بينما تُنفَّذ حَلْقة التَكْرار، فقد تَستدعِي نفس تلك التَعْليمَة <code>someShape.redraw();‎</code> -أثناء تَّنْفيذها عدة مرات- توابعًا (methods) مختلفة، وتَرسِم أنواعًا مختلفة من الأشكال. يُقال عندها أن التابع <code>redraw()‎</code> هو مُتعدِّد الأشكال (polymorphic). في العموم، يُعدّ تابع معين مُتعدد الأشكال (polymorphic) إذا كان ما يُنفِّذه ذلك التابع مُعتمدًا على النوع الفعليّ (actual type) للكائن المُطبقّ عليه التابع أثناء وقت التَّنْفيذ (run time)، وتُعدّ التعددية الشكلية (polymorphism) واحدة من أهم السمات المميزة للبرمجة كائنية التوجه (object-oriented programming).
</p>

<p>
	تَتضِح الصورة أكثر إذا كان لديك مصفوفة أشكال (array of shapes). لنَفْترِض أن <code>shapelist</code> هو مُتْغيِّر من النوع <code>Shape[]‎</code> يُشير إلى مصفوفة قد أُنشأت وهُيأت عناصرها بمجموعة كائنات، والتي قد تَكُون من النوع <code>Rectangle</code> أو <code>Oval</code> أو <code>RoundRect</code>. نستطيع رسم جميع الأشكال بالمصفوفة بكتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_41" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> shapelist</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Shape</span><span class="pln"> shape </span><span class="pun">=</span><span class="pln"> shapelist</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
    shape</span><span class="pun">.</span><span class="pln">redraw</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بينما تُنفَّذ الحلقة (loop) -بالأعلى-، فإن التَعْليمَة <code>shape.redraw()‎</code> قد تَرسِم مستطيلًا أو شكلًا بيضاويًا أو مستطيلًا دائري الأركان اعتمادًا على نوع الكائن الذي يُشير إليه عنصر المصفوفة برقم المَوضِع <code>i</code>.
</p>

<p>
	ربما تَتضِح الفكرة أكثر إذا بدّلنا المصطلحات قليلًا كالتالي: بالبرمجة كائنية التوجه (object-oriented programming)، تُعدّ تَعْليمَة استدعاء أي تابع بمثابة إرسال رسالة إلى كائن، بحيث يَرُد ذلك الكائن على تلك الرسالة بتَّنْفيذ التابع المناسب. مثلًا، التَعْليمَة <code>someShape.redraw();‎</code> هي بمثابة رسالة إلى الكائن المُشار إليه باِستخدَام <code>someShape</code>، ولمّا كان هذا الكائن يَعرِف بالضرورة النوع الذي ينتمي إليه، فإنه يَعرِف الكيفية التي ينبغي أن يَرُد بها على تلك الرسالة. وفقًا لذلك، يُنفِّذ الحاسوب الاستدعاء <code>someShape.redraw();‎</code> بنفس الطريقة دائمًا، أيّ يُرسِل رسالة يَعتمِد الرد عليها على المُستقبِل. تَكُون الكائنات (objects) عندها بمثابة كيانات نَشِطة تُرسِل الرسائل وتَستقبِلها، كما تَكُون التعددية الشكلية (polymorphism) -وفقًا لهذا التصور- جزءًا طبيعًيا، بل وضروريًا، فتَعنِي فقط أنه من الممكن لكائنات مختلفة الرد على نفس الرسالة بطرائق مختلفة.
</p>

<p>
	تَسمَح لك التعددية الشكلية (polymorphism) بكتابة شيفرة تُنفِّذ أشياء قد لا تَتَصوَّرها حتى أثناء الكتابة، وهو في الواقع أحد أكثر الأشياء الرائعة المُتعلِّقة بها. لنَفْترِض مثلًا أنه قد تَقرَّر إضافة مستطيلات مشطوفة الأركان (beveled rectangles) إلى أنواع الأشكال التي يُمكِن للبرنامج التَعامُل معها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2021_01/004Beveled_Rects.png.810e2dcf65b5d998f5cd0d58bf735be0.png" data-fileid="56459" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="56459" data-unique="77ywylcnu" src="https://academy.hsoub.com/uploads/monthly_2021_01/004Beveled_Rects.png.810e2dcf65b5d998f5cd0d58bf735be0.png" alt="004Beveled_Rects.png"></a>
</p>

<p>
	لتَّنْفيذ (implement) المستطيلات مشطوفة الأركان، يُمكِننا أن نُضيف صنفًا فرعيًا (subclass) جديدًا، وليَكُن <code>BeveledRect</code>، مُشتقًّا من الصنف <code>Shape</code>، ثم نُعرِّف به نسخته من التابع <code>redraw()‎</code>. تلقائيًا، ستَجِدْ أن الشيفرة التي كنا قد كتبناها سابقًا، أيّ <code>someShape.redraw()‎</code>، قد أَصبَح بإِمكانها فجأة رسم المستطيلات مشطوفة الأركان على الرغم من أن صَنْف تلك المستطيلات لم يَكُن موجودًا من الأساس أثناء كتابة تلك التَعْليمَة.
</p>

<p>
	تُرسَل الرسالة <code>redraw</code> إلى الكائن <code>someShape</code> أثناء تَّنْفيذ التَعْليمَة <code>someShape.redraw();‎</code>. لنَفْحَص مرة آخرى التابع المسئول عن تَعْديل لون الشكل والمُعرَّف بالصنف <code>Shape</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_43" style="">
<span class="kwd">void</span><span class="pln"> setColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pln"> newColor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   color </span><span class="pun">=</span><span class="pln"> newColor</span><span class="pun">;</span><span class="pln"> </span><span class="com">// غير قيمة متغير النسخة</span><span class="pln">
   redraw</span><span class="pun">();</span><span class="pln"> </span><span class="com">// أعد رسم الشكل ليظهر باللون الجديد</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بالأعلى، تُرسَل الرسالة <code>redraw</code> ولكن إلى أي كائن؟ حسنًا، يُعدّ التابع <code>setColor</code> هو الآخر بمثابة رسالة كانت قد أُرْسِلَت إلى كائن معين. لذا، فإن الرسالة <code>redraw</code> تُرسَل إلى نفس ذلك الكائن الذي كان قد اِستقبَل الرسالة <code>setColor</code>. إذا كان هذا الكائن مستطيلًا، فإنه يَحتوِي على تابع <code>redraw()‎</code> لرسم المستطيلات وذاك التابع هو ما سيُنفَّذ. أما إذا كان هذا الكائن شكلًا بيضاويًا، فسيُنفَّذ التابع <code>redraw()‎</code> المُعرَّف بالصنف <code>Oval</code>. يَعنِي ذلك أنه ليس من الضروري لتَعْليمَة <code>redraw();‎</code> بالتابع <code>setColor()‎</code> أن تَستدعِي التابع <code>redraw()‎</code> المُعرَّف بالصنف <code>Shape</code>، وإنما قد يُنفَّذ أي تابع <code>redraw()‎</code> طالما كان مُعرَّفًا بصنف فرعي مشتق من <code>Shape</code>. هذه هي فقط حالة آخرى من التعددية الشكلية (polymorphism).
</p>

<h2>
	الأصناف المجردة (abstract classes)
</h2>

<p>
	وقتما يَضطرّ كائن من الصنف <code>Rectangle</code> أو <code>Oval</code> أو <code>RoundRect</code> إلى رَسْم نفسه، سيُنفَّذ التابع <code>redraw()‎</code> المُعرَّف بالصنف المناسب، وهو ما يَقُودنا للسؤال التالي: ما دور التابع <code>redraw()‎</code> المُعرَّف بالصنف <code>Shape</code>؟ وكيف لنا أن نُعرِّفه؟
</p>

<p>
	قد تتفاجئ من الإجابة، ففي الحقيقة ينبغي أن نَترُكه فارغًا لأن الصنف <code>Shape</code> لا يُمثِل سوى فكرة مُجردة (abstract) لشكل، وبالتأكيد ليس هناك طريقة لرسم شيء كذلك، وإنما يُمكِن فقط رَسْم الأشكال الحقيقية (concrete) كالمستطيلات والأشكال البيضاوية. إذًا، لماذا ينبغي أن نُعرِّف التابع <code>redraw()‎</code> بالصنف <code>Shape</code> من الأساس؟ حسنًا، نحن مُضطرون إلى ذلك وإلا لن يَصِح استدعائه بالتابع <code>setColor()‎</code> المُعرَّف بالصنف <code>Shape</code> كما لن يَصِح كتابة شيئًا مثل <code>someShape.redraw();‎</code>؛ حيث سيَعترِض المُصرِّف لكَوْن <code>someShape</code> مُتْغيِّرًا من النوع <code>Shape</code> في حين لا يَحتوِي ذلك الصَنْف على التابع <code>redraw()‎</code>.
</p>

<p>
	وعليه، لن يُستدعَى التابع <code>redraw()‎</code> المُعرَّف بالصنف <code>Shape</code> نهائيًا، وربما -إذا فكرت بالأمر- ستَجِدْ أنه ليس هناك أي سبب قد يَضطرّنا حتى إلى إنشاء كائن فعليّ من الصنف <code>Shape</code>. تستطيع بالتأكيد إنشاء مُتْغيِّرات من النوع <code>Shape</code>، ولكنها دومًا ستُشير إلى كائنات (objects) من الأصناف الفرعية (subclasses) للصَنْف <code>Shape</code>. في مثل تلك الحالات، يُعدّ الصنف <code>Shape</code> صنفًا مُجرَّدًا (abstract class). لا تُستخدَم الأصناف المُجرّدة (abstract class) لإنشاء كائنات، وإنما فقط تُمثِل قاعدة وأساسًا لإنشاء أصناف فرعية أيّ أنها موجودة فقط للتعبير عن بعض السمات والخاصيات المشتركة بجميع أصنافها الفرعية (subclasses). أما الصَنْف غَيْر المُجرّد فيُعدّ صنفًا حقيقيًا (concrete class). في العموم، تستطيع إنشاء كائنات تنتمي لأصناف حقيقية (concrete class) وليس لأصناف مُجرّدة (abstract class)، كما يستطيع مُتْغيِّر من نوع هو عبارة عن صنف مُجرّد (abstract class) أن يُشير فقط إلى كائنات تنتمي لأحد الأصناف الفرعية الحقيقية (concrete subclasses) المُشتقّة من ذلك الصنف المُجرّد (abstract class).
</p>

<p>
	بالمثل، يُعدّ التابع <code>redraw()‎</code> المُعرَّف بالصنف <code>Shape</code> تابعًا مجردًا (abstract method)؛ فهو لا يُستدعَى نهائيًا وإنما تُستدعَى توابع <code>redraw()‎</code> المُعرَّفة بالأصناف الفرعية للصنف <code>Shape</code> لرَسْم الأشكال بصورة فعليّة. اِضطرّرنا مع ذلك لتعريف التابع <code>redraw()‎</code> بالصَنْف <code>Shape</code> للتأكيد على فِهم جميع الأشكال للرسالة <code>redraw</code>، أي أنه موجود فقط لتَخْصِيص الواجهة (interface) المُشتركة لنُسخ التابع <code>redraw()‎</code> الفعليّة والحقيقية (concrete) والمُعرَّفة بالأصناف الفرعية، وبالتالي ليس هناك ما يَستدعِي احتواء التابع المُجرَّد <code>redraw()‎</code> بالصنف <code>Shape</code> على أيّ شيفرة.
</p>

<p>
	كما ذَكَرَنا سابقًا، يُعدّ كُلًا من الصنف <code>Shape</code> وتابعه <code>redraw()‎</code> بمثابة أفكارًا مجردةً (abstract) على نحو دلالي فقط حتى الآن، ولكن تستطيع إِعلام الحاسوب بهذه الحقيقة على نحو صياغي (syntactically) أيضًا عن طريق إضافة المُبدِّل <code>abstract</code> إلى تعريف (definition) كُلًا منهما. بخلاف أي تابع عادي، لا يُكتَب الجزء التنفيذي (implementation) للتوابع المُجرّدة (abstract method) وإنما تُستخدَم فاصلة منقوطة (semicolon). في المقابل، لابُدّ من كتابة الجزء التنفيذي (implementation) للتابع المُجرّد (abstract method) ضِمْن أي صَنْف فرعي حقيقي (concrete subclass) مُشتقّ من الصنف المُجرّد (abstract class). تَستعرِض الشيفرة التالية طريقة تعريف الصَنْف <code>Shape</code> كصنف مُجرّد (abstract class):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_45" style="">
<span class="kwd">public</span><span class="pln"> abstract </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Shape</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">Color</span><span class="pln"> color</span><span class="pun">;</span><span class="pln">   </span><span class="com">// لون الشكل</span><span class="pln">

    </span><span class="kwd">void</span><span class="pln"> setColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pln"> newColor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// تابع لتغيير لون الشكل</span><span class="pln">
       color </span><span class="pun">=</span><span class="pln"> newColor</span><span class="pun">;</span><span class="pln"> </span><span class="com">// غير قيمة متغير النسخة</span><span class="pln">
       redraw</span><span class="pun">();</span><span class="pln"> </span><span class="com">// أعد رسم الشكل ليظهر باللون الجديد</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    abstract </span><span class="kwd">void</span><span class="pln"> redraw</span><span class="pun">();</span><span class="pln">
          </span><span class="com">// تابع مجرد ينبغي أن يُعرَّف بالأصناف الفرعية الحقيقية</span><span class="pln">

    </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">  </span><span class="com">// المزيد من التوابع والنسخ</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// نهاية الصنف‫ Shape</span></pre>

<p>
	بمُجرّد اِستخدَامك للمُبدِّل <code>abstract</code> أثناء التَّصْريح عن الصَنْف، لا تستطيع إنشاء كائنات فعليّة من النوع <code>Shape</code>، وسيُبلِّغ الحاسوب عن حُدوث خطأ في بناء الجملة (syntax error) إذا حاولت القيام بذلك.
</p>

<p>
	ربما يُعدّ الصَنْف <code>Vehicle</code> -الذي ناقشناه بالأعلى- صنفًا مجردًا (abstract class) أيضًا، فليس هناك أي طريقة لامتلاك مركبة كتلك، وإنما لابُدّ للمركبة الفعليّة أن تَكُون سيارة أو شاحنة أو دراجة نارية أو حتى أي نوع حقيقي (concrete) آخر.
</p>

<p>
	ذَكَرنا بالقسم الفرعي ٥.٣.٢ أن أيّ صنف لا يُصرَّح عن كَوْنه صنف فرعي (subclass) من أيّ صنف آخر -أيّ بدون الجزء <code>extends</code>- فإنه تلقائيًا يُصبِح صنفًا فرعيًا من الصنف القياسي <code>Object</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_47" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> myClass </span><span class="pun">{</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	يُعدّ التَّصْريح بالأعلى مُكافئًا للتالي تمامًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6058_49" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> myClass extends </span><span class="typ">Object</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<p>
	يُمكِننا القول إذًا أن الصَنْف <code>Object</code> يقع بقمة سُلالة أصناف (class hierarchy) ضخمة تَتَضمَّن جميع الأصناف الآخرى. من الناحية الدلالية، يُعدّ الصنف <code>Object</code> صنفًا مجردًا (abstract class) بل ربما أكثر صنف مجرد على الإطلاق، ومع ذلك، لم يُصرَّح عنه على هذا الأساس من الناحية الصياغية، أيّ تستطيع إنشاء كائنات من النوع <code>Object</code> ولكن لن تَجِدْ الكثير لتفعله بها.
</p>

<p>
	ولأن أيّ صَنْف هو بالضرورة صنف فرعي (subclass) من الصَنْف <code>Object</code>، فيُمكِن لمُتْغيِّر من النوع <code>Object</code> أن يُشير إلى أي كائن مهما كان نوعه. بالمثل، تستطيع مصفوفة من النوع <code>Object[]‎</code> أن تَحمِل كائنات من أي نوع.
</p>

<p>
	تَستخدِم الشيفرة المصدرية بالملف <a href="http://math.hws.edu/javanotes/source/chapter5/ShapeDraw.java" rel="external nofollow"><code>ShapeDraw.java</code></a> الصَنْف المُجرّد <code>Shape</code>، بالإضافة إلى مصفوفة من النوع <code>Shape[]‎</code> لحَمْل قائمة من الأشكال. قد تَرغَب بإلقاء نظرة على ذلك الملف مع أنك لن تَتَمكَّن من فهمه بالكامل حاليًا؛ فتعريفات الأصناف الفرعية بالملف مختلفة نوعًا ما عن تلك التي رأيتها بالأعلى. مثلًا، يَستقبِل التابع <code>draw()‎</code> مُعامِلًا (parameter) من النوع <code>GraphicsContext</code>؛ لأن الرَسْم بالجافا يَتطلَّب سياقًا رُسوميًا. سنَتَعرَّض لأمثلة شبيهة بالفصول اللاحقة بعدما تَتَعرَف على برمجة واجهات المُستخدِم الرُسومية (GUI). يُمكِنك مع ذلك فَحْص تعريف الصنف <code>Shape</code> وأصنافه الفرعية (subclasses) بالإضافة إلى طريقة اِستخدَام المصفوفة لحَمْل قائمة الأشكال. تَستعرِض الصورة التالية لقطة للشاشة أثناء تَشْغِيل البرنامج:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2021_01/005Shapedraw_Screenshot.png.1d407e8772fc96d4de42aa1986096d17.png" data-fileid="56460" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="56460" data-unique="5gsmk32u8" src="https://academy.hsoub.com/uploads/monthly_2021_01/005Shapedraw_Screenshot.png.1d407e8772fc96d4de42aa1986096d17.png" alt="005Shapedraw_Screenshot.png"></a>
</p>

<p>
	بعد تَشْغِيل البرنامج <code>ShapeDraw</code>، تستطيع أن تُضيف شكلًا جديدًا إلى الصورة عن طريق الضغط على أيّ من الأزرار الموجودة بالأسفل. يُمكِنك أيضًا اختيار لون ذلك الشكل من خلال قائمة الألوان الموجودة أسفل مساحة الرسم (drawing area). سيَظهَر الشكل الجديد بالركن الأيسر العلوي من مساحة الرسم (drawing area)، وبمُجرّد ظُهوره، تستطيع سَحْبه باستخدَام الفأرة. سيُحافِظ الشكل دومًا على ترتيبه بالنسبة لبقية الأشكال الموجودة على الشاشة حتى أثناء عملية السَحْب، ومع ذلك، تستطيع اختيار شكل معين ليُصبِح أمام جميع الأشكال الأخرى عن طريق الضغط باستمرار على المفتاح "shift" بينما تَضغَط على ذلك الشكل.
</p>

<p>
	في الحقيقة، يُستخدَم الصنف الفعليّ للشكل أثناء إضافته إلى الشاشة فقط. بعد ذلك، يُعالَج بالكامل على أساس كَوْنه شكلًا مجردًا (abstract). على سبيل المثال، يَتعامَل البرنامج (routine) المسئول عن عملية السَحْب مع مُتْغيِّرات من النوع <code>Shape</code>، ولا يُشير نهائيًا إلى أي من الأصناف الفرعية (subclasses). عندما يحتاج إلى رَسْم شكل، فإنه فقط يَستدعِي التابع <code>draw</code> أي أنه غَيْر مضطرّ لمَعرِفة طريقة رَسْم الشكل أو حتى مَعرِفة نوعه الفعليّ، وإنما تُوكَل عملية الرسم إلى الكائن ذاته. إذا أردت أن تُضيف نوعًا جديدًا من الأشكال إلى البرنامج، كل ما عليك القيام به هو الآتي: أولًا، ستُعرِّف صنفًا فرعيًا (subclass) جديدًا مُشتقًّا من <code>Shape</code>، ثم ستُضيف زرًا جديدًا وتُبرمجه بحيث يُضيف الشكل إلى الشاشة.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s5.html" rel="external nofollow">Section 5: Inheritance, Polymorphism, and Abstract Classes</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1112</guid><pubDate>Tue, 26 Jan 2021 12:36:13 +0000</pubDate></item><item><title>&#x62A;&#x637;&#x628;&#x64A;&#x642; &#x639;&#x645;&#x644;&#x64A;: &#x628;&#x646;&#x627;&#x621; &#x644;&#x639;&#x628;&#x629; &#x648;&#x631;&#x642; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%88%D8%B1%D9%82-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1111/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/34.png.6433d9febe7e28c1358774499a297e7c.png" /></p>
<p>
	سنتناول خلال هذا القسم عدة أمثلة للتصميم كائني التوجه (object-oriented). اخترنا أن تَكُون تلك الأمثلة بسيطة بما فيه الكفاية لتَسمَح لنا بتصميم أصناف قابلة لإعادة الاِستخدَام على نحو معقول. تحديدًا، سنُصمّم لعبة ورق تَستخدِم مجموعة ورق اللعب القياسية (deck) والمعروفة باسم "مجموعة ورق لعب البوكر".
</p>

<h2>
	تصميم الأصناف
</h2>

<p>
	يمكننا توصيف لعبة الورق كالتالي: "كأيّ لعبة ورق عادية، سيَحصُل كل لاعب على أكثر من ورقة لعب. بدايةً ستُخلَط (shuffle) مجموعة ورق اللعب (deck). بعد ذلك، ستُسحَب ورقة واحدة بكل مرة من مجموعة ورق اللعب (deck)، ثُمَّ تُوزَّع إلى يد (hand) أحد اللاعبين. قد يُستبعَد بعض ورق اللعب (cards) من يد أحد اللاعبين، وقد يُضاف ورق جديد. يَعتمِد فوز لاعب معين بالمباراة أو خسارته لها على كُلًا من قيم (values) (آص، ٢، ٣، ..، ملك)، ورموز (suits) ("بستوني spades"، "ديناري diamonds"، "قلب hearts"، "سباتي clubs") ورق اللعب الذي تَسلَّمه".
</p>

<p>
	إذا بحثنا عن الأسماء المذكورة بالتوصيف (specification) السابق، فسنَجِدْ التالي: لعبة، لاعب، يد (hand)، ورقة لعب (card)، مجموعة ورق اللعب (deck)، قيمة (value)، رمز (suit). يُعدّ كُلًا من الاسمين "قيمة" و "رمز" مُجرَّد قيم بسيطة، ويُمكِن تمثيلها كمُتْغيِّرات نُسخ (instance variables) بالصنف المُمثِل لورقة اللعب <code>Card</code>. يُمكِننا تمثيل الأسماء الخمسة الآخرى بواسطة أصناف (classes)، ولكننا سنَقْتصِر فقط على تلك الأسماء الأكثر قابلية لإعادة الاِستخدَام: ورقة لعب (card)، يد (hand)، مجموعة ورق اللعب (deck). أما إذا بحثنا عن الأفعال المذكورة بالتوصيف، فسنَجِدْ التالي: "خلط (shuffle) مجموعة ورق اللعب" و "توزيع ورقة لعب من مجموعة ورق اللعب". سنَستخدِم تابعي النُسخ (instance methods)‏ <code>shuffle()‎</code> و <code>dealCard()‎</code> لتمثيل كُلًا منهما ضِمْن الصَنْف <code>Deck</code>. بالإضافة إلى ذلك، هنالك أيضًا: "إضافة ورقة لعب إلى يد اللاعب" و "استبعاد ورقة لعب من يد اللاعب". سنَستخدِم تابعي النُسخ (instance methods)‏ <code>addCard()‎</code> و <code>removeCard()‎</code> لتمثيل كُلًا منهما ضِمْن الصنف <code>Hand</code>. أخيرًا، ورق اللعب هو كيان سلبي نوعًا ما، ولكن ينبغي على الأقل أن نَتَمكَّن من تَحْديد كُلًا من قيمته (value) ورمزه (suit). سنكتشف المزيد من توابع النُسخ الضرورية لاحقًا.
</p>

<p>
	أولًا، سنبدأ بتصميم الصنف <code>Deck</code> تفصيليًا. عندما نُنشِئ مجموعة ورق لعب (deck) لأول مرة، فإنها ستَحتوِي على ٥٢ ورقة لعب (card) مُرتَّبة معياريًا. لإنشاء مجموعة ورق لعب (deck) جديدة، سيَحتاج الصَنْف <code>Deck</code> إلى بَانِي (constructor) بدون أي مُعامِلات (parameters)؛ لأن أي مجموعة ورق لعب جديدة دائمًا ما تَكُون هي نفسها. سيَتَضمَّن الصَنْف <code>Deck</code> تابع النُسخة (instance method)‏ <code>shuffle()‎</code> المسئول عن ترتيب مجموعة ورق اللعب عشوائيًا، كما سيَتَضمَّن تابع النُسخة <code>dealCard()‎</code> المسئول عن جَلْب ورقة اللعب (card) التالية من مجموعة ورق اللعب (deck). هذا التابع هو عبارة عن دالة (function) تُعيد قيمة من النوع <code>Card</code>؛ لأن المُستدعِي يحتاج إلى مَعرِفة ورقة اللعب (card) المُوزَّعة. في المقابل، لن يَستقبِل ذلك التابع أي مُعامِلات (parameters)؛ فليس هناك أيّ معلومات ينبغي تمريرها إلى مجموعة ورق اللعب (deck) عند سَحْب ورقة لعب (card)، فأنت فقط تُوزِّع ورقة اللعب التالية أيًا كانت. لكن ماذا سيَحدُث عند استدعاء التابع <code>dealCard()‎</code> بينما لم يَعُدْ هناك أي ورق لعب إضافي بمجموعة ورق اللعب؟ في العموم، تُعدّ محاولة توزيع ورقة لعب من مجموعة ورق لعب فارغة بمثابة خطأ، لذا يُمكِن للصَنْف عندها أن يُبلِّغ عن اعتراض (exception). ولكن كيف سيَتَمكَّن البرنامج من مَعرِفة ما إذا كانت مجموعة ورق اللعب فارغة أم لا؟ قد يحتفظ البرنامج بعدد ورق اللعب المُستخدَم إلى الآن، ولكن ينبغي لمجموعة ورق اللعب نفسها أن تَكُون على دراية بتلك المعلومة، ولهذا سنُضيف تابع النسخة <code>cardsLeft()‎</code> ليُعيد عدد ورق اللعب المُتبقِّي بمجموعة ورق اللعب، مما سيُمكِّن البرنامج أيضًا من سؤال مجموعة ورق اللعب عن العدد المُتبقِّي. اُنظر التوصيف الكامل لجميع البرامج الفرعية (subroutines) بالصنف <code>Deck</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_7" style=""><span class="pln">     </span><span class="com">/**
      * Constructor.  Create an unshuffled deck of cards.
      */</span><span class="pln">
     </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Deck</span><span class="pun">()</span><span class="pln">

     </span><span class="com">/**
      * Put all the used cards back into the deck,
      * and shuffle it into a random order.
      */</span><span class="pln">
     </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> shuffle</span><span class="pun">()</span><span class="pln">

     </span><span class="com">/**
      * As cards are dealt from the deck, the number of 
      * cards left decreases.  This function returns the 
      * number of cards that are still left in the deck.
      */</span><span class="pln">
     </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> cardsLeft</span><span class="pun">()</span><span class="pln">

     </span><span class="com">/**
      * Deals one card from the deck and returns it.
      * @throws IllegalStateException if no more cards are left.
      */</span><span class="pln">
     </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Card</span><span class="pln"> dealCard</span><span class="pun">()</span></pre>

<p>
	يَتَضمَّن التوصيف كل ما تحتاج إلى مَعرِفته لكي تَتَمكَّن من اِستخدَام الصَنْف <code>Deck</code>، ولكنه لا يُخبرنا عن كيفية تّنْفيذ الصنف، فبالنهاية هذا ليس تمرينًا على كتابة الشيفرة (coding) وإنما على التصميم (design). يمكنك أيضًا الإطلاع على الشيفرة المصدرية للصنف بالملف <code><a href="http://math.hws.edu/javanotes/source/chapter5/Deck.java" rel="external nofollow">Deck.java</a></code> إذا شئت. ستلاحظ أن الصنف يحتوي على متغير نسخة (instance variable) عبارة عن مصفوفة من النوع <code>Cards</code>. ربما لن تفهم بعض النقاط بالتنفيذ (implementation) الداخلي للصنف، ولكن ما يزال بإمكانك استخدامه بالبرامج الخاصة بك كصندوق أسود (black box).
</p>

<p>
	بالمثل، يُمكِننا تحليل الصَنْف <code>Hand</code>. عندما نُنشِئ يد (hand) لأول مرة، فإنها لا تَحتوِي على أي ورق لعب (card). سيَتَضمَّن الصَنْف <code>Hand</code> تابع النُسخة (instance method)‏ <code>addCard()‎</code> المسئول عن إضافة ورقة لعب إلى اليد. يَستقبِل ذلك التابع مُعامِلًا (parameter) من النوع <code>Card</code> لتَخْصِيص ورقة اللعب (card) المضافة. سيَتَضمَّن الصنف أيضًا تابع النُسخة <code>removeCard()‎</code> المسئول عن استبعاد ورقة لعب من اليد. بالمثل، يَستقبِل ذلك التابع مُعامِلًا (parameter) من النوع <code>Card</code> لتَخْْصِيص ورقة اللعب (card) المُستبعدَة. والآن، لنطرح السؤال التالي: عند استبعاد ورقة لعب معينة من يد، هل ينبغي أن نُخصِّص ورقة اللعب ذاتها كأن نقول "استبعد ورقة الآص البستوني" أم عَبْر مَوضِعها باليد كأن نقول "استبعد ورقة اللعب الثالثة باليد"؟ يُمكِننا في الواقع السماح بالخيارين، فكما تَعلَم، يُمكِن لأيّ صنف أن يَتَضمَّن تابعين (methods) بنفس الاسم بشَّرْط أن يَكُون لديهما أعداد مختلفة أو أنواع مختلفة من المُعامِلات. ولهذا، سيَتَضمَّن الصَنْف نسختين من تابع النسخة <code>removeCard()‎</code>. يَستقبِل الأول مُعامِلًا (parameter) من النوع <code>Card</code>؛ لتَخْصِيص ورقة اللعب ذاتها المطلوب استبعادها، بينما يَستقبِل الآخر مُعامِلا (parameter) من النوع <code>int</code>؛ لتَخْصِيص مَوضِع ورقة اللعب المطلوب استبعادها. قد تَحتوِي اليد على عدد مُتْغيِّر من ورق اللعب، لذا سنُضيف تابع النُسخة <code>getCardCount()‎</code> ليُعيد عدد ورق اللعب الموجود باليد. وأخيرًا، يُمكِننا أيضًا أن نضيف توابع نسخ (instance methods) آخرى لترتيب ورق اللعب باليد؛ حيث يُفضِّل كثير من اللاعبين ترتيب ورق اللعب بأيديهم بحيث يَتجَاوَر ورق اللعب من نفس القيمة. اُنظر التوصيف الكامل للصَنْف <code>Hand</code> القابل لإعادة الاستخدام:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_9" style=""><span class="pln">    </span><span class="com">/**
     * Constructor. Create a Hand object that is initially empty.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Hand</span><span class="pun">()</span><span class="pln">

    </span><span class="com">/**
     * Discard all cards from the hand, making the hand empty.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> clear</span><span class="pun">()</span><span class="pln">

    </span><span class="com">/**
     * Add the card c to the hand.  c should be non-null.
     * @throws NullPointerException if c is null.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> addCard</span><span class="pun">(</span><span class="typ">Card</span><span class="pln"> c</span><span class="pun">)</span><span class="pln">

    </span><span class="com">/**
     * If the specified card is in the hand, it is removed.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> removeCard</span><span class="pun">(</span><span class="typ">Card</span><span class="pln"> c</span><span class="pun">)</span><span class="pln">

    </span><span class="com">/**
     * Remove the card in the specified position from the
     * hand.  Cards are numbered counting from zero.
     * @throws IllegalArgumentException if the specified 
     *    position does not exist in the hand.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> removeCard</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> position</span><span class="pun">)</span><span class="pln">

    </span><span class="com">/**
     * Return the number of cards in the hand.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getCardCount</span><span class="pun">()</span><span class="pln">

    </span><span class="com">/**
     * Get the card from the hand in given position, where 
     * positions are numbered starting from 0.
     * @throws IllegalArgumentException if the specified 
     *    position does not exist in the hand.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Card</span><span class="pln"> getCard</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> position</span><span class="pun">)</span><span class="pln">

    </span><span class="com">/**
     * Sorts the cards in the hand so that cards of the same 
     * suit are grouped together, and within a suit the cards 
     * are sorted by value.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> sortBySuit</span><span class="pun">()</span><span class="pln">

    </span><span class="com">/**
     * Sorts the cards in the hand so that cards are sorted into
     * order of increasing value.  Cards with the same value 
     * are sorted by suit. Note that aces are considered
     * to have the lowest value.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> sortByValue</span><span class="pun">()</span></pre>

<p>
	يمكنك الإطلاع على الشيفرة المصدرية للصَنْف بالملف <code><a href="http://math.hws.edu/javanotes/source/chapter5/Hand.java" rel="external nofollow">Hand.java</a></code>. لاحِظ أنك لن تَتَمكَّن من فهم بعض الأشياء ضِمْن تَّنْفيذ (implementation) الصَنْف، ولكنها لن تمنعك من اِستخدَام الصَنْف بمشروعاتك.
</p>

<h2>
	الصنف <code>Card</code>
</h2>

<p>
	سنَفْحَص تصميم الصَنْف <code>Card</code> وتَّنْفيذه (implementation) تفصيليًا. يَحتوِي الصنف على بَانِي (constructor) يَستقبِل كُلًا من قيمة (value) ورقة اللعبة المُنشئة ورمزها (suit). نستطيع تمثيل الرموز الأربعة باستخدام الأعداد الصحيحة ٠ و ١ و ٢ و ٣، ولكن لصعوبة تَذكُّر الرمز الذي يُمثله كل عدد منها، عَرَّفنا أربعة ثوابت مُسماة (named constants) بالصَنْف <code>Card</code> لتمثيل الاحتمالات الأربعة، فمثلًا، يُمثِل <code>Card.SPADES</code> الرمز "بستوني spades". صَرَّحنا عن تلك الثوابت لتَكُون من النوع <code>int</code>، وباِستخدَام المُبدِّلات <code>public final static</code>. كان من المُمكن أن نَستخدِم أنواع التعداد (enumerated type)، ولكن سنكتفي بالاعتماد على الثوابت من النوع العددي الصحيح. أما قيم ورق اللعب المُحتملة فهي الأعداد من ١ وصولًا إلى ١٣، بحيث تُمثِل الأعداد ١ و ١١ و ١٢ و ١٣ كُلًا من "الآص" و "الرجل أو الشبّ" و "الملكة أو البنت" و "الملك أو الشايب" على الترتيب. بالمثل، عَرَّفنا بعض الثوابت المُسماة (named constants) لتمثيل كُلًا من ورقة الآص، وورق اللعب المُصور. ستَجِدْ أيضًا أننا قد أضفنا ورقة "الجوكر".
</p>

<p>
	بمُجرَّد معرفة كُلًا من قيمة (value) ورقة اللعب، ورمزها (suit)، نستطيع استدعاء البَانِي (constructor) لإنشاء كائن من النوع <code>Card</code>، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_11" style=""><span class="pln">card1 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">.</span><span class="pln">ACE</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">.</span><span class="pln">SPADES </span><span class="pun">);</span><span class="pln">  </span><span class="com">// أنشئ ورقة آص بستوني</span><span class="pln">
card2 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">(</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">.</span><span class="pln">DIAMONDS </span><span class="pun">);</span><span class="pln">   </span><span class="com">// أنشئ ورقة عشرة ديناري</span><span class="pln">
</span><span class="com">// ‫يُسمَح بذلك طالما كانت s و v تعبيرات من النوع العددي الصحيح</span><span class="pln">
card3 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">(</span><span class="pln"> v</span><span class="pun">,</span><span class="pln"> s </span><span class="pun">);</span><span class="pln">  </span></pre>

<p>
	يحتاج أي كائن من الصَنْف <code>Card</code> إلى مُتْغيِّرات نُسخ (instance variables) لتمثيل كُلًا من قيمة ورقة اللعب ورمزها، لذلك أضفنا مُتْغيِّرات النسخ <code>suit</code> و <code>value</code> للصَنْف. بدايةً، صَرَّحنا عنها باِستخدَام المُبدِّل <code>private</code> حتى يَستحِيل تَعْديلها من خارج الصَنْف، وفي المقابل، أضفنا تابعي الجَلْب <code>getSuit()‎</code> و <code>getValue()‎</code> لكي تَتَمكَّن الشيفرة خارج الصَنْف من قراءة قيمة ورقة اللعب ورمزها. يَعنِي ذلك أن قيم تلك المُتْغيِّرات لن تتغير نهائيًا بَعْد تهيئتها المبدئية بالباني (constructor)، ولهذا كان من المنطقي التَّصْريح عنها باستخدام المُبدِّل <code>final</code>. يُمكِنك التَّصْريح عن أيّ مُتْغيِّر نُسخة (instance variable) عمومًا باِستخدَام المُبدِّل <code>final</code>، بشَّرْط إِسْناد قيمة إليه إما بتَعْليمَة التَّصْريح (declaration) أو بكل البواني (constructor) المُعرَّفة بذلك الصَنْف. تعدّ كائنات ذلك الصَنْف كائنات ثابتة أو غَيْر قابلة للتعديل (immutable)؛ لأن جميع مُتْغيِّرات النسخ المُعرَّفة ضِمْنها قد صُرِّح عنها باِستخدَام المُبدِّل <code>final</code>.
</p>

<p>
	أضفنا أيضًا بعض التوابع (methods) الآخرى إلى الصَنْف لطباعة كائناته (objects) بصيغة مقروءة، فمثلًا، بدلًا من طباعة العدد ٢ المُستخدَم لتمثيل الرمز "ديناري diamonds" ضِمْن الصَنْف، يُفضَّل طباعة الكلمة "Diamonds". ولأن عملية طباعة رمز ورقة اللعب شيئًا يُرجَح أن نحتاج إليه بالكثير من البرامج الآخرى، كان من المعقول إضافته للصَنْف، ولهذا أضفنا توابع النُسخ <code>getValueAsString()‎</code> و <code>getSuitAsString()‎</code> بحيث تُعيد التمثيل النصي (string representations) لكُلًا من قيمة ورقة اللعب ورمزها على الترتيب. بالإضافة إلى ذلك، عَرَّفنا تابع النسخة <code>toString()‎</code> ليُعيد سِلسِلة نصية تَتَضمَّن كُلًا من قيمة ورقة اللعب ورمزها، مثل "ملكة القلوب". يُستدعَى هذا التابع (method) تلقائيًا لتَحْوِيل كائن من النوع <code>Card</code> إلى النوع <code>String</code> أينما اِستخدَمناه ضِمْن سياق يحتاج إلى سِلسِلة نصية، مثلًا عند ضمه (concatenate) إلى سِلسِلة نصية باِستخدَام العَامِل <code>+</code>. أي أن التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_13" style=""><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="str">"Your card is the "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> card </span><span class="pun">);</span></pre>

<p>
	تُكافئ تمامًا التَعْليمَة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_15" style=""><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="str">"Your card is the "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> card</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	إذا كانت ورقة اللعب هي "ملكة القلوب"، فستَطبَع التَعْليمَتان السابقتان نفس السِلسِلة النصية "Your card is the Queen of Hearts".
</p>

<p>
	اُنظر شيفرة الصَنْف <code>Card</code> بالكامل، كما أنها موجودة بالملف <code><a href="http://math.hws.edu/javanotes/source/chapter5/Card.java" rel="external nofollow">Card.java</a></code>. يُعدّ هذا الصنف عامًا بما فيه الكفاية بما يُتيِح إعادة اِستخدَامه، أيّ أن العمل الذي بذلناه أثناء كُلًا من التصميم (designing)، وكتابة الشيفرة، والاختبار (testing) سيُؤتي ثماره على المدى الطويل.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_17" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Card</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> SPADES </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">   
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> HEARTS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> DIAMONDS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> CLUBS </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> JOKER </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ACE </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">      
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> JACK </span><span class="pun">=</span><span class="pln"> </span><span class="lit">11</span><span class="pun">;</span><span class="pln">    
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> QUEEN </span><span class="pun">=</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span><span class="pln">   
   </span><span class="kwd">public</span><span class="pln"> final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> KING </span><span class="pun">=</span><span class="pln"> </span><span class="lit">13</span><span class="pun">;</span><span class="pln">

   </span><span class="com">/**
    * This card's suit, one of the constants SPADES, HEARTS, DIAMONDS,
    * CLUBS, or JOKER.  The suit cannot be changed after the card is
    * constructed.
    */</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> final </span><span class="typ">int</span><span class="pln"> suit</span><span class="pun">;</span><span class="pln"> 

   </span><span class="com">/**
    * The card's value.  For a normal card, this is one of the values
    * 1 through 13, with 1 representing ACE.  For a JOKER, the value
    * can be anything.  The value cannot be changed after the card
    * is constructed.
    */</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> final </span><span class="typ">int</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">

   </span><span class="com">/**
    * Creates a Joker, with 1 as the associated value.  (Note that
    * "new Card()" is equivalent to "new Card(1,Card.JOKER)".)
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      suit </span><span class="pun">=</span><span class="pln"> JOKER</span><span class="pun">;</span><span class="pln">
      value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Creates a card with a specified suit and value.
    * @param theValue the value of the new card.  For a regular card (non-joker),
    * the value must be in the range 1 through 13, with 1 representing an Ace.
    * You can use the constants Card.ACE, Card.JACK, Card.QUEEN, and Card.KING.  
    * For a Joker, the value can be anything.
    * @param theSuit the suit of the new card.  This must be one of the values
    * Card.SPADES, Card.HEARTS, Card.DIAMONDS, Card.CLUBS, or Card.JOKER.
    * @throws IllegalArgumentException if the parameter values are not in the
    * permissible ranges
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Card</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> theValue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> theSuit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">theSuit </span><span class="pun">!=</span><span class="pln"> SPADES </span><span class="pun">&amp;&amp;</span><span class="pln"> theSuit </span><span class="pun">!=</span><span class="pln"> HEARTS </span><span class="pun">&amp;&amp;</span><span class="pln"> theSuit </span><span class="pun">!=</span><span class="pln"> DIAMONDS </span><span class="pun">&amp;&amp;</span><span class="pln"> 
            theSuit </span><span class="pun">!=</span><span class="pln"> CLUBS </span><span class="pun">&amp;&amp;</span><span class="pln"> theSuit </span><span class="pun">!=</span><span class="pln"> JOKER</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">IllegalArgumentException</span><span class="pun">(</span><span class="str">"Illegal playing card suit"</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">theSuit </span><span class="pun">!=</span><span class="pln"> JOKER </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">theValue </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> theValue </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">13</span><span class="pun">))</span><span class="pln">
         </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">IllegalArgumentException</span><span class="pun">(</span><span class="str">"Illegal playing card value"</span><span class="pun">);</span><span class="pln">
      value </span><span class="pun">=</span><span class="pln"> theValue</span><span class="pun">;</span><span class="pln">
      suit </span><span class="pun">=</span><span class="pln"> theSuit</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Returns the suit of this card.
    * @returns the suit, which is one of the constants Card.SPADES, 
    * Card.HEARTS, Card.DIAMONDS, Card.CLUBS, or Card.JOKER
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getSuit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> suit</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Returns the value of this card.
    * @return the value, which is one of the numbers 1 through 13, inclusive for
    * a regular card, and which can be any value for a Joker.
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Returns a String representation of the card's suit.
    * @return one of the strings "Spades", "Hearts", "Diamonds", "Clubs"
    * or "Joker".
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getSuitAsString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> suit </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">case</span><span class="pln"> SPADES</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Spades"</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">case</span><span class="pln"> HEARTS</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Hearts"</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">case</span><span class="pln"> DIAMONDS</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Diamonds"</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">case</span><span class="pln"> CLUBS</span><span class="pun">:</span><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Clubs"</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">       </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Joker"</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Returns a String representation of the card's value.
    * @return for a regular card, one of the strings "Ace", "2",
    * "3", ..., "10", "Jack", "Queen", or "King".  For a Joker, the 
    * string is always numerical.
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getValueAsString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">suit </span><span class="pun">==</span><span class="pln"> JOKER</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">return</span><span class="pln"> </span><span class="str">""</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> value </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Ace"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">2</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"2"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">3</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"3"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">4</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"4"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">5</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"5"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">6</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"6"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">7</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"7"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">8</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"8"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">9</span><span class="pun">:</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"9"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">10</span><span class="pun">:</span><span class="pln">  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"10"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">11</span><span class="pun">:</span><span class="pln">  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Jack"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="pln">  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Queen"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"King"</span><span class="pun">;</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="com">/**
    * Returns a string representation of this card, including both
    * its suit and its value (except that for a Joker with value 1,
    * the return value is just "Joker").  Sample return values
    * are: "Queen of Hearts", "10 of Diamonds", "Ace of Spades",
    * "Joker", "Joker #2"
    */</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> toString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">suit </span><span class="pun">==</span><span class="pln"> JOKER</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">value </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Joker"</span><span class="pun">;</span><span class="pln">
         </span><span class="kwd">else</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Joker #"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln">
         </span><span class="kwd">return</span><span class="pln"> getValueAsString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">" of "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> getSuitAsString</span><span class="pun">();</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">


</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class Card</span></pre>

<h2>
	لعبة ورق بسيطة
</h2>

<p>
	أخيرًا، تَستعرِض الشيفرة التالية برنامجًا كاملًا يَستخدِم الصَنْفين <code>Card</code> و <code>Deck</code>. يَسمَح البرنامج للمُستخدِم بلعب لعبة ورق بسيطة اسمها هو HighLow. تُخلَط مجموعة ورق اللعب (deck)، وتُسحَب ورقة لعب واحدة، ليراها المُستخدِم. بَعْد ذلك، عليه أن يَتوقَّع ما إذا كانت ورقة اللعب التالية ستَكُون أكبر أو أقل من ورقة اللعب الحالية. إذا كان تَوقُّع المُستخدِم صحيحًا، تَحلّ ورقة اللعب التالية مَحلّ ورقة اللعب الحالية، ثم يَتوقَّع المُستخدِم مرة آخرى، ويستمر الأمر إلى أن يَتوقَّع المُستخدِم تَوقُّعًا خاطئًا. مجموع النقاط التي تَحصَّل عليها المُستخدِم تُساوِي عدد التوقُّعات الصحيحة.
</p>

<p>
	يَتَضمَّن البرنامج تابعًا ساكنًا (static method). تُوكَل مُهِمّة لعب مباراة واحدة فقط من لعبة الورق HighLow لذلك التابع. يَسمَح البرنامج <code>main()‎</code> للمُستخدِم بلعب عدة مباريات، وبالنهاية، يُخبره بمتوسط النقاط التي تَحصَّل عليها خلال جميع المباريات.
</p>

<p>
	لن نَمُرّ عَبْر مراحل تطوير الخوارزمية (algorithm) المُستخدَمة بالبرنامج، لكن ينبغي عليك أن تقرأها بحرص، وأن تتأكَّد من فِهم طريقة عملها. يُعيد البرنامج الفرعي (subroutine) المَسئول عن لعب مباراة واحدة من لعبة الورق HighLow نقاط المُستخدِم بالمباراة كقيمة مُعادة (return value) إلى البرنامج <code>main</code> حيث تُستخدَم. اُنظر البرنامج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8351_19" style=""><span class="kwd">import</span><span class="pln"> textio</span><span class="pun">.</span><span class="typ">TextIO</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HighLow</span><span class="pln"> </span><span class="pun">{</span><span class="pln">


   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"This program lets you play the simple card game,"</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"HighLow.  A card is dealt from a deck of cards."</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"You have to predict whether the next card will be"</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"higher or lower.  Your score in the game is the"</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"number of correct predictions you make before"</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"you guess wrong."</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">

      </span><span class="typ">int</span><span class="pln"> gamesPlayed </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">     </span><span class="com">// عدد المباريات التي لعبها المستخدم</span><span class="pln">
      </span><span class="typ">int</span><span class="pln"> sumOfScores </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">     </span><span class="com">// مجموع نقاط المستخدم</span><span class="pln">
      </span><span class="kwd">double</span><span class="pln"> averageScore</span><span class="pun">;</span><span class="pln">     </span><span class="com">// متوسط نقاط المستخدم</span><span class="pln">
       </span><span class="com">// يحمل رد المستخدم على سؤاله عما إذا كان يريد لعب مباراة إضافية</span><span class="pln">
      boolean playAgain</span><span class="pun">;</span><span class="pln">       

      </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="typ">int</span><span class="pln"> scoreThisGame</span><span class="pun">;</span><span class="pln">        </span><span class="com">// نقاط المستخدم بمباراة واحدة</span><span class="pln">
         scoreThisGame </span><span class="pun">=</span><span class="pln"> play</span><span class="pun">();</span><span class="pln">   </span><span class="com">// العب مباراة مع المستخدم</span><span class="pln">
         sumOfScores </span><span class="pun">+=</span><span class="pln"> scoreThisGame</span><span class="pun">;</span><span class="pln">
         gamesPlayed</span><span class="pun">++;</span><span class="pln">
         </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">print</span><span class="pun">(</span><span class="str">"Play again? "</span><span class="pun">);</span><span class="pln">
         playAgain </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TextIO</span><span class="pun">.</span><span class="pln">getlnBoolean</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playAgain</span><span class="pun">);</span><span class="pln">

      averageScore </span><span class="pun">=</span><span class="pln"> </span><span class="pun">((</span><span class="kwd">double</span><span class="pun">)</span><span class="pln">sumOfScores</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> gamesPlayed</span><span class="pun">;</span><span class="pln">

      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"You played "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> gamesPlayed </span><span class="pun">+</span><span class="pln"> </span><span class="str">" games."</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">printf</span><span class="pun">(</span><span class="str">"Your average score was %1.3f.\n"</span><span class="pun">,</span><span class="pln"> averageScore</span><span class="pun">);</span><span class="pln">

   </span><span class="pun">}</span><span class="pln">  </span><span class="com">// ‫نهاية main()</span><span class="pln">


   </span><span class="com">/**
    * Lets the user play one game of HighLow, and returns the
    * user's score in that game.  The score is the number of
    * correct guesses that the user makes.
    */</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> play</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

       </span><span class="com">// أنشئ كائن مجموعة ورق لعب جديدة وخزن مرجعه بالمتغير</span><span class="pln">
      </span><span class="typ">Deck</span><span class="pln"> deck </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Deck</span><span class="pun">();</span><span class="pln">  

      </span><span class="typ">Card</span><span class="pln"> currentCard</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ورقة اللعب الحالية التي يراها المستخدم</span><span class="pln">

       </span><span class="com">// ورقة اللعب التالية التي يتوقع المستخدم ما </span><span class="pln">
       </span><span class="com">// إذا كانت أكبر أو أقل من ورقة اللعب الحالية</span><span class="pln">
      </span><span class="typ">Card</span><span class="pln"> nextCard</span><span class="pun">;</span><span class="pln">   

      </span><span class="typ">int</span><span class="pln"> correctGuesses </span><span class="pun">;</span><span class="pln">  </span><span class="com">// عدد توقعات المستخدم الصحيحة</span><span class="pln">

      </span><span class="kwd">char</span><span class="pln"> guess</span><span class="pun">;</span><span class="pln">   </span><span class="com">// ‫تخمين المستخدم ويحمل القيمة L أو H</span><span class="pln">

      deck</span><span class="pun">.</span><span class="pln">shuffle</span><span class="pun">();</span><span class="pln">  </span><span class="com">// رتب مجموعة ورق اللعب عشوائيًا</span><span class="pln">

      correctGuesses </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      currentCard </span><span class="pun">=</span><span class="pln"> deck</span><span class="pun">.</span><span class="pln">dealCard</span><span class="pun">();</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The first card is the "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> currentCard</span><span class="pun">);</span><span class="pln">

      </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// تنتهي الحلقة عند التخمين الخاطئ</span><span class="pln">

         </span><span class="com">/* اقرأ تخمين المستخدم */</span><span class="pln">

         </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">print</span><span class="pun">(</span><span class="str">"Will the next card be higher (H) or lower (L)?  "</span><span class="pun">);</span><span class="pln">
         </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
             guess </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TextIO</span><span class="pun">.</span><span class="pln">getlnChar</span><span class="pun">();</span><span class="pln">
             guess </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Character</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">(</span><span class="pln">guess</span><span class="pun">);</span><span class="pln">
             </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">guess </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'H'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> guess </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'L'</span><span class="pun">)</span><span class="pln"> 
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">print</span><span class="pun">(</span><span class="str">"Please respond with H or L:  "</span><span class="pun">);</span><span class="pln">
         </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">guess </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'H'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> guess </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'L'</span><span class="pun">);</span><span class="pln">

         </span><span class="com">/* اسحب ورقة اللعب التالية وأظهرها للمستخدم */</span><span class="pln">

         nextCard </span><span class="pun">=</span><span class="pln"> deck</span><span class="pun">.</span><span class="pln">dealCard</span><span class="pun">();</span><span class="pln">
         </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The next card is "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> nextCard</span><span class="pun">);</span><span class="pln">

         </span><span class="com">/* افحص تخمين المستخدم */</span><span class="pln">

         </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nextCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> currentCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The value is the same as the previous card."</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"You lose on ties.  Sorry!"</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">  </span><span class="com">// أنهي المباراة</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
         </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nextCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> currentCard</span><span class="pun">.</span><span class="pln">getValue</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">guess </span><span class="pun">==</span><span class="pln"> </span><span class="str">'H'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Your prediction was correct."</span><span class="pun">);</span><span class="pln">
                correctGuesses</span><span class="pun">++;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Your prediction was incorrect."</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">  </span><span class="com">// أنهي المباراة</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
         </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// إذا كانت ورقة اللعب التالية أقل</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">guess </span><span class="pun">==</span><span class="pln"> </span><span class="str">'L'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Your prediction was correct."</span><span class="pun">);</span><span class="pln">
                correctGuesses</span><span class="pun">++;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Your prediction was incorrect."</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">  </span><span class="com">// أنهى المباراة</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">

         </span><span class="com">/* تهيئة ضرورية للتكرار التالي ضمن الحلقة  */</span><span class="pln">

         currentCard </span><span class="pun">=</span><span class="pln"> nextCard</span><span class="pun">;</span><span class="pln">
         </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">
         </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The card is "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> currentCard</span><span class="pun">);</span><span class="pln">

      </span><span class="pun">}</span><span class="pln"> </span><span class="com">// نهاية الحلقة</span><span class="pln">

      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The game is over."</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"You made "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> correctGuesses 
                                           </span><span class="pun">+</span><span class="pln"> </span><span class="str">" correct predictions."</span><span class="pun">);</span><span class="pln">
      </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">

      </span><span class="kwd">return</span><span class="pln"> correctGuesses</span><span class="pun">;</span><span class="pln">

   </span><span class="pun">}</span><span class="pln">  </span><span class="com">// ‫نهاية play()</span><span class="pln">


</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف HighLow</span></pre>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s4.html" rel="external nofollow">Section 4: Programming Example: Card, Hand, Deck</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>

<p>
	 
</p>

<h2>
	اقرأ أيضًا 
</h2>

<p>
	<a href="https://academy.hsoub.com/programming/game-development/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8/" rel="">تعرف على أشهر لغات برمجة الألعاب</a>
</p>
]]></description><guid isPermaLink="false">1111</guid><pubDate>Sun, 24 Jan 2021 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; (Objects)</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-r1110/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/33.png.f8c8e26ff691e0e373540c6905b561ce.png" /></p>
<p>
	يُمكِن تطبيق المفاهيم كائنية التوجه (object-oriented) على عملية تصميم البرامج وكتابتها في العموم بأكثر من طريقة، منها التحليل والتصميم كائني التوجه (object-oriented analysis and design). تُطبِق تلك الطريقة الأساليب كائنية التوجه (object-oriented) على أُولَى مراحل تطوير البرمجيات، والمسئولة عن تصميم البرنامج ككل. تَتَلخَّص تلك الطريقة بتَحْديد مجموعة الكيانات المُرتَبِطة بموضوع المشكلة (problem domain)، والتي يُمكِن تمثيلها ككائنات (objects). على مستوى آخر، تُشجِع البرمجة كائنية التوجه (object-oriented programming) المبرمجين على إنشاء "أدوات برمجية مُعمَّمة" قابلة للاِستخدَام بالعديد من المشروعات البرمجية المختلفة.
</p>

<p>
	سنَستخدِم "الأدوات البرمجية المُعمَّمة" بصورة أكبر حين نبدأ باِستخدَام الأصناف القياسية (standard classes) بالجافا. سنبدأ هذا القسم بفَحْص بعضًا من تلك الأصناف المَبنية مُسْبَقًا (built-in)، والمُستخدَمة لإنشاء أنواع معينة من الكائنات (objects)، وسنعود مُجدّدًا إلى الصورة العامة بنهاية القسم.
</p>

<h2>
	بعض الأصناف المبنية مسبقًا
</h2>

<p>
	بالبرمجة كائنية التوجه (object-oriented programming)، يَكُون التركيز عادةً على تصميم أصناف جديدة، وكتابة تَّنْفيذها (implementation)، ولكن يَنبغي أيضًا ألا نُهمِل ذلك العدد الكبير من الأصناف (classes) التي وفرها لنا مُصمّمي الجافا. يُمكِننا أن نَستخدِم بعضًا من تلك الأصناف لإنتاج أصناف جديدة، بينما قد نَستخدِم البعض الآخر مُباشرة لإِنشاء كائنات. لكي تَتَمكَّن فعليًا من لغة الجافا، ينبغي أن تَكُون على دراية بعدد كبير من تلك الأصناف المَبنية مُسْبَقًا (built-in classes)، وهو ما يَحتاج إلى الكثير من الوقت والخبرة لتطويره. سنَفْحَص خلال هذا القسم بعضًا من تلك الأصناف.
</p>

<p>
	يُمكِننا أن نُنشِئ سِلسِلة نصية (string) من نصوص أصغر باِستخدَام العَامِل <code>+</code>، ولكن لا يَكُون ذلك دائمًا هو الحل الأفضل. فمثلًا، إذا كان <code>str</code> مُتْغيِّرًا من النوع <code>String</code> و <code>ch</code> محرفًا (character)، فإن تّنْفيذ الأمر <code>str = str + ch;‎</code> يَتَضمَّن إنشاء كائن جديد كليًا من النوع <code>String</code>، مُكوَّن من نُسخة من <code>str</code> مع قيمة <code>ch</code> مُلحقَّة بآخره. يَستغرِق نَسخ السِلسِلة النصية بعض الوقت كما يَتَطلَّب قدرًا كبيرًا من المعالجة. في المقابل، يَسمَح الصنف <code>StringBuilder</code> بإنشاء سِلسِلة نصية طويلة من نصوص أصغر بكفاءة. يُمكِننا أن نُنشِئ كائنًا ينتمي إلى الصنف <code>StringBuilder</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_7" style=""><span class="typ">StringBuilder</span><span class="pln"> builder </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">StringBuilder</span><span class="pun">();</span></pre>

<p>
	تُصرِّح تلك التَعْليمَة عن المُتْغيِّر <code>builder</code>، وتُهيئه مبدئيًا، بحيث يُشير إلى كائن من الصَنْف <code>StringBuilder</code>. بالقسم الفرعي ٤.٨.١، ناقشنا دمج عمليتي التّصريح (declaration) والتهيئة المبدئية (initialization) ضِمْن تَعْليمَة واحدة فيما يَتعَلَّق بالأنواع الأساسية (primitive types)، ولكن الأمر هو نفسه بالنسبة للكائنات.
</p>

<p>
	تمامًا كأي كائن من الصَنْف <code>String</code>، تَتَكوَّن كائنات الصَنْف <code>StringBuilder</code> من مُتتالية من المحارف. ولكن بإمكانك إلحاق محارف جديدة بدون إنشاء نُسخ جديدة من نفس البيانات التي تَتَضمَّنها تلك الكائنات بالفعل. إذا كانت <code>x</code> عبارة عن قيمة من أيّ نوع، وكان <code>builder</code> هو المُتْغيِّر المُعرَّف بالأعلى، فإن الأمر <code>builder.append(x)‎</code> سيُضيف تمثيل <code>x</code> النصي (string representation) إلى نهاية البيانات الموجودة بالمُتْغيِّر <code>builder</code>، وهو ما يُعدّ أكثر كفاءة عمومًا من الاستمرار بنَسْخ البيانات مع كل مرة نُلحِق خلالها شيئًا جديدًا. تستطيع إنشاء سِلسِلة نصية طويلة باِستخدَام كائن من الصَنْف <code>StringBuilder</code>، وعبر مُتتالية من أوامر <code>append()‎</code>، وعندما تَكتمِل السِلسِلة النصية، ستُعيد الدالة <code>builder.toString()‎</code> نُسخة من السِلسِلة النصية كقيمة عادية من النوع <code>String</code>. يَتوفَّر الصَنْف <code>StringBuilder</code> ضِمْن الحزمة (package) القياسية <code>java.lang</code>، ولذلك يُمكِن اِستخدَام الاسم البسيط للصَنْف بدون استيراده (import).
</p>

<p>
	تَحتوِي حزمة <code>java.util</code> على عدد من الأصناف المفيدة، فمثلًا، تَتَوفَّر أصناف للتَعامُل مع تجميعات (collections) من الكائنات، والتي سنتناولها تفصيليًا بالفصل العاشر. تَعرَّضنا للصَنْف <code>java.util.Scanner</code> المُعرَّف ضِمْن نفس الحزمة بالقسم الفرعي ٢.٤.٦. يَتوفَّر أيضًا الصَنْف <code>java.util.Date</code>، والذي يُستخدَم لأغراض تمثيل الوقت، فمثلًا، عندما تُنشِئ كائنًا (object) من النوع <code>Date</code> بدون مُعامِلات (parameters)، تُمثِل النتيجة كُلًا من التاريخ والوقت الحالي. يُمكِن عَرْض تلك المعلومة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_9" style=""><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	لمّا كان الصَنْف <code>Date</code> مُعرَّفًا بحزمة <code>java.util</code>، كان لابُدّ من إِتاحته أولًا بالبرنامج عن طريق استيراده، وذلك بكتابة أي من التَعْليمَتين <code>import java.util.Date;‎</code> أو <code>import java.util.*;‎</code> ببداية البرنامج، وبَعْدها ستَتَمكَّن من اِستخدَام الصَنْف. (ناقشنا الحزم والاستيراد بالقسم الفرعي ٤.٦.٣).
</p>

<p>
	لنَفْحَص أيضًا الصَنْف <code>java.util.Random</code>. تُعدّ كائنات ذلك الصَنْف مصدرًا للأعداد العشوائية، فيُمكِن لكائن من النوع <code>Random</code> أن يُنتج أعدادًا عشوائية سواء كانت صحيحة (integers) أو حقيقية (real). وفي الحقيقة، تَستخدِم الدالة القياسية <code>Math.random()‎</code> كائنًا من ذلك الصَنْف وراء الكواليس؛ لإنتاج أعدادها العشوائية. اُنظر الشيفرة التالية لإنشاء كائن من الصَنْف <code>Random</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_11" style=""><span class="typ">Random</span><span class="pln"> randGen </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Random</span><span class="pun">();</span></pre>

<p>
	بفَرْض أن <code>N</code> هو عدد صحيح موجب، سيُنتج <code>randGen.nextInt(N)‎</code> عددًا صحيحًا عشوائيًا ضِمْن نطاق يتراوح من صفر وحتى <code>N-1</code>. يُمكِننا استخدام ذلك بتجربة رمِي حجري النَّرد لتَسهيل المُهِمّة؛ فبدلًا من كتابة <code>die1 = (int)(6*Math.random())+1;‎</code>، يُمكِننا كتابة <code>die1 = randGen.nextInt(6)+1;‎</code>. قد لا تتفق على أن تلك الطريقة أسهل نوعًا ما خاصة مع اضطرارنا لاستيراد (import) الصَنْف <code>java.util.Random</code>، وإنشاء كائن منه. يُمكِننا أيضًا اِستخدَام كائن من النوع <code>Random</code> لإنتاج ما يُعرَف باسم "التوزيع الاحتمالي الغاوسي (gaussian distribution)".
</p>

<p>
	تُستخدَم الكثير من أصناف جافا القياسية ببرمجة واجهات المُستخدِم الرسومية (GUI)، وسنَمُرّ عبر الكثير منها بالفصل السادس، ولكن سنَمُرّ هنا سريعًا على الصَنْف <code>Color</code> من حِزمة <code>javafx.scene.paint</code> حتى نَستخدِمه بالمثال التالي. يُمثِل أي كائن من الصَنْف <code>Color</code> لونًا يُستخدَم أثناء الرسم، ولقد تَعرَّضنا بالفعل لعدة ألوان ثابتة (constants) مثل <code>Color.RED</code> بالقسم ٣.٩. في الواقع، تلك الثوابت ما هي إلا مُتْغيِّرات أعضاء ساكنة (static) ونهائية (final) مُعرَّفة بالصَنْف <code>Color</code>، وقيمها عبارة عن كائنات من النوع <code>Color</code>. بالإضافة إلى تلك الألوان المُعرَّفة مُسْبَقًا، يُوفِّر الصَنْف <code>Color</code> مجموعة من البَوانِي (constructors) تَسمَح لك بإنشاء كائنات جديدة من الصَنْف لتَمثيل أيّ لون آخر. يَستقبِل إحداها ٣ مُعامِلات (parameters) من النوع <code>double</code> ويُمكِنك استدعائه باِستخدَام <code>new Color(r,g,b)‎</code>. تُمثِل تلك المُعامِلات كُلًا من اللون الأحمر والأخضر والأزرق بنظام الألوان RGB، ويَنبغي أن تَقع قيمها ضِمْن نطاق يتراوح من ٠ وحتى ١. تَعنِي القيمة ٠ للمُعامِل <code>r</code> أن اللون الفعليّ لا يَتَضمَّن اللون الأحمر نهائيًا، بينما تَعنِي القيمة ١ أنه يَتَضمَّن أكبر قدر مُمكن من اللون الأحمر. يَتوفَّر باني آخر يُستدعَى على الصورة <code>new Color(r,g,b,t)‎</code>، والذي يَستقبِل مُعامِلًا إضافيًا من النوع <code>double</code>، ويَنبغي أن تَقع قيمته ضِمْن نطاق يتراوح من ٠ وحتى ١. يُحدِّد ذلك المعامل درجة شفافية اللون (transparency)، بحيث تُمثِل القيم الأكبر من المُعامِل <code>t</code> لونًا أقل شفافية، فمثلًا، عندما تَرسم بلون شفاف جزئيًا، تَظهَر الخلفية عبر اللون إلى حد معين.
</p>

<p>
	.تَتَضمَّن الكائنات من الصَنْف <code>Color</code> عددًا قليلًا من توابع النسخ (instance methods). على سبيل المثال، تَتَوفَّر دوال (functions) مثل <code>getRed()‎</code> لجَلْب قيمة اللون الأحمر بنظام RGB، وبالمثل للون الأخضر والأزرق. مع ذلك، لا يُوفِّر الصَنْف أي توابع ضَبْط (setter methods) لتَعْديل قيم تلك الألوان، حيث تُعدّ الكائنات من الصَنْف <code>Color</code> كائنات ثابتة أو غَيْر قابلة للتعديل (immutable)، بمعنى أن جميع مُتْغيِّرات النُسخ (instance variables) المُعرَّفة بداخلها هي نهائية -مُعرَّفة باِستخدَام المُبدِّل <code>final</code>-، وبالتالي لا يُمكِن تَعْديلها بَعْد إنشاء الكائن. لاحِظ أن السَلاسِل النصية من النوع <code>String</code> هي مثال آخر على الكائنات غَيْر القابلة للتعديل (immutable).
</p>

<p>
	النقطة الرئيسية مما سبق هو التأكيد على أن أصناف جافا القياسية تُوفِّر حلولًا للكثير من المشاكل التي قد تواجهها، لذا إذا تَعرَّضت لمُهِمّة (task) شائعة نوعًا ما، فلرُبما من الأفضل أن تَبحَث قليلًا بمَرجِع الجافا، لتَرى ما إذا كان هناك صَنْف يؤدي الغرض الذي تحتاج إليه.
</p>

<h2>
	الصنف <code>Object</code>
</h2>

<p>
	تُعدّ القدرة على إنشاء أصناف فرعية (subclasses) مُشتقَّة من صَنْف واحدة من أهم سمات البرمجة كائنية التوجه (object-oriented programming). يَرِث (inherit) الصَنْف الفرعي جميع خاصيات (properties) الصَنْف الأصلي وسلوكياته (behaviors)، ولكن بإِمكانه تَعْديلها وكذلك الإضافة إليها. ستَتَعلَّم طريقة إنشاء الأصناف الفرعية (subclasses) في القسم ٥.٥. في الواقع، أيّ صَنْف بلغة الجافا -باستثناء صَنْف وحيد- هو بالنهاية صَنْف فرعي من صَنْف آخر، فحتى لو أنشأت صَنْفًا، ولم تُصرِّح عن كَوْنه صَنْفًا فرعيًا من صَنْف آخر، فإنه تلقائيًا يُصبِح صنفًا فرعيًا من صَنْف خاص اسمه <code>Object</code> مُعرَّف بحزمة <code>java.lang</code>، وهو الصَنْف الاستثنائي الوحيد الذي لا يُعدّ صَنْفًا فرعيًا من أي صَنْف آخر.
</p>

<p>
	يُعرِّف الصنف <code>Object</code> مجموعة من توابع النُسخ (instance methods)، والتي تَرثها (inherit) جميع الأصناف الآخرى، لذا يُمكِن لأيّ كائن (object) مهما كان أن يَستخدِم تلك التوابع. سنَذكُر واحدة منها فقط بهذا القسم، وسنَتَعرَّض لمجموعة آخرى منها لاحقًا.
</p>

<p>
	يُعيد تابع النسخة <code>toString()‎</code> المُعرَّف بالصنف <code>Object</code> قيمة من النوع <code>String</code>، والتي يُفْترَض أن تَكُون بمثابة تمثيلًا نصيًا (string representation) للكائن. في أي مرة نَطبَع فيها كائنًا أو نَضُمّه (concatenate) إلى سِلسِلة نصية، أو بصورة أعم نَستخدِمه بسياق يَتطلَّب سِلسِلة نصية، يُستدعَى هذا التابع ضمنيًا ليُحوِّل ذلك الكائن تلقائيًا إلى النوع <code>String</code>.
</p>

<p>
	تُعيد نسخة <code>toString</code> المُعرَّفة بالصنف <code>Object</code> اسم الصنف الذي ينتمي إليه الكائن مع ترميز التجزئة (hash code) الخاص به، وهو ما قد لا يَكُون مفيدًا. لذا عندما تُنشِئ صَنْفًا، تستطيع إعادة تعريف التابع <code>toString()‎</code> بحيث تَحلّ النسخة الجديدة مَحلّ النسخة الموروثة، فمثلًا، يُمكِننا إضافة التابع (method) التالي لأيّ من أصناف <code>PairOfDice</code> المُعرَّفة بالقسم السابق:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_13" style=""><span class="com">/**
 * يعيد تمثيلًا نصيًا لحجري نرد
 */</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> toString</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">die1 </span><span class="pun">==</span><span class="pln"> die2</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"double "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> die1</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">else</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> die1 </span><span class="pun">+</span><span class="pln"> </span><span class="str">" and "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> die2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا كان <code>dice</code> مُتْغيِّرًا يُشير إلى كائن من الصَنْف <code>PairOfDice</code>، فسيُعيد <code>dice.toString()‎</code> سَلاسِل نصية مثل "‎3 and 6" و "‎5 and 1" و "double 2‎" بِحَسْب الأعداد الظاهرة على حجري النَّرد. يُستدَعى هذا التابع تلقائيًا لتَحْوِيل المُتْغيِّر <code>dice</code> إلى النوع <code>String</code> بالتَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_15" style=""><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="str">"The dice came up "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> dice </span><span class="pun">);</span></pre>

<p>
	قد يَكُون خَرْج التعليمة -بالأعلى- "The dice came up 5 and 1" أو "The dice came up double 2". سنَتَعرَّض لمثال آخر للتابع <code>toString()‎</code> بالقسم التالي.
</p>

<h2>
	كتابة صنف واستخدامه
</h2>

<p>
	سنَكْتُب الآن برنامج تَحرِيكة (animation)، بالاعتماد على نفس المنصة المُستخدَمة بالقسم الفرعي ٣.٩.٣، سيَكُون بمثابة مثالًا على تصميم صَنْف (class) جديد واِستخدَامه. ستُظهِر التَحرِيكة عددًا من الأقراص شبه الشفافة، والمُلوَّنة عشوائيًا، والمُتموضِعة بأماكن عشوائية عَبْر النافذة. عند تشغيل التحريكة، سيَزدَاد حجم تلك الأقراص، وعندما يَصِل حجم أي منها إلى حد معين، فإنه سيَختفِي ليَحلّ مَحلّه قرص آخر جديد بمكان عشوائي. قد يَحدُث ذلك -أيّ الاختفاء والظهور- بصورة عشوائية أيضًا بعيدًا عن حجم القرص. تَستعرِض الصورة التالية لقطة للشاشة أثناء تَشْغِيل البرنامج:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="56455" href="https://academy.hsoub.com/uploads/monthly_2021_01/001Growing_Circles.png.24d8401a91cce4ce61d82eae64dc24bf.png" rel="" data-fileext="png"><img alt="001Growing_Circles.png" class="ipsImage ipsImage_thumbnailed" data-fileid="56455" data-unique="yhxrf523n" src="https://academy.hsoub.com/uploads/monthly_2021_01/001Growing_Circles.png.24d8401a91cce4ce61d82eae64dc24bf.png"></a>
</p>

<p>
	سيَكُون كل قرص ضِمْن التَحرِيكة عبارة عن كائن (object)، ولأن أيّ قرص يَملُك عدة خاصيات (properties)، مثل اللون والموقع والحجم، فسنَستخدِم مُتْغيِّرات نُسخ (instance variables) لتمثيل كُلًا منها ضِمْن الكائن. أما بالنسبة لتوابع النُسخ (instance methods)، فيَنبغِي لنا التفكير أولًا بالكيفية التي سنَستخدِم بها القرص عَبْر البرنامج. سنَحتاج عمومًا إلى رسم القرص، لذا سنُضيف تابع النسخة <code>draw(g)‎</code>، حيث <code>g</code> هو كائن السِّياق الرُسومي (graphics context) المُستخدَم للرسم. يُمكِن للصَنْف أيضًا أن يَتَضمَّن بَانِي كائن (constructors) واحد أو أكثر لتهيئة الكائنات مبدئيًا. لا تَكُون البيانات المُفْترَض تمريرها للباني كمُعامِلات (parameters) واضحة دومًا. في هذا المثال، سنكتفي بتمرير كُلًا من موقع الدائرة وحجمها كمُعامِلات، أما لونها فسيَصنَعه الباني باِستخدَام قيم عشوائية للألوان الثلاثة بنظام RGB. اُنظر تعريف الصَنْف كاملًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_17" style=""><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">paint</span><span class="pun">.</span><span class="typ">Color</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> javafx</span><span class="pun">.</span><span class="pln">scene</span><span class="pun">.</span><span class="pln">canvas</span><span class="pun">.</span><span class="typ">GraphicsContext</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CircleInfo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> radius</span><span class="pun">;</span><span class="pln">    </span><span class="com">// نصف قطر الدائرة</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> x</span><span class="pun">,</span><span class="pln">y</span><span class="pun">;</span><span class="pln">       </span><span class="com">// موقع مركز الدائرة</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> color</span><span class="pun">;</span><span class="pln">   </span><span class="com">// لون الدائرة</span><span class="pln">

    </span><span class="com">/**
     * Create a CircleInfo with a given location and radius and with a
     * randomly selected, semi-transparent color.
     * @param centerX   The x coordinate of the center.
     * @param centerY   The y coordinate of the center.
     * @param rad       The radius of the circle.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">CircleInfo</span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> centerX</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> centerY</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> rad </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        x </span><span class="pun">=</span><span class="pln"> centerX</span><span class="pun">;</span><span class="pln">
        y </span><span class="pun">=</span><span class="pln"> centerY</span><span class="pun">;</span><span class="pln">
        radius </span><span class="pun">=</span><span class="pln"> rad</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">double</span><span class="pln"> red </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">double</span><span class="pln"> green </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">double</span><span class="pln"> blue </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">();</span><span class="pln">
        color </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="pln"> red</span><span class="pun">,</span><span class="pln">green</span><span class="pun">,</span><span class="pln">blue</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.4</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/**
     * Draw the disk in graphics context g, with a black outline.
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> draw</span><span class="pun">(</span><span class="pln"> </span><span class="typ">GraphicsContext</span><span class="pln"> g </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setFill</span><span class="pun">(</span><span class="pln"> color </span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">fillOval</span><span class="pun">(</span><span class="pln"> x </span><span class="pun">-</span><span class="pln"> radius</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">-</span><span class="pln"> radius</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">radius</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">radius </span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">setStroke</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">.</span><span class="pln">BLACK </span><span class="pun">);</span><span class="pln">
        g</span><span class="pun">.</span><span class="pln">strokeOval</span><span class="pun">(</span><span class="pln"> x </span><span class="pun">-</span><span class="pln"> radius</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">-</span><span class="pln"> radius</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">radius</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">radius </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحِظ أننا قد صَرَّحنا عن مُتْغيِّرات النُسخ (instance variables) على أساس كَوْنها عامة (public)؛ لتبسيط الأمور، لكن يُفضَّل عمومًا كتابة ضوابط (setters) وجوالب (setters) لكُلًا منها.
</p>

<p>
	سنُعرِّف البرنامج <code>main</code> داخل الصنف <code>GrowingCircleAnimation</code>. ولأن البرنامج سيَستخدِم ١٠٠ قرص، كُلًا منها عبارة عن كائن من الصنف <code>CircleInfo</code>، فإنه سيَحتاج إلى مصفوفة من الكائنات لتَخْزِينها. لذا يُعرِّف البرنامج مُتْغيِّر مصفوفة (array variable) كمُتْغيِّر نُسخة (instance variable) ضِمْن الصنف، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_19" style=""><span class="kwd">private</span><span class="pln"> </span><span class="typ">CircleInfo</span><span class="pun">[]</span><span class="pln"> circleData</span><span class="pun">;</span><span class="pln"> </span><span class="com">// يحمل بيانات 100 قرص</span></pre>

<p>
	لاحِظ أن المُتْغيِّر <code>circleData</code> ليس ساكنًا (static). تَعتمِد برمجة واجهات المُستخدِم الرسومية (GUI) في العموم على الكائنات (objects) بدلًا من المُتْغيِّرات والتوابع الساكنة. يُمكِننا عمومًا تَخيُّل وجود عدة كائنات من الصنف <code>GrowingCircleAnimation</code> تَعمَل بصورة مُتزامنة. لذا ينبغي لكُلًا منها أن يمتلك مصفوفته الخاصة من الأقراص. بصيغة آخرى، كل تحريكة (animation) عبارة عن كائن، وكل كائن يَمتلك نسخته الخاصة من مُتْغيِّر النسخة <code>circleData</code>. إذا كان <code>circleData</code> ساكنًا، فسيَكُون هناك مصفوفة واحدة فقط، وستبدو جميع التَحرِيكات مُتطابقة تمامًا.
</p>

<p>
	الآن، لابُدّ أن نُنشِئ المصفوفة ونَملؤها بالبيانات. في البرنامج التالي، قُمنا بذلك حتى قَبْل رسم أول إطار (frame) ضِمْن التَحرِيكة. أولًا، اِستخدَمنا التعبير <code>new CircleInfo[100]‎</code> لإنشاء المصفوفة، ثُمَّ أنشأنا مائة كائن من النوع <code>CircleInfo</code> لمَلئ المصفوفة. لاحِظ أن الكائنات الجديدة تَكُون عشوائية فيما يَتعلَّق بحجمها ومَوضِعها. بفَرْض أن <code>width</code> و <code>height</code> هي أبعاد مساحة الرسم (drawing area)، اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_21" style=""><span class="pln">circleData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CircleInfo</span><span class="pun">[</span><span class="lit">100</span><span class="pun">];</span><span class="pln">  </span><span class="com">// أنشئ المصفوفة</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> circleData</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// أنشئ الكائنات</span><span class="pln">
    circleData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CircleInfo</span><span class="pun">(</span><span class="pln"> 
                            </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">width</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()),</span><span class="pln">
                            </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">height</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()),</span><span class="pln">
                            </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="lit">100</span><span class="pun">*</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">())</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَزداد نصف قطر القرص بكل إطار (frame)، ثم يُعاد رسمه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7827_23" style=""><span class="pln">circleData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">radius</span><span class="pun">++;</span><span class="pln">
circleData</span><span class="pun">[</span><span class="pln">i</span><span class="pun">].</span><span class="pln">draw</span><span class="pun">(</span><span class="pln">g</span><span class="pun">);</span></pre>

<p>
	قد تبدو التَعْليمَات -بالأعلى- مُعقدة نوعًا ما، لذا دَعْنَا نَفْحَصها عن قرب. أولًا، يُمثِل <code>circleData‎</code> أحد عناصر المصفوفة <code>circleData</code>، أيّ أنه مُتْغيِّر من النوع <code>CircleInfo</code>. يُشير ذلك المُتْغيِّر إلى كائن من النوع <code>CircleInfo</code>، والذي لابُدّ أن يَحتوِي على مُتْغيِّر النُسخة (instance variable) العام <code>radius</code>، ويَكُون اسمه الكامل هو <code>circleData.radius</code>. لمّا كان مُتْغيِّر النسخة ذاك من النوع <code>int</code>، نستطيع تطبيق عامل الزيادة <code>++</code> عليه لزيادة قيمته بمقدار الواحد، أيّ أن تأثير التَعْليمَة <code>circleData.radius++‎</code> هو زيادة نصف قطر الدائرة بمقدار الواحد. يُعدّ السطر الثاني من الشيفرة مشابهًا للأول، باستثناء أن <code>circleData.draw</code> يُمثِل تابع نُسخة (instance method) بالكائن. تَستدعِي التَعْليمَة <code>circleData.draw(g)‎</code> تابع النُسخة ذاك، وتُمرِّر له المُعامِل <code>g</code>، والذي يُمثِل كائن السِّياق الرسومي المُستخدَم للرسم.
</p>

<p>
	يُمكِنك الإطلاع على الشيفرة المصدرية للبرنامج بالملف <code>GrowingCircleAnimation.java</code> إذا كنت مهتمًا. ولأن البرنامج يَستخدِم الصَنْف <code>CircleInfo</code>، ستحتاج أيضًا إلى نسخة الملف <code>CircleInfo.java</code> لتَصْرِيف البرنامج وتَشْغِيله.
</p>

<h2>
	التحليل والتصميم كائني التوجه
</h2>

<p>
	يَبنِي كل مبرمج عادةً مَخزونًا من التقنيات والخبرات، تَتَمثَل بهيئة قطع من الشيفرة مَكْتُوبة ومُجربَة، بحيث يَتمكَّن من إعادة اِستخدَامها ضِمْن أي برامج جديدة. تُنسَخ الشيفرة القديمة ببساطة إلى البرنامج الجديد، ثم تُعدَّل بما يَتلائَم مع البرنامج الجديد. يَعتمِد عندها كامل المشروع على قدرة المبرمج على سَحْب جزء الشيفرة ذاك، الذي كان قد كَتبَه ضِمْن مشروع سابق، والذي بدا أنه قابل للتَخْصِيص بحيث يَتَناسب مع المشروع الجديد. ولكن في الواقع، تُعانِي تلك الطريقة من كَوْن عملية التَعْدِيل تلك عُرضة للخطأ، كما أنها مُستهلِكة للوقت. تُحبِّذ أيضًا الشركات الكبيرة توفير الموارد بتَجنُّب إعادة اختراع العجلة مع كل مشروع جديد، ولكن تُصبِح مُهِمّة تَعقُّب كل تلك العجلات القديمة أكثر صعوبة مع الحجم الهائل من المشروعات بتلك الشركات.
</p>

<p>
	من الجهة الآخرى، تُعدّ الأصناف قطعًا برمجية قابلة لإعادة الاِستخدَام بدون الحاجة لإجراء أي تَعْديلات عليها بشَّرْط أن تَكُون قد صُمّمت تصميمًا جيدًا. مثلًا، لا تُعدّ الأصناف المصنوعة خصيصًا لإنجار مُهِمّة مُحدَّدة جدًا ضِمْن برنامج مُحدَّد مُصمّمة تصميمًا جيدًا، وإنما لابُدّ أن يَكُون الصنف مصنوعًا بحيث يُمثِل نوعًا واحدًا محددًا من الأشياء أو مفهومًا واحدًا مترابطًا. لمّا كانت تلك المفاهيم والأشياء عامة كفاية لأن تَتكرَّر ضِمْن العديد من المشاكل، تزداد إمكانية الحاجة لاِستخدَام تلك الأصناف ضِمْن عدة مشروعات وبدون إجراء أي تَعْديلات.
</p>

<p>
	ولأننا نَتعامَل مع لغة كائنية التوجه (object-oriented)، يُمكِننا أيضًا إنشاء أصناف فرعية (subclasses) من صَنْف موجود، وهو ما يُزيد من قابلية الأصناف لإعادة الاستخدَام. فمثلًا، إذا احتاج صَنْف معين لمزيد من التَخْصيص، يُمكِننا ببساطة إنشاء صَنْف فرعي (subclass) منه، وإجراء أي إضافات أو تَعْديلات على الصَنْف الفرعي بدون تَعْديل الصَنْف الأصلي. نستطيع القيام بذلك حتى لو لم يَكُن لدينا صلاحية وصول لشيفرة الصَنْف، ولا نعلم أي شيء عن التفاصيل التَّنْفيذية (implementation) الداخلية الخاصة به.
</p>

<p>
	في الواقع، يُعدّ الصنف <code>PairOfDice</code> -من القسم السابق- مثالًا على قطعة برمجية مُعمَّمة، حيث يُمثِل الصنف مفهومًا واحدًا متماسكًا هو "حجري نَّرد". تَحمِل مُتْغيِّرات النُسخ (instance variables) بيانات مُتعلِّقة بحالة حجري النَّرد أي العدد الظاهر بكل حجر، كما يُمثِل تابع النُسخة (instance method) سلوك حجري النَّرد أي القدرة على رَميهما. على الرغم من إمكانية تحسِّين ذلك الصَنْف، فإنه قابلًا لإعادة الاستخدام بالكثير من المشروعات البرمجية المختلفة.
</p>

<p>
	في المقابل، لا يُعدّ الصنف <code>Student</code> -من القسم السابق- قابلًا لإعادة الاستخدام؛ حيث يُبدو وكأنه قد صُمّم خِصيصًا لتمثيل مجموعة طلبة ضِمْن دورة تدريبية من نوع خاص، تَعتمِد درجاتها على ثلاثة اختبارات فقط، فإذا كان هناك حاجة لمزيد من الاختبارات أو الأوراق، فسيُصبِح الصنف عديم الفائدة. علاوة على ذلك، ستَحدُث مشكلة في حالة وجود شخصين لهما نفس الاسم، لذا عادةً ما يُستخدَم رقم هوية عددي بدلًا من الاسم. ولكن للإنصاف، فإن إنشاء صَنْف مُتعدد الأغراض لتمثيل الطلبة هو أمر أصعب كثيرًا من مُجرّد إنشاء صَنْف مُتعدد الأغراض لتمثيل حجري نَّرد.
</p>

<p>
	تَتَكوَّن عملية تطوير أيّ مشروع برمجي ضخم من مجموعة من المراحل، بدايةً من مرحلة توصيف (specification) المشكلة المطلوب حلّها، ثُمَّ تحليلها (analysis)، وتصميم (design) البرنامج اللازم لحلّها، ثُمَّ تأتي مرحلة كتابة الشيفرة (coding)، والتي يَتحوِّل خلالها التصميم إلى لغة برمجية فعليّة، وبعد ذلك تأتي مرحلة الاختبار (testing)، وتَنْقيح الأخطاء (debugging). يَتبَع ذلك فترة طويلة من الصيانة (maintenance)، والتي تَتَضمَّن إصلاح أي مشاكل جديدة عُثر عليها بالبرنامج، وكذلك تَعْديله بحيث يَتكيَّف مع أي تَغْيير بمُتطلبات البرنامج. تُسمَى مُتتالية المراحل تلك باسم "دورة حياة <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/" rel="">تطوير البرمجيات</a> (software life cycle)". نادرًا ما تَتتابَع تلك المراحل على نحو مُتتالي تمامًا، فمثلًا، قد يَتَّضِح أن التوصيف مُتعارض أو غَيْر مُكتمل أثناء مرحلة التحليل، أو قد يَتطلَّب العُثور على مشكلة أثناء مرحلة الاختبار (testing) عودة سريعة إلى مرحلة كتابة الشيفرة (coding) على الأقل، أو حتى تصميمًا جديدًا إذا كانت المشكلة كبيرة بما فيه الكفاية. وأخيرًا، عادةً ما تَتَضمَّن مرحلة الصيانة (maintenance) إعادة بعض الأعمال من المراحل السابقة.
</p>

<p>
	نجاح أيّ مشروع برمجي ضخم عادةً ما يَكُون مَشْرُوطًا بتبَنّي أسلوب منهجي دقيق خلال جميع مراحل التطوير. يُعرَف ذلك الأسلوب المنهجي، والذي يَستخدِم مبادئ التصميم الجيد، باسم "هندسة البرمجيات (software engineering)". يُحاوِل مهندسي البرمجيات عمومًا بناء برامج مستوفية لتوصيفاتها (specifications)، وبحيث تَكُون مكتوبة بطريقة تُسهِل من تَعْدِيلها إن اقْتَضَت الضرورة. هنالك الكثير من الأساليب التي يُمكِن تطبيقها لتصميم البرامج بطريقة منهجية، وتَشتمِل غالبيتها على رسم صناديق صغيرة تُمثِل مُكوِّنات البرنامج مع أسهم مُعنوَنة لتمثيل العلاقات بين تلك الصناديق.
</p>

<p>
	لقد ناقشنا كائنية التوجه (object orientation) فيما يَتعلَّق بمرحلة كتابة الشيفرة (coding)، لكن تَتوفَّر أيضًا أساليب كائنية التوجه لمرحلتي التحليل والتصميم. يَكُون السؤال بهذه المرحلة من مراحل تطوير البرمجيات: كيف نَتَمكَّن من اكتشاف أو اختراع البنية الكلية للبرنامج؟ اتبع النصيحة التالية، والتي تُعدّ بمثابة أسلوبًا بسيطًا كائني التوجه لمرحلتي التحليل والتصميم: سَجِّل توصيف المشكلة، ثُمَّ ارسم خطًا أسفل جميع الأسماء الموجودة بذلك التوصيف. ينبغي أن تَكُون تلك الأسماء مُرشَّحة كأصناف (classes) أو ككائنات (objects) ضِمْن تصميم البرنامج. بالمثل، ارسم خطًا أسفل جميع الأفعال (verbs) الموجودة بالتوصيف. ينبغي أن تَكُون تلك الأفعال مُرشَّحة كتوابع (methods). هذه هي نقطة البداية فقط، فقد يَكشِف مزيد من التحليل (analysis) عن الحاجة لإضافة أصناف أو توابع آخرى، كما قد يَكشِف عن إمكانية اِستخدَام أصناف فرعية (subclasses) تَتشارَك الخاصيات والسلوكيات المُتشابهة بينها.
</p>

<p>
	"حلّل المشكلة لاكتشاف المفاهيم المُتضمَّنة، ثم عرِّف أصنافًا لتمثيل تلك المفاهيم. ينبغي أن يُحاكِي التصميم المشكلة، أو بتعبير آخر، ينبغي أن تَعكِس بنية البرنامج بنية المشكلة نفسها بصورة طبيعية." قد يَكُون ذلك تبسيطًا مُخِلًّا، ولكنه واضح وفعال عمومًا.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s3.html" rel="external nofollow">Section 3: Programming with Objects</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1110</guid><pubDate>Fri, 22 Jan 2021 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x648;&#x627;&#x646;&#x64A; &#x648;&#x62A;&#x647;&#x64A;&#x626;&#x629; &#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; Object Initialization &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D9%88%D8%A7%D9%86%D9%8A-%D9%88%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-object-initialization-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1109/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/32.png.8a69eed9bf4960394b0e4809e53e794a.png" /></p>

<p>
	تختلف الأنواع الكائنية (object types) بلغة الجافا عن الأنواع البسيطة (primitive type)، فمثلًا، لا تُنشِئ تَعْليمَة التّصْريح (declare) عن مُتْغيِّر، نوعه عبارة عن صَنْف (class)، كائنًا (object) من ذلك الصنف، بل ينبغي أن تقوم بذلك صراحةً. وفقًا للحاسوب، تَتَكوَّن عملية إنشاء كائن من محاولة العثور على مساحة غَيْر مُستخدَمة بقسم الكومة (heap) من الذاكرة وبحيث تَكُون كبيرة بما فيه الكفاية لحَمْل الكائن المطلوب إنشائه، ومن ثَمَّ مَلْئ مُتْغيِّرات نُسخ (instance variables) ذلك الكائن. كمبرمج، أنت لا تهتم عادةً بالمكان الذي يُخزَّن فيه الكائن بالذاكرة، ولكن ستَرغَب غالبًا بالتَحكُّم بالقيم المبدئية المُخزَّنة بمُتْغيِّرات نُسخ ذلك الكائن، كما قد تحتاج في بعض الأحيان إلى إجراء تهيئة (initialization) أكثر تعقيدًا مع كل كائن جديد يُنشَئ.
</p>

<h2>
	تهيئة متغيرات النسخ (initialization)
</h2>

<p>
	تستطيع عمومًا إِسْناد قيمة مبدئية إلى أيّ مُتْغيِّر نسخة (instance variable) أثناء التَّصْريح (declaration) عنه، وذلك كأيّ مُتْغيِّر عادي آخر. على سبيل المثال، لنَفْترِض مثلًا أن لدينا الصَنْف <code>PairOfDice</code> المُعرَّف بالأسفل. سيُمثِل أيّ كائن من هذا الصَنْف حجري نَّرد، وسيَحتوِي على مُتْغيِّري نسخة لتمثيل الأعداد الظاهرة على حجري النَّرد، بالإضافة إلى تابع نُسخة (instance method) مسئول عن مُهِمّة رمِي حجرى النَّرد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_462_7" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> die1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر على الحجر الأول</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> die2 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر على الحجر الثاني</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> roll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// حاكي تجربة رمي حجري النرد</span><span class="pln">
         die1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
         die2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف PairOfDice</span></pre>

<p>
	وفقًا لتعريف الصَنْف بالأعلى، سيُهيَئ (initialize) مُتْغيِّري النُسخة <code>die1</code> و <code>die2</code> إلى القيم ٣ و ٤ على الترتيب أينما بَنينا كائنًا من الصنف <code>PairOfDice</code>. ينبغي أن تفهم كيفية حُدوث ذلك: لمّا كان من المُمكن إِنشاء عدة كائنات من الصَنْف <code>PairOfDice</code>، فإنه، وبكل مرة يُنشَئ فيها واحد منها، فإن الكائن المُنشَىء سيَحصُل على مُتْغيِّري نُسخة (instance variables) خاصين به، ثم ستُنفَّذ تَعْليمتَي الإِسْناد <code>die1 = 3</code> و <code>die2 = 4</code> لمَلئ قيم مُتْغيِّراته. اُنظر النسخة التالية من الصَنْف <code>PairOfDice</code> لمزيد من الإيضاح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_9" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> roll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         die1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
         die2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف PairOfDice</span></pre>

<p>
	وفقًا للتعريف بالأعلى، فإنه، وبكل مرة يُنشَئ فيها كائن (object) من الصَنْف <code>PairOfDice</code>، ستُهيَئ مُتْغيِّرات النُسخ إلى قيم عشوائية كما لو كنا نَرمِي حجري نَّرد على طاولة اللعب. لمّا كانت تلك التهيئة (initialization) تُنفَّذ لكل كائن على حدى، فستُحسَب تلك القيم لكل حجري نَّرد، وسيَحصُل كل حجري نَّرد مختلفين على قيم مبدئية مختلفة. سيَكُون الوضع مختلفًا بالتأكيد في حالة تهيئة المُتْغيِّرات الأعضاء الساكنة (static member variables)؛ وذلك لوجود نُسخة وحيدة فقط من أيّ مُتْغيِّر ساكن (static)، والتي تُهيَئ (initialization) مرة واحدة فقط عند تحميل الصَنْف (class) لأول مرة.
</p>

<p>
	تُهيَئ مُتْغيِّرات النُسخ (instance variable) بقيم مبدئية افتراضية تلقائيًا إذا لَمْ يُوفِّر لها المبرمج قيمة مبدئية. فمثلًا، تُهيَئ مُتْغيِّرات النسخ من الأنواع العددية مثل <code>int</code> و <code>double</code> تلقائيًا إلى الصفر إذا لم يُوفِّر لها المبرمج قيم آخرى، أما المُتْغيِّرات من النوع <code>boolean</code> فتُهيئ إلى القيمة <code>false</code>، بينما المُتْغيِّرات من النوع <code>char</code> تُهيَئ للمحرف المقابل لقيمة ترميز اليونيكود (Unicode code) رقم صفر <code>‎\u0000</code>. وأخيرًا، بالنسبة لمُتْغيِّرات النُسخ كائنية النوع (object type)، فإن قيمها المبدئية الافتراضية هي القيمة الفارغة <code>null</code>. على سبيل المثال، لمّا كانت السَلاسِل النصية من النوع <code>String</code> عبارة عن كائنات (objects)، فإن القيمة المبدئية الافتراضية للمُتْغيِّرات من النوع <code>String</code> تُساوِي <code>null</code>.
</p>

<h2>
	بواني الكائنات (constructors)
</h2>

<p>
	يُستخدَم العَامِل <code>new</code> لإنشاء الكائنات (objects)، فمثلًا، يُمكِننا كتابة الشيفرة التالية لإنشاء كائن من الصَنْف <code>PairOfDice</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_11" style="">
<span class="typ">PairOfDice</span><span class="pln"> dice</span><span class="pun">;</span><span class="pln">   </span><span class="com">// ‫صرح عن متغير من النوع PairOfDice</span><span class="pln">

</span><span class="com">// أنشئ كائنا جديدا من الصنف واسنده مرجعه الى المتغير</span><span class="pln">
dice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">();</span><span class="pln">  </span></pre>

<p>
	يُخصِّص التعبير <code>new PairOfDice()‎</code> مساحة بالذاكرة للكائن، ويُهيِئ مُتْغيِّرات النسخ (instance variables) الخاصة به، وأخيرًا، يُعيد مَرجِعًا (reference) إليه كقيمة للتعبير، والتي تُخزِّنها تَعْليمَة الإِسْناد (assignment statement) بالمُتْغيِّر <code>dice</code>، أي سيُشير <code>dice</code> إلى الكائن الجديد المُنشَئ للتو بعد تَّنْفيذ تَعْليمَة الإِسْناد. يبدو الجزء <code>PairOfDice()‎</code> وكأنه عملية استدعاء لبرنامج فرعي (subroutine call). ليس هذا في الواقع مجرد مصادفة؛ فهو بالفعل يُمثِل عملية استدعاء، ولكن لنوع خاص من البرامج الفرعية تُعرَف باسم "البَانِي أو بَانِي الكائن (constructor)". قد يُربكك ذلك خاصة وأن تعريف الصَنْف <code>PairOfDice</code> لا يَحتوِي على أي برنامج فرعي بنفس تلك البصمة. في الحقيقة، لابُدّ لأيّ صَنْف من أن يَحتوِي على بَانِي كائن (constructor) واحد على الأقل، لذا يُوفِّر النظام بَانِي كائن افتراضي (default constructor) لأي صَنْف لم يُعرِّف له المبرمج بَانِي كائن (constructor). يَقْتصِر دور البواني الافتراضية على تَخْصِيص مساحة بالذاكرة للكائن، وتهيئة مُتْغيِّرات النُسخ (instance variables)، أما إذا أردت تَّنْفيذ أشياء آخرى عند إنشاء كائن من صَنْف معين، فستحتاج إلى تعريف باني كائن (constructor) واحد أو ربما أكثر ضِمْن تعريف ذلك الصَنْف.
</p>

<p>
	تُشبه تعريفات البَوانِي (constructors) في العموم تعريف أيّ برنامج فرعي (subroutine) آخر مع ثلاثة استثناءات. أولًا، لا يُمكِن تَخْصيص النوع المُعاد (return type) من البَانِي بما في ذلك المُبدِّل <code>void</code>. ثانيًا، ينبغي أن يَكُون اسم الباني هو نفسه اسم الصَنْف المُعرَّف بداخله. أخيرًا، تَقْتصِر المُبدِّلات (modifiers) التي يُمكِن اِستخدَامها بتعريف أيّ باني على مُبدِّلات الوصول (access modifiers)، أي <code>public</code> و <code>private</code> و <code>protected</code>، ولا يُمكِن اِستخدَام المُبدِّل <code>static</code> بالتحديد أثناء التَّصْريح عنها.
</p>

<p>
	في المقابل، يُمكِنك كتابة مَتْن البَانِي ككُتلَة من التَعْليمَات (statements) كأيّ مَتْن برنامج فرعي (subroutine body) تقليدي آخر؛ فلا يوجد أي قيود على التَعْليمَات التي يُمكِن اِستخدَامها ضِمْن المَتْن، كما يُمكِن للبَانِي أن يَستقبِل قائمة من المُعامِلات الصُّوريّة (formal parameters)، بل إن قدرته على اِستقبَال تلك المُعامِلات هي السبب الرئيسي من اِستخدَامه أساسًا، حيث تُوفِّر تلك المُعامِلات البيانات الضرورية لإِنشاء الكائن (object)، فمثلًا، يُمكِن لأحد بواني الصَنْف <code>PairOfDice</code> أن يُوفِّر قيم الأعداد المبدئية لحجري النَّرد. تَعرَض الشيفرة التالية تعريف الصَنْف في تلك الحالة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_13" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die1</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر بالحجر الأول</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die2</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر بالحجر الثاني</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> val1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> val2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يُنشئ الباني حجري نرد ويُهيئهما مبدئيًا بالقيم الممررة</span><span class="pln">
         die1 </span><span class="pun">=</span><span class="pln"> val1</span><span class="pun">;</span><span class="pln">  
         die2 </span><span class="pun">=</span><span class="pln"> val2</span><span class="pun">;</span><span class="pln"> 
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> roll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// حاكي تجربة الرمي</span><span class="pln">
         die1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
         die2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف PairOfDice</span></pre>

<p>
	صَرَّحت الشيفرة بالأعلى عن بَانِي كائن (constructor) على الصورة <code>public PairOfDice(int val1, int val2) ...‎</code>. ستُلاحِظ أننا لَمْ نُخصِّص النُوع المُعاد (return type) من البَانِي، كما أننا أعطيناه نفس اسم الصَنْف، وهذه هي الكيفية التي تُمكِّن مُصرِّف الجافا (Java compiler) من تَمييز بَوانِي الكائن. نُلاحِظ أيضًا أن البَانِي يَستقبِل مُعامِلين (parameters)، ينبغي لقيمهما أن تُمرَّر عند استدعاء البَانِي. على سبيل المثال، سيُنشِئ التعبير <code>new PairOfDice(3,4)‎</code> كائنًا (object) من الصَنْف <code>PairOfDice</code>، ويُهيِئ مُتْغيِّرات نُسخه <code>die1</code> و <code>die2</code> مبدئيًا إلى القيم ٣ و ٤ على الترتيب. أخيرًا، ينبغي أن تُستخدَم القيمة المُعادة (return value) من البَانِي بطريقة ما، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_15" style="">
<span class="com">// ‫صرح عن متغير من الصنف PairOfDice</span><span class="pln">
</span><span class="typ">PairOfDice</span><span class="pln"> dice</span><span class="pun">;</span><span class="pln">

</span><span class="com">// ‫سيشير dice إلى كائن جديد من الصنف PairOfDice مُهيَأ مبدئيًا بالقيم 1 و 1</span><span class="pln">
dice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">1</span><span class="pun">);</span><span class="pln"> </span></pre>

<p>
	الآن، وبعد أن أَضفنا بَانِي كائن إلى الصَنْف <code>PairOfDice</code>، لَمْ يَعُدْ بمقدورنا اِستخدَام التعبير <code>new PairOfDice()‎</code> لإنشاء كائن؛ حيث تَحتوِي النسخة الجديدة من الصَنْف <code>PairOfDice</code> -بالأعلى- على بَانِي وحيد، والذي يَتَطلَّب مُعامِلين فعليين (actual parameter)، بينما نحن نحاول استدعاء بَانِي بدون أيّ مُعامِلات، ولأن النظام لا يُوفِّر البَانِي الافتراضي (default constructor) سوى للأصناف التي لا يَتَضمَّن تعريفها (class definition) أي بَانِي على الإطلاق، فسنحتاج إلى إضافة بَانِي آخر لا يَستقبِل أي مُعامِلات للصنف، وهو في الواقع أمر بسيط. تستطيع عمومًا إضافة أيّ عدد من البَوانِي (constructors) طالما كانت بَصْمتهم (signatures) مختلفة، أيّ طالما كان لديهم أعداد مختلفة أو أنواع مختلفة من المُعامِلات الصُّوريّة (formal parameters). بالشيفرة التالية، أَضفنا بَانِي بدون أي مُعامِلات إلى الصَنْف <code>PairOfDice</code> سيُهيِئ حجري النَّرد بقيم مبدئية عشوائية، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_17" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die1</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر بالحجر الأول</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> die2</span><span class="pun">;</span><span class="pln">   </span><span class="com">// العدد الظاهر بالحجر الثاني</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// يرمي الباني حجري النرد ليحصل على قيم عشوائية مبدئية</span><span class="pln">
        roll</span><span class="pun">();</span><span class="pln">  
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> val1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> val2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يُنشئ الباني حجري نرد ويُهيئهما مبدئيًا بالقيم الممررة</span><span class="pln">
         die1 </span><span class="pun">=</span><span class="pln"> val1</span><span class="pun">;</span><span class="pln">  
         die2 </span><span class="pun">=</span><span class="pln"> val2</span><span class="pun">;</span><span class="pln"> 
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> roll</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// حاكي تجربة الرمي</span><span class="pln">
        die1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        die2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()*</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// end class PairOfDice</span></pre>

<p>
	الآن، تستطيع إنشاء كائن من الصَنْف <code>PairOfDice</code> بطريقتين، إِما باِستخدَام التعبير <code>new PairOfDice()‎</code> أو باِستخدَام التعبير <code>new PairOfDice(x,y)‎</code>، حيث <code>x</code> و <code>y</code> هي تعبيرات من النوع العَدَدَي الصحيح.
</p>

<p>
	تستطيع أيضًا اِستخدَام نفس الصَنْف -بالأعلى- بأي برنامج آخر يَتَعامَل مع حجري نَّرد، وبذلك لن تَضَطرّ إلى اِستخدَام التعبير الغامض نوعًا ما <code>‎(int)(Math.random()*6)+1</code> مرة آخرى؛ لأنه مُضمَّن بالفعل داخل الصَنْف <code>PairOfDice</code>، أيّ ستحتاج للتَعامُل مع مسألة رمِي حجري النَّرد مرة واحدة ضِمْن الصَنْف، ثم لا حاجة للقلق بشأنها مُجددًا. بالمثال التالي، يَستخدِم البرنامج <code>main</code> الصَنْف <code>PairOfDice</code>؛ لعدّ عدد مرات رمِي زوجين من حجري النَّرد حتى يَتساوَى حاصل مجموع كِلا الزوجين، وهو ما يُوضِح إِمكانية إنشاء عدة نُسخ (instances) من نفس الصَنْف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_19" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">RollTwoPairs</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

        </span><span class="typ">PairOfDice</span><span class="pln"> firstDice</span><span class="pun">;</span><span class="pln">  </span><span class="com">// يشير إلى الزوج الأول من حجري النرد</span><span class="pln">
        firstDice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">PairOfDice</span><span class="pln"> secondDice</span><span class="pun">;</span><span class="pln"> </span><span class="com">// يشير إلى الزوج الثاني من حجري النرد</span><span class="pln">
        secondDice </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PairOfDice</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">int</span><span class="pln"> countRolls</span><span class="pun">;</span><span class="pln">  </span><span class="com">// عدد مرات الرمي</span><span class="pln">

        </span><span class="typ">int</span><span class="pln"> total1</span><span class="pun">;</span><span class="pln">      </span><span class="com">// حاصل مجموع الزوج الأول</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> total2</span><span class="pun">;</span><span class="pln">      </span><span class="com">// حاصل مجموع الزوج الثاني</span><span class="pln">

        countRolls </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// إرم زوجي حجري النرد حتى يتساوى حاصل مجموع كلا منهما</span><span class="pln">

            firstDice</span><span class="pun">.</span><span class="pln">roll</span><span class="pun">();</span><span class="pln">    </span><span class="com">// إرم الزوج الأول</span><span class="pln">
            total1 </span><span class="pun">=</span><span class="pln"> firstDice</span><span class="pun">.</span><span class="pln">die1 </span><span class="pun">+</span><span class="pln"> firstDice</span><span class="pun">.</span><span class="pln">die2</span><span class="pun">;</span><span class="pln">   </span><span class="com">// Get total.</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"First pair comes up  "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> total1</span><span class="pun">);</span><span class="pln">

            secondDice</span><span class="pun">.</span><span class="pln">roll</span><span class="pun">();</span><span class="pln">    </span><span class="com">// إرم الزوج الثاني</span><span class="pln">
            total2 </span><span class="pun">=</span><span class="pln"> secondDice</span><span class="pun">.</span><span class="pln">die1 </span><span class="pun">+</span><span class="pln"> secondDice</span><span class="pun">.</span><span class="pln">die2</span><span class="pun">;</span><span class="pln">   </span><span class="com">// Get total.</span><span class="pln">
            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Second pair comes up "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> total2</span><span class="pun">);</span><span class="pln">

            countRolls</span><span class="pun">++;</span><span class="pln">   </span><span class="com">// أزد عدد الرميات</span><span class="pln">

            </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">();</span><span class="pln">  </span><span class="com">// Blank line.</span><span class="pln">

        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">total1 </span><span class="pun">!=</span><span class="pln"> total2</span><span class="pun">);</span><span class="pln">

        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"It took "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> countRolls 
                          </span><span class="pun">+</span><span class="pln"> </span><span class="str">" rolls until the totals were the same."</span><span class="pun">);</span><span class="pln">

    </span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية main()</span><span class="pln">

</span><span class="pun">}</span><span class="pln"> </span><span class="com">// ‫نهاية الصنف RollTwoPairs</span></pre>

<p>
	البَوانِي في العموم عبارة عن برامج فرعية (subroutines)، ولكن من نوع خاص. هي بلا شك ليست توابع نُسخ (instance methods)؛ لأنها لا تنتمي إلى الكائنات (objects). لكونها مسئولة عن إِنشاء الكائنات، أي لابُدّ من وُجودها قَبْل وُجود أيّ كائن من الصَنْف، فإنها قد تَكُون أكثر شبهًا بالبرامج الفرعية الأعضاء الساكنة (static) مع أنه لا يُسمَح باِستخدَام المُبدِّل <code>static</code> أثناء تعريفها. تقنيًا، البَوانِي ليست أعضاء (members) ضِمْن الصَنْف على الإطلاق، ولا يُشار إليها على أساس كَوْنها توابع (methods) بالصَنْف.
</p>

<p>
	بخلاف البرامج الفرعية (subroutines) الآخرى، تستطيع استدعاء البَوانِي فقط من خلال العَامِل <code>new</code>، ويُكْتَب على الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_21" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="pun">&lt;</span><span class="kwd">class</span><span class="pun">-</span><span class="pln">name</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">parameter</span><span class="pun">-</span><span class="typ">list</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	قد تَكُون قائمة المُعامِلات <strong><parameter-list></parameter-list></strong> فارغة. تُعدّ الشيفرة بالأعلى تعبيرًا (expression)؛ لأنها بالنهاية تَحسِب قيمة وتُعيدها، حيث تُعيد مَرجِعًا (reference) إلى الكائن المنُشَئ، والذي يُفترَض عادةً تَخْزِينه بمُتْغيِّر. تستطيع أيضًا اِستدعاء البَوانِي (constructor call) بطرائق شتى آخرى، مثلًا، كمُعامِل (parameter) ضِمْن تَعْليمَة اِستدعاء برنامج فرعي (subroutine call)، أو كجُزء من تعبير (expression) أكثر تعقيدًا. لاحِظ أنه في حالة عَدْم الاحتفاظ بالمَرجِع (reference) المُعاد داخل مُتْغيِّر، فلن تَتَمكَّن من الإشارة إلى الكائن المُنشَئ مرة آخرى.
</p>

<p>
	يُعدّ استدعاء البَانِي (constructor call) عمومًا أكثر تعقيدًا من أيّ استدعاء عادي لبرنامج فرعي (subroutine) أو لدالة (function)، لذا من المهم أن تَفهَم الخطوات التي يُنفِّذها الحاسوب أثناء استدعاء البَوانِي:
</p>

<ol>
<li>
		يَعثُر الحاسوب على كُتلَة غَيْر مُستخدَمة بقسم الكَوْمة (heap) من الذاكرة، بشَّرْط أن تَكُون كبيرة بما فيه الكفاية لتَحمِل كائن من النوع المُخصَّص.
	</li>
	<li>
		يُهيِئ الحاسوب مُتْغيِّرات النُسخ (instance variables) للكائن، فإذا كان التَّصْريح عنها يَتَضمَّن قيمة مبدئية، فإنه يَحسِب تلك القيمة ويُخزِّنها بمُتْغيِّر النُسخة. أما إن لَمْ تَكُن مُضمَّنة، فإنه يَستخدِم القيمة المبدئية الافتراضية.
	</li>
	<li>
		تُحسَب قيم المُعامِلات الفعليّة (actual parameters) بالبَانِي -إن وُجدت-، ثم تُسنَد إلى المُعامِلات الصُّوريّة (formal parameters).
	</li>
	<li>
		تُنفَّذ التَعْليمَات الموجودة بمَتْن البَانِي (constructor body) إن وُجدت.
	</li>
	<li>
		يُعاد مَرجِع (reference) إلى الكائن المُنشَئ كقيمة لتعبير استدعاء البَانِي (constructor call)
	</li>
</ol>
<p>
	تَكُون النتيجة النهائية هي حُصولك على مَرجِع (reference) يُشير إلى الكائن المُنشَئ حديثًا. كمثال آخر، سنُضيف بَانِي كائن (constructor) إلى الصَنْف <code>Student</code> المُستخدَم بالقسم الأول، كذلك أَضفنا مُتْغيِّر نُسخة (instance variable) خاص اسمه هو <code>name</code>. اُنظر الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_23" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Student</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">                 </span><span class="com">// اسم الطالب</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> test1</span><span class="pun">,</span><span class="pln"> test2</span><span class="pun">,</span><span class="pln"> test3</span><span class="pun">;</span><span class="pln">   </span><span class="com">// درجات الطالب</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> theName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// يستقبل باني الكائن اسم الطالب</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> theName </span><span class="pun">==</span><span class="pln"> null </span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">IllegalArgumentException</span><span class="pun">(</span><span class="str">"name can't be null"</span><span class="pun">);</span><span class="pln">
        name </span><span class="pun">=</span><span class="pln"> theName</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// تابع جلب لقراءة قيمة متغير النسخة</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> getAverage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
        </span><span class="com">// احسب متوسط درجات الطالب</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">test1 </span><span class="pun">+</span><span class="pln"> test2 </span><span class="pun">+</span><span class="pln"> test3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// ‫نهاية الصنف Student</span></pre>

<p>
	عَرَّفنا بَانِي كائن (constructor) يَستقبِل مُعامِلًا (parameter) من النوع <code>String</code> ليُمثِل اسم الطالب.
</p>

<p>
	يَحتوِي أي كائن من النوع <code>Student</code> على بيانات طالب معين، ويُمكِننا عمومًا إِنشاء كائنات من ذلك الصَنْف باِستخدَام التَعْليمَات التالية مثلًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_25" style="">
<span class="pln">std </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">(</span><span class="str">"John Smith"</span><span class="pun">);</span><span class="pln">
std1 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">(</span><span class="str">"Mary Jones"</span><span class="pun">);</span></pre>

<p>
	بالنسخة الأصلية من ذلك الصَنْف، كان المُبرمج يَضطرّ لإِسْناد قيمة مُتْغيِّر النُسخة <code>name</code> بعد إنشاء الكائن، مما يَعنِي عدم وجود ضمانة لأن يَتَذكَّر المُبرمج ضَبْط قيمة ذلك المُتْغيِّر بصورة سليمة. في المقابل، بالنسخة الجديدة من الصَنْف، لا يُمكِن إنشاء كائن من الصَنْف <code>Student</code> إلا عن طريق استدعاء البَانِي (constructor)، والذي يَضبُط قيمة مُتْغيِّر النُسخة <code>name</code> تلقائيًا، كما أنه يَضمَن ألا تَكُون تلك القيمة فارغة. يُسهِل ذلك عمومًا من عَمَل المبرمج، ويُجنِّبه عددًا كبيرًا من الأخطاء البرمجية (bugs). لاحِظ أننا قد اِستخدَمنا المُبدِّل <code>private</code> ضِمْن تَعْليمَة التَّصْريح عن مُتْغيِّر النُسخة <code>name</code>، وهو ما يُوفِّر نوعًا آخر من الضمانة؛ فبذلك لن يَتَمكَّن أي مكان بالشيفرة خارج الصنف <code>Student</code> من الوصول مباشرة إلى مُتْغيِّر النُسخة <code>name</code>، فمثلًا، تُضبَط قيمته بشكل غَيْر مباشر عند استدعاء البَانِي (constructor). في حين يَتَضمَّن الصَنْف دالة جَلْب (getter function) هي <code>getName()‎</code>، والتي يُمكِن من خلالها مَعرِفة قيمة مُتْغيِّر النُسخة <code>name</code> لطالب معين من مكان خارج الصَنْف، فإن الصَنْف لا يَحتوِي على أيّ تابع ضَبْط (setter method) أو على أي طريقة آخرى لتَعْدِيل قيمة المُتْغيِّر <code>name</code>، أيّ بمُجرَّد إنشاء كائن من ذلك الصَنْف، فإنه سيَظلّ مُحتفِظًا بنفس قيمة المُتْغيِّر <code>name</code> المَبدئية طوال فترة وجوده بالبرنامج.
</p>

<p>
	في تلك الحالة، رُبما من الأفضل التَّصْريح عن مُتْغيِّر النُسخة <code>name</code> باِستخدَام المُبدِّل <code>final</code>. يُمكِنك التَّصْريح عن أيّ مُتْغيِّر نُسخة (instance variable) عمومًا باِستخدَام المُبدل <code>final</code> بشَّرْط إِسْناد قيمة إليه إما بتَعْليمَة التَّصْريح (declaration) أو بكل البواني (constructor) المُعرَّفة بذلك الصَنْف. في العموم، لا يُمكِن إِسْناد قيمة إلى مُتْغيِّر نُسخة (instance variable) قد صُرِّح عنه باِستخدَام المُبدِّل <code>final</code>، ولكن يُستَثنَى من ذلك مَتْن البواني (constructor).
</p>

<p>
	حسنًا، ماذا سيَحدُث عندما تَستخدِم أعضاء ساكنة (static) وآخرى غَيْر ساكنة (non-static) ضِمْن نفس الصَنْف؟ يَعرِف أيّ كائن عمومًا الصَنْف الذي يَنتمِي إليه، ويُمكِنه الإشارة إلى أعضاء صَنْفه الساكنة (static members). لذا يُمكِن لأيّ تابع نُسخة (instance method) بالصَنْف أن يُشير إلى المُتْغيِّرات الأعضاء (member variables) الساكنة أو أن يَستدعِي البرامج الفرعية الأعضاء (member subroutines) الساكنة. لكن، لاحِظ أن هناك دائمًا نُسخة وحيدة فقط من أيّ عضو ساكن، والتي تَنتمِي للصَنْف ذاته، مما يَعنِي أن جميع كائنات صَنْف معين تَتَشارك نُسخة وحيدة من أيّ عضو ساكن مُعرَّف بالصَنْف.
</p>

<p>
	كمثال، اُنظر النسخة التالية من الصَنْف <code>Student</code>، والتي أُضيف إليها مُتْغيِّر نُسخة <code>ID</code> لكل طالب، بالإضافة إلى عضو ساكن (static member) هو <code>nextUniqueID</code>. على الرغم من وجود مُتْغيِّر <code>ID</code> بكل كائن من النوع <code>Student</code>، فهناك مُتْغيِّر <code>nextUniqueID</code> واحد فقط:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_27" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Student</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">                 </span><span class="com">// اسم الطالب</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> test1</span><span class="pun">,</span><span class="pln"> test2</span><span class="pun">,</span><span class="pln"> test3</span><span class="pun">;</span><span class="pln">   </span><span class="com">// درجات الطالب</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> ID</span><span class="pun">;</span><span class="pln">  </span><span class="com">// رقم معرف لهوية الطالب</span><span class="pln">

   </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> nextUniqueID </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// للاحتفاظ برقم الهوية المتاح التالي</span><span class="pln">

   </span><span class="typ">Student</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> theName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// باني كائن يستقبل اسم الطالب ويسند رقم هوية فريد إليه</span><span class="pln">
      name </span><span class="pun">=</span><span class="pln"> theName</span><span class="pun">;</span><span class="pln">
      nextUniqueID</span><span class="pun">++;</span><span class="pln">
      ID </span><span class="pun">=</span><span class="pln"> nextUniqueID</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫تابع جلب لقراءة قيمة متغير النسخة name</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> getID</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// تابع جلب لقراءة قيمة رقم الهوية</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> ID</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> getAverage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
        </span><span class="com">// احسب متوسط درجات الطالب</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">test1 </span><span class="pun">+</span><span class="pln"> test2 </span><span class="pun">+</span><span class="pln"> test3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// ‫نهاية الصنف Student</span></pre>

<p>
	لمّا كان المُتْغيِّر <code>nextUniqueID</code> ساكنًا (static)، فإنه يُهيَئ مبدئيًا باِستخدَام التعبير <code>nextUniqueID = 0</code> مرة واحدة فقط عند تحميل الصَنْف لأول مرة. بعد ذلك، عندما نُحاوِل إنشاء كائن من الصَنْف <code>Student</code>، سيُنفِّذ الباني (constructor) التَعْليمَة <code>nextUniqueID++;‎</code>، والتي تُزِيد دائمًا قيمة نفس المُتْغيِّر العضو الساكن (static member variable) بمقدار الواحد، فتُصبِح قيمة المُتْغيِّر <code>nextUniqueID</code> مُساوِية للعدد ١ بَعْد إنشاء أول كائن من الصَنْف <code>Student</code>، ثم تُصبِح مُساوِية للعدد ٢ بَعْد إنشاء الكائن الثاني، وتُصبِح مُساوِية للعدد ٣ بَعْد الكائن الثالث، ويستمر المُتْغيِّر بالزيادة مع كل كائن جديد يُنشَىء من ذلك الصَنْف. في المقابل، لمّا كان المُتْغيِّر <code>ID</code> عبارة عن مُتْغيِّر نُسخة (instance variable)، فإن كل كائن لديه نُسخته الخاصة من ذلك المُتْغيِّر، والتي يُخْزِّن فيها البَانِي القيمة الجديدة من المُتْغيِّر <code>nextUniqueID</code> بَعْد إِنشائه للكائن. صُمّم الصَنْف <code>Student</code> عمومًا بحيث يَحصُل كل طالب على قيمة مختلفة لنُسخته من المُتْغيِّر <code>ID</code> تلقائيًا. ولأننا قد صَرَّحنا عن المُتْغيِّر <code>ID</code> بكَوْنه خاصًا (private)، فيَستحِيل أيضًا التَلاعُب بقيمة ذلك المُتْغيِّر بأيّ طريقة بَعْد إنشاء الكائن، وهو ما يَضمَن تَخْصِيص رقم هوية فريد ودائم لكل كائن من الصَنْف <code>Student</code>، وهو أمر رائع إذا فكرت بالأمر!
</p>

<p>
	لو أعدت التفكير بالأمر، ستَجِدْ أن تلك الضمانة ليست مُطلقة تمامًا، وإنما هي مُقْتصِرة فقط على البرامج التي تَستخدِم خيطًا (thread) واحدًا، أما البرامج مُتعددة الخيوط (multi-thread)، أي التي يُمكِن خلالها تَّنْفيذ عدة أشياء بنفس ذات الوقت، فإن الأشياء تُصبِح غريبة قليلًا، فقد يُنشِئ خيطان كائنين من الصَنْف <code>Student</code> بنفس ذات الوقت، ولهذا قد يَحصُل كِلا الكائنين في تلك الحالة على نفس رقم الهوية. سنعود إلى هذا الموضوع بالقسم الفرعي ١٢.١.٣، حيث ستَتَعلَّم كيفية حلّ تلك المشكلة.
</p>

<h2>
	كانس المهملات (garbage collection)
</h2>

<p>
	حتى الآن، كان حديثنا خلال هذا القسم مُقْتصِرًا على إنشاء الكائنات، فماذا عن هَدْمِها؟ تُحذَف الكائنات بلغة الجافا أتوماتيكيًا. يَقع أيّ كائن عمومًا بقسم الكَوْمة (heap) من الذاكرة، ويُمكِن الوصول إلى كائن معين فقط عَبْر المُتْغيِّرات التي تَحمِل مَراجِع (references) تُشير إليه. والآن، ماذا سيَحدُث لو لم يَعُدْ هناك أيّ مُتْغيِّرات تُشير إلى كائن معين؟ اُنظر مثلًا للشيفرة التالية (لاحِظ أنها لأغراض تعليمية فقط؛ فليس من المُحتمل على الإطلاق كتابة تَعْليمَتين على هذا النحو ببرنامج فعليّ):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_462_29" style="">
<span class="typ">Student</span><span class="pln"> std </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">(</span><span class="str">"John Smith"</span><span class="pun">);</span><span class="pln">
std </span><span class="pun">=</span><span class="pln"> null</span><span class="pun">;</span></pre>

<p>
	بالسطر الأول، اُنشِئ كائن من الصَنْف <code>Student</code>، ثم خُزِّن مَرجِع (reference) يُشير إليه بالمُتْغيِّر <code>std</code>، ثم بالسطر التالي، تَغيَّرت قيمة <code>std</code>، ولم يَعُدْ مَرجِع الكائن المُنشَئ للتو موجودًا، بل لم يَعُدْ هناك أي مَراجِع (references) تُشير إليه بأي مُتْغيِّر آخر، وبالتالي، لن يَتَمكَّن البرنامج من اِستخدَام ذلك الكائن مُجددًا، ولهذا يَنبغي استعادة مساحة الذاكرة المُخصَّصة للكائن لكي تُصبِح مُتاحة للاِستخدَام لأغراض آخرى.
</p>

<p>
	تَستخدِم الجافا إجراءً (procedure) يُعرَف باسم "كَنْس المُهملات (garbage collection)"؛ لاستعادة أجزاء الذاكرة التي كانت قد خُصِّصت للكائنات (objects)، والتي لَمْ يَعُدْ بإِمكان البرنامج الوصول إليها. يَتولَّى النظام -لا المبرمج- مسئولية تَعقُّب تلك الكائنات التي أَصبحت ضِمْن "المُهملات (garbage)". بالمثال السابق، تستطيع بسهولة أن تَرى أن الكائن قد أَصبح ضِمْن المُهملات، ولكن تَكُون الأمور في العادة أكثر تعقيدًا خاصة بَعْد اِستخدَام الكائن لفترة، فيُحتمَل عندها وجود عدة مَراجِع (references) تُشير إليه مُخزَّنة بعدة مُتْغيِّرات. لاحِظ أن الكائن لا يُعدّ جزءًا من المُهملات إلا بعد أن تُصبِح جميع مَراجِعه غَيْر مُتوفرة.
</p>

<p>
	تَقَع مسئولية حَذْف المُهملات على عاتق المبرمج بالكثير من اللغات البرمجية الأخرى. ولمّا كان تَعقُّب اِستخدَام الذاكرة يَنطوِي على الكثير من التعقيدات، فإنه عادةً ما يَتسبَّب بحُدوث الكثير من الأخطاء البرمجية (bugs) الخطيرة. أحد تلك الأخطاء هو خطأ المُؤشر المُعلّق (dangling pointer)، والذي يَحدُث عندما يَحذِف المبرمج عن طريق الخطأ كائنًا من الذاكرة ما يزال البرنامج يَملُك مَرجِعًا (references) إليه، مما يَتسبَّب بحُدوث مشاكل إذا حَاول البرنامج الوصول إلى كائن لم يَعُدْ موجودًا. نوع آخر من الأخطاء هو تَسريب الذاكرة (memory leak)، والذي يَحدُث عندما لا يهتم المبرمج بحَذْف الكائنات التي لم تَعُدْ مُستخدَمة، مما يُؤدي إلى مَلئ الذاكرة بكائنات غَيْر قابلة للوصول، ومِن ثَمَّّ قد يُعاني البرنامج من مشكلة نفاد الذاكرة مع أنها فقط مُهدرة في الواقع.
</p>

<p>
	يَستحِيل حُدوث مثل تلك الأخطاء بالجافا؛ لأنها تَعتمِد على إجراء كَنْس المُهملات (garbage collection)، وهو عمومًا فكرة قديمة اِستخدَمتها بعض اللغات البرمجية بدايةً من ستينيات القرن الماضي. لماذا إذًا لا تَستخدِمها جميع اللغات؟ لأنها في الماضي كانت بطيئة جدًا، ولكنها أَصبحت عملية بفضل العديد من الدراسات العلمية التي تناولت موضوع كَنْس المهملات عن كثب، بالإضافة إلى السرعة الفائقة للحواسيب الحديثة.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s2.html" rel="external nofollow">Section 2: Constructors and Object Initialization</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1109</guid><pubDate>Tue, 19 Jan 2021 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; (Objects) &#x648;&#x62A;&#x648;&#x627;&#x628;&#x639; &#x627;&#x644;&#x646;&#x633;&#x62E; (Instance Methods) &#x648;&#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x646;&#x633;&#x62E; (Instance Variables) &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;</title><link>https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-instance-methods-%D9%88%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-instance-variables-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1108/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_01/31.png.d3e2c440dece32dac80737a0b54cb561.png" /></p>

<p>
	تُعدّ البرمجة كائنية التوجه (object-oriented programming - <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr>) بمثابة محاولة لتَمْكِين البرامج من نمذجة طريقة تفكيرنا بالعالم الخارجي وتَعامُلنا معه. بالأنماط الأقدم من البرمجة، كان المبرمج مُضطرًّا لتَحْديد المُهِمّة الحاسوبية التي ينبغي له تّنْفيذها لكي يَحلّ المشكلة الفعليّة التي تُواجهه. ووفقًا لهذا التصور، فإن البرمجة تَتكوَّن من محاولة إيجاد متتالية التَعْليمَات المسئولة عن إنجاز تلك المُهِمّة. في المقابل، تُحاوِل البرمجة كائنية التوجه (object-oriented programming) العُثور على عدة كائنات (objects) تُمثِل كيانات (entities) حقيقية أو مُجرّدة مرتبطة بموضوع المشكلة (problem domain). تَملُك تلك الكيانات سلوكيات معينة (behavior)، كما تَتَضمَّن مجموعة من البيانات، وتستطيع أن تَتَفاعَل مع بعضها البعض. ووفقًا لهذا التصور، فإن البرمجة تَتكوَّن من عملية تصميم مجموعة الكائنات (objects) التي يُمكِنها محاكاة المشكلة المطلوب حَلّها. يجعل ذلك تصميم البرامج أكثر طبيعية، وأسهل في الكتابة والفهم عمومًا.
</p>

<p>
	يُمكِننا القول أن البرمجة كائنية التوجه (<abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr>) هي مجرد تَغيير بوجهات النظر؛ فبحَسَب مفاهيم البرمجة القياسية، قد نُفكِر بالكائن (object) على أساس أنه مُجرّد مجموعة من المُتْغيِّرات والبرامج الفرعية (subroutines) المسئولة عن معالجة تلك المُتْغيِّرات ضُمِّنت معًا بطريقة ما. بل حتى يُمكِننا اِستخدَام الأساليب كائنية التوجه (object-oriented) بأي لغة برمجية، ولكن بالطبع هنالك فرق كبير بين لغة تَسمَح بالبرمجة كائنية التوجه، وآخرى تُدعِّمها بشكل نَشِط. تُدعِّم اللغات البرمجية كائنية التوجه، مثل الجافا، عدة مِيزَات تَجعلها مختلفة عن أي لغة قياسية آخرى، ولكي نَستغِل تلك السمات والميزات بأكبر قدر مُمكن، ينبغي أن نُعيد توجيه تفكيرنا على نحو سليم.
</p>

<p>
	ملحوظة: عادةً ما يُستخدَم مصطلح "التوابع (methods)" للإشارة إلى البرامج الفرعية (subroutines) ضِمْن سياق البرمجة كائنية التوجه (object-oriented)، ولهذا سيَستخدِم هذا الفصل مصطلح "التوابع (method)" بدلًا من مصطلح "البرامج الفرعية (subroutine)".
</p>

<h2>
	الكائنات (objects) والأصناف (classes) والنسخ (instances)
</h2>

<p>
	لقد تحدثنا عن الأصناف (classes) خلال الفصول السابقة، واَتضح لنا أن الصَنْف يُمكِنه أن يَحتوِي على مُتْغيِّرات وتوابع (methods) يُشار إليها باسم البرامج الفرعية. سنَنتقل الآن إلى الحديث عما يُعرَف باسم الكائنات (objects)، والتي هي وثيقة الصلة بالأصناف. بدايةً، تَعَرَّضنا للكائنات (objects) مرة وحيدة من قَبْل خلال القسم ٣.٩، ولم نَلحظ خلالها فارقًا جوهريًا؛ فلقد اِستبعدنا فقط كلمة <code>static</code> من تعريف إحدى البرامج الفرعية (subroutine definition). في الواقع، يَتَكوَّن أي كائن من مجموعة من المُتْغيِّرات والتوابع، فكيف يختلف إذًا عن الأصناف (classes)؟ للإجابة على مثل هذا السؤال، ستحتاج إلى طريقة تفكير مختلفة نسبيًا وذلك حتى تُدرِك أهمية الكائنات وتَتَمكَّن من اِستخدَامها بطريقة فعالة.
</p>

<p>
	ذَكَرَنا أن الأصناف تُستخدَم لوصف الكائنات (objects)، أو بتعبير أدق، أن الأجزاء غَيْر الساكنة (non-static) من الأصناف تُستخدَم لوصف الكائنات. قد لا يَكُون المقصود من ذلك واضحًا بما فيه الكفاية، لذا دَعْنَا نُصيغه بطريقة آخرى والتي هي أكثر شيوعًا نوعًا ما. نستطيع أن نقول أن الكائنات تنتمي إلى أصناف (classes)، ولكن ليس بنفس الطريقة التي ينتمي بها مُتْغيِّر عضو معين (member variable) لصنف. فلربما من الأدق أن نقول أن الأصناف تُستخدَم لإنشاء كائنات، فالصَنْف (class) هو أَشْبه ما يَكُون بمصنع أو مُخطط أوَّليّ (blueprint) لإنشاء الكائنات بحيث تُحدِّد الأجزاء غَيْر الساكنة (non-static) منه مجموعة المُتْغيِّرات والتوابع التي يُفْترَض أن تَتَضمَّنها كائنات ذلك الصنف. لذا فإن أحد الاختلافات الجوهرية بين الأصناف والكائنات هو حقيقة أن الكائنات تُنشَئ وتُهدَم أثناء تَشْغِيل البرنامج، بل يُمكِن اِستخدَام صَنْف معين لإنشاء أكثر من كائن (object) بحيث يَكُون لها جميعًا نفس البنية.
</p>

<p>
	لنَفْترِض أن لدينا الصَنْف التالي، والذي يَحتوِي على عدة مُتْغيِّرات أعضاء ساكنة (static member variables). يُمكِننا أن نَستخدِمه لتَخْزِين معلومات عن مُستخدِم البرنامج مثلًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_7" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">UserData</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> age</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما يُحمِّل الحاسوب ذلك الصَنْف، فإنه سيُكرِّس جزءًا من الذاكرة له بحيث يَتَضمَّن ذلك الجزء مساحة لقيم المُتْغيِّرين <code>name</code> و <code>age</code>، أيّ سيَكُون هناك نسخة وحيدة فقط من كلا المُتْغيِّرين <code>UserData.name</code> و <code>UserData.age</code> ضِمْن البرنامج عمومًا. تُبيِّن الصورة التالية تَصوُّرًا للصَنْف داخل الذاكرة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="56447" href="https://academy.hsoub.com/uploads/monthly_2021_01/001Class_Userdata.png.f7d243cd350710abe5b522979a096cf4.png" rel=""><img alt="001Class_Userdata.png" class="ipsImage ipsImage_thumbnailed" data-fileid="56447" data-unique="dbijxzmw4" src="https://academy.hsoub.com/uploads/monthly_2021_01/001Class_Userdata.png.f7d243cd350710abe5b522979a096cf4.png"></a>
</p>

<p>
	تُعدّ المُتْغيِّرات الأعضاء الساكنة (static) جزءًا من تمثيل الصنف داخل الذاكرة، ولهذا تُستخدَم الأسماء الكاملة المُتضمِّنة لاسم الصَنْف ذاته مثل <code>UserData.name</code> و <code>UserData.age</code> للإشارة إليها. في المثال السابق، قررنا اِستخدَام الصنف <code>UserData</code> لتمثيل مُستخدِم البرنامج، ولمّا كان لدينا مساحة بالذاكرة لتَخْزِين البيانات المُتعلِّقة بمُستخدِم واحد فقط، فبطبيعة الحال سيَكُون البرنامج قادرًا على حَمْل بيانات مُستخدِم وحيد. لاحِظ أن الصنف <code>UserData</code> والمُتْغيِّرات الساكنة (static) المُعرَّفة بداخله تَظلّ موجودة طوال فترة تَشْغِيل البرنامج، وهذا هو المقصود بكَوْنها ساكنة بالأساس. لنَفْحَص الآن صَنْفًا مُشابهًا، ولكنه يَتَضمَّن بعضًا من المُتْغيِّرات غَيْر الساكنة (non-static):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_9" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">PlayerData</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> playerCount</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> age</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أُضيف إلى الصنف <code>PlayerData</code> مُتْغيِّر ساكن هو <code>playerCount</code> أيّ أن اسمه الكامل هو <code>PlayerData.playerCount</code>، كما أنه مُخزَّن كجزء من تمثيل الصَنْف بالذاكرة، وتَتَوَّفر منه نُسخة واحدة فقط تَظلّ موجودة طوال فترة تَّنْفيذ البرنامج. بالإضافة إلى ذلك، يَتَضمَّن تعريف الصَنْف (class definition) مُتْغيِّرين غَيْر ساكنين (non-static)، ولأن المُتْغيِّرات غَيْر الساكنة لا تُعدّ جزءًا من الصنف ذاته، فإنه لا يُمكِن الإشارة إلى هذين المُتْغيِّرين بالأسماء <code>PlayerData.name</code> أو <code>PlayerData.age</code>. نستطيع الآن اِستخدَام الصَنْف <code>PlayerData</code> لإنشاء كائنات بأيّ قدر نُريده، بحيث يَمتلك كل كائن منها مُتْغيِّراته الخاصة به والتي تَحمِل الأسماء <code>name</code> و <code>age</code>، أيّ يَحصُل كل كائن على نسخته الخاصة من أجزاء الصَنْف غَيْر الساكنة (non-static)، وهذا هو ما يَعنِيه القول بأن أجزاء الصَنْف غَيْر الساكنة تُعدّ بمثابة مُخططًا أوَّليًّا أو قالبًا (template) للكائنات. تُبيِّن الصورة التالية تصورًا لذاكرة الحاسوب بَعْد إِنشاء عدة كائنات:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="56448" href="https://academy.hsoub.com/uploads/monthly_2021_01/002Instance_Of_Player_Data.png.cfe7389eee0ce02a1f438fccd09cafd9.png" rel=""><img alt="002Instance_Of_Player_Data.png" class="ipsImage ipsImage_thumbnailed" data-fileid="56448" data-unique="zfl7o02fm" src="https://academy.hsoub.com/uploads/monthly_2021_01/002Instance_Of_Player_Data.png.cfe7389eee0ce02a1f438fccd09cafd9.png"></a>
</p>

<p>
	كما ترى بالأعلى، يُعدّ المُتْغيِّر الساكن <code>playerCount</code> جزءًا من الصنف وتَتَوفَّر منه نُسخة وحيدة، بينما تَتَوفَّر نُسخة خاصة من المُتْغيِّرين غَيْر الساكنين <code>name</code> و <code>age</code> بكل كائن (object) مُنشَئ من ذلك الصنف. يُعدّ أيّ كائن (object) نُسخة (instance) من صَنْفه، ويَكُون على دراية بالصَنْف المُستخدَم لإِنشائه. تُبيِّن الصورة أن الصَنْف <code>PlayerData</code> يَحتوِي على ما يُعرَف باسم "البَانِي أو بَانِي الكائن (constructor)". البَانِي ببساطة هو برنامج فرعي (subroutine) يُستخدَم لإِنشاء الكائنات سنتحدث عنه لاحقًا.
</p>

<p>
	ولأننا نستطيع إِنشاء كائنات (objects) جديدة من الصَنْف <code>PlayerData</code> لتَمْثيل لاعبين جُدد بقَدْر ما نَشَاء، فقد يُصبِح لدينا الآن مجموعة من اللاعبين. على سبيل المثال، قد يَستخدِم برنامج معين الصَنْف <code>PlayerData</code> لتَخْزين بيانات مجموعة لاعبين ضِمْن لعبة بحيث يَملُك كُلًا منهم <code>name</code> و <code>age</code> خاص به، وعندما يَنضَم لاعب جديد إلى اللعبة، يَستطيع البرنامج أن يُنشِئ كائنًا جديدًا من الصَنْف <code>PlayerData</code> لتمثيل ذلك اللاعب، أما إذا غادر أحد اللاعبين، يَستطيع البرنامج هَدْم الكائن المُمثِل لذلك اللاعب، أي سيَستخدِم ذلك البرنامج شبكة من الكائنات لنَمذجة أحداث اللعبة بصورة ديناميكية (dynamic)، وهو ما لا يُمكِنك القيام به باِستخدَام المُتْغيِّرات الساكنة (static).
</p>

<p>
	يُعدّ أيّ كائن (object) نُسخة (instance) من الصَنْف المُستخدَم لإِنشائه، ونقول أحيانًا بأنه يَنتمي إلى ذلك الصَنْف. يُطلَق اسم مُتْغيِّرات النُسخ (instance variables) على المُتْغيِّرات التي يَتَضمَّنها الكائن بينما يُطلَق اسم توابع النُسخ (instance methods) على التوابع (methods) -أو البرامج الفرعية- ضِمْن الكائن. فمثلًا، إذا اِستخدَمنا الصَنْف <code>PlayerData</code> المُعرَّف بالأعلى لإِنشاء كائن، فإن ذلك الكائن يُعدّ نُسخة (instance) من الصنف <code>PlayerData</code> كما يُعدّ كُلًا من <code>name</code> و <code>age</code> مُتْغيِّرات نُسخ (instance variable) ضِمْن الكائن.
</p>

<p>
	لا يَتَضمَّن المثال بالأعلى أيّ توابع (methods)، ولكنها تَعَمَل عمومًا بصورة مشابهة للمُتْغيِّرات أيّ تُعدّ التوابع الساكنة (static) جزءًا من الصَنْف ذاته بينما تُعدّ التوابع غَيْر الساكنة (non-static) أو توابع النُسخ (instance methods) جزءًا من الكائنات (objects) المُنشئة من ذلك الصَنْف. لكن، لا تَأخُذ ذلك بمعناه الحرفي؛ فلا يَحتوِي كل كائن على نُسخته الخاصة من شيفرة توابع النُسخ (instance method) المُعرَّفة ضِمْن الصَنْف بشكل فعليّ وإنما يُعدّ ذلك صحيحًا على نحو منطقي فقط، وعمومًا سنستمر بالإشارة إلى احتواء كائن معين على توابع نُسخ (instance method) صَنْفه.
</p>

<p>
	ينبغي عمومًا أن تُميز بين الشيفرة المصدرية (source code) للصَنْف والصَنْف ذاته بالذاكرة. تُحدِّد الشيفرة المصدرية كُلًا من هيئة الصَنْف وهيئة الكائنات المُنشئة منه: تُحدِّد التعريفات الساكنة (static definitions) بالشيفرة العناصر التي ستُصبِح جُزءًا من الصَنْف ذاته بذاكرة الحاسوب بينما تُحدِّد التعريفات غَيْر الساكنة (non-static definitions) بالشيفرة العناصر التي ستُصبِح جُزءًا من كل كائن يُنشَئ من ذلك الصنف. بالمناسبة، يُطلَق اسم مُتْغيِّرات الأصناف (class variables) وتوابع الأصناف (class methods) على المُتْغيِّرات الأعضاء (member variables) الساكنة والبرامج الفرعية الأعضاء (member subroutines) الساكنة المُعرَّفة ضِمْن الصنف على الترتيب؛ وذلك لكَوْنها تنتمي للصَنْف ذاته وليس لنُسخ (instances) ذلك الصَنْف.
</p>

<p>
	يَتَّضِح لنا الآن الكيفية التي تَختلِف بها الأجزاء الساكنة من صنف معين عن الأجزاء غَيْر الساكنة، حيث يَخدِم كُلًا منهما غرضًا مُختلفًا تمامًا. وفي العموم، ستَجِدْ أن كثيرًا من الأصناف إِما أنها تَحتوِي على أعضاء ساكنة (static) فقط أو على أعضاء غَيْر ساكنة (non-static) فقط، ومع ذلك سنَرى أمثلة قليلة لأصناف تَحتوِي على مَزيج منهما.
</p>

<h2>
	أساسيات الكائنات
</h2>

<p>
	اِقْتصَر حديثنا إلى الآن على الكائنات (objects) في العموم دون ذِكْر أيّ تفاصيل عن طريقة اِستخدَامها بصورة فعليّة ضِمْن برنامج، لذا سنَفْحَص مثالًا مُحدَّدًا لنوضح ذلك. لنَفْترِض أن لدينا الصَنْف <code>Student</code> المُعرَّف بالأسفل، والذي يُمكِننا اِستخدَامه لتَخْزِين بعض بيانات الطلبة المُلتحِقة بدورة تدريبية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_11" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Student</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">  </span><span class="com">// اسم الطالب</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> test1</span><span class="pun">,</span><span class="pln"> test2</span><span class="pun">,</span><span class="pln"> test3</span><span class="pun">;</span><span class="pln">   </span><span class="com">// الدرجة بثلاثة اختبارات</span><span class="pln">

   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> getAverage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="com">// احسب متوسط درجة الاختبارات</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">test1 </span><span class="pun">+</span><span class="pln"> test2 </span><span class="pun">+</span><span class="pln"> test3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">  </span><span class="com">// ‫نهاية الصنف Student</span></pre>

<p>
	لم يُستخدَم المُبدِّل <code>static</code> أثناء التَّصْريح عن أيّ من أعضاء الصَنْف <code>Student</code>، لذا فالصَنْف موجود فقط بغَرْض إِنشاء الكائنات (objects). يُحدِّد تعريف الصَنْف <code>Student</code> -بالأعلى- أن كائناته أو نُسخه (instance) ستَحتوِي على مُتْغيِّرات النُسخ (instance variables)‏ <code>name</code> و <code>test1</code> و <code>test2</code> و <code>test3</code> كما ستَحتوِي على تابع نسخة (instance method) هو <code>getAverage()‎</code>. سيَحتوِي كل كائن -يُمثِل طالبًا- في العموم على اسم طالب معين ودرجاته، وعندما يُستدعَى التابع <code>getAverage()‎</code> لكائن معين، ستُحسَب قيمة المتوسط باِستخدَام درجات الاختبار ضِمْن ذلك الكائن تَحْديدًا أيّ سيَحصُل كل كائن على قيمة متوسط مختلفة، وهذا هو في الواقع ما يَعنِيه انتماء توابع النُسخ (instance method) للكائنات المُفردة لا الأصناف نفسها.
</p>

<p>
	الأصناف بلغة الجافا هي أنواع تمامًا كالأنواع المَبنية مُسْبَقًا (built-in) مثل <code>int</code> و <code>boolean</code>، لذا تستطيع اِستخدَام اسم صَنْف معين لتَحْدِيد نوع مُتْغيِّر ضِمْن تَعْليمَة تَّصْريح (declaration statement) أو لتَخْصِيص نوع مُعامِل صُّوريّ (formal parameter) أو لتَحْدِيد نوع القيمة المُعادة (return type) من دالة (function). يُمكِن مثلًا تعريف مُتْغيِّر اسمه <code>std</code> من النوع <code>Student</code> باِستخدَام التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_13" style="">
<span class="typ">Student</span><span class="pln"> std</span><span class="pun">;</span></pre>

<p>
	انتبه للنقطة الهامة التالية، لا يُؤدي تَّصْرِيحك (declare) عن مُتْغيِّر نوعه عبارة عن صنف إلى إِنشاء الكائن ذاته! تَرتبط هذه النقطة بالحقيقتين التاليتين: لا يُمكِن لأيّ مُتْغيِّر أن يَحمِل كائنًا. يستطيع المُتْغيِّر أن يَحمِل مَرجِعًا (reference) يُشير إلى كائن.
</p>

<p>
	يُفضَّل أن تُفكِر بالكائنات (objects) كما لو أنها تَعيش مُستقلة بذاكرة الحاسوب، تحديدًا بقسم الكَوْمة (heap) من الذاكرة. لا تَحمِل المُتْغيِّرات كائنية النوع (object type) قيم الكائنات نفسها وإنما تَحمِل المعلومات الضرورية للعُثور على تلك الكائنات بالذاكرة، تَحْديدًا تَحمِل عناوين مَواضِعها (location address) بالذاكرة، ويُقال عندها أن المُتْغيِّر يَحمِل مَرجِعًا (reference) أو مُؤشرًا (pointer) إلى الكائن. عندما تَستخدِم مُتْغيِّر كائني النوع (object type)، يَستخدِم الحاسوب المَرجِع (reference) المُخْزَّن بالمُتْغيِّر للعُثور على قيمة الكائن الفعليّة.
</p>

<p>
	يُنشِئ العَامِل (operator)‏ <code>new</code> كائنًا جديدًا (object) حيث يَستدعِى برنامجًا فرعيًا -ذكرناه سابقًا- يقع ضِمْن الصَنْف يُطلَق عليه اسم البَانِي (constructor)، ثم يُعيد مَرجِعًا (reference) يُشير إلى الكائن المُنشَىء. لنَفْترِض مثلًا أن لدينا مُتْغيِّر <code>std</code> من النوع <code>Student</code> المُصرَّح عنه بالأعلى، يُمكِننا الآن كتابة تَعْليمَة الإِسْناد التالية (assignment statement):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_15" style="">
<span class="pln">std </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">();</span></pre>

<p>
	تُنشِئ تَعْليمَة الإِسْناد -بالأعلى- كائنًًا (object) جديدًا بقسم الكَوْمة (heap) من الذاكرة، والذي يُعدّ نُسخة (instance) من الصَنْف <code>Student</code>، ثم تُخزِّن التَعْليمَة مَرجِعًا (reference) إلى ذلك الكائن بالمُتْغيِّر <code>std</code> أيّ أن قيمة المُتْغيِّر عبارة عن مَرجِع (reference) أو مُؤشر (pointer) إلى الكائن الواقع بمكان ما. لا يَصِح في العموم أن تقول "قيمة المُتْغيِّر <code>std</code> هي الكائن ذاته" على الرغم من صعوبة تَجنُّب ذلك أحيانًا، ولكن لا تَقل أبدًا أن "الكائن مُخزَّن بالمُتْغيِّر <code>std</code>" فهو أمر غَيْر صحيح على الإطلاق. يُفْترَض عمومًا أن تقول "يُشير المُتْغيِّر <code>std</code> إلى الكائن"، وهو ما سنُحاول الالتزام به قَدْر الإِمكان. لاحِظ أنه إذا ذَكَرَنا مثلًا أن "<code>std</code> هو كائن"، فالمقصود هو "<code>std</code> هو مُتْغيِّر يُشيِر إلى كائن".
</p>

<p>
	لنَفْترِض أن المُتْغيِّر <code>std</code> يُشير إلى كائن (object) عبارة عن نُسخة (instance) من الصَنْف <code>Student</code> أيّ يَحتوِي ذلك الكائن بطبيعة الحال على مُتْغيِّرات النُسخ <code>name</code> و <code>test1</code> و <code>test2</code> و <code>test3</code>. يُمكِننا الإشارة إلى تلك المُتْغيِّرات باِستخدَام <code>std.name</code> و <code>std.test1</code> و <code>std.test2</code> و <code>std.test3</code> على الترتيب. يَتَّبِع ذلك نفس نَمْط التسمية المُعتاد بأنه عندما يَكُون <code>B</code> جزءًا من <code>A</code>، فإن اسم <code>B</code> الكامل هو <code>A.B</code>. اُنظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_17" style="">
<span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Hello, "</span><span class="pln">  </span><span class="pun">+</span><span class="pln">  std</span><span class="pun">.</span><span class="pln">name  </span><span class="pun">+</span><span class="pln">  </span><span class="str">".  Your test grades are:"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">std</span><span class="pun">.</span><span class="pln">test1</span><span class="pun">);</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">std</span><span class="pun">.</span><span class="pln">test2</span><span class="pun">);</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">std</span><span class="pun">.</span><span class="pln">test3</span><span class="pun">);</span></pre>

<p>
	ستَطبَع الشيفرة بالأعلى الاسم وقيم الدرجات بالكائن المُشار إليه باِستخدَام المُتْغيِّر <code>std</code>. بنفس الأسلوب، يَحتوِي ذلك الكائن على تابع نسخة هو <code>getAverage()‎</code>، والذي يُمكِننا استدعائه باِستخدَام <code>std.getAverage()‎</code>. يُمكِنك كتابة الشيفرة التالية لطباعة مُتوسط درجات الطالب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_19" style="">
<span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="str">"Your average is "</span><span class="pln">  </span><span class="pun">+</span><span class="pln">  std</span><span class="pun">.</span><span class="pln">getAverage</span><span class="pun">()</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	تستطيع عمومًا اِستخدَام <code>std.name</code> أينما أَمَكَن اِستخدَام مُتْغيِّر من النوع <code>String</code>. فمثلًا، قد تَستخدِمه ضِمْن تعبير (expression) أو قد تُسنِد قيمة إليه أو قد تَستخدِمه حتى لاستدعاء أحد البرامج الفرعية (subroutines) المُعرَّفة بالصَنْف <code>String</code>. فمثلًا، يُمثِل التعبير <code>std.name.length()‎</code> عدد المحارف باسم الطالب.
</p>

<p>
	قد لا تُشير مُتْغيِّرات الكائنات (object variables) أيّ تلك التي نوعها هو عبارة عن صَنْف -مثل المُتْغيِّر <code>std</code>- إلى أيّ كائن نهائيًا، وفي تلك الحالة، يُقال أنها تَحمِل مُؤشرًا فارغًا (null pointer) أو مَرجِعًا فارغًا (null reference). يُكْتَب المُؤشر الفارغ (null pointer) باِستخدَام كلمة <code>null</code>، ولهذا تستطيع اِستخدَام الشيفرة التالية لتَخْزِين مَرجِع فارغ بالمُتْغيِّر <code>std</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_21" style="">
<span class="pln">std </span><span class="pun">=</span><span class="pln"> null</span><span class="pun">;</span></pre>

<p>
	في المثال بالأعلى، لا يَحتوِي المُتْغيِّر على مُؤشر لأيّ شيء آخر وإنما تُعدّ <code>null</code> هي قيمته الفعليّة، لذا لا يَصِح أن تقول: "يُشير المُتْغيِّر إلى القيمة الفارغة (null)"، فالمُتْغيِّر نفسه هو قيمة فارغة. تستطيع اختبار ما إذا كانت قيمة المُتْغيِّر <code>std</code> فارغة باِستخدَام التالي:
</p>

<pre class="ipsCode">
if (std == null) . . .
</pre>

<p>
	عندما تَكُون قيمة مُتْغيِّر كائن (object variable) فارغة أيّ تَحتوِي على <code>null</code>، يَعنِي ذلك أن المُتْغيِّر لا يُشير إلى أيّ كائن، وبالتالي لا يَكُون هناك أيّ مُتْغيِّرات نُسخ (instance variables) أو توابع نُسخ (instance methods) يُمكِن الإشارة إليها، ويَكُون من الخطأ محاولة القيام بذلك عبر ذاك المُتْغيِّر. فمثلًا، إذا كانت قيمة المُتْغيِّر <code>std</code> هي <code>null</code>، وحَاوَل برنامج معين الإشارة إلى <code>std.test1</code>، سيُؤدِي ذلك إلى محاولة اِستخدَام مؤشر فارغ (null pointer) بطريقة خاطئة، ولهذا سيُبلَّغ عن اِعتراض مُؤشِر فارغ (null pointer exception) من النوع <code>NullPointerException</code> أثناء تّنْفيذ البرنامج.
</p>

<p>
	لنَفْحَص مُتتالية التَعْليمَات التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_23" style="">
<span class="typ">Student</span><span class="pln"> std</span><span class="pun">,</span><span class="pln"> std1</span><span class="pun">,</span><span class="pln">       </span><span class="com">// ‫صرح عن أربعة متغيرات من النوع Student</span><span class="pln">
          std2</span><span class="pun">,</span><span class="pln"> std3</span><span class="pun">;</span><span class="pln">    

</span><span class="com">// ‫أنشئ كائن جديد من الصنف Student وخزن مرجعه بالمتغير std</span><span class="pln">
std </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">();</span><span class="pln">     

</span><span class="com">// ‫أنشئ كائن آخر من الصنف Student وخزن مرجعه بالمتغير std1</span><span class="pln">
std1 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">();</span><span class="pln">    

</span><span class="com">// ‫انسخ مرجع std1 إلى المتغير std2</span><span class="pln">
std2 </span><span class="pun">=</span><span class="pln"> std1</span><span class="pun">;</span><span class="pln">             

</span><span class="com">// ‫خزن مؤشرا فارغا بالمتغير std3</span><span class="pln">
std3 </span><span class="pun">=</span><span class="pln"> null</span><span class="pun">;</span><span class="pln">             

</span><span class="com">// اضبط قيم متغيرات الأعضاء</span><span class="pln">
std</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John Smith"</span><span class="pun">;</span><span class="pln"> 
std1</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Mary Jones"</span><span class="pun">;</span></pre>

<p>
	بعدما يُنفِّذ الحاسوب التَعْليمَات بالأعلى، ستُصبِح ذاكرة الحاسوب كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="56449" href="https://academy.hsoub.com/uploads/monthly_2021_01/003Objects_In_Heap.png.c7bd3728df01de27f1bb16d4e03617a7.png" rel=""><img alt="003Objects_In_Heap.png" class="ipsImage ipsImage_thumbnailed" data-fileid="56449" data-unique="awi2pogef" src="https://academy.hsoub.com/uploads/monthly_2021_01/003Objects_In_Heap.png.c7bd3728df01de27f1bb16d4e03617a7.png"></a>
</p>

<p>
	يَتَبين لنا التالي بَعْد فَحْص الصورة بالأعلى: أولًا، يَحتوِي أيّ مُتْغيِّر على مَرجِع (reference) إلى كائن ظَهَرَت قيمته ضِمْن الصورة بهيئة سهم يُشير إلى الكائن (object). ثانيًا، يُمكِننا أن نَرَى أن السَلاسِل النصية من النوع <code>String</code> هي كائنات. ثالثًا، لا يُشير المُتْغيِّر <code>std3</code> إلى أيّ مكان؛ لأنه فارغ ويَحتوِي على القيمة <code>null</code>. وأخيرًا، تُشير الأسهم من <code>std1</code> و <code>std2</code> إلى نفس ذات الكائن وهو ما يُبرِز النقطة المهمة التالية: عندما نُسنِد مُتْغيِّر كائن (object variable) إلى آخر، يُنسَخ المَرجِع (reference) فقط لا الكائن (object) المُشار إليه بواسطة ذلك المَرجِع.
</p>

<p>
	لنَأخُذ مثلًا التَعْليمَة <code>std2 = std1;‎</code> كمثال، لمّا كانت تَعْليمَة الإِسْناد (assignment statement) في العموم تَنسَخ فقط القيمة المُخْزَّنة بمُعاملها الأيمن <code>std1</code> إلى مُعاملها الأيسر <code>std2</code>، ولأن القيمة في تلك الحالة هي مُجرَّد مُؤشِر (pointer) إلى كائن وليست الكائن (object) ذاته، فإن تلك التَعْليمَة لم تُنشِئ كائنًا جديدًا، وإنما ضَبَطَت <code>std2</code> بحيث يُشير إلى نفس الكائن الذي يُشير إليه <code>std1</code>. يَترتَب على ذلك عدة نتائج قد تَكُون مفاجئة بالنسبة لك. مثلًا، تُعدّ كُلًا من <code>std1.name</code> و <code>std2.name</code> أسماءً مختلفة لنفس مُتْغيِّر النُسخة (instance variable) المُعرَّف ضِمْن الكائن الذي يُشير إليه كِلا المُتْغيِّرين <code>std1</code> و <code>std2</code>، ولهذا عندما تُسنِد السِلسِلة النصية "Mary Jones" مثلًا إلى <code>std1.name</code>، ستُصبِح قيمة <code>std2.name</code> هي أيضًا "Mary Jones". إذا التبس عليك الأمر، حَاوِل فقط أن تَستحضر الحقيقة التالية: "المُتْغيِّر ليس هو الكائن (object)، فالأول يَحمِل فقط مُؤشرًا (pointer) إلى الثاني."
</p>

<p>
	يُستخدَم العَامِلان <code>==</code> و <code>‎!=‎</code> لاختبار تَساوِي كائنين أو عدم تَساوِيهما على الترتيب، ولكن لاحِظ أن المَعنَى الدلالي (semantics) وراء تلك الاختبارات مُختلف عما أنت مُعتاد عليه. فمثلًا، يَفْحَص الاختبار <code>if (std1 == std2)‎</code> ما إذا كانت القيم المُخْزَّنة بكُلًا من <code>std1</code> و <code>std2</code> مُتساوية، ولأن تلك القيم هي مُجرَّد مَراجِع (references) تُشير إلى كائنات وليست الكائنات ذاتها، فإن ذلك الاختبار يَفْحَص ما إذا كان <code>std1</code> و <code>std2</code> يُشيران إلى نفس ذات الكائن أيّ ما إذا كانا يُشيران إلى نفس المَوضِع بالذاكرة. لا يُعدّ ذلك مشكلة إذا كان هذا هو ما تُريد اختباره، لكن أحيانًا يَكُون المقصود هو مَعرِفة ما إذا كانت مُتْغيِّرات النُسخ (instance variables) تَحمِل نفس القيم بغَضْ النظر عن انتمائها لنفس ذات الكائن. في تلك الحالات، ستحتاج إلى إجراء الاختبار التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_25" style="">
<span class="pln">std1</span><span class="pun">.</span><span class="pln">test1 </span><span class="pun">==</span><span class="pln"> std2</span><span class="pun">.</span><span class="pln">test1 
    </span><span class="pun">&amp;&amp;</span><span class="pln"> std1</span><span class="pun">.</span><span class="pln">test2 </span><span class="pun">==</span><span class="pln"> std2</span><span class="pun">.</span><span class="pln">test2 
    </span><span class="pun">&amp;&amp;</span><span class="pln"> std1</span><span class="pun">.</span><span class="pln">test3 </span><span class="pun">==</span><span class="pln"> std2</span><span class="pun">.</span><span class="pln">test3 
    </span><span class="pun">&amp;&amp;</span><span class="pln"> std1</span><span class="pun">.</span><span class="pln">name</span><span class="pun">.</span><span class="pln">equals</span><span class="pun">(</span><span class="pln">std2</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span></pre>

<p>
	ذَكَرَنا مُسْبَقًا أن السَلاسِل النصية من النوع <code>String</code> هي بالأساس كائنات، كما أَظَهَرنا السِلسِلتين النصيتين "Mary Jones" و "John Smith" بهيئة كائنات بالصورة التوضيحية بالأعلى ولكن بدون البنية الداخلية للنوع <code>String</code>. تُعدّ تلك السِلاسِل كائنات مميزة، وتُعامَل وفقًا لقواعد صيغة خاصة. كأيّ مُتْغيِّر كائن (object variable)، تَحمِل المُتْغيِّرات من النوع <code>String</code> مَرجِعًا (reference) إلى سِلسِلة نصية وليس السِلسِلة النصية ذاتها. بُناءً على ذلك، لا يَصِح عادةً اِستخدَام العَامِل <code>==</code> لاختبار تَساوِي السَلاسِل النصية. فمثلًا، إذا كان لدينا مُتْغيِّر من النوع <code>String</code> اسمه هو <code>greeting</code> ويُشير إلى السِلسِلة النصية "Hello"، فهل يَكُون الاختبار <code>greeting == "Hello"‎</code> مُتحقِّقًا؟ في الواقع، يُشير كُلًا من المُتْغيِّر <code>greeting</code> والسِلسِلة النصية المُجرَّدة "Hello" إلى سِلسِلة نصية تَحتوِي على المحارف H-e-l-l-o. ومع ذلك، فإنهما قد يَكُونان كائنين مختلفين صَدَفَ فقط احتوائهما على نفس المحارف، ولأن ذلك التعبير يَختبِر ما إذا كان <code>greeting</code> و "Hello" يَحتوِيان على نفس المحارف، وكذلك ما إذا كانا واقْعين بنفس مَوضِع الذاكرة، فإن الاختبار <code>greeting == "Hello"‎</code> لا يُعدّ مُتحقِّقًا. في المقابل، تَقْتصِر الدالة <code>greeting.equals("Hello")‎</code> على اختبار ما إذا كان <code>greeting</code> و "Hello" يَحتوِيان على نفس المحارف وهو عادةً ما تَرغَب باختباره. أخيرًا، يُمكِن للمُتْغيِّرات من النوع <code>String</code> أن تَكُون فارغة أيّ تحتوي على القيمة <code>null</code>، وفي تلك الحالة، سيَكُون اِستخدَام العَامِل <code>==</code> مُناسبًا لاختبار ما إذا كان المُتْغيِّر فارغًا كالتالي <code>greeting == null</code>. ذَكَرَنا أكثر من مرة أن مُتْغيِّرات الكائنات لا تَحمِل الكائنات (objects) ذاتها، وإنما تَحمِل مَراجِع (references) تُشير إلى تلك الكائنات، وهو ما يَترتَب عليه بعض من النتائج الآخرى التي ينبغي أن تَكُون على دراية بها والتي ستَجِدها منطقية إذا استحضرت بذهنك الحقيقة التالية: "لا تُخْزَّن الكائنات (object) بالمُتْغيِّرات، وإنما بأماكن آخرى تُشير إليها المُتْغيِّرات". النتائج كالتالي:
</p>

<p>
	أولًا، لنَفْترِض أن لدينا مُتْغيِّر كائن (object variable) صُرِّح عنه باِستخدَام المُبدِّل <code>final</code> أي لا يُمكِن تَغْيير القيمة المُخْزَّنة بذلك المُتْغيِّر نهائيًا بَعْد تهيئته مبدئيًا (initialize)، ولأن تلك القيمة هي مَرجِع (reference) يُشير إلى كائن، فإن المُتْغيِّر سيَستمر بالإشارة إلى نفس الكائن طالما كان موجودًا. مع ذلك، يُمكِن تَعْديل البيانات المُخْزَّنة بالكائن لأن المُتْغيِّر هو ما صُرِّح عنه باِستخدَام المُبدِّل <code>final</code> وليس الكائن نفسه، لذا يُمكِن كتابة الآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_27" style="">
<span class="pln">final </span><span class="typ">Student</span><span class="pln"> stu </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">();</span><span class="pln">

stu</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John Doe"</span><span class="pun">;</span></pre>

<p>
	ثانيًا، لنَفْترِض أن <code>obj</code> هو مُتْغيِّر يُشير إلى كائن، ماذا سيَحدُث عند تمرير <code>obj</code> كمُعامِل فعليّ (actual parameter) إلى برنامج فرعي (subroutine)؟ ببساطة، ستُسنَد قيمة <code>obj</code> إلى مُعامِل صُّوريّ (formal parameter)، ثم سيُنفَّذ البرنامج الفرعي، ولأن البرنامج الفرعي ليس لديه سوى نُسخة من قيمة المُتْغيِّر <code>obj</code>، فإنه لن يَستطيع تَغْيير القيمة المُخْزَّنة بالمُتْغيِّر الأصلي. ومع ذلك، لمّا كانت تلك القيمة هي مَرجِع (reference) إلى كائن، سيَتَمكَّن البرنامج الفرعي من تَعْديل البيانات المُخْزَّنة بالكائن. أي أنه وبانتهاء البرنامج الفرعي، فحتمًا ما يزال المُتْغيِّر <code>obj</code> يُشير إلى نفس الكائن، ولكن البيانات المُخزَّنة بالكائن ربما تَكُون قد تَغيَّرت. لنَفْترِض أن لدينا مُتْغيِّر <code>x</code> من النوع <code>int</code>، اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_29" style="">
<span class="kwd">void</span><span class="pln"> dontChange</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> z</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">17</span><span class="pun">;</span><span class="pln">                                 
dontChange</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln">                          
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln">  </span><span class="com">// output the value 17.            </span></pre>

<p>
	لاحظ أن البرنامج الفرعي لم يَتَمكَّن من تَغْيير قيمة <code>x</code>، وهو ما يُكافئ التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_31" style="">
<span class="pln">z </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">                                 
z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span></pre>

<p>
	والآن، لنَفْترِض أن لدينا مُتْغيِّر <code>stu</code> من النوع <code>Student</code>، اُنظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_33" style="">
<span class="kwd">void</span><span class="pln"> change</span><span class="pun">(</span><span class="typ">Student</span><span class="pln"> s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    s</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Fred"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

stu</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Jane"</span><span class="pun">;</span><span class="pln">
change</span><span class="pun">(</span><span class="pln">stu</span><span class="pun">);</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln">stu</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// output the value "Fred".</span></pre>

<p>
	في حين لم تَتَغيَّر قيمة <code>stu</code>، تَغيَّرت قيمة <code>stu.name</code>، وهو ما يُكافئ كتابة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_35" style="">
<span class="pln">s </span><span class="pun">=</span><span class="pln"> stu</span><span class="pun">;</span><span class="pln">
s</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Fred"</span><span class="pun">;</span></pre>

<h2>
	الضوابط (setters) والجوالب (getters)
</h2>

<p>
	ينبغي أن تُراعِي مسألة التَحكُّم بالوصول (access control) عند كتابة أصناف (classes) جديدة، فتَّصْريحك عن كَوْن العضو (member) عامًا (public) يَجعله قابلًا للوصول من أي مكان بما في ذلك الأصناف الآخرى، في حين أن تَّصْريحك عن كَوْنه خاصًا (private) يجعل اِستخدَامه مَقْصورًا على الصَنْف المُعرَّف بداخله فقط.
</p>

<p>
	يُفضَّل عمومًا التَّصْريح عن غالبية المُتْغيِّرات الأعضاء (member variables) -إن لم يَكُن كلها- على أساس كَوْنها خاصة (private)؛ حتى يَكُون بإمكانك التَحكُّم بما يُمكِن القيام به بتلك المُتْغيِّرات تَحكُّمًا كاملًا. بَعْد ذلك، إذا أردت أن تَسمَح لأصناف آخرى بالوُصول لقيمة أحد تلك المُتْغيِّرات المُصرَّح عنها بكَوْنها خاصة، فيُمكِنك ببساطة كتابة تابع وُصول عام (public accessor method) يُعيد قيمة ذلك المُتْغيِّر. على سبيل المثال، إذا كان لديك صَنْف يَحتوِي على مُتْغيِّر عضو (member variable) خاص اسمه <code>title</code> من النوع <code>String</code>، تستطيع كتابة التابع (method) التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_37" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getTitle</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> title</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعيد التابع -بالأعلى- قيمة المُتْغيِّر العضو <code>title</code>. اُصطلح على تسمية توابع الوصول (accessor methods) للمُتْغيِّرات وفقًا لنمط معين بحيث يَتكوَّن الاسم من كلمة "get" مَتبوعة باسم المُتْغيِّر بعد تَكْبير حُروفه الأولى (capitalizing)، ولهذا يَتكوَّن اسم تابع وصول (accessor method) المُتْغيِّر <code>title</code> -بالأعلى- من الكلمتين "get" و "Title" أيّ يُصبح الاسم <code>getTitle()‎</code>. ولهذا يُطلَق عادةً على توابع الوصول اسم توابع الجَلْب (getter methods)، لأنها تُوفِّر "تَّصْريح قراءة (read access)" للمُتْغيِّرات. انتبه أنه في حالة المُتْغيِّرات من النوع <code>boolean</code>، تُستخدَم عادةً كلمة "is" بدلًا من "get" أيّ أنه إذا كان لدينا مُتْغيِّر عضو من النوع <code>boolean</code> اسمه <code>done</code>، فإن اسم جَالِبه (getter) قد يَكُون <code>isDone()‎</code>.
</p>

<p>
	قد تحتاج أيضًا إلى تَوْفِير "تصريح كتابة (write access)" لمُتْغيِّر خاص (private)؛ حتى تَتَمكَّن الأصناف الآخرى من تَخْصيص قيم جديدة لذلك المُتْغيِّر. تُستخدَم توابع الضَبْط (setter method) -أو تسمى أحيانًا بتوابع التَعْدِيل (mutator method)- لهذا الغرض، وبالمثل من توابع الجَلْب (getter methods)، ينبغي أن يَتكوَّن اسم تابع ضَبْط (setter method) مُتْغيِّر معين من كلمة "set" مَتبوعة باسم ذلك المُتْغيِّر بعد تَكْبير حُروفه الأولى. بالإضافة إلى ذلك، لابُدّ أن يَستقبِل تابع الضَبْط مُعامِلًا (parameter) من نفس نوع المُتْغيِّر. يُمكِن كتابة تابع ضْبْط (setter method) المُتْغيِّر <code>title</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_39" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> setTitle</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> newTitle </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   title </span><span class="pun">=</span><span class="pln"> newTitle</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستحتاج غالبًا إلى كتابة كُلًا من تابعي الجَلْب (getter method) والضَبْط (setter method) لمُتْغيِّر عضو خاص معين؛ حتى تَتَمكَّن الأصناف الآخرى من رؤية قيمة ذلك المُتْغيِّر والتَعْدِيل عليها كذلك. قد تتساءل، لما لا نُصرِّح عن المُتْغيِّر على أساس كَوْنه عامًا (public) من البداية؟ قد يَكُون ذلك منطقيًا إذا كانت الجوالب (getters) والضوابط (setters) مُقْتصرة على قراءة قيم المُتْغيِّرات وكتابتها، ولكنها في الحقيقة قادرة على القيام بأيّ شيء آخر. فمثلًا، يستطيع تابع جَلْب (getter method) مُتْغيِّر معين أن يَحتفظ بعَدَدَ مرات قرائته كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_41" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getTitle</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    titleAccessCount</span><span class="pun">++;</span><span class="pln">  </span><span class="com">// Increment member variable titleAccessCount.</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> title</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما يستطيع تابع ضَبْط (setter method) مُتْغيِّر آخر أن يَفْحص القيمة المطلوب إِسْنادها إليه لتَحْديد ما إذا كانت صالحة أم لا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_43" style="">
<span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> setTitle</span><span class="pun">(</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> newTitle </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> newTitle </span><span class="pun">==</span><span class="pln"> null </span><span class="pun">)</span><span class="pln">   </span><span class="com">// لا تسمح بسَلاسِل نصية فارغة</span><span class="pln">
      title </span><span class="pun">=</span><span class="pln"> </span><span class="str">"(Untitled)"</span><span class="pun">;</span><span class="pln">  </span><span class="com">// استخدم قيمة افتراضية</span><span class="pln">
   </span><span class="kwd">else</span><span class="pln">
      title </span><span class="pun">=</span><span class="pln"> newTitle</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُفضَّل عمومًا كتابة تابعي ضَبْط (setter method) وجَلْب (getter method) المُتْغيِّر حتى لو وَجَدَت أن دورهما مُقْتصِر على قراءة قيمة المُتْغيِّر وكتابتها، فأنت لا تدري، ربما تُغيِّر رأيك مُستقبلًا بينما تُعيد تصميم ذلك الصنف أو تُحسنه. ولأن تابعي الضبط والجلب جزء من الواجهة العامة (public interface) للصنف بعكس المُتْغيِّرات الأعضاء (member variable) الخاصة، فستستطيع ببساطة أن تُعدِّل تَّنْفيذ (implementations) تلك التوابع -هذا إذا كنت قد اِستخدَمتها منذ البداية- بدون تَغْيير الواجهة العامة للصَنْف وبدون أن تُؤثِر على أيّ أصناف أُخرى كانت قد اِستخدَمت ذلك الصَنْف. أما إذا لَمْ تَكُن قد اِستخدَمتها وتُريد ذلك الآن، فستَضطرّ آسفًا إلى التَواصُل مع كل شخص صَدَف وأن اِستخدَم الصَنْف؛ لتُبلِّغه بأن عليه فقط أن يَتَعقَّب كل مَوضِع بشيفرته يَستخدِم فيه ذلك المُتْغيِّر ويُعدِّله بحيث يَستخدِم تابعي الضَبْط (setter method) والجَلْب (getter method) للمُتْغيِّر العضو بدلًا من اسمه.
</p>

<p>
	كملاحظة أخيرة: تَشترِط بعض الخصائص المُتقدمة بلغة الجافا تَسمية توابع الضَبْط (setter methods) والجَلْب (getter methods) وفقًا للنَمْط المذكور بالأعلى، لهذا ينبغي في العموم أن تَتَّبِعه بصرامة. انتبه أيضًا للتالي: في حين أننا قد نَاقشنا تابعي الضَبْط والجَلْب ضِمْن سياق المُتْغيِّرات الأعضاء، فإنه في الواقع يُمكِن تَعْرِيف كُلًا منهما حتى في حالة عدم وجود مُتْغيِّر. يُعرِّف تابعي الضَبْط والجَلْب بالأساس خاصية (property) ضِمْن الصَنْف، والتي قد يُناظرها مُتْغيِّر أو لا. بتعبير آخر، إذا كان لديك صَنْف يَحتوِي على تابع نسخة (instance method) مُعرَّف باِستخدَام المُبدِّلات <code>public void</code>، وبَصمته (signature) هي <code>setValue(double)‎</code>، فإن ذلك الصَنْف حتمًا يَمتلك "خاصية (property)" اسمها <code>value</code> من النوع <code>double</code>، وذلك بغض النظر عن وجود مُتْغيِّر عضو (member variable) يَحمِل الاسم <code>value</code> ضِمْن ذلك الصَنْف من عدمه.
</p>

<h2>
	المصفوفات والكائنات
</h2>

<p>
	ذَكَرنا بالقسم الفرعي ٣.٨.١ أن المصفوفات (arrays) هي بالأساس كائنات أو ربما كائنات مميزة -تمامًا كالسَلاسِل النصية من النوع <code>String</code>-، وتُكْتَب وفقًا لقواعد صيغة (syntax) خاصة. فمثلًا تَتَوفَّر نسخة خاصة من العَامِل <code>new</code> لإنشاء المصفوفات. كأيّ مُتْغيِّر كائن (object variable)، لا يَحمِل أيّ مُتْغيِّر مصفوفة (array variable) قيمة مصفوفة فعليّة وإنما يَحمِل مرجعًا (reference) يُشير إلى كائن مصفوفة (array object) والذي يَكُون مُْخْزَّنًا بجزء المَكْدَس (heap) من الذاكرة. يُمكِن أيضًا لمُتْغيِّر مصفوفة أن يَحمِل القيمة <code>null</code> في حالة عدم وجود مصفوفة فعليّة. لاحظ أن كل نوع مصفوفة (array type) كالنوع <code>int[]‎</code> أو النوع <code>String[]‎</code> يُقابله صَنْف (class).
</p>

<p>
	فلنَفْترِض أن <code>list</code> هو مُتْغيِّر من النوع <code>int[]‎</code>. إذا كان ذلك المُتْغيِّر يَحمِل القيمة <code>null</code>، فسيَكُون من الخطأ أن تحاول قراءة <code>list.length</code> أو قراءة أيّ من عناصر المصفوفة (array element)‏ <code>list‎</code>، وسيَتَسبَّب بحُدوث اعتراض (exception) من النوع <code>NullPointerException</code>. بفَرْض أن <code>newlist</code> هو مُتْغيِّر آخر من نفس النوع <code>int[]‎</code>، اُنظر التَعْليمَة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_45" style="">
<span class="pln">newlist </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span></pre>

<p>
	يَقْتصِر دور تَعْليمَة الإِسْناد (assignment statement) بالأعلى على نَسْخ قيمة المَرجِع (reference) المُخْزَّنة بالمُتْغيِّر <code>list</code> فقط إلى <code>newlist</code>. إذا كان المُتْغيِّر <code>list</code> يَحمِل القيمة <code>null</code>، فسيَحمِل المُتْغيِّر <code>newlist</code> القيمة <code>null</code> أيضًا أما إذا كان المُتْغيِّر <code>list</code> يُشير إلى مصفوفة، فلن تَنسَخ تَعْليمَة الإِسْناد تلك المصفوفة وإنما ستَضبُط المُتْغيِّر <code>newlist</code> فقط بحيث يُشير إلى نفس المصفوفة التي يُشير إليها المُتْغيِّر <code>list</code>. اُنظر الشيفرة التالية كمثال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_47" style="">
<span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span><span class="pln">
</span><span class="typ">list</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">17</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ‫تشير newlist إلى نفس المصفوفة التي يشير إليها list</span><span class="pln">
newlist </span><span class="pun">=</span><span class="pln"> </span><span class="typ">list</span><span class="pun">;</span><span class="pln"> 
newlist</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln">
</span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="pln"> </span><span class="typ">list</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	لأن <code>list[1]‎</code> و <code>newlist[1]‎</code> هي مجرد أسماء مختلفة لنفس عنصر المصفوفة، فسيَكُون خَرْج الشيفرة بالأعلى هو 42 وليس 17. قد تَجِدْ كل تلك التفاصيل مُربكة بالبداية، ولكن بمُجرَّد أن تَستحضِر بذهنك أن "أي مصفوفة هي عبارة عن كائن (object) وأن أيّ مُتْغيِّر مصفوفة (array variables) هو فقط يَحمِل مؤشرًا (pointers) إلى مصفوفة"، سيُصبِح كل شيء بديهيًا.
</p>

<p>
	تَنطبِق تلك الحقيقة أيضًا على تمرير مصفوفة كمُعامِل (parameter) إلى برنامج فرعي (subroutine) حيث سيَستقبِل البرنامج في تلك الحالة نُسخة فقط من المُؤشر (pointer) وليس المصفوفة ذاتها، وبذلك سيَملُك مَرجِعًا (reference) إلى المصفوفة الأصلية، وعليه فإن أيّ تَعْدِيلات قد يُجرِيها على عناصر المصفوفة (array elements)، سيَمتدّ أثرها إلى المصفوفة الأصلية وستستمر إلى ما بَعْد انتهاء البرنامج الفرعي.
</p>

<p>
	بالإضافة إلى كَوْن المصفوفات كائنات (objects) بالأساس، فإنها قد تُستخدَم لحَمل كائنات أيضًا، ويَكُون نوع المصفوفة الأساسي (base type) في تلك الحالة عبارة عن صَنْف. في الواقع، لقد تَعرَّضنا لذلك بالفعل عندما تَعامَلنا مع المصفوفات من النوع <code>String[]‎</code>، ولكن لا يَقْتصِر الأمر على النوع <code>String</code> بل تستطيع اِستخدَام أيّ صَنْف آخر. فمثلًا، يُمكِننا إنشاء مصفوفة من النوع <code>Student[]‎</code>، والتي نوعها الأساسي هو الصنف <code>Student</code> المُعرَّف مُسْبَقًا بهذا القسم، وفي تلك الحالة، يَكُون كل عنصر بالمصفوفة عبارة عن مُتْغيِّر من النوع <code>Student</code>. يُمكِننا مثلًا كتابة الشيفرة التالية لتَخْزين بيانات 30 طالب داخل مصفوفة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_49" style="">
<span class="typ">Student</span><span class="pun">[]</span><span class="pln"> classlist</span><span class="pun">;</span><span class="pln">  </span><span class="com">// Declare a variable of type Student[].</span><span class="pln">
classlist </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">[</span><span class="lit">30</span><span class="pun">];</span><span class="pln">  </span><span class="com">// The variable now points to an array.</span></pre>

<p>
	تَتَكوَّن المصفوفة -بالأعلى- من 30 عنصر هي <code>classlist[0]‎</code> و <code>classlist[1]‎</code> وحتى <code>classlist[29]‎</code>. عند إنشاء تلك المصفوفة، تُهيَئ عناصرها إلى قيمة مبدئية افتراضية تُساوِي القيمة الفارغة <code>null</code> في حالة الكائنات، لذلك يُصبِح لدينا 30 عنصر مصفوفة، كُلًا منها أينعم من النوع <code>Student</code> لكنه يَحتوِي على القيمة الفارغة (null) وليس على كائن فعليّ من النوع <code>Student</code>. ينبغي أن نُنشِئ تلك الكائنات بأنفسنا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3363_51" style="">
<span class="typ">Student</span><span class="pun">[]</span><span class="pln"> classlist</span><span class="pun">;</span><span class="pln">
classlist </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">[</span><span class="lit">30</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">30</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    classlist</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Student</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وعليه، يُشير كل عنصر مصفوفة <code>classlist‎</code> إلى كائن من النوع <code>Student</code> أيّ يُمكِن اِستخدَام <code>classlist‎</code> بأيّ طريقة يُمكِن بها اِستخدَام مُتْغيِّر من النوع <code>Student</code>. فمثلًا، يُمكِن اِستخدَام <code>classlist[3].name</code> للإشارة إلى اسم الطالب المُخْزَّن برقم المَوضِع الثالث. كمثال آخر، يُمكِن استدعاء <code>classlist.getAverage()‎</code> لحساب متوسط درجات الطالب المُخْزَّن برقم المَوضِع <code>i</code>.
</p>

<p>
	ترجمة -بتصرّف- للقسم <a href="http://math.hws.edu/javanotes/c5/s1.html" rel="external nofollow">Section 1: Objects, Instance Methods, and Instance Variables</a> من فصل Chapter 5: Programming in the Large II: Objects and Classes من كتاب <a href="http://math.hws.edu/javanotes/index.html" rel="external nofollow">Introduction to Programming Using Java</a>.
</p>
]]></description><guid isPermaLink="false">1108</guid><pubDate>Sun, 17 Jan 2021 13:00:00 +0000</pubDate></item></channel></rss>
