درس سيزدهم – واسطها (Interfaces)
در اين درس با واسطها در زبان C# آشنا خواهيم شد. اهداف اين درس بشرح زير ميباشند :
1- آشنايي با مفهوم كلي واسطها
4- پيادهسازي ارثبري در interface ها
5- نكات مهم و پيشرفته
6- مثالي كاربردي از واسطها
7- منابع مورد استفاده
واسطها از لحاظ ظاهري بسيار شبيه به كلاس هستند با اين تفاوت كه داراي هيچ گونه پيادهسازي نميباشند. تنها چيزي كه در interface به چشم ميخورد تعاريفي نظير رخدادها، متدها، انديكسرها و يا property ها است. يكي از دلايل اينكه واسطها تنها داراي تعاريف هستند و پيادهسازي ندارند آنست كه يك interface ميتوان توسط چندين كلاس يا property مورد ارثبري قرار گيرد، از اينرو هر كلاس يا property خواستار آنست كه خود به پيادهسازي اعضا بپردازد.
حال بايد ديد چرا با توجه به اينكه interface ها داراي پيادهسازي نيستند مورد استفاده قرار ميگيرند يا بهتر بگوئيم سودمندي استفاده از interface ها در چيست؟ تصور كنيد كه در يك برنامه با مولفههايي سروكار داريد كه متغيرند ولي داراي فيلدها يا متدهايي با نامهاي يكساني هستند و بايد نام اين متدها نيز يكسان باشد. با استفاده از يك interface مناسب ميتوان تنها متدها و يا فيلدهاي مورد نظر را اعلان نمود و سپس كلاسها و يا property هاي مورد از آن interface ارثبري نمايند. در اين حالت تمامي كلاسها و property ها داراي فيلدها و يا متدهايي همنام هستند ولي هر يك پيادهسازي خاصي از آنها را اعمال مينمايند.
نكته مهم ديگر درباره interface ها، استفاده و كاربرد آنها در برنامههاي بزرگي است كه برنامهها و يا اشياؤ مختلفي در تماس و تراكنش (transact) هستند. تصور كنيد كلاسي در يك برنامه با كلاسي ديگر در برنامهاي ديگر در ارتباط باشد. فرض كنيد اين كلاس متدي دارد كه مقداري از نوع int بازميگرداند. پس از مدتي طراح برنامه به اين نتيجه ميرسد كه استفاده از int پاسخگوي مشكلش نيست و بايد از long استفاده نمايد. حال شرايط را در نظر بگيريد كه براي تغيير يك چنين مسئله سادهاي چه مشكل بزرگي پيش خواهد آمد. تمامي فيلدهاي مورتبط با اين متد بايد تغيير داده شوند. در ضمن از مسئله side effect نيز نميتوان چشم پوشي كرد.( تاثيرات ناخواسته و غير منتظره و يا به عبارتي پيش بيني نشده كه متغير يا فيلدي بر روي متغير يا فيلدي ديگر اعمال ميكند، در اصطلاح side effect گفته ميشود.) حال فرض كنيد كه در ابتدا interface اي طراحي شده بود. درصورت اعمال جزئيترين تغيير در برنامه مشكل تبديل int به long قابل حل بود، چراكه كاربر يا برنامه و در كل user برنامه در هنگام استفاده از يك interface با پيادهسازي پشت پرده آن كاري ندارد و يا بهتر بگوئيم امكان دسترسي به آن را ندارد. از اينرو اعمال تغييرات درون آن تاثيري بر رفتار كاربر نخواهد داشت و حتي كاربر از آن مطلع نيز نميشود. در مفاهيم كلي شيء گرايي، interface ها يكي از مهمترين و كاربردي ترين اجزاء هستند كه در صورت درك صحيح بسيار مفيد واقع ميشوند. يكي از مثالهاي مشهود درباره interface ها (البته در سطحي پيشرفته تر و بالاتر) رابطهاي كاربر گرافيكي (GUI) هستند. كاربر تنها با اين رابط سروكار دارد و كاري به نحوه عمليات پشت پرده آن ندارد و اعمال تغييرات در پيادهسازي interface كاربر را تحت تاثير قرار نميدهد.
از ديدگاه تكنيكي، واسطها بسط مفهومي هستند كه از آن به عنوان انتزاع (Abstract) ياد ميكنيم. در كلاسهاي انتزاعي (كه با كلمه كليد abstract مشخص ميشدند.) سازندة كلاس قدر بود تا فرم كلاس خود را مشخص نمايد : نام متدها، نوع بازگشتي آنها و تعداد و نوع پارامتر آنها، اما بدون پيادهسازي بدنه متد. يك interface همچنين ميتواند داراي فيلدهايي باشد كه تمامي آنها static و final هستند. يك interface تنها يك فرم كلي را بدون پيادهسازي به نمايش ميگذارد.
از اين ديدگاه، يك واسط بيان ميدارد كه : " اين فرم كلي است كه تمامي كلاسهايي كه اين واسط را پيادهسازي ميكنند، بايد آنرا داشته باشند." از سوي ديگر كلاسها و اشياء ديگري كه از كلاسي كه از يك واسط مشتق شده استفاده ميكنند، ميدانند كه اين كلاس حتماً تمامي متدها و اعضاي واسط را پيادهسازي ميكند و ميتوانند به راحتي از آن متدها و اعضا استفاده نمايند. پس به طور كلي ميتوانيم بگوئيم كه واسطها بمنظور ايجاد يك پروتكل (protocol) بين كلاسها مورد استفاده قرار ميگيرند. (همچنان كه برخي از زبانهاي برنامهسازي بجاي استفاده از كلمه كليدي interface از protocol استفاده مينمايند.)
به دليل اينكه كلاسها و ساختارهايي كه از interface ها ارثبري ميكنند موظف به پيادهسازي و تعريف آنها هستند، قانون و قاعدهاي در اين باره ايجاد ميگردد. براي مثال اگر كلاس A از واسط IDisposable ارثبري كند، اين ضمانت بوجود ميآيد كه كلاس A داراي متد Dispose() است، كه تنها عضو interface نيز ميباشد. هر كدي كه ميخواهد از كلاس A استفاده كند، ابتدا چك مينمايد كه آيا كلاس A واسط IDisposable را پيادهسازي نموده يا خير. اگر پاسخ مثبت باشد آنگاه كد متوجه ميشود كه ميتواند از متد A.Dispose() نيز استفاده نمايد. در زير نحوه اعلان يك واسط نمايش داده شده است.
interface IMyInterface
{
void MethodToImplement();
}
در اين مثال نحوه اعلان واسطي با نام IMyInterface نشان داده شده است. يك قاعده (نه قانون!) براي نامگذاري واسطها آنست كه نام واسطها را با "I" آغاز كنيم كه اختصار كلمه interface است. در interface اين مثال تنها يك متد وجود دارد. اين متد ميتوان هر متدي با انواع مختلف پارامترها و نوع بازگشتي باشد. توجه نماييد همانطور كه گفته شد اين متد داراي پيادهسازي نيست و تنها اعلان شده است. نكته ديگر كه بايد به ان توجه كنيد آنست كه اين متد به جاي داشتن {} به عنوان بلوك خود، داراي ; در انتهاي اعلان خود ميباشد. علت اين امر آنست كه interface تنها نوع بازگشتي و پارامترهاي متد را مشخص مينمايد و كلاس يا شياي كه از آن ارث ميبرد بايد آنرا پيادهسازي نمايد. مثال زير نحوه استفاده از اين واسط را نشان ميدهد.
مثال 1-13 : استفاده از واسطها و ارثبري از آنها
class InterfaceImplementer : IMyInterface
{
static void
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}
در اين مثال، كلاس InterfaceImplementer همانند ارثبري از يك كلاس، از واسط IMyInterface ارثبري كرده است. حال كه اين كلاس از واسط مورد نظر ارثبري كرده است، بايد، توجه نماييد بايد، تمامي اعضاي آنرا پيادهسازي كند. در اين مثال اين عمل با پيادهسازي تنها عضو واسط يعني متد MethodToImplement() انجام گرفته است. توجه نماييد كه پيادهسازي متد بايد دقيقا از لحاظ نوع بازگشتي و تعداد و نوع پارامترها شبيه به اعلان موجود در واسط باشد، كوچكترين تغييري باعث ايجاد خطاي كامپايلر ميشود. مثال زير نحوه ارثبري واسطها از يكديگر نيز نمايش داده شده است.
مثال 2-13 : ارثبري واسطها از يكديگر
using System;
interface IParentInterface
{
void ParentInterfaceMethod();
}
interface IMyInterface : IParentInterface
{
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
static void
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
iImp.ParentInterfaceMethod();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
public void ParentInterfaceMethod()
{
Console.WriteLine("ParentInterfaceMethod() called.");
}
}
مثال 2-13 داراي 2 واسط است : يكي IMyInterface و واسطي كه از آن ارث ميبرد يعني IParentInterface. هنگاميكه واسطي از واسط ديگري ارثبري ميكند، كلاس يا ساختاري كه اين واسطها را پيادهسازي ميكند، بايد تمامي اعضاي واسطهاي موجود در سلسله مراتب ارثبري را پيادهسازي نمايد. در مثال 2-13، چون كلاس InterfaceImplementer از واسط IMyInterface ارثبري نموده، پس از واسط IParentInterface نيز ارثبري دارد، از اينرو بايد كليه اعضاي اين دو واسط را پيادهسازي نمايد.
1- با استفاده از كلمه كليد interface در حقيقت يك نوع مرجعي (Reference Type) جديد ايجاد نمودهايد.
2- از لحاظ نوع ارتباطي كه واسطها و كلاسها در ارثبري ايجاد مينمايند بايد به اين نكته اشاره كرد كه، ارثبري از كلاس رابطه "است" يا "بودن" (is-a relation) را ايجاد ميكند (ماشين يك وسيله نقليه است) ولي ارثبري از يك واسط يا interface نوع خاصي از رابطه، تحت عنوان "پيادهسازي" (implement relation) را ايجاد ميكند. ("ميتوان ماشين را با وام بلند مدت خريد" كه در اين جمله ماشين ميتواند خريداري شدن بوسيله وام را پيادهسازي كند.)
3- فرم كلي اعلان interface ها بشكل زير است :
[attributes] [access-modifier] interface interface-name [:base-list]{interface-body}
كه در اعضاي آن بشرح زير مي باشند :
attributes : صفتهاي واسط
access-modifiers : private يا public سطح دسترسي به واسط از قبيل
interface-name : نام واسط
:base-list : ليست واسطهايي كه اين واسط آنها را بسط ميدهد.
Interface-body : بدنه واسط كه در آن اعضاي آن مشخص ميشوند
توجه نماييد كه نميتوان يك واسط را بصورت virtual اعلان نمود.
4- هدف از ايجاد يك interface تعيين توانائيهاييست كه ميخواهيم در يك كلاس وجود داشته باشند.
5- به مثالي در زمينه استفاده از واسطها توجه كنيد :
فرض كنيد ميخواهيد واسطي ايجاد نماييد كه متدها و property هاي لازم براي كلاسي را كه ميخواهد قابليت خواندن و نوشتن از/به يك پايگاه داده يا هر فايلي را داشته باشد، توصيف نمايد. براي اين منظور ميتوانيد از واسط IStorable استفاده نماييد.
در اين واسط دو متد Read() و Write() وجود دارند كه در بدنه واسط تعريف ميشوند ك
interface IStorable
{
void Read( );
void Write(object);
}
حال ميخواهيد كلاسي با عنوان Document ايجاد نماييد كه اين كلاس بايد قابليت خواندن و نوشتن از/به پايگاه داده را داشته باشد، پس ميتوانيد كلاس را از روي واسط IStorable پيادهسازي كنيد.
public class Document : IStorable
{
public void Read( ) {...}
public void Write(object obj) {...}
// ...
}
حال بعنوان طراح برنامه، شما وظيفه داري تا به پيادهسازي اين واسط بپردازيد، بطوريكه كليه نيازهاي شما را برآورده نمايد. نمونهاي از اين پيادهسازي در مثال 3-13 آورده شده است.
مثال 3-13 : پيادهسازي واسط و ارثبري – مثال كاربردي
using System;
// interface اعلان
interface IStorable
{
void Read( );
void Write(object obj);
int Status { get; set; }
}
public class Document : IStorable
{
public Document(string s)
{
Console.WriteLine("Creating document with: {0}", s);
}
public void Read( )
{
Console.WriteLine("Implementing the Read Method for IStorable");
}
public void Write(object o)
{
Console.WriteLine("Implementing the Write Method for IStorable");
}
public int Status
{
get
{
return status;
}
set
{
status = value;
}
}
private int status = 0;
}
public class Tester
{
static void
{
Document doc = new Document("Test Document");
doc.Status = -1;
doc.Read( );
Console.WriteLine("Document Status: {0}", doc.Status);
IStorable isDoc = (IStorable) doc;
isDoc.Status = 0;
isDoc.Read( );
Console.WriteLine("IStorable Status: {0}", isDoc.Status);
}
}
خروجي برنامه نيز بشكل زير است :
Output:
Creating document with: Test Document
Implementing the Read Method for IStorable
Document Status: -1
Implementing the Read Method for IStorable
IStorable Status: 0
6- در مثال فوق توجه نماييد كه براي متدها واسط IStorable هيچ سطح دسترسي (public,private و ...) در نظر گرفته نشده است. در حقيقت تعيين سطح دسترسي باعث ايجاد خطا ميشود چراكه هدف اصلي از ايجاد يك واسط ايجاد شيء است كه تمامي اعضاي آن براي تمامي كلاسها قابل دسترسي باشند.
7- توجه نماييد كه از روي يك واسط نميتوان نمونهاي جديد ايجاد كرد بلكه بايد كلاسي از آن ارثبري نمايد.
8- كلاسي كه از واسط ارثبري ميكند بايد تمامي متدهاي آنرا دقيقا همان گونه كه در واسط مشخص شده پيادهسازي نمايد. به بيان كلي، كلاسي كه از يك واسط ارث ميبرد، فرم و ساختار كلي خود را از واسط ميگيرد و نحوه رفتار و پيادهسازي آنرا خود انجام ميدهد.
خلاصه :
در اين درس با مفاهيم كلي و اصلي درباره واسطها آشنا شديد. هم اكنون ميدانيد كه واسطها چه هستند و سودمندي استفاده از آنها چيست. همچنين نحوه پيادهسازي واسط و ارثبري از آنرا آموختيد.
مبحث واسطها بسيار گسترده و مهم است و اميد است در بخشهاي آينده در سايت، بتوانم تمامي مطالب را بطور حرفهاي و كامل در اختيار شما قرار دهم.
نوشته شده در دوشنبه 5 فروردین1381 توسط اقبال سهرابی | لينك ثابت |

