دريافت
دستورات در برنامههاي مختلف، بهشيوههاي مختلف انجام ميپذيرد. ممکن است
از طريق صفحه نمايش چند لمسي دستورات به برنامه وارد شود، ممکن است از
طريق ماوس، و يا حتي ممکن است با تايپ دستورات، بخواهيم برنامه را کنترل
کنيم.
کنترل برنامهها با تايپ دستورات، زماني
بهکار ميآيد که دسترسي مستقيم يا دسترسي گرافيکي به برنامه نداريم و يا
بايد از طريق کنسول با برنامه خود ارتباط برقرار کنيم و پاسخ را دريافت
کنيم. در اين حالت، قابليت اجرا از طريق خط فرمان يک مزيت براي برنامهمان
بهحساب خواهد آمد.
در اين مقاله قصد نداريم بهسراغ دستورات پيچيده
برويم و تلاش بر اين است که با مفاهيم شيگراي فعلي، يک پارسر براي خط
فرمان بنويسيم. با بسط و گسترش همين کدها ميتوان هر برنامهاي را به
موتور تشخيص دستور از راه خط فرمان مجهز کرد.
نخست آنکه وجه شباهت تمامي دستورات با همديگر،
اين است که همهشان داراي پارامترهاي ورودي هستند و همه آنها بايد
بهشيوهاي صحيح اجرا شوند. بهعبارت ديگر وقتي دستوري وارد ميشود، بايد
تشخيص داده شده، اجرا شده، و در صورت عدم شناسايي دستور، پيغام خطا ارسال
کند. قدم نخست براي راهاندازي يک خط فرمان، توليد کلاسي پايه بهصورت زير
است:
 class baseComman_d {
public :
baseComman_d(int argc,char** argv)
baseComman_d()
virtual void execute()
virtual bool contain(const char* arg)
virtual void printHelp()
private :
int argc
char** argv
{
اين کلاس چيست و چه کاري قرار است انجام دهد؟
اين کلاس تمامي متدها و فيلدهاي مورد نياز براي
اجرا و نگهداري هر دستور را در خود دارد، و بهعبارت بهتر يک شي از يک
دستور است. بقيه دستورات از اين کلاس ارث بري ميکنند و مستلزم بازنويسي
متدهايي هستند که بهصورت مجازي (Virtual) تعريف شدهاند. اين کلاس دو
فيلد با دسترسي خصوصي (Private) دارد. دليل اين امر اين است که اشيا ديگر
نيازي به دانستن مقدار آنها و ديدن آنها از يک شي ديگر ندارند و در واقع
اين در اين فيلدها مشخص ميشود که چه دستوري چگونه بايد اجرا بشود. فيلد
اول argc تعداد آرگومانهاي ورودي هر دستور را مشخص ميکند و فيلد argv يک
آرايه دو بعدي از کاراکترها (آرايهاي از رشتهها) است، که شامل دستور و
پارامترهاي ورودي آن دستور است.
اين کلاس دو سازنده دارد که يکي از آنها مقدارهاي
argc و argv را از ورودي ميگيرد و ديگري هيچ آرگوماني را نمي پذيرد و از
آن به سازنده پيش فرض (Default Constructor) ياد ميشود. متد execute
دستور را اجرا ميکند و وظيفه آن مديريت اجراي صحيح دستور است. و متد
ديگر printHelp است، اين متد زماني اجرا ميشود که دستور با پارامتر /?
اجرا شود و هدف آن نشان دادن اطلاعاتي در مورد دستور است. متد ديگر
contain است اين متد بررسي ميکند آيا پارامتر خاصي در argv موجود است يا
خير.
بسيار خب، حالا به يک فرمت کلي دست پيدا کرديم و
ميدانيم براي پيادهسازي هر دستور به چه چيزهايي نياز است و هر دستور
بايد از چه الگويي بايد پيروي کند. حالا ميتوان بهشيوهاي ديگري به هر
دستور نگاه کرد تا پيادهسازي راحتتري داشته باشد. هر دستور يکسري
پارامتر را به عنوان ورودي دريافت ميکند. خوب هر پارامتر براي انجام عمل
خاصي است، پس در کلاسهاي مربوط به هر دستور علاوه بر دستورات فوق بايد
متدهايي براي انجام هر عمل نوشته شود. بهطور مثال دستور Copy، اين دستور
بايد دو پارامتر را بهصورت پيشفرض دريافت کند. اولين پارامتر فايل منبع
و دومي فايل مقصد را مشخص ميکند حال اگر دستور copy با فرمان /x اجرا شود
بايد فايل منبع به مقصد منتقل شود و کپي گرفته نشود. بنابراين، دو متد به
نامهاي CopyFile و MoveFile را بايد در کلاس مربوط به دستور copy بنويسيم
که عمليات مربوطه را انجام دهند.
مشکلي که در اين ميان ممکن است رخ بدهد، اين است
که متدهاي MoveFile و CopyFileرا بايد بهچه صورتي تبديل کنيم که امنيت
کافي داشته باشد؟ آيا اين متدها private باشند يا public؟ از آنجا که اين
متدها براي کلاس مربوط به دستور copy هستند پس بايد بهصورت private تعريف
شوند. اما اگر طور ديگري به اين دو متد نگاه کنيم، و اگر دستور ديگري
داشتيم که نياز به کپي و کات کردن فايلها داشت نتيجه چه ميشد؟ بايد اين
متدها هر بار نوشته شوند؟ يا اينکه آنها را بصورت عمومي تعريف کنيم؟
اگر بهصورت عمومي تعريف کنيم، طراحي ما غلط است
چون ديگر کلاسها نبايد متدهايي که براي اجراي پارامترهاي ورودي هر دستور
هستند را ببينند، راه حل چيست؟ميتوانيم با نوشتن يک کلاس جداگانه براي
کارکردن با فايلها، اين مشکل را حل کنيم. اين کار دو مزيت دارد:
1- متدهاي CopyFile و MoveFile از سطح کلاس
دستورها جدا خوهد شد و بطور مستقل کار ميکنند. اين کار debug (اشکال
زدايي) برنامه را راحتتر و نگهداري کد را سادهتر ميکند.
در ضمن طراحي يکپارچهاي خواهيم داشت، بدين ترتيب کلاس File را براي کار با فايلها را بهصورت زير مينويسيم:
class File{
const static int BUF_SIZE = 4096
public:
static void Copy(char* source,char* destination)
static void Move(char* source,char* destination)
static void Delete(char* filename)
static void Rename(char* oldname,char* newname)
static bool Exist(char* filename)
static void Create(char* filename,bool overwrite)
}
نيازي به توضيح در مورد متدهاي کلاس نيست، بسيار خوب پس پيادهسازي متد execute از کلاس مربوط به دستور copy به صورت زير خواهد شد:
void execute(){
if(this-»contain("/X") || this-»contain("/x")){
File::Move(this-»argv[1],this-»argv[2])
}
else
File::Copy(this-»argv[1],this-»argv[2])
}
حالا که موفق شديم کلاس مربوط به Copy را پياده
کنيم، به بررسي کلاس ديگري مي پردازيم. اين کلاس براي ايجاد رابطه با
کاربر مورد استفاده قرار ميگيرد، نام اين کلاس را Helper ميگذاريم، اين
کلاس قرار است دستوراتي را که کاربر وارد ميکند، اعتبارسنجي کند و با
توجه به دستورات وارد شده، عمليات مناسب را اجرا کند. اين کلاس بصورت زير
تعريف شده است :
class utility{
public :
utility(char* comman_d)
utility()
void Run()
private :
char* comman_d
char** argv
int argc
baseComman_d* internaleComman_d
void parse()
void createComman_d()
{
اين کلاس شامل 3 فيلد است يکي argv وديگري argc که
قبلا درمورد آنها و کاربردشان صحبت کردهايم. ديگري يک شي از baseComman_d.
همانطور که گفتيم کلاس baseComman_d کلاس پدر همه کلاسهاي مربوط به
دستورات است و مقدار آن ميتواند تمامي دستورات را قبول کند، متد Run اين
کلاس در واقع دستوري که کاربر وارد کرده است، را تجزيه و تحليل ميکند و
فرمان مربوطه را اجرا ميکند. در اين متد ابتدا عبارت وارد شده توسط متد
parse تجزيه و تحليل ميشود و فيلدهاي argv و argc مقدار دهي ميشوند، و
سپس متد createComman_d اجرا ميشود و بر اساس مقدار argv فيلد
internalComman_d مقدار دهي ميشود و سپس متدexecute از کلاس baseComman
اجرا ميشود. با اين کار دستور مورد نظر انجام شده و خروجيها چاپ ميشود،
تا اينجا ما موفق شديم دستوري را وارد و آن را اجرا کنيم. حال بگذاريد
شرايطي را بررسي کنيم که ما بخواهيم دستورات را پست سر هم وارد كنيم و
اينکار تا آنجا ادامه پيدا کند که کاربر دستور exit را وارد کند و از
برنامه خارج شود (يعني يک محيط خط فرمان براي کاربر ايجاد کنيم)، وظيفه
اين کار بر عهده متد main يا متد اجرايي برنامه است که به اين صورت پياده
سازي شده است:
با اين پيادهسازي، يک
محيط خوب براي برنامه خود داريد که ميتوانيد در آن دستورات مختلف را
پياده کنيد. در اين مقاله، دستورات اجرايي، همان دستورات سيستم عامل
ويندوز بود. آيا ميتوانيد توابع و کلاسهاي داخلي سيستم خود را با
استفاده از اين روش، راهاندازي و اجرا کنيد. موفق باشيد.