بسم الله الرحمن الرحيم
الحلقة الرابع : Iterator Pattern
أو نمط الحلقات التكرارية أو نمط إعادة التكرار
تابع معي النقاط التالية
النقطة الأولي :
دائما ما نحتاج عمل حلقة تكرارية Looping باستخدام جمل ال for أو حلقات ال while أو do while
فلو طلبت منك مثلا أن تقوم بكتابة كود يقوم بطباعة مجموع الأعداد لمصفوفة ما
ربما لأسرعت وقمت باستخدام حلقة تكرارية من نوع for للقيام بذلك
ولكني أري علي الطرف الأخر مبرمج أخر قد قام بكتابة الكود مستخدما جملة while أو ربما جملة do while
وكلا له فكره الذي يوصله للنتيجة
دعني أعرض عليك أمر أخر , ماذا لو قلت لك أني أريدك أن تقوم بتصميم كود يقوم بسرد أسماء الملفات الموجودة بفهرس ما folder وليكن c:\\windows
ثم قلت لك أني أريدك أيضا أن تقوم بسرد أسماء الفهارس الموجودة بفهرس ما
فربما سنجد من يقوم بعمل حلقة تكرارية مستخدما جملة for والأغلبية سوف يستخدم جملة while
ثم هب أنني طلبت منك طلب أخر ( وأرجو أن تتحملني بصدر رحب )
أريدك أن تقوم بسرد محتويات جدول ما وليكن اسمه students مرة من قاعدة بيانات من نوع مزود الخدمة MySQL ومرة من
قاعدة بيانات Access ومرة من نوع SqlServer و كمان Oracle
ربما سوف تميل لإستخدام جملة while
بعد كل هذه الحالات السابقة , ربما يتبادر في أذهننا تسائل هل يوجد طريقة ما لتوحيد وسيلة إجراء هذه المهام ؟؟؟!!!
النقطة الثانية :
تخيل معي الحالة التالية , أنك تعمل كعضو في فريق عمل كبير ربما لا يري أو يعرف بعضهم بعض (وربما تتعرض لهذا يوما ما)
ولنقول أن هذا الفريق مقسم إلي قسمين مثلا هما قسم A وقسم B
وأعضاء الفريق بقسم A هم الأعضاء الأكثر خبرة والمسئولين عن تصميم البنية التحتية للمشروع اي هم المسئولين عن تصميم المكاتب البرمجية
التي سوف يستخدمها أعضاء الفريق B لإتمام مهامهم
فهب أنه قد طلب من العضو رقم 3 بالفريق B أن يقوم بتصميم صفحة تقوم بعرض الوظائف الجديدة من جدول باسم jobs
لذلك سوف يقوم هذا العضو بالبحث في وثائق مخطط الدوال والفئات التي قام بتصميمها الفريق A ,
عن دالة function تقوم بإرجاع مصفوفة تحمل الوظائف الجديدة , وفعلا نجده بعد وقت قليل قد عثر علي وظيفة باسم getNewJobs
تقوم بإرجاع مصفوفة تحمل الوظائف الجديدة , وقد تبقي له أن يقوم بإستدعاء هذه الدالة ثم يقوم بعمل حلقة تكرارية داخل هذه المصفوفة لعرضها
بمحتوي الصفحة بنسق html , مممممممم ولكن ماذا بعد
فلو تركتك وقت ما حتي تفكر مع نفسك لوجدت أن العضو الموجود بالفريق A الذي قام بتصميم وبرمجة الدالة getNewJobs
قد أدي لإيقاع العضو رقم 3 بالفريق B في ورطة , ما هي هذه الورطه ؟؟؟
تخيل معي أن الجدول المسمي jobs يحتوي علي 30 سجل للوظائف الجديدة
فكما نعلم أو نتوقع أن الكود الموجود بالدالة getNewJobs سوف يقوم بتعريف مصفوفة ما تكون المسئولة عن حمل محتوي الجدول السابق
ثم سوف يتم عمل حلقة تكرارية وبالطبع سوف يتم ملء هذه المصفوفة داخل هذه الحلقة التي سوف تتكرر 30 مره , ثم بعد ذلك يتم إرجاع هذه المصفوفة
ثم يأتي العضو رقم 3 (المسكين) القاطن بالفريق B بإستدعاء هذه الدالة ثم يخزن القيمة العائده منها في مصفوفة جديدة
وحتي يتمكن من عرض هذه السجلات بشكل منسق يقوم بعمل حلقة تكرارية أخري تلف داخل المصفوفة السابقة
وكم لاحظنا أنه سوف يتم الدوران لعدد 30 مره جديدة لعرض المحتوي بالصفحة
لذلك قد تكلفنا في المحصله 60 دوره حتي نعرض 30 سجل فقط
وهذه الورطه ربما في بعض الأحيان تكون بسيطة ولكن دائما ما تكون ورطه غير هينه لما يترتب عليها من تكلفه في وقت تنفيذ الكود
ولكن ما هو الحل ؟؟؟
أنتهي .
نقطه جانبية فلسفية (شخصية)
دائما ما تكون الحلول وليدة المشاكل
فعندما تواجه في حياتك البرمجية أو حتي الشخصية مشكلة ما , فاعلم أن هذه المشكلة ستكون وليدة لشئ جديد في حياتك .
دعنا من الفلسفة الأن ولنعد سريعا إلي ما قد كنا بدئنا الحديث عنه
نمط Iterator Pattern أو نمط الحلقات التكرارية
تم تصميم فكرة هذا النمط حتي يتيح لنا التعامل بشكل موحد مع المشاكل البرمجية المختلفه التي نحتاج فيها القيام بعمل حلقات تكرارية .
تابع المخطط التالي

الفئة الأم Iterator
يتم في هذا النمط تصميم فئة باسم Iterator تكون بمثابة الفئة الأم لكل الفئات التي نريد أن يتوفر بها هذا النمط
وتتميز هذه الفئة بالتالي :
- أنها فئة مجردة abstract class اي لا يمكن أن نقوم بتخليق كائنات منها (إن صح التعبير)
- تحتوي علي الوظائف التالية :
+ size : وتقوم هذه الوظيفة بإعلامنا بعدد العناصر للمصفوفة الهدف
+ reset : تقوم بإعادة موشر الإنتقال pointer إلي وضع الصفر اي وضع البداية
+ fetch : تقوم بإرجاع عنصر واحد فقط من المصفوفة الهدف , ثم تقوم بتحريك مؤشر القراءة إلي الأمام (اي حركه تزايدية بمقدار عنصر واحد)
وهي كما يلي
<?
class Iterator
{
function Iterator() {
die("Iterator is abstract class");
}
/**
* Fetches an element from the collection and moves the internal
* pointer forward one
*
* @return variant
*/
function fetch() {
die("fetch method must be implemented");
}
/**
* Returns the number of elements in the collection
*
* @return int
*/
function size() {
die("size method must be implemented");
}
/**
* Resets the collection pointer to the start
*
* @return void
*/
function reset() {
die("reset method must be implemented");
}
}
?>أما بالنسبة للإصدار الخامس php5 يفضل أن نقوم بتصميم الفئة الأم Iterator بحيث تكون عبارة عن واجهة interface كما يلي
<?
interface Iterator
{
/**
* Fetches an element from the collection and moves the internal
* pointer forward one
*
* @return variant
*/
public function fetch();
/**
* Returns the number of elements in the collection
*
* @return int
*/
public function size();
/**
* Resets the collection pointer to the start
*
* @return void
*/
public function reset();
}
?>ملحوظة : توفر لنا اللغات الحالية مثل الجافا ولغات الدوت نت والإصدار الخامس لل php فئة داخلية تكون بمثابة الفئة الأم لنمط Iterator
بشكل مختلف قليلا ولكن بنفس الفكر , وربما أتطرق لشرح هذا الأمر لاحقا كتابع لهذا الدرس .
الفئات المشتقة inherited classes
- فئة ListIterator
وهي فئة خاصة للتعامل مع الحلقات التكرارية الخاصة بالمصفوفات العادية , وهي كما يلي
<?
class ListIterator extends Iterator
{
var $arrayList;
function ListIterator(&$array) {
$this->arrayList = &$array;
}
function fetch() {
$element = each($this->arrayList);
return $element['value'];
}
function size() {
return count($this->arrayList);
}
function reset() {
reset($this->arrayList);
}
}
?>- فئة MysqlIterator
وهي فئة خاصة للتعامل مع الحلقات التكرارية الخاصة بقاعدة بيانات من نوع Mysql , وهي كما يلي
<?
class MysqlIterator extends Iterator
{
var $result;
function MysqlIterator( &$result ) {
$this->result = &$result;
}
function fetch() {
return mysql_fetch_array($this->result);
}
function size() {
return mysql_num_rows($this->result);
}
function reset() {
return mysql_data_seek($this->result,0);
}
}
?>- فئة FileIterator
وهي فئة خاصة للتعامل مع الحلقات التكرارية الخاصة بقراءة محتويات ملف ما , وهي كما يلي
<?
class FileIterator extends Iterator
{
var $fp;
function FileIterator( &$fp ) {
$this->fp = &$fp;
}
function fetch() {
if( !feof($this->fp) )
return fgets($this->fp, 4096);
else
return false;
}
function size() {
$i=0;
$this->reset();
while( $this->fetch() ) {
$i++;
}
$this->reset();
return $i;
}
function reset() {
fseek ($this->fp, 0);
}
}
?>- فئة DirectoryIterator
وهي فئة خاصة للتعامل مع الحلقات التكرارية الخاصة بالفهارس directories , وهي كما يلي
<?
class DirectoryIterator extends Iterator
{
var $dir;
function DirectoryIterator( &$dir ) {
$this->dir = &$dir;
}
function fetch() {
return $this->dir->read();
}
function size() {
$i=0;
$this->reset();
while( $this->fetch() ) {
$i++;
}
$this->reset();
return $i;
}
function reset() {
$this->dir->rewind();
}
}
?>ويمكننا عمل فئات أخري حسب الحاجة لذلك ولكنني أكتفي بهذه الفئات المشتقة لأنها تكفي معظم حاجاتنا
مثال تطبيقي بسيط
نريد تصميم صفحة بسيطة جدا تقوم بسرد محتويات فهرس ما وليكن الفهرس الهدف هو c:\php
الحل إستناد علي الفئات السابقة يمكننا فعل التالي :
DirectoryTest.php
<?
require_once "lib/DirectoryIterator.php";
// Modify this to some directory
$dir = dir("c:\\php");
$iterator = new DirectoryIterator($dir);
echo ( "<b>Number of results:</b> ".$iterator->size()."<br />\n" );
while ( $element = $iterator->fetch() ) {
echo( $element . "<br/>" );
}
$dir->close();
?>
مثال تطبيقي بسيط أخر
نريد تصميم صفحة بسيطة تقوم بسرد محتويات ملف ما وليكن الملف التالي c:\php\install.txt
الحل إستناد علي الفئات السابقة يمكننا فعل التالي :
FileIteratorTest.php
<?
require_once("lib/FileIterator.php");
// Modify this to point at a real file
$fp=fopen("c:\\php\\install.txt",'r');
$iterator = new FileIterator($fp);
echo ( "<b>Number of Lines:</b> ".$iterator->size()."<br />\n" );
while ( $element = $iterator->fetch() ) {
echo($element . "<br/>");
}
fclose($fp);
?>مثال تطبيقي بسيط أخر
نريد تصميم صفحة بسيطة تقوم بسرد محتويات جدول ما بقاعدة بيانات من نوع mysql
وليكن الجدول الهدف هو جدول user الموجود بقاعدة البيانات mysql
الحل إستناد علي الفئات السابقة يمكننا فعل التالي :
MysqlIteratorTest.php
<?
require_once("lib/MysqlIterator.php");
// Modift these and the query to some database / table of your own
mysql_connect('localhost', 'root', '') or die(mysql_error());
mysql_select_db('mysql') or die(mysql_error());
$sql = "SELECT * FROM user";
$result = mysql_query($sql);
$iterator = new MysqlIterator($result);
echo ( "<b>Number of results:</b> ".$iterator->size()."<br />\n" );
while ( $element = $iterator->fetch() ) {
echo( $element["Host"] . "<br/>");
}
mysql_free_result($result);
?>أعتقد أننا لاحظنا من الأمثلة السابقة أننا قد وحدنا فكرنا, والأن نقوم بعمل الحلقات التكرارية علي اي هدف بنفس الطريقة ,
دعنا الأن نعود لمثال الفريق A والفريق B حتي تزداد الصوره وضوح
مثال تطبيقي أخر
كما قد عرضنا سابقا حالة هذا المثال المتنقل بين الفريقيين A و B
وسوف يكون الجدول المراد التعامل معه هو جدول ال Jobs وهو كما يلي
#
# Table structure for table 'jobs'
#
CREATE TABLE jobs (
JobID tinyint(3) unsigned zerofill NOT NULL auto_increment,
JobTitle varchar(255) default NULL,
IsNew tinyint(1) unsigned default NULL,
PRIMARY KEY (JobID),
UNIQUE KEY JobID (JobID)
) TYPE=MyISAM;
#
# Dumping data for table 'jobs'
#
INSERT INTO jobs VALUES("001", "مطلوب مبرمج php", "1");
INSERT INTO jobs VALUES("002", "مطلوب مبرمج asp", "1");
INSERT INTO jobs VALUES("003", "مطلوب بائع لمحل مجوهرات", "0");
INSERT INTO jobs VALUES("004", "مطلوب مضيف ليس حسن المظهر", "1");الحل بالنسبة للعضو بالفريق A
سوف يقوم بتصميم دالة باسم getNewJobs تقوم بإرجاع كائن من نوع الفئة Iterator
وفي مثالنا هذا سوف يكون الكائن من نوع الفئة MysqlIterator
وهي كما يلي
Jobs.php
<?
require_once("lib/MysqlIterator.php");
function getNewJobs() {
// Modift these and the query to some database / table of your own
mysql_connect('localhost', 'root', '') or die(mysql_error());
mysql_select_db('test') or die(mysql_error());
$sql = "SELECT * FROM jobs where IsNew=1";
$result = mysql_query($sql);
return new MysqlIterator($result);
}
?>ثم يقوم العضو رقم 3 بالفريق B بتصميم الصفحة الخاصة بعرض أحدث الوظائف كما يلي
NewJobsView.php
<?
include_once "Jobs.php";
$iterator = getNewJobs();
while ( $element = $iterator->fetch() ) {
echo( $element["JobTitle"] . "<br/>");
}
?>وهكذا لا يتم عمل مضاعفة في عدد الدورات التي تم التنبيه عنها سابقا
إلي اللقاء بإذن الله مع نمط جديد
وبالله التوفيق
والسلام عليكم ورحمة الله وبركاته
