Interface — قراردادی است که مجموعهای از متدهای انتزاعی را تعریف میکند که یک کلاس باید پیادهسازی کند. در Java و Kotlin اینترفیسها مکانیسم اصلی انتزاع و چندریختی هستند. در Java 8+ اینترفیسها میتوانند متدهای default و static داشته باشند، در Kotlin — پیادهسازی پیشفرض. طبق Google Android Developers (2025)، اینترفیسها در 90% پروژههای Android برای تعریف لایههای معماری — مخزنها، UseCase و سرویسها استفاده میشوند.
نکات اصلی
Interface — یک نوع ارجاعی است که شامل متدهای انتزاعی، ثابتها و متدهای پیشفرض است. کلاسی که اینترفیس را پیادهسازی میکند موظف است پیادهسازی تمام متدهای انتزاعی آن را ارائه دهد. در Java اینترفیس نمیتواند وضعیت (فیلدهای نمونه) داشته باشد، Kotlin نیز از این محدودیت پیروی میکند اما ویژگیهای با دسترسیکنندهها را پشتیبانی میکند.
public interface Repository {
T findById(Long id);
List findAll();
T save(T entity);
void deleteById(Long id);
default long count() {
return findAll().size();
}
}
@Entity
public class UserEntity {
private Long id;
private String email;
// getterها و setterها
}
public class UserRepositoryImpl implements Repository {
private final EntityManager em;
public UserEntity findById(Long id) {
return em.find(UserEntity.class, id);
}
public List findAll() {
return em.createQuery("FROM UserEntity", UserEntity.class)
.getResultList();
}
// سایر متدها
}Repository<T> — یک اینترفیس generic با متدهای CRUD. متد پیشفرض count() یک پیادهسازی پیشفرض ارائه میدهد که میتوان آن را بازنویسی کرد. UserRepositoryImpl اینترفیس را پیادهسازی میکند و از EntityManager برای دسترسی به دادهها استفاده میکند. این رویکرد امکان تست لایه داده را از طریق mock اینترفیس بدون پایگاه داده واقعی فراهم میکند.
انتخاب بین اینترفیس و کلاس انتزاعی به وجود وضعیت مشترک و روابط بین نوعها بستگی دارد. اینترفیس قرارداد (آنچه کلاس میتواند انجام دهد) را تعریف میکند، کلاس انتزاعی — پیادهسازی مشترک (آنچه کلاس هست).
| معیار | Interface | Abstract Class |
|---|---|---|
| وضعیت (فیلدها) | فقط ثابتهای static final | بله، هر فیلدی |
| سازندهها | خیر | بله |
| وراثت چندگانه | بله (implements) | خیر (extends یکی) |
| اصلاحکنندههای دسترسی | public (Java 8-)، متدهای default | همه (private, protected, public) |
| چه زمانی استفاده شود | قرارداد برای کلاسهای نامرتبط | پایه مشترک برای کلاسهای مرتبط |
در Clean Architecture اینترفیسها در لایه داخلی (domain) و پیادهسازیها در لایه خارجی (data) قرار میگیرند. این امکان رعایت قانون وابستگی را فراهم میکند: لایه خارجی به داخلی وابسته است، نه برعکس. کلاسهای انتزاعی بیشتر برای متدهای قالبی (Template Method pattern) استفاده میشوند.
اینترفیسهای Kotlin انعطافپذیرتر از Java هستند: میتوانند ویژگیهای انتزاعی اعلام کرده و پیادهسازی متدها را ارائه دهند. برخلاف Java، Kotlin از واگذاری از طریق کلمه کلیدی by پشتیبانی میکند که boilerplate را هنگام پیادهسازی الگوی Delegate به طور قابل توجهی کاهش میدهد.
interface ApiService {
val baseUrl: String // ویژگی انتزاعی
suspend fun fetchData(): Result>
fun getEndpoint(path: String): String {
return "$baseUrl/$path" // پیادهسازی پیشفرض
}
}
class RetrofitApiService(
override val baseUrl: String
) : ApiService {
private val client = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
override suspend fun fetchData(): Result>
{
// پیادهسازی درخواست
}
}
// واگذاری از طریق by
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println(message)
}
class UserService(logger: Logger) : Logger by loggerUserService اینترفیس Logger را از طریق واگذاری (by) پیادهسازی میکند. تمام فراخوانیهای log() بدون نوشتن متدهای wrapper به شی logger هدایت میشوند. این مثالی از ترکیب است که در Java به 5-10 خط کد boilerplate نیاز داشت.
Clean Architecture برای Android برنامه را به وضوح به لایهها تقسیم میکند. اینترفیسها نقش مرزهای بین لایهها را بازی میکنند: Domain اینترفیسهای مخزنها و UseCaseها را تعریف میکند، Data پیادهسازیها را ارائه میدهد. این امکان جایگزینی پیادهسازیها را بدون تغییر منطق تجاری فراهم میکند — مزیت کلیدی هنگام مهاجرت از Room به Firebase یا از REST به GraphQL.
// لایه Domain — اینترفیس (قرارداد)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun updateUser(user: User)
}
// لایه Domain — use case (وابسته به اینترفیس)
class GetUserUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result {
return runCatching { repository.getUser(id) }
}
}
// لایه Data — پیادهسازی
class UserRepositoryImpl(
private val api: UserApi,
private val dao: UserDao
) : UserRepository {
override suspend fun getUser(id: String): User {
val cached = dao.getUser(id)
if (cached != null) return cached
val remote = api.fetchUser(id)
dao.insertUser(remote)
return remote
}
// updateUser...
}UserRepository — اینترفیس در لایه domain. GetUserUseCase به اینترفیس وابسته است، نه به پیادهسازی. UserRepositoryImpl در لایه data اینترفیس را پیادهسازی میکند و API و پایگاه داده محلی را ترکیب میکند. چنین معماری امکان تست UseCase را با مخزن mock بدون پیکربندی پایگاه داده یا شبکه فراهم میکند.
متدهای default در Java 8 برای گسترش تدریجی اینترفیسها بدون نقض سازگاری معکوس اضافه شدند. اگر ArrayList متد جدید stream() اضافه شده به Collection را پیادهسازی نمیکرد، کد قدیمی به کار خود ادامه میداد. متدهای static در اینترفیسها برای ابزارهای مرتبط با اینترفیس خدمت میکنند — جایگزینی برای کلاسهای ابزاری.
public interface Vehicle {
void start();
void stop();
default void honk() {
System.out.println("Beep!");
}
static Vehicle of(String type) {
if ("car".equals(type)) return new Car();
return new Bicycle();
}
// ثابت
String CATEGORY = "transport";
}متدهای default مشکل الماس (diamond problem) را حل میکنند: اگر کلاسی دو اینترفیس با متد default یکسان پیادهسازی کند، کامپایلر بازنویسی صریح را الزامی میکند. متدهای static از طریق نام اینترفیس فراخوانی میشوند — Vehicle.of("car")، بدون نمونه.
خطاها در طراحی اینترفیسها منجر به کد شکننده، دشواری تست و نقض SOLID میشوند. سه مشکل رایج را بررسی میکنیم.
اینترفیس با 15+ متد، اصل Interface Segregation Principle (ISP) را نقض میکند. مثال — java.util.Dictionary قدیمی با 10+ متد. راهحل: تقسیم به چند اینترفیس کوچک — ReadableRepository, WritableRepository, SearchableRepository. مشتری (سرویس) فقط به متدهای مورد نیاز خود وابسته است.
ایجاد اینترفیس برای هر کلاس بدون نیاز واقعی به چندریختی، ضدالگوی Interface overkill است. نشانه: اینترفیس دقیقاً یک پیادهسازی دارد و در پروژه برنامهای برای اضافه کردن جایگزین وجود ندارد. راهحل: اینترفیس را فقط زمانی اضافه کنید که نسخه دوم پیادهسازی یا نیاز به mock برای تست ظاهر شود.
سوالات متداول
Interface فقط قرارداد (امضای متدها) را تعریف میکند، نمیتواند وضعیت داشته باشد و از وراثت چندگانه پشتیبانی میکند. Abstract class میتواند فیلدها، سازندهها و متدهای پیادهسازی شده داشته باشد، اما کلاس فقط میتواند یک کلاس انتزاعی را به ارث ببرد. از Java 8 اینترفیسها متدهای default و static دریافت کردند و شکاف را کاهش دادند.
بله، در Java و Kotlin اینترفیسها از وراثت پشتیبانی میکنند. public interface AdvancedRepository<T> extends Repository<T>, Pageable — اینترفیسی که دو اینترفیس دیگر را ترکیب میکند. کلاسی که AdvancedRepository را پیادهسازی میکند باید تمام متدهای هر دو اینترفیس والد را پیادهسازی کند. وراثت چندگانه فقط برای اینترفیسها مجاز است.
اینترفیس تابعی — اینترفیسی با یک متد انتزاعی (SAM — Single Abstract Method). حاشیهنویسی @FunctionalInterface این محدودیت را تضمین میکند. مثالها: Runnable, Callable, Comparator, Consumer. اینترفیسهای تابعی اساس عبارات لامبدا در Java 8 هستند: () -> System.out.println() Runnable را پیادهسازی میکند.
متدهای default امکان افزودن متدهای جدید به اینترفیس را بدون تغییر تمام کلاسهای پیادهساز فراهم میکنند. به عنوان مثال، Java 8 متد stream() را به عنوان متد default به Collection اضافه کرد. بدون این مکانیزم، foreach()، stream() و سایر متدها نیاز به تغییر هزاران کلاس در JDK داشتند. Default — روشی سازگار با عقب برای گسترش است.
Kotlin وراثت چندگانه کلاسها را ممنوع میکند اما پیادهسازی چندگانه اینترفیسها را مجاز میکند. اگر دو اینترفیس متدی با امضای یکسان و پیادهسازی پیشفرض داشته باشند، کامپایلر بازنویسی صریح با فراخوانی super<InterfaceName>.method() را الزامی میکند. این مشکل الماس را در سطح کامپایل حل میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید