IranIT.info Articles
عنوان الگوهاي طراحي، قسمت يازدهم : فصل دوم، بخش 2-2
نويسندهحسن ابوالحسنى تاريخ ارسال 17/02/1382 نام قسمت فناورى
2.2 ساختار مستند
يک مستند در نهايت تنها آرايشي از عناصر گرافيکي نظير کاراکترها، خطوطو، چند ضلعي ها و نظاير آنهاست. اين عناصر محتواي اطلاعاتي مستند را اشغال مي کنند. ولي يک نويسنده معمولا اين عناصر را نه بصورت مفاهيم گرافيکي بلکه بر اساس ساختار فيزيکي مستند در نظر مي گيرد – رديف ها، ستون ها، اشکال، جداول و زيرساختهاي ديگر (نويسندگان اغلب يک مستند را براساس ساختارهاي منطقي آن نيز در نظر مي گيرند، يعني بصوورت جملات، پاراگرافها، بخشها، زير بخشها و فصول. براي ساده نگهداشتن اين مثال، نمايش داخلي ما اطلاعاتي درباره ساختار منطقي را بطور صريح ذخيره نمي کند. ولي راه حلي که در اينجا شرح مي دهيم بخوبي براي نمايش چنين اطلاعاتي کار مي کند).

رابط استفاده کننده lexi بايستي اجازه دستکاري اين زيرساختارها را بطور مستقيم بدهد. براي مثال يک استفاده کننده بايستي قادر باشد تا يک دياگرام را بصورت يک واحد مورد عمل قرار دهد به جاي اينکه آنرا بصورت مجموعه اي از عناصري گرافيکي ابتدايي در نظر بگيرد. استفاده کننده بايستي قادر باشد تا به يک جدول به صورت يک عنصر واحد ارجاع کند و نه بصورت توده اي از متون و عناصر گرافيکي. اينها باعث ساده سازي و قابل درک شدن رابط استفاده کننده خواهد شد. به منظور دادن کيفيت مشابه در پياده سازي lexi، يک ساختار داخلي که با ساختار فيزيکي مستند تطابق داشته باشد را انتخاب کنيم.

بطور مشخص، نمايش داخلي بايستي موارد زير را تدارک ببيند:
- نگه داري ساختار فيزيکي مستند يعني آرايش متون و عناصر گرافيکي بصورت رديف ها، ستون ها، جداول و نظاير آن.
- توليد وارائه مستند بصورت تصويري (visual)
- نگاشت مکانها در صفحه نمايش به عناصر در نمايش داخلي. با اينکار lexi مي تواند تعيين کند که با اشاره کردن به يک مکان در صفحه نمايش، استفاده کننده به چه عنصري ارجاع مي کند.

به همراه اين اهداف تعدادي محدويت نيز وجود دارد. اول اينکه بايستي متون و گرافيک را بصورت يکپارچه در نظر بگيريم. رابط استفاده کننده اجازه مي دهد که استفاده کننده متون را بصورت آزاد در داخل گرافيک جاسازي کرده و يا بر عکس. بايستي از در نظر گرفتن گرافيک بعنوان حالت خاصي از متن يا متن بعنوان حالت خاصي از گرافيک پرهيز کنيم. در غير اينصورت طراحي مان با قالب ها و مکانيزم هاي اضافي پايان خواهد يافت. يک مجموع از مکانيزم ها بايستي براي هم متن و هم گرافيک کافي باشد.

دوم اينکه پياده سازي مان بايستي تمايزي بين عناصر منفرد و گروهي از عناصر در نمايش داخلي قائل نباشد. Lexi بايستي بتواند عناصر ساده و مرکب در نمايش داخلي را بصورت واحدي در نظر گرفته و بنابراين ايجاد مستندات با پيچيدگي دلخواهي را بدهد. بعنوان نمونه، عنصر دهم در خط پنجم از ستون دوم مي تواند يک کاراکتر واحد يا يک دياگرام با زير عناصر زيادي باشد. ماداميکه بدانيم اين عنصر مي تواند خودش را ترسيم (draw) کرده و ابعادش را مشخص نمايد، پيچيدگي اش تاثيري بر چگونگي و مکاني که بايستي بر روي صفحه ظاهر شود ندارد.

برخلاف محدوديت دوم، به هرحال نيازي براي تحليل متون براي اعمالي نظير چک اسپل و نقاط خط پيوند گذاري وجود دارد. گاهي اوقات اينکه عنصري از يک خط شئ ساده يا مرکبي است براي ما اهميت ندارد. ولي برخي مواقع يک عمل تحليلي به شئ که مورد تحليل قرار مي گيرد بستگي دارد. براي مثال چک اسپل يک چند ضلعي يا خط پيوند گذاري آن عمل بي ربطي است. نمايش داخلي بايستي اين محدوديت و ديگر محدوديتهاي ناسازگار را در نظر بگيرد.

ترکيب بازگشتي (recursive)
روشي براي نمايش اطلاعات ساختيافته بصورت سلسله مراتبي از طريق تکنيکي بنام ترکيب بازگشتي است که روشي براي ساختن عناصر پيچيده از عناصر ساده فراهم مي کند. اين تکنيک روشي براي تشکيل يک مستند از عناصر ساده گرافيکي ارائه مي دهد. بعنوان اولين قدم، مي توانيم مجموعه اي از کاراکترها را دنبال هم قرار داده تا يک رديف از مستند را بسازيم. سپس چندين رديف مي تواند براي تشکيل يک ستون آرايش داده شود؛ چندين ستون مي تواند يک صفحه را تشکيل داده و الي آخر (شکل 2.2 را ببينيد).

مي توانيم اين ساختار فيزيکي را با اختصاص يک شئ به هر عنصر مهم نمايش دهيم. اين شامل نه فقط عناصري که ديده مي شوند مانند کاراکترها و گرافيک ها بلکه عناصر غير قابل روئيت نظير رديف ها و ستون ها مي شود. نتيجه ساختار شئ است که در شکل 23 آورده شده است.

با استفاده از يک شئ براي هر کاراکتر يا عنصر گرافيکي در مستند، قابليت انعطاف را در عالي ترين سطح از طراحي lexi ترغيب مي کنيم. مي توانيم متون و عناصر گرافيکي را براساس اينکه چگونه نمايش داده شده، فرمت بندي شده و در يکديگر جاسازي مي شوند، بصورت يکساني مورد عمل قرار دهيم. مي توانيم lexi را گسترش داده تا مجموعه کاراکترهاي جديدي را بدون تاثير گذاري بر روي عملکردهاي ديگر، تدارک ديد. ساختار شئ lexi ساختار فيزيکي مستند را تقليد مي کند.

اين روش دو نتيجه مهم دارد. اولي بديهي است: اشياء نياز به کلاسهاي مربوطه دارند. دومين نتيجه، که کمتر بديهي است، اين است که اين کلاسها بايستي اينترفيسهاي سازگاري داشته باشند، چراکه مي خواهيم آنها را بصورت يکساني مورد عمل قرار دهيم. روش ساخت اينترفيس هاي سازگار در زبان ++C ربط دادن اين کلاسها از طريق وراثت است.

Glyphها
يک کلاس مجرد بنام Glyph براي تمام اشيائي که مي تواند در يک ساختار مستند ظاهر شود تعريف مي کنيم (Calder اولين فردي است که " glyph“ را در اين گستره مورد استفاده قرار داد (CL90). بسياري از ويراستارهاي مستندات براي هر کاراکتر از يک شئ استفاده نمي کنند، احتمالا براي مسائل کارائي. Calder در تز خود نشان داد که اين روش امکان پذير است (Cal93). در اينجا Glyphهاي ما کمتر از آنچه او تعريف کرده پيشرفته اند. براي ساده سازي خودمان را به سلسله مراتب محض محدود کرده ايم. Glyphهاي Calder مي تواند بطور مشترک استفاده شود که بنابراين يک ساختار گرافيک غير سيکلک جهت دار ايجاد مي کند. مي توانيم از الگوي مگس وزن براي حاصل کردن اتري يکسان استفاده کنيم ولي آنرا بعنوان تمرين براي خواننده رها مي کنيم). زير کلاسهاي Glyph تعريف کننده هم عناصر گرافيکي ابتدايي (نظير کاراکترها و اشکال) و هم عناصر ساختاري (نظير رديف ها و ستون ها) است. شکل 2.4 قسمتي از سلسله مراتب کلاس Glyph را نمايش داده و جدول 2.1 اينترفيس پايه آنرا بصورت جزيي تر با استفاده از نماد ++C نمايش مي دهد (اينترفيس تشريح شده در اينجا بصورت عمدي براي ساده سازي بحث حداقل است). يک اينترفيس کامل شامل اعمالي براي مديريت ويژگيهاي گرافيکي نظير رنگ، فونت، و انتقالات مختصات، به علاوه اعمالي براي مديريت بچه ها بطور خاص تر است).

وظيفهاعمال
نمايش(*virtual void Draw(Window
(&virtual void Bounds(Rect
تشخيص کليک خوردن(&virtual bool Intersects(const Point
ساختار(virtual void Insert(Glyph*, int
(*virtual void Remove(Glyph
(virtual Glyph* Child(int
()virtual Glyph* Parent
جدول 2.1 اينترفيس پايه glyph

Glyphها سه وظيفه ابتدايي دارند. آنها مي دانند 1) چگونه خود را ترسيم کنند 2) چه فضايي اشغال مي کنند و 3) بچه ها و پدرشان کيست.

زير کلاسهاي Glyph عمل Draw را دوباره تعريف کرده تا خود را در داخل يک پنجره رائه کنند. به آنها ارجاعي به يک شئ Window در هنگام صدا کردن Draw رد مي شود. کلاس Window اعمال گرافيکي براي ارائه کردن متون و اشياء گرافيکي ابتدايي در يک پنجره را دارا مي باشد. يک Rectangle بعنوان زير کلاسي از Glyph مي تواند Draw را بصورت زير تعريف کند:

}(void Rectangle::Draw(Window* w
;(w->DrawRect(_x0, _y0, _x1, _y1
{

در اينجا x0_ و y0_ و x1_ و y1_ داده هاي کلاس Rectangle است که دو گوشه مخالف از مربع مستطيل را تعريف مي کند. DrawRect عملي از کلاس Window است که باعث مي شود Rectangle بر روي صفحه ظاهر شود.

يک glyph پدر اغلب نياز دارد تا بداند يک glyph فرزند چه مقدار فضا اشغال مي کند، تا براي مثال آن glyph و ديگران را در يک رديف بطوريکه با يکديگر تداخل پيدا نکنند آرايش دهد (همانظوريکه در شکل 2.2 نشان داده شده است). عمل Bounds ناحيه مستطيلي که يک glyph اشغال مي کند را برمي گرداند. اين عمل گوشه هاي مخالف از کوچکترين مستطيلي که شامل glyph است را برمي گرداند. زير کلاسهاي Glyph اين عمل را دوباره تعريف مي کنند تا مستطيلي که آنها خود را در آن ترسيم مي کنند را برگرداند.

عمل Intersects اينکه آيا يک نقطه مشخص شده glyph را قطع مي کند را برمي گرداند. وقتيکه استفاده کننده جايي در مستند را کليک مي کند، Lexi اين عمل را براي تعيين اينکه چه glyph يا ساختار glyphها تحت موس قرار دارد ، صدا مي کند. کلاس Rectangle اين عمل را براي محاسبه تقاطع مستطيل و نقطه مورد نظر دوباره تعريف مي کند.

چونکه glyphها مي توانند بچه هايي داشته باشند، نياز به اينترفيس مشترکي براي افزودن، حذف و دسترسي به آن بچه ها داريم. براي مثال بچه هاي يک Row تمام glyphهايي هستند که در يک رديف آرايش مي دهد. عمل Insert يک glyph را دريک مکان مشخص شده بوسيله يک ايندکس اضافه مي کنند (مشخص کردن يک انديس بسته به اينکه ساختار داده بکار رفته براي glyph چيست لزوما بهترين راه براي تعيين فرزند يک glyph نيست. اگر glyph بجه ها را در يک ليست مرتبط ذخيره سازد، آنگاه اشاره گري به ليست مي تواند کاراتر باشد. روش بهتري براي مسئله انديس دهي، در بخش 2.8 هنگاميکه تحليل مستند را مورد بررسي قرار مي دهيم، خواهيم ديد). عمل Remove بچه مشخص شده را حذف مي کند.

عمل Child بچه اي در انديس مشخص شده را برمي گرداند. Glyphهايي نظير Row که مي توانند بچه داشته باشند بايستي از عمل Child استفاده کننده بجاي دسترسي به ساختار داده آن. به اين طريق با تغيير ساختار داده از مثلا آرايه به ليست مرتبط نيازي به تغيير اعمالي نظير Draw که بچه ها را پيمايش مي کند نخواهيد داشت.

بطور مشابه Parent اينترفيس استانداردي براي پدر glyph تدارک مي بيند. Glyphها در Lexi ارجاعي به پدرشان ذخيره ساخته و عمل Parent بسادگي آن ارجاع را برمي گرداند.

الگوي ترکيب
ترکيب بازگشتي براي چيزهاي ديگري غير از مستندات نيز مفيد است. مي توان براي نمايش هر ساختار سلسله مراتب پيچيده اي استفاده کرد. الگوي ترکيب عصاره ترکيب بازگشتي را رد دامنه شئ گرا تسخير مي کند. حالا موقع مناسبي است تا آن الگو را مورد مطاله قرار دهيد؛ به اين سناريو در صورت نياز برگرديد.