سلام! امروز قصد داریم در مورد یک مفهوم مهم در جاوا صحبت کنیم: رابط ها. احتمالاً این کلمه برای شما آشناست. به عنوان مثال، اکثر برنامه ها و بازی های کامپیوتری دارای رابط هستند. در یک مفهوم گسترده، یک رابط نوعی "کنترل از راه دور" است که دو طرف تعامل را به هم متصل می کند. یک مثال ساده از یک رابط در زندگی روزمره یک کنترل از راه دور تلویزیون است. این دو شی - یک فرد و یک تلویزیون - را به هم متصل می کند و وظایف مختلفی را انجام می دهد: صدا را کم یا زیاد کنید، کانال ها را تغییر دهید، و تلویزیون را روشن یا خاموش کنید. یکی از طرفین (شخص) باید به رابط دسترسی داشته باشد (یک دکمه روی کنترل از راه دور را فشار دهید) تا باعث شود طرف دوم این عمل را انجام دهد. به عنوان مثال، برای تغییر تلویزیون به کانال بعدی. علاوه بر این، کاربر نیازی به دانستن نحوه سازماندهی تلویزیون یا نحوه اجرای فرآیند تغییر کانال در داخل ندارد. تنها چیزی که کاربر به آن دسترسی دارد رابط است. هدف اصلی رسیدن به نتیجه مطلوب است. این چه ربطی به برنامه نویسی و جاوا داره؟ همه چیز :) ایجاد یک رابط بسیار شبیه به ایجاد یک کلاس معمولی است، اما به جای استفاده از کلمه class ، کلمه interface را نشان می دهیم . بیایید به ساده ترین رابط جاوا نگاه کنیم، ببینیم چگونه کار می کند و چرا به آن نیاز داریم:
کار کرده اید ! بهطور دقیقتر، شما با پیادهسازیهای آنها کار کردهاید - ،،، و غیره. همین نمودار به وضوح مثالی را نشان میدهد که در آن یک کلاس چندین رابط را به طور همزمان پیادهسازی میکند. به عنوان مثال، رابط های و (صف دو پایانی) را پیاده سازی می کند . شما با رابط کاربری یا بهتر است بگوییم با پیاده سازی آن آشنا هستید . به هر حال، این نمودار یک ویژگی را نشان می دهد: رابط ها می توانند رابط های دیگر را به ارث ببرند. رابط ارث می برد ، در حالی که ارث می برد . اگر می خواهید رابطه بین اینترفیس ها را نشان دهید که در آن یک رابط نسخه توسعه یافته دیگری است، این امر ضروری است. بیایید یک مثال با رابط را در نظر بگیریم. ما هنوز بررسی نکردهایم ، اما نسبتاً ساده است و مانند یک صف معمولی یا صف در یک فروشگاه کار میکند. شما فقط می توانید موارد را به انتهای صف اضافه کنید و فقط می توانید آنها را از ابتدا بردارید. در برخی مواقع، توسعهدهندگان به یک نسخه بهبودیافته از صف نیاز داشتند تا آیتمهایی را در هر دو طرف اضافه کنند. بنابراین آنها یک رابط ایجاد کردند که یک صف دو طرفه است. تمام روش های یک صف معمولی را دارد. به هر حال، والد صف دو طرفه است، اما روشهای جدیدی را نیز اضافه میکند.
public interface CanSwim {
public void swim();
}
ما یک رابط CanSwim ایجاد کرده ایم . این کمی شبیه به کنترل از راه دور ما است، اما با یک دکمه: روش swim() . اما چگونه از این ریموت کنترل استفاده کنیم؟ برای این کار باید یک متد یعنی دکمه ریموت کنترل خود را پیاده سازی کنیم. برای استفاده از یک رابط، برخی از کلاس ها در برنامه ما باید متدهای آن را پیاده سازی کنند. بیایید کلاسی را اختراع کنیم که اشیاء آن «می توانند شنا کنند». به عنوان مثال، یک کلاس Duck مناسب است:
public class Duck implements CanSwim {
public void swim() {
System.out.println("Duck, swim!");
}
public static void main(String[] args) {
Duck duck = new Duck();
duck.swim();
}
}
"ما در اینجا چه می بینیم؟ کلاس Duck با کلمه کلیدی Implements با رابط CanSwim مرتبط است . ممکن است به خاطر داشته باشید که ما از مکانیزم مشابهی برای مرتبط کردن دو کلاس از طریق ارث بری استفاده کردیم، اما در این مورد از کلمه extends استفاده کردیم. با وضوح کامل، میتوانیم « کلاس عمومی Duck Implements CanSwim » را به معنای واقعی کلمه ترجمه کنیم: « کلاس عمومی Duck رابط CanSwim را پیادهسازی میکند.» این بدان معناست که یک کلاس مرتبط با یک رابط باید تمام متدهای آن را پیادهسازی کند. توجه: کلاس ما، درست مانند اینترفیس یک متد دارد و حاوی مقداری منطق است. این یک الزام اجباری است. اگر فقط بدون ایجاد متد در کلاس بنویسیم ، کامپایلر به ما یک خطا می دهد: Duck abstract نیست و روش abstract swim را لغو نمی کند. () در CanSwim چرا؟ چرا این اتفاق می افتد؟ اگر خطا را با استفاده از مثال تلویزیون توضیح دهیم، مانند این است که یک کنترل از راه دور تلویزیون را با دکمه 'تغییر کانال' به کسی بدهید که نمی تواند کانال را تغییر دهد. می توانید دکمه را فشار دهید. هر چقدر که دوست دارید، اما کار نمی کند. کنترل از راه دور به خودی خود کانال ها را تغییر نمی دهد: فقط سیگنالی را به تلویزیون می فرستد که فرآیند پیچیده تغییر کانال را اجرا می کند. و در مورد اردک ما هم همینطور است: باید شنا بلد باشد تا بتوان با استفاده از رابط صدا کرد. اگر نداند چگونه، رابط دو طرف - شخص و برنامه را به هم متصل نمی کند. فرد نمی تواند از روش برای شنا کردن در داخل برنامه استفاده کند. اکنون واضح تر متوجه می شوید که رابط ها برای چه چیزی هستند. یک اینترفیس رفتاری را توصیف می کند که کلاس های پیاده سازی اینترفیس باید داشته باشند. "رفتار" مجموعه ای از روش ها است. اگر می خواهیم چندین پیام رسان ایجاد کنیم، ساده ترین کار ایجاد یک رابط است. هر پیام رسان به چه چیزی نیاز دارد؟ در سطح پایه، آنها باید بتوانند پیام دریافت و ارسال کنند. Duck
CanSwim
swim()
public class Duck implements CanSwim
swim()
Duck
CanSwim
CanSwim
swim()
Duck
Messenger
public interface Messenger{
public void sendMessage();
public void getMessage();
}
اکنون می توانیم به سادگی کلاس های پیام رسان خود را ایجاد کنیم که رابط مربوطه را پیاده سازی کنند. خود کامپایلر ما را مجبور می کند تا آنها را در کلاس های خود پیاده سازی کنیم. تلگرام:
public class Telegram implements Messenger {
public void sendMessage() {
System.out.println("Sending a Telegram message!");
}
public void getMessage() {
System.out.println("Receiving a Telegram message!");
}
}
واتس اپ:
public class WhatsApp implements Messenger {
public void sendMessage() {
System.out.println("Sending a WhatsApp message!");
}
public void getMessage() {
System.out.println("Reading a WhatsApp message!");
}
}
وایبر:
public class Viber implements Messenger {
public void sendMessage() {
System.out.println("Sending a Viber message!");
}
public void getMessage() {
System.out.println("Receiving a Viber message!");
}
}
این چه مزایایی را فراهم می کند؟ مهمترین آنها کوپلینگ شل است. تصور کنید که ما در حال طراحی برنامه ای هستیم که داده های مشتری را جمع آوری می کند. کلاس Client
قطعاً به یک فیلد نیاز دارد که نشان دهد مشتری از کدام پیام رسان خاص استفاده می کند. بدون رابط، این امر عجیب به نظر می رسد:
public class Client {
private WhatsApp whatsApp;
private Telegram telegram;
private Viber viber;
}
ما سه فیلد ایجاد کردیم، اما یک کلاینت تنها می تواند یک پیام رسان داشته باشد. ما فقط نمی دانیم کدام یک. بنابراین باید هر امکانی را به کلاس اضافه کنیم تا بتوانیم با مشتری ارتباط برقرار کنیم. به نظر می رسد که یک یا دو مورد از آنها همیشه null
برای برنامه بی نیاز خواهند بود. به جای آن بهتر است از رابط کاربری خود استفاده کنید:
public class Client {
private Messenger messenger;
}
این یک نمونه از کوپلینگ شل است! به جای تعیین یک کلاس پیام رسان خاص در Client
کلاس، فقط نشان می دهیم که مشتری یک پیام رسان دارد. در حین اجرای برنامه دقیقا کدام یک مشخص می شود. اما چرا به اینترفیس برای این کار نیاز داریم؟ چرا حتی به زبان اضافه شدند؟ این سؤال خوبی است - و سؤال درستی! آیا نمی توانیم با استفاده از ارث معمولی به همان نتیجه برسیم؟ کلاس Messenger
به عنوان والدین، و Viber
، Telegram
و WhatsApp
به عنوان فرزندان. در واقع، این امکان پذیر است. اما یک مشکل وجود دارد. همانطور که می دانید جاوا ارثی چندگانه ندارد. اما پشتیبانی از چندین رابط وجود دارد. یک کلاس می تواند هر تعداد اینترفیس را که می خواهید پیاده سازی کند. تصور کنید کلاسی داریم Smartphone
که یک App
فیلد دارد که نشان دهنده یک برنامه نصب شده بر روی گوشی هوشمند است.
public class Smartphone {
private App app;
}
البته، یک اپلیکیشن و یک پیام رسان شبیه هم هستند، اما هنوز چیزهای متفاوتی هستند. ممکن است نسخه های موبایل و دسکتاپ یک پیام رسان وجود داشته باشد، اما App به طور خاص یک برنامه تلفن همراه را نشان می دهد. معامله اینجاست - اگر از وراثت استفاده کنیم، نمیتوانیم یک Telegram
شی به Smartphone
کلاس اضافه کنیم. از این گذشته ، Telegram
کلاس نمی تواند به طور همزمان ارث بری کند App
و Messenger
! و ما قبلاً آن را به ارث برده Messenger
و به Client
کلاس اضافه کردیم. اما Telegram
کلاس به راحتی می تواند هر دو اینترفیس را پیاده سازی کند! بر این اساس، میتوانیم به Client
کلاس یک Telegram
شی بهعنوان a Messenger
و میتوانیم آن را Smartphone
به عنوان یک به کلاس بدهیم App
. در اینجا نحوه انجام این کار آمده است:
public class Telegram implements Application, Messenger {
// ...methods
}
public class Client {
private Messenger messenger;
public Client() {
this.messenger = new Telegram();
}
}
public class Smartphone {
private Application application;
public Smartphone() {
this.application = new Telegram();
}
}
اکنون ما از Telegram
کلاس همانطور که می خواهیم استفاده می کنیم. در بعضی جاها به عنوان یک App
. در جاهای دیگر، به عنوان یک Messenger
. مطمئناً قبلاً متوجه شده اید که روش های رابط همیشه "خالی" هستند، یعنی هیچ پیاده سازی ندارند. دلیل این امر ساده است: رابط رفتار را توصیف می کند، اما آن را اجرا نمی کند. «همه اشیایی که CanSwim
رابط را پیادهسازی میکنند باید بتوانند شنا کنند»: این تمام چیزی است که رابط به ما میگوید. روش خاصی که ماهی ها، اردک ها و اسب ها شنا می کنند یک سوال برای کلاس های Fish
, Duck
و و Horse
نه رابط است. همانطور که تغییر کانال یک وظیفه برای تلویزیون است. ریموت به سادگی یک دکمه برای این کار به شما می دهد. با این حال، یک افزودنی جالب در جاوا 8 ظاهر شد - روش های پیش فرض. به عنوان مثال، رابط شما دارای 10 روش است. 9 تا از آنها پیاده سازی های متفاوتی در کلاس های مختلف دارند، اما یکی برای همه یکسان است. قبلاً، قبل از جاوا 8، متدهای واسط هیچ پیاده سازی نداشتند: کامپایلر بلافاصله خطا می داد. حالا می توانید کاری شبیه به این انجام دهید:
public interface CanSwim {
public default void swim() {
System.out.println("Swim!");
}
public void eat();
public void run();
}
با استفاده از default
کلمه کلیدی، ما یک روش رابط با پیاده سازی پیش فرض ایجاد کرده ایم. ما باید پیاده سازی خود را برای دو روش دیگر ارائه دهیم - eat()
و run()
- در تمام کلاس هایی که پیاده سازی می کنند CanSwim
. ما نیازی به انجام این کار با روش نداریم swim()
: پیاده سازی در هر کلاس یکسان خواهد بود. به هر حال، شما قبلاً در کارهای گذشته با اینترفیس مواجه شده اید، حتی اگر متوجه نشده باشید :) در اینجا یک مثال واضح است: شما با اینترفیس و 
List
Set
ArrayList
LinkedList
HashSet
LinkedList
List
Deque
Map
HashMap
SortedMap
Map
Deque
Queue
Queue
Queues
Deque
GO TO FULL VERSION