mohammad_110 :
اول یه تاریخچه ای از روشهای برنامه نویسی بگیم بد نیست : (از کتاب آموزش Turbo C++ ترجمه : کیوان فلاح مشفقی )
قدیمیترین الگوهای برنامه نویسی را به بهترین وجه ممکن می توان توسط صفت
هرج و مرج توصیف کرد .
در این مدل تمام فکر و ذکر طراحان تنها به حل کردن مشکل مورد نظر معطوف است و توجه اندکی به چگونگی رسیدن به این راه حل دارند (و یا اینکه کلا به این موضوع توجهی ندارند). زبانهایی که در طبقه هرج و مرج می گنجند عبارتند از
انواع اولیه Basic ( که حتی فاقد ساختار برنامه فرعی بودند).
تحت این شیوه استفاده کننده به هیچوجه با افکاری نظیر نحوه سازماندهی برنامه یا توسعه آتی آن درگیر نمی شود و تمام توجه وی بر روی مشکل مورد نظر متمرکز می گردد. پس از آنکه برنامه نوشته شده تحت این الگو ، به هر ترتیب ، شروع به کار نمود عملکرد آن تنها به برنامه های بسیار ساده محدود می گردد ( یعنی برنامه های
کمتر از صد خط ) .
در مورد برنامه هایی که ابعاد آنها از این حد بیشتر می شد تعداد مسیرهای این نوع برنامه ها ( که بدون شک بسیار زیاد خواهند بود ) عمل اشکال زداییو بررسی برنامه را به یک
کابوس شبانه تبدیل خواهند کرد ( این همان مشکل کلاسیک شیوه
کد اسپاگتی Spaghetti code است ).
اولين پيشرفت نسبت به شيوه هرج و مرج الگوي برنامه نويسي
پروسه اي Procedural بود .
زبانهايي از قبيل
فورترن و کوبول در اين دسته جاي مي گيرند.
در اين الگو برنامه نويس سعي مي کند مشکل خود را به چندين قسمت ( يا به عبارت منفرد رفتار کند. در اين حالت پيچيدگي هر تابع به جاي خود باقي مي ماند .
در واقع در اين حالت بين برنامه اصلي و داده ها يک ديوار بلند وجود دارد . در اين روش کليه توجه برنامه نويس به
ايزوله کردن برنامه معطوف است و وي توجهي به
داده ها ندارد.
تغيير بعدي در اين زمينه الگوي برنامه نويسي
ساخت يافته Structured است که در آن تجريد برنامه به ماورا توابع نيز گسترش مي يابد . ( توجه کنيد که اينجا استراکت به دو معني اومده يکيش رو که مي دونيد يکيش هم منظور ساختار برنامه است )
در اين حالت به جاي آنکه بين توابع هرج و مرج برقرار باشد (چنانچه در روش پروسه اي چنين است ) ، برنامه نويس ساخت يافته قوانيني را ارائه مي کند که برنامه نويس را قادر مي سازد هر سازه( Struct ) ، نظير : بلوکهاي for ، while ، if را بعنوان يک زير تابع يا تابع فرعي ( Subfunction) کوچک در نظر بگيرد.
خود اين توابع فرعي را مي توان متعاقبا ايزوله کرد. به عنوان مثال در اين حالت استفاده کنندگان مي توانند يک حلقه for را به عنوان يک قسمت مستقل در نظر بگيرند ، زيرا آنها مي دانند که
کليه مسيرهاي ورودي و خروجي به حلقه مشخص هستند و هيچگونه مسيري به اواسط حلقه وجود ندارد ( يعني نمي توان به يکباره به وسط حلقه دسترسي يافت و چند عمل را انجام داد و سپس از آن بيرون رفت ). به منظور پشتيباني از يک الگوي ساخت يافته ( Structured ) ، يک زبان بايد محتوي مجموعه اي کامل از سازه هاي کنترلي ( Control Structures ) باشد. يک نمونه مناسب از اينگونه زبانها
پاسکال است.
در الگوي برنامه نويسي ساخت يافته ديگر داده ها ناديده گرفته نشده اند . در اين الگو قوانيني در مورد ارسال داده ها بين توابع وجود دارد . توابع ملزم هستند تنها به آن داده هايي دسترسي داشته باشند که آنها را به عنوان آرگومانهاي ( arguments ) ورودي قبول نموده اند و برقراري ارتباط از طريق
متغير هاي عمومي ( global variable ) به هيچوجه توصيه نمي گردد.
اين محدويديت ها در واقع عکس العملي بود نسبت به ضعف مدل پروسه اي ( Procedural ) . کار با يک تابع به عنوان يک جسم تجريدي ( يا به عبارتي ايزوله نسبت به دنياي خارج )، در صورتيکه تابع بتواند متغيرهاي عمومي را دريافت کرده و آنها را تغيير دهد ، عمل بسيار دشواري خواهد بود. از آنجاييکه سر و کار داشتن و تحت قاعده در آوردن نتايج حاصله از اين عمل دشوار است، مسيرهاي ارتباطاتي مشخصي بوجود آمدند که توصيف آنها ساده تر است. با اين وجود تبعيت از اين قوانين اختياري است و از اينرو بين همه استفاده کنندگان مختلف مشترک نيست.
اکثر زبانهاي برنامه نويسي ساخت يافته ( Structured ) برنامه نويسان خود را تشويق مي کنند که
انواع داده اي (Data Type ) جديدي را تعريف نمايند که بتواند توصيفي دقيفتر از شي ( Object ) حقيقي ايجاد کند .
در C انواع داده اي ساده را مي توان توسط عبارات enum يا typedef تعريف کرد .
انواع داده اي پيچيده اي بدون شک نياز به
تعريف يک سازه ( Structre ) بخصوص دارند. تعاريف سازه اي جهت در کنار يکديگر قرار دادن داده هاي مشابه مورد استفاده قرار مي گيرند. در اين حالت داده ها در يک واحد منفرد که پس از مدل شدن تا حداکثر ممکن به ( Object ) واقع در دنياي واقعي شباهت دارد گردآوري مي شوند.
در برنامه نويسي
مدولار ( Modular ) يا قطعه قطعه که در واقع نوع توسعه يافته اي از الگوي برنامه نويسي ساخت يافته محسوب مي گردد توابع باز هم به فايلهاي برنامه اي مجزاي سازگار با يکديگر بيشتري تقسيم مي گردند .
به هر يک از اين تکه برنامه ها يک مدول ( Module ) يا به عبارتي ماژول گفته مي شود .
هر مدول داراي مجموعه اي از توابع و داده هاست که تنها خود وي به آنها دسترسي دارد و در ضمن يک مجموعه جداگانه از توابع و داده ها وجود دارند که در دسترس کليه مدولها مي باشند.
کليه توابع مشابه در داخل يک مدول جاي مي گيرند و ضرورتي ندارد که کليه توابع تعريف شده در داخل يک مدول در خارج از آن نيز شناخته شده باشند. محدود کردن تعداد توابع عمومي تعريف شده سبب کاهش تعداد مسيرهاي ارتباطي مورد نياز بين مدولها شده و از اينرو سبب
کاهش پيچيدگي کل سيستم مي گردد .
در اين الگو همچنين عمل
پنهان کردن داده ها ( data hiding ) يا کپسوله کردن ( encapsulating ) آنها امکان پذير شده است. در اين حالت داده هايي که توسط توابع جداگانه اي مورد استفاده قرار مي گيرند از مدولهاي ديگر پنهان مي گردند . بنابراين برنامه نويس مي تواند يک سازه داده اي ( Data Structure ) جديد تعريف کرده و توسط آن مجموعه اي از توابعي که بايد با آنها به کار پرداخته شود شود را معرفي نمايد . در اين حالت ديگر مدولهايي که در خارج از مدول تعريف شده مي باشند مي توانند اين سازه را به عنوان يک نوع داده اي ( data type ) جديد که تنها از توابعي که جهت دسترسي عمومي به آن در نظر گرفته شده اند استفاده مي کند در نظر بگيرند.
داده هايي که در خارج از اين مدولها نيز شناخته شده هستند
داده هاي عمومي ( public ) ناميده مي شوند و در مقابل ، از داده هاي ديگر ( که مدولها ممکن است از آنها جهت کار با يک نوع داده اي جديد استفاده کنند و يا آن آن داده هايي که در خارج از يک مدول قابل مشاهده نيستند ) بعنوان
داده هاي خصوصي ( private ) ياد مي شود. در C از المانها ( elements ) يا عناصري که استاتيک يا اتوماتيک ( automatic ) مي باشند بعنوان خصوصي و از مابقي عناصر به عنوان عمومي ياد مي شود.
اين الگو حقيقتا يک الگوي بسيار قوي است. در حين بررسي مسئله مورد نظر ، برنامه نويس ابتدا بايد کليه انواع داده اي جديدي که در رابطه با حل مسئله مورد نياز مي باشند را تعريف نمايد. اين توابع اعمالي نظير : ايجاد ، حذف ، نمايش ، و يا اعمال ديگر را بر روي يک نوع داده اي منفرد انجام مي دهند. پس از ايجاد اين انواع داده اي جديد مابقي برنامه کوچکتر شده و خواندن آن نيز ساده تر مي گردد .
در اين حالت ديگر برنامه نويس مي تواند يک الگوريتم ( algorithm ) را با نگراني و ترديد کمتري جهت کار با سازه هاي داده اي مورد استفاده قرار دهد . جزئيات مربوط به نوع سازه ( Structre type ) در داخل مدول تعريف شده پنهان مي گردد. عمل اصلاح و تغيير دادن سازه هاي داده اي را نيز اغلب مي توان با تغيير دادن مدول تعريف شده به انجام رساند ، بدون آنکه به مدولهاي اصلي ديگر تغييري وارد گردد.
در بهترين برنامه هاي به زبان C از اين الگوي برنامه نويسي استفاده مي شود.
متاسفانه تبعيت از اين الگو در C اختياري است . در اين حالت اگر در يک عنصر از يک سازه يک مقدار نامعتبر وجود داشته باشد برنامه نويس نمي تواند مطمئن باشد که آيا علت اين امر آن است که توابع دسترسي اين مقدار را در اين مکان ذخيره کرده اند و يا اينکه يک مدول بي وجدان مستقيما به عناصر سازه مورد بحث دسترسي پيدا کرده است . زبانهايي که از الگوي کپسوله کردن داده ها پشتيباني مي کنند استفاده کننده را به استفاده از توابع دسترسي مقيد مي کنند.
ساختار هر نوع داده اي جديدا ايجاد شده نبايد با انواع داده اي از پيش ساخته موجود در زبان برنامه نويسي مورد نظر شباهت داشته باشد . داده هايي که از الگوي ايزوله کردن داده ها پشتيباني مي کنند برنامه نويس را قادر مي سازند تا عملگرهاي ( operators ) ويژه مورد نياز جهت انواع داده اي جديدا تعريف شده خود را نيز تعريف کند. به عنوان مثال با تعريف يک نوع داده اي جديد با نام Complex برنامه نويس مي تواند عملگرهاي رياضي ساده ( يعني : + ، - ، * ، / ) را در مورد اين نوع داده اي تعريف کند به عبارت ديگر برنامه نويس مي تواند تعريف نمايد که منظور وي از وارد کردن دستور زير چيست :
کد:
Complex A, B, C;
C = A + B;
توجه داشته باشيد که من اصطلاح تعريف مجدد ( redefine ) را به کار نبرده ام . عملگرهاي رياضي اوليه همچنان به قوت خود باقي مي مانند.
عمل
ايزوله کردن ( abstraction ) داده ها برنامه نويس را قادر مي سازد که در آينده ، در حين به کارگيري يک الگوريتم ، جزئيات را در نظر نگيرد .
علاوه بر C++ ، زبان Ada متداولترين زبان برنامه نويسي است که مي تواند از قابليت
ايزوله کردن داده ها پشتيباني نمايد.
تعريف يک نوع داده اي جديد که شامل کليه عملگرهاي مورد نياز نيز مي باشد ، مي تواند يک پروسه دست و پا گير باشد ، بويژه براي شخصي که همواره بايد کار را از ابتدا آغاز نمايد .
تعريف يک نوع داده اي (data type ) با توجه به انواع عملگرهاي اوليه ، سبب نامفهوم شدن ارتباطات احتمالا موجود بين انواع عملگرهاي تعريف شده توسط استفاده کننده ( user defined type ) خواهد شد.
به عنوان مثال يک دانشجوي در حال تحصيل در يک کالج يک حالت بخصوص از يک دانشجو ( به مفهوم عمومي آن) است. اين دو نوع ( يعني دانشجوي کالج و يک دانشجو ، در حالت کلي ) در بسياري از صفات با يکديگر مشترکند. بنابراين اينکه ما يک نوع داده اي جديد با نام CollageStudent را با توجه به نوع داده اي اوليه Student ايجاد کرده و يه اين ترتيب رابطه بين اين دو را با يکديگر مشخص کنيم عملي مفيد خواهد بود. به اين موضوع ارث بردن ( inheritance ) گفته مي شود.
با ايجاد چنين تعريفي بايد بتوان بين اين دو تعريف به عقب و جلو حرکت کرد. (يعني بين اين دو تعريف ارتباط برقرار نمود ). يک داده نوع CollageStudent بايد بتواند به توابع تعريف شده جهت نوع Student دسترسي پيدا نمايد .
از اينرو تابعي که انتظار دارد يک اشاره گر ( pointer ) به يک داده از Student را دريافت نمايد مي تواند مستفيما يک اشاره گر به يک داده از CollageStudent را دريافت کند زيرا يک دانشجوي کالج نيز در واعق يک دانشجو است. به قابليت تشخيص دادن
انواع ميراثي ( inherited types ) از يکديگر
پلي مورفيسم (Polymorphism ) گفته مي شود.
بنابراين ما مجددا به تعريف خود از زبانهاي شئ- شناس ( object-oriented ) باز مي گرديم. يک زبان نوع شئ-شناس مي تواند از قابليتهاي کپسوله کردن ، ايزوله کردن ، ارث بردن ، و پلي مورفيسم پشتيباني نمايد.
++C از اين خواص پشتيباني مي کند ، درست به همان ترتيب که پدر آن يعني زبان C از خواص برنامه نويسي ساخت يافته پشتيباني مي کند.