السلام عليكم و رحمه الله تعالى و بركاته
ال Interface هو وسيله لتحديد الوظائف التى يجب ان يمتلكها الكائن ، لكنه لا يحدد كيف تم تنفيذ هذه الوظيفه ، هذا يمكنك من استخدام كائنات مختلفه بالتبادل لأداء نفس الوظيفه مادام لهم نفس ال Interface ، لنفرض مثلا ان هناك ريموت كنترول يقوم بفتح و قفل تلفيزيون من نوع معين ، اذا قمت بتوجيه هذا الريموت كنترول لتلفيزيون لا يماثل interface -وظائف- التليفزيون الاول فإن الريموت لن يعمل مع هذا التلفيزيون ، لكن اذا كان التلفيزيونين يحتويان على نفس ال interface التى يستخدمها الريموت او كان كل تلفيزيونات العالم كلها لها نفس ال intreface الذى يستخدمه الريموت فإن هذا الريموت سيفتح و يغلق التلفيزيونات كلها و سيكون highly reusable ، و لن نحتاج الى تصميم ريموت خاص لكل تلفيزيون، انظر الى المثال البسيط التالى :
function SonyTV(){}; SonyTV.prototype={ open:function(){/*code*/}, close:function(){/*code*/}, /*rest of SonyTV functions*/ } function SonyRemote(tv){ this.tv = tv; }; SonyRemote.prototype = { open:function(){ this.tv.close(); }, close:function(){ tv.close(); }, tuneChannel:function(){/*code*/}, adjustColor:function(){/*code*/}, adjustVolume:function(){/*code*/}, mute:function(){/*code*/}, }
يمكننا استخدام SonyRemote من SonyTV مع العلم انه ليس من شأننا كيف يتم قفل او فتح او ميزه اخرى داخل التلفيزيون Implementation ، نحن نتعامل مع اسماء الوظائف فقط Interface كما يوضح المثال البسيط التالى :
var myTV = new SonyTV();
var myRemote = new SonyRemote(myTV);
myRemote.open();
myRemote.close();
هيا بنا ننشأ كائن جديد لتلفيزيون LG مع العلم ان الكود الخاص بكيفيه تنفيذ كل وظيفه ليس من شأننا ، لأننا سوف نتعامل مع Interface كما يوضح الكود البسيط التالى :
function LGTV(){}; LGTV.prototype = { openTV:function(){/*code*/}, closeTV:function(){/*code*/}, adjustChannel:function(){/*code*/}, tuneColors:function(){/*code*/}, setVolume:function(){/*code*/} }
سنختبر هل يعمل SonyRemote مع تلفيزيون LG كما يوضح الكود البسيط التالى :
var myTV = new LGTV();
var myRemote = new SonyRemote(myTV);
myRemote.open(); // Error
myRemote.close(); // Error
اسماء الوظائف التى يستخدمها تلفيزيون LG تختلف تماما عن الوظائف التى يستخدمها تلفيزيون SONY و يتعامل معها الريموت ، هذا الريموت لا يهمه كيف يتم تشغيل او اطفائه – هذا شغله مهنسدين ال hardware- لكن مايهمه انه سيجد الوظيفه المناسبه بالإسم المناسب حتى يستدعيها ، اذا نظرت للكود الخاص بالريموت ستجد ان ال constructor تبعه يقبل instance من التلفيزيون الذى سيتعامل معه .
يمكن عمل Interface بين الكائنات بمنتهى السهوله فى اللغات مثل JAVA و C# و PHP و غيرهم ، و الكود التالى يوضح كيفيه عمل Interface فى لغه PHP :
interface MyInterface {
public function interfaceMethod($argumentOne, $argumentTwo);
}
هذا الكود يقوم بعمل Interface بإسم MyInterface ، اى class سيقوم بادراج هذا ال Interface لابد من ان يدرج الوظيفه interfaceMethod التى تقبل عبارتين argumentOne و argumentTwo ، الكود التالى يوضح كيفيه ادرج هذا ال Interface فى صنفين الاول يوافقه MyClass و التانى BadClass يخالفه :
class MyClass implements MyInterface {
public function interfaceMethod($argumentOne, $argumentTwo){
return $argumentOne . $argumentTwo;
}
}
لا توجد مشكله فى هذا الصنف لأنه يدرج الوظيفه interfaceMethod التى صرح بها MyInterface ، لكن الصنف التالى BadClass ستنشأ عنه مشاكل :
class BadClass implements MyInterface {
// No Methods
}
سينتج عن هذا الكود error فى وقت التشغيل runtime لأن المترجم يفصح اى صنف يقوم بأنه يدرج MyInterface عن طريق كتابه implements MyInterface فى تعريف الكلاس كما يوضح الكود السابق ، رساله الخطأ من الكود السابق كما يلى :
Fatal error: Class BadClass contains 1 abstract method and must therefore be declared (MyInterface::interfaceMethod)
اى صنف يقوم بادراج ال MyInterface بطريقه صحيحه ، يمكن استخدامه بالتبادل مكان الكائن الحالى الذى يدعم MyInterface .
فى جافاسكربت لا توجد خاصيه فى اللغه تقوم بعمل Intreface ولا يقوم المترجم بفحص هل الكائن صادق فى كونه يدعم هذا ال interface ام لا ، لكن لكون لغه الجافاسكربت مرنه جدا ، يمكننا تقليد عمل ال Interface و استغلال امكانياته من اجل كتابه API مرن و قابل لاعاده الاستعمال .
يمكننا تقليد عمل ال Intreface عن طريق Duck typing هذه التسميه جاءت من المقوله :
If it walks like a duck and quacks like a duck, it's a duck
يعنى لو شفت حاجه ماشيه ذى البطه و بتكاكى ذى البطه ، تبقى بطه ، هذا التكنيك يستخدم لفحص هل كائن ما مشتق من صنف ما عن طريق فحص اسماء الوظائف التى بهذا الكائن ، لكن هذا التكنيك مثالى لفحص هل كائن ما يقوم بإدراج Interface ما .
function Interface(name,methods){ // Interface function constructor this.name=name; this.methods=methods; } // public static method Interface.ensureImplements = function(object,interface){ for(var i=0, len=interface.methods.length; i<len; i++){ var method = interface.methods[i]; if(!object[method] || typeof object[method] != 'function'){ throw new Error ("The Interface "+interface.name+ " method >"+ method+ "is not implemented"); } } }
يمكننا استخدام هذا ال Interface constructor كما يلى :
var remoteInterface = new Interface("remoteInterface",['open','close']);
function GlobalRemote(tv){
Interface.ensureImplements(tv,remoteInterface);
this.tv = tv;
}
GlobalRemote.prototype= {
open:function(){
this.tv.open();
},
close:function(){
this.tv.close();
}
}
قمت بعمل inerface بإسم remoteInterface يحتوى على وظيفتين open و close ، و داخل ال GlobalRemote constructor قمت بفحص هل ال tv الذى يمرر لها يطابق ال remoteInterface ام لا ، لو طابق ال interface سيعمل الكود طبيعى ، اما اذا لم يطابق سيتم ارسال error يقول انه هناك وظيفه لم يتم ادراجها من هذا ال interface ، و لنفرض ان تلفيزيون LG و SONY جميعهم ادرجوا ال interface الذى بإسم globalInterface فإن الريموت سيعمل معهم جميعا ميعادا تلفيزيون Toshiba الذى لم يدرجه كما يوضح الكود التالى :
function SonyTV(){}; SonyTV.prototype = { open:function(){}, close:function(){}, } function LGTV(){}; LGTV.prototype= { open:function(){}, close:function(){}, } function ToshibaTV(){}; ToshibaTV.prototype = { openTV:function(){}, closeTV:function(){}, } var myRemote = new GlobalRemote(new SonyTV); myRemote.open(); myRemote.close(); var myRemote = new GlobalRemote(new LGTV) myRemote.open(); myRemote.close(); var myRemote = new GlobalRemote(new ToshibaTV); myRemote.open(); // Error : The Interface remoteInterface method > open is not implemented
ال Interface مهم للغايه فى بعض ال design patterns مثل Factory و Composite و Decorator و Command ، كما انه لا بد منه عند العمل على مشاريع كبيره يشترك فيها اكثر من مبرمج حيث ستكون وسيلها التواصل بينهم هى ال Interface ، مادام هناك كائنات تدرج نفس ال Interface اذا لا يوجد مشكله من استخدام اى منها ، و اذا تطور ال api الذى تكتبه فإنه لا يوجد مشكله فى عمله على الانظمه القديمه مادام يدرج نفس ال Interface الموجود فى الانظمه القديمه ، انا قد قمت بمسح بعض الكود الخاص ب type checking فى Interface و Interface.ensureImpelemnts للتسهيل ، يمكنك ايضا عمل وظيفه ensureImpelemnts تقبل اكثر من interface لتفحص هلى الكائن يطابقهم ام لا ، اذا كان ال interface لا يضيف قيمه للكود التى تكتبه إذا فلا تضيفه لأنه سيزيد من تعقيد الكود نسبيا و سيصعب على المبرمجين المبتدئين معرفه ماهيه ال interface ،هذا الموضوع هو الفصل الثانى من كتاب APRESS: Pro JavaScript design patterns الموجود فى العمود الجانبى فى المدونه .
الأوسمة: javascript, oop
05/03/2009 عند 6:39 ص |
يعتمد هذا التكنيك على النظم في انشاء تكنيك uml ومن دونه لايمكن استعيباب البرمجيات الكبيرة مع ي عالمنا العربي وانا اولهم في بر مجياتنا لانعطي اي اهتمام لهذه التقنية المستقبلية
مستقبل لانها تلازمك في كل تطوير باختصار دون تخطيط ===دون نجاح
بارك الله فيك وجعلها الله في ميزان حسناتك
05/03/2009 عند 8:22 م |
بالرغم انى غير متخصص فى الجافا اسكربت ولكن طريقة شرحك جيدة جداً لدرجة جعلتنى افهم هذا الموضوع بسهولة و الحمد لله
ينبغى ان تستخدم الصور التوضيحية لكى تجعلها اكثر بساطة ايضاً
21/03/2009 عند 7:25 ص |
WAOOOOO