أنماط المجموعات Collections
#1
السلام عليكم ورحمة الله

قد نحتاج في حالات كثيرة زمن التشغيل إلى استخدام سلسلة من الكائنات ديناميكا
الجداول الثابتة Static Array أو الديناميكية قد تفي بالغرض في حالات ضيقة
لكن لاستخدامات أوسع: الحذف، الترتيب، المقارنة، البحث، الدمج... ينبغي برمجة ذلك يدويا

لنفكر لتوجه غرضي، البديل استخدام TList المعرف في المكتبة Classes.pas
يمثل سلسلة من الكائنات TObject فهو بذلك نمط مرن
وبالتالي باستخدام القولبة يمكنك توظيفه لأي كائن آخر
لتستفيد من الخصائص التقليدية للسلاسل المتصلة Exchange, Expand, Extract, First, IndexOf, Insert, Last, Move ,Remove, Pack, Sort, Capacity, Count, Items وغيرها...
طبعا دون إعادة كتابة الخوارزميات الجاهزة يكفي إعادة توريثها
مثال TIntegerList مصغرة:
PHP كود :
type
  TIntegerList 
= class(TList)
  private
    function 
Get(IndexInteger): Integer;
...
function 
TIntegerList.Get(IndexInteger): Integer;
begin 
   Result 
:= Integer(inherited Items[Index]); 
end
تتمة الكود في المرفق IntList.rar

عند استخدامه مع أصناف أخرى يستحسن عدم توريثه مباشرة
وإلا قد تجبر مستخدمه على القولبة عند كل استخدام
PHP كود :
type
  TMyList 
= class
  private
    
FMyListTList;//<-- Best practice
    
function GetItems(IndexInteger): TMyList;
    
procedure SetItems(IndexInteger; const ValueTMyList);
  public
    function 
Add(ItemTMyList): Integer;
    
property Items[IndexInteger]: TMyList read GetItems write SetItems; default;
  
end
TList خفيف ويصلح مع الأنماط الصغيرة

عند التعامل مع صنفيات أكثر أهمية (مثل المكونات)
أو تحتاج إلى استخدامات متقدمة
يمكنك الاستفادة من خدمات المكتبة Contnrs.pas المرفقة مع دلفي (إصدار >= 5 على الأقل)
كود :
TObjectList
TClassList
TComponentList

باختصار:
TCollection وTCollectionItem تصميم رائع يختصر الكثير من العمل
يسمح هذان الصنفان بتجسيد العلاقة واحد إلى متعدد بين أصناف دلفي
كيف ذلك؟
لنشاهد المثال الآتي: نرغب بإنشاء صنفين "عامل" TEmployee و"مجموعة عمال" TEmployees
عامل واحد يمثل هنا غرض من TCollectionItem
في حين تمثل مجموعة العمال بالصنف TCollection
PHP كود :
type
  TEmployee 
= class(TCollectionItem)
  private
    
FFirstNamestring;
    
FAgeSmallInt;
    
procedure SetFirstName(const Valuestring);
    
procedure SetAge(const ValueSmallInt);
  public
    
property FirstNamestring read FFirstName write SetFirstName;
    
property AgeSmallInt read FAge write SetAge;
  
end;

  
TEmployees = class(TCollection)
  private
    function 
GetItem(IndexInteger): TEmployee;
  public
    function 
AddTEmployee;
    
property Items[IndexInteger]: TEmployee read GetItem; default;
  
end
انسى أمر الـ Implementation، لنشاهد الاستخدام
الآن، لن نستخدم الصنف TEmployee (إلا مرة واحدة عند الإنشاء) لن نتعامل مع "عامل" واحد
بل سنقوم بإنشاء كائن من مجموعة العمال TEmployees ونخبره بالصنف الذي سنستخدمه كعنصر Item
كود :
[color=black][COLOR="Red"]TEmployees[/color].Create([color=Blue]TEmployee[/color])[/COLOR]
كل ما نحن بحاجة إليه موجود في الصنف TEmployees، للإضافة Add وDelete للحذف وغيرهما...
مثال:
PHP كود :
var
  
TeamTEmployees;
  
OfficerTEmployee;
begin

  Team 
:= TEmployees.Create(TEmployee);
  
Officer := Team.Add;
  
Officer.FirstName := 'Mohamed';
  
Officer.Age := 35;

  
Writeln('Officer:');
  
with Team[0] do
    
Writeln('Name: 'FirstName#13#10'Age: ', Age);
  
Team.Free
وما الذي سنستفيد من كل ذلك؟
الصنف TCollection مشتق من الصنف TPersistent وبذلك تستفيد من كل ما يوفره من Assign، التدفقات Streaming، وOwnership إذا أضفنا TOwnedCollection...
وسنختزل الكثير من الوقت مقارنة بما لو كتبنا ذلك من الصفر...
صممت هذه الأنماط لأجل برمجة المكونات وهي مناسبة جدا لها:
[صورة: attachment.php?attachmentid=3343&stc=1&d=1331163295]
معظم مكونات دلفي تستخدم هذه الأنماط TActionListItem، TListColumn، TColumn...

أنماط أخرى أسهل من هذه؟
TObjectList أسهل من حيث التعامل
تنطلق من TList ولا تختلف عنها كثيرا
لتضيف ميزة رائعة وهي تدمير تلقائي للأغراض المضافة (مع ضبط OwnsObjects = True وهي كذلك افتراضيا)، مثال: لنفترض الصنف TPerson له خاصية FullName ترفق له عند الإنشاء، نكتب:
PHP كود :
type
  TPersonList 
= class(TObjectList)
  private
    function 
GetItem(IndexInteger): TPerson;
    
procedure SetItem(IndexInteger; const ValueTPerson);
  public
    
property Items[Index Integer]: TPerson read GetItem write SetItem; default;
  
end;
...
  
with TPersonList.Create do begin//By default OwnsObjects = True
    
Add(TPerson.Create('Ahmed'));
    
Add(TPerson.Create('Said'));
    for 
:= 0 to Count do
      
Writeln('Member: 'Items[I].FullName);
//    Clear; //Free;  <- XE!
  
end
بعض إصدارات دلفي تحوي أخطاء بخصوص تسيير الذاكرة يستحسن التحقق منها ReportMemoryLeaksOnShutdown أو مكتبات خارجية أو غير ذلك...

بقيت TClassList التي تمثل TList لتسيير صنف من الأصناف TClass = class of TObject
أما TComponentList فتشتق من TObjectList لتضيف إمكانية التحديث التلقائي عند حذف العناصر وتحرير الذاكرة عند التدمير...
كود الأمثلة السابقة في المرفق CollectionsExample.rar

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

بالتوفيق.


الملفات المرفقة
.rar   IntList.rar (الحجم : 536 بايت / التحميلات : 81)
.jpg   TCollectionDesign.JPG (الحجم : 100.32 ك ب / التحميلات : 204)
.rar   CollectionsExample.rar (الحجم : 2.02 ك ب / التحميلات : 92)
اللهم احقن دماء المسلمين، لا تنسوهم بالدعاء...
الرد


التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم