ISP — ce este, principiul separării interfețelor în dezvoltare

Autor: IT Sectr Publicat: 2026-05-12 Timp de citire: 9 min

ISP (Interface Segregation Principle) — al patrulea principiu SOLID care afirmă: clienții nu trebuie să depindă de metode pe care nu le folosesc. Principiul a fost formulat de Robert Martin în contextul proiectării interfețelor pentru sisteme orientate pe obiecte. După cum este descris în cartea Clean Architecture (2017), principiul separării interfețelor necesită crearea de interfețe îngust specializate în locul uneia universale, ceea ce reduce cuplarea și simplifică efectuarea modificărilor.

Principalele

  • ISP — principiul separării interfețelor, al patrulea în SOLID
  • Clienții nu trebuie să depindă de metode pe care nu le apelează
  • Interfețe groase (Fat Interfaces) conțin metode irelevante pentru o parte din clienți
  • Divizarea interfețelor reduce cuplarea și crește reutilizarea codului
  • ISP este strâns legat de SRP și Single Responsibility la nivelul interfețelor

Ce este ISP (Interface Segregation Principle)?

ISP (Interface Segregation Principle) — principiul separării interfețelor care interzice crearea de interfețe „groase” cu metode neutilizate de toți clienții. În locul unei singure interfețe cu zece metode, se proiectează mai multe interfețe mici, fiecare pentru grupul său de clienți.

Principiul a fost introdus de Robert Martin ca soluție la problema „poluării interfețelor”, când o clasă este forțată să implementeze metode de care nu are nevoie doar pentru că sunt declarate într-o interfață comună. În limbile cu tipizare statică, aceasta duce la implementări goale sau aruncări de excepții — un semn direct al încălcării ISP.

ISP și SRP se completează reciproc: SRP se referă la responsabilitatea clasei, ISP se referă la contractele interfețelor. SRP spune „o clasă — un motiv de schimbare”, ISP spune „o interfață — un scenariu client”. Împreună formează o arhitectură modulară unde fiecare element al sistemului are limite clare.

Interfețe groase și consecințele lor

Fat Interface — o interfață care conține mai multe metode decât are nevoie un client specific. De exemplu, interfața Worker cu metodele work, eat, sleep. Un robot muncitor nu ar trebui să implementeze eat și sleep, dar este forțat. Soluția — împărțirea în Workable, Eatable, Sleepable. Fiecare client primește exact ceea ce are nevoie.

În dezvoltarea mobilă, interfețele groase apar în protocoalele de delegat și DataSource. Un protocol poate conține metode pentru două scenarii diferite (editare + afișare), deși un ecran specific folosește doar unul dintre ele.

Cum funcționează principiul separării interfețelor

Implementarea ISP începe cu analiza clienților fiecărei interfețe. Dacă doi clienți folosesc seturi diferite de metode ale aceleiași interfețe — interfața trebuie divizată. Fiecare interfață nouă grupează metodele care sunt apelate împreună în cadrul unui singur scenariu.

Mecanismul de divizare: interfața originală este împărțită în mai multe interfețe înguste, fiecare moștenind partea comună (dacă există). Clienții trec la dependența de interfața îngustă necesară în locul celei generale. Clasele care implementau interfața originală acum implementează doar acele interfețe înguste de care au cu adevărat nevoie.

Precizare importantă: gradul de divizare este determinat de numărul clienților și scenariilor lor. ISP nu necesită divizare maximă (microinterfețe cu o singură metodă). Aceasta ar duce la o complexitate excesivă. Scopul este eliminarea dependenței clienților de metode inutile, nu minimizarea dimensiunii fiecărei interfețe.

Semne de încălcare a ISP

Semnele principale de încălcare a ISP includ: clase care implementează o interfață cu metode goale (implementare fictivă), aruncarea excepției UnsupportedOperationException în implementări, un număr mare de parametri sau tipuri de returnare care nu sunt utilizate de o parte din clienți și modificări frecvente ale interfeței care afectează doar o parte din clienți.

În dezvoltarea Android, un exemplu tipic de încălcare a ISP este interfața OnItemClickListener care include metode pentru click, click lung și swipe. Dacă un ecran specific folosește doar click — metodele rămase rămân goale. Soluția — divizarea în OnItemClickListener, OnItemLongClickListener, OnItemSwipeListener.

În dezvoltarea iOS, încălcarea ISP se manifestă în delegații UIKit: un protocol conține metode pentru diferite stări ale unui component. UITableViewDelegate include metode pentru afișare, selecție, editare și acțiuni swipe. Adesea, dezvoltatorii implementează întregul protocol cu zece metode goale. Divizarea în mai multe protocoale pe grupuri de responsabilitate rezolvă problema.

Dependența de metode neutilizate

Problema nu este doar de estetică a codului. Când o interfață se modifică (se adaugă o metodă nouă), toate clasele care o implementează trebuie actualizate — chiar și cele care nu au nevoie de metoda nouă. În dezvoltarea mobilă cu zeci de ecrane, aceasta duce la modificări în cascadă. ISP izolează fiecare client de schimbările care nu îl privesc.

Încălcarea implicită a ISP are loc prin parametri de configurare. Dacă o metodă primește un obiect cu un număr mare de câmpuri, iar clientul folosește doar 2-3 dintre ele — acesta este un semnal de divizare. Alternativă: mai multe metode specializate cu un set minim de parametri.

În dezvoltarea Android, ISP este încălcat atunci când se folosește un singur SharedPreferencesManager pentru citirea și scrierea tuturor setărilor aplicației. Un Fragment care trebuie doar să citească tema primește o dependență de managerul global cu zeci de metode pentru diferite tipuri de date. Divizarea în ThemePreferenceProvider, AuthPreferenceProvider, FeatureFlagProvider — aplicarea ISP la nivelul serviciilor de configurare. Fiecare furnizor conține exact metodele de care au nevoie clienții săi.

Exemple ISP în aplicațiile mobile

Să examinăm exemplul Android cu o interfață pentru lucrul cu date. Încălcarea ISP — o singură interfață pentru toate operațiile CRUD, deși nu toți clienții au nevoie de toate operațiile.

kotlin
// Încălcarea ISP: interfață groasă
interface UserRepository {
    fun getAll(): List<User>
    fun getById(id: Int): User
    fun save(user: User)
    fun delete(id: Int)
}

// După aplicarea ISP: interfețe înguste
interface UserReader {
    fun getAll(): List<User>
    fun getById(id: Int): User
}

interface UserWriter {
    fun save(user: User)
    fun delete(id: Int)
}

// ReadOnlyViewModel nu depinde de metodele de scriere
class ReadOnlyViewModel(
    private val reader: UserReader
)

Exemplul iOS cu divizarea protocoalelor pentru lucrul cu media:

swift
// Încălcarea ISP: un singur protocol pentru tot lucrul cu media
protocol MediaService {
    func play(url: URL)
    func pause()
    func stop()
    func upload(data: Data) async -> URL
    func download(url: URL) async -> Data
}

// După ISP: împărțirea pe protocoale după responsabilitate
protocol MediaPlayer {
    func play(url: URL)
    func pause()
    func stop()
}

protocol MediaTransfer {
    func upload(data: Data) async -> URL
    func download(url: URL) async -> Data
}

// PlayerViewModel nu depinde de metodele de încărcare
class PlayerViewModel {
    private let player: MediaPlayer
}

Concluzia practică: ISP protejează clienții de modificările în părțile nelegate de ei ale interfeței. Divizarea UserRepository în UserReader și UserWriter înseamnă că modificările în save nu afectează ReadOnlyViewModel și invers. Fiecare client este izolat de funcționalitatea pe care nu o folosește și nu necesită modificări la îmbunătățirea altor părți ale sistemului.

ISP și SRP — o pereche naturală. SRP stabilește că o clasă ar trebui să aibă un singur motiv de schimbare. ISP aplică aceeași logică interfețelor: o interfață ar trebui să servească un singur scenariu client. O clasă poate implementa mai multe interfețe înguste (fiecare corespunzând unei responsabilități), ceea ce este mai curat decât o interfață groasă cu multiple responsabilități.

ISP și OCP sunt de asemenea legate: interfețele înguste sunt mai ușor de extins. Adăugarea unei metode noi într-o interfață îngustă afectează doar clienții săi. Adăugarea unei metode într-o interfață groasă afectează toți clienții — încălcând potențial OCP, dacă clienții sunt forțați să își modifice implementarea.

ISP și DIP lucrează împreună: DIP necesită dependența de abstracții. ISP face aceste abstracții înguste și focalizate. Dependența de o interfață largă este încă o dependență de abstracție, dar de o abstracție „re” din punctul de vedere al ISP. Patru principii (SRP, OCP, ISP, DIP) formează „piramida modularității”: SRP și ISP definesc granițele, OCP și DIP — modalitățile de extindere și conectare.

Aplicarea ISP în arhitectura pe componente

Arhitectura pe componente în proiectele mobile (module, feature-uri, straturi) beneficiază de ISP la nivelul API-urilor publice. Fiecare modul exportă interfețe înguste pentru consumatorii săi, nu o singură fațadă generală. Aceasta permite modificarea implementării interne a modulului fără a afecta consumatorii care folosesc doar o parte din funcționalitatea sa.

În proiectele Android cu Clean Architecture, ISP se aplică la UseCase: fiecare UseCase este o interfață separată cu o singură metodă invoke sau execute. Clientul (ViewModel) depinde doar de UseCase-ul de care are nevoie, nu de întregul repository. Aceasta face dependențele transparente și testabile.

Întrebări frecvente

ISP duce la un număr excesiv de interfețe?

Da, divizarea excesivă este posibilă. ISP nu necesită o interfață pentru fiecare metodă. Criteriul: există un client care are nevoie doar de o parte din metodele interfeței? Dacă toți clienții folosesc toate metodele — interfața nu trebuie divizată. Nivelul optim de divizare este determinat de scenariile reale de utilizare.

Cum se aplică ISP parametrilor funcțiilor?

ISP la nivel de parametri înseamnă: o funcție nu ar trebui să primească obiecte cu un număr mare de câmpuri dacă folosește doar o parte din ele. În schimb, ar trebui transmise doar datele necesare sau folosite interfețe specializate (de exemplu, interfața Renderable în locul User-ului complet).

Cu se deosebește ISP de LSP?

LSP se referă la moștenirea corectă și compatibilitatea comportamentală a subtipurilor. ISP se referă la proiectarea interfețelor: clienții nu trebuie să depindă de metode pe care nu le folosesc. LSP răspunde la întrebarea „poate fi folosită o subclasă în locul clasei de bază?”, ISP — „are clientul nevoie de întreaga interfață?”.

Cum simplifică ISP testarea?

Interfețele înguste simplifică crearea obiectelor mock: testul creează un mock cu una-două metode, nu cu zece. Cu cât sunt mai puține metode într-o interfață, cu atât este mai ușor să simulezi comportamentul său. Aceasta reduce sarcina cognitivă a dezvoltatorului de test și scade probabilitatea erorilor în logica mock-ului.

Când se poate deroga de la ISP?

Dacă interfața este stabilă și toți clienții folosesc toate metodele — divizarea este redundantă. Un exemplu tipic: protocoalele UIKit proiectate de Apple. Divizarea lor este riscantă, deoarece UIKit așteaptă implementarea completă a delegatului. În astfel de cazuri, încălcarea ISP este justificată de stabilitatea API-ului.

Rezumat

  • ISP (Interface Segregation Principle) — principiul separării interfețelor, al patrulea în SOLID
  • Clientul nu trebuie să depindă de metode pe care nu le folosește
  • Interfețele groase forțează clasele să implementeze metode inutile ca goale sau cu excepții
  • Separarea interfețelor reduce cuplarea și izolează clienții de modificări
  • ISP + SRP formează granițele modulelor: o responsabilitate — un contract îngust
  • Testarea mock se simplifică: o interfață îngustă necesită mai puține simulări
  • Divizarea optimă este determinată de scenariile reale ale clienților, nu de divizarea maximă

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și