ورود

View Full Version : Interface ها و abstract ها



Raminab
جمعه 01 آبان 1394, 22:20 عصر
سلام دوستان
کلاس های abstract و متد های abstract رو تقریبا متوجه میشم ی مثال خوب برای این موضوع تعریف کلاس Shape با متد های abstract مثل محاسبه محیط و مساحت برای کلاس هاییه که از shape ارث بری میکنند , چون در واقعیت هم Shape یک موضوع انتزاعیه
ولی Interface ها رو متوجه نمیشم! اینجور که من مفاهیم این دو موضوع رو فهمیدم میشه هر Interface رو با ی کلاس abstarct پیاده سازی کرد فقط فرقشون میشه اینکه subclass ها با Implement پیاده سازی بشن یا با extends .
چه دلیلی برای استفاده از Interfce ها هست ؟ کاربردشون چیه ؟ و اینکه چه مفاهیمی رو میشه با interface پیاده سازی کرد ولی با abstract ها نمیشه ؟
ممنون

-سیّد-
یک شنبه 03 آبان 1394, 00:34 صبح
سلام
۲ جور می‌شه به این قضیه نگاه کرد.
اول از نظر مفهومی:
از نظر مفهومی، interface یعنی واسط انجام یه سری کارها. یعنی شما یه مفهوم کاملاً انتزاعی دارید، که هیچ کدوم از عملکردهاش رو نمی‌تونید تصور کنید چطوری انجام می‌شه. اینجا از interface استفاده می‌کنید. مثلاً یه List (اونطور که توی جاوا تعریف شده)، به صورت پیش‌فرض، یه سری کارا بلده انجام بده. مثلاً بلده به شما یه iterator روی اعضاش ارائه بده که بتونید روش حرکت کنید. یا بلده بگه تعداد المان‌های داخلش چقدره. ولی این که چطوری این کارا انجام می‌شه رو نمی‌شه برای یه List انتزاعی متصور بود. مثلاً تا وقتی که معلوم نباشه این List از چه نوعی هست، تابع get رو نمی‌شه توش پیاده‌سازی کرد (اگه LinkedList باشه، باید به تعداد گفته شده بریم جلو و بعد المان مربوطه رو برگردونیم (که می‌شه (O(n )، ولی اگه ArrayList باشه در جا می‌ریم از توی آرایه برش می‌داریم بر می‌گردونیم (می‌شه (O(1 )).
اما یه abstract class یعنی این که شما یه مفهوم داری که کاملاً انتزاعی نیست و بخشیش مشخصه. برای این کار می‌تونی بگی اون بخش که مشخصه رو پیاده‌سازی می‌کنم، و بخشی که انتزاعی هست رو abstract می‌کنم که سر جاش پیاده‌سازی بشه. مثلاً همین کار رو در کلاس AbstractList انجام دادن و بخشی از مفاهیمی که در List واضحتر هستن رو پیاده‌سازی کردن (توجه کنید که این پیاده‌سازی‌ها لزوماً پیاده‌سازی‌های حتمی یه List نیستن، بلکه پیاده‌سازی‌هایی هستن که به صورت معمول استفاده می‌شن. وگرنه اصلاً نیازی به تعریف واسط List نبود و می‌شد مستقیماً یه abstract class تعریف کرد). بنابراین شما اگه بخواین یه List جدید پیاده‌سازی کنید، نیازی نیست همه‌ی توابط واسط List رو یکی یکی پیاده‌سازی کنید و می‌تونید کلاستون رو از AbstractList مشتق کنید و فقط تعداد کمی از توابع که باقی می‌مونه رو پیاده‌سازی کنید (مثل LinkedList و ArrayList).

یکی دیگه از کاربردهای interface ها جایی هست که واقعاً درگاه ورود به یه سیستم هستن. مثلاً وقتی دارید RPC استفاده می‌کنید، یه واسط برای سیستمتون تعریف می‌کنید، و سمت سرور اون واسط رو پیاده‌سازی می‌کنید. سمت کلاینت هم یه نسخه از اون واسط رو از سیستم می‌گیرید و توابعش رو فراخوانی می‌کنید، که در نتیجه‌ی این فراخوانی یه RPC روی شبکه انجام می‌شه. مثال:

public interface PingInterface {
void ping();
}

public class PingServer implements PingInterface {
public void ping() {
// do something, log, ...
}
}

public class PingClient {
public static PingInterface getRpcInterface() {
// return RPC interface using some RPC mechanism (like hadoop RPC, gRPC, ...)
}

public static void main(String[] args) {
PingInterface client = getRpcInterface();
long t1 = System.currentTimeMillis();
client.ping();
long t2 = System.currentTimeMillis();
System.out.println("ping took " + (t2 - t1) + "ms);
}
}

خوب اینجا واقعاً interface کاربرد داره، نه abstract class. از نظر مفهومی این واقعاً یک واسط هست (API هم یک واسط هست: Application Programmer's Interface) که مشخص می‌کنه سرور شما چه کاری رو بلده انجام بده.

البته همونطور که در مورد List و AbstractList اشاره کردم، این pattern خیلی کاربرد داره که یه interface تعریف می‌کنن، بعد یه پیاده‌سازی پیش‌فرض ازش انجام می‌دن که همچنان abstract هست و برخی از پیاده‌سازی‌ها توش انجام نشده، و فقط پیاده‌سازی‌های کلی انجام شده. اون وقت شما وقتی می‌خوای یه دونه جدید از این واسط بنویسی، می‌تونی از این پیاده‌سازی پیش‌فرض استفاده کنی که کارت خیلی راحت‌تر می‌شه، یا اگه می‌خوای کلاً سیستم رو زیر و رو کنی، می‌تونی مستقیماً خود interface رو پیاده‌سازی کنی.

دوم از نظر کاربردی:
به طور کلی می‌شه گفت interface از نظر کاربردی یه کلاس abstract هست که هیچ تابعی توش پیاده‌سازی نشده، و هیچ متغیری هم توش تعریف نشده، و فقط یه تعداد تابع abstract هست:

public abstract class PingInterface {
public abstract void ping();
}

البته توجه کنید که توی interface تمام توابع فقط می‌تونن public باشن و لازم نیست برای هر کدوم عبارت abstract رو هم بنویسیم (یعنی انگار همشون public abstract هستن).
یه تفاوت کلاس‌های abstract این شکلی که گفتم (یعنی کلاسی بدون تعریف متغیر و فقط شامل توابع abstract) با interface اینه که شما وقتی می‌خواین یه کلاسی رو از یه کلاس abstract با استفاده از دستور extends مشتق کنید، فقط و فقط از یک کلاس می‌تونید ارث‌بری کنید و ارث‌بری چندگانه (multiple inheritance (https://en.wikipedia.org/wiki/Multiple_inheritance)) توی جاوا نداریم (در مورد دلیل این امر می‌تونید این پست stackoverflow (http://stackoverflow.com/questions/995255/why-is-multiple-inheritance-not-allowed-in-java-or-c) رو بخونید). اما کلاس شما می‌تونه همزمان چندین interface رو پیاده‌سازی کنه (توسط دستور implements).

به طور کلی می‌شه گفت که یه کلاس abstract برای خودش یال و کوپالی داره و در حد و اندازه‌ی یه کلاس واقعی تحویل گرفته می‌شه! ولی یه interface فقط شامل یه فهرستی از یه مشت تابع هست که نحوه‌ی کار یه موجود رو مشخص می‌کنن.
به عنوان مثال، توی یه interface هم می‌شه inner class تعریف کرد:

public interface TestInterface {
...

public class InnerClass {
...
}
}

یا توی یه interface می‌شه تابع static تعریف کرد:

public interface TestInterface {
...

public static void testStatic() {
System.out.println("I'm a static method inside an interface!");
}
}

ولی اینا خیلی کارای معمول و قشنگی نیستن و توصیه نمی‌شن. بهتره شأن کلاس‌ها رو حفظ کنید و interface رو در حد و اندازه‌ی خودش نگه دارید! :لبخند: