Liigu peamise sisu juurde

I - Interface Segregation Principle

Sissejuhatus

A class should not be forced to implement methods it does not use.

Ehk klassi ei tohiks sundida teostama meetodeid, mida tal vaja ei ole. See on Interface Segregation Principle (ISP) ehk liideste segregeerimise printsiip.

Selle printsiibi eesmärk on hoida liideseid fokuseerituna ühe kindla aspekti ümber. Liidesed peaksid defineerima võimekusi. Kui üls liides kogub enda alla mitu omavahel mitte seotud olevaid võimekusi on seda liidest teostav klass sunnitud kõikidele nendele meetoditele implementatsiooni pakkuma, kuigi selleks vajadust ei ole. See tekitab tarbetut sidusust ning koormab klasse meetoditega, mis sinna tegelikult ei kuulu.

Liideste peatükis juba tutvusime korrektse ISP rakendamisega, kus Drawable ja Resizable olid eraldi liidesed. TextLabel teostas ainult Drawable liidest, kuna esimesel polnud vajadust suuruse muutmise võimekuse jaoks.

Liides, millel on liiga palju võimekusi

Vaatleme kontoritarkvara, mis haldab erinevaid kontoriseadmeid. Seal on üks liides, mis defineerib endas kõik võimalikud võimekused:

interface OfficeMachine {
void print(String document);
void scan(String document);
void fax(String document, String number);
void staple(int copies);
}

Kaasaegne mitmeotstarbeline suudab seda kõike teha:

class AllInOnePrinter implements OfficeMachine {
@Override
public void print(String document) { System.out.println("Printing: " + document); }

@Override
public void scan(String document) { System.out.println("Scanning: " + document); }

@Override
public void fax(String document, String number) {
System.out.println("Faxing " + document + " to " + number);
}

@Override
public void staple(int copies) { System.out.println("Stapling " + copies + " copies"); }
}

Lisame nüüd lihtsa laserprinteri, mis suudab ainult dokumente välja printida:

class BasicLaserPrinter implements OfficeMachine {
@Override
public void print(String document) { System.out.println("Printing: " + document); }

@Override
public void scan(String document) {
throw new UnsupportedOperationException("This printer cannot scan");
}

@Override
public void fax(String document, String number) {
throw new UnsupportedOperationException("This printer cannot fax");
}

@Override
public void staple(int copies) {
throw new UnsupportedOperationException("This printer cannot staple");
}
}

BasicLaserPrinter on sunnitud teostama kõiki meetodeid, mis tulevad OfficeMachine liidesest. See rikub ISP printsiipi, kuna antud masin poleks võimeline seda kõike teostama. Kutsuja, kellel on viide OfficeMachine-ile, ei saa turvaliselt kutsuda scan() meetodit ilma tegelikku tüüpi teadmata, mille tõttu liideste kasutamine kaotaks oma mõtte.

ISP rakendamine

Probleem laheneks, kui see üks suur liides tükeldada mitmeks väiksemaks liideseks vastavalt pakutavale võimekusele:

interface Printable {
void print(String document);
}

interface Scannable {
void scan(String document);
}

interface Faxable {
void fax(String document, String number);
}

interface Stapleable {
void staple(int copies);
}

Iga klass nüüd implementeerib neid võimekusi, mida neil tegelt vaja on:

class AllInOnePrinter implements Printable, Scannable, Faxable, Stapleable {
@Override
public void print(String document) { System.out.println("Printing: " + document); }

@Override
public void scan(String document) { System.out.println("Scanning: " + document); }

@Override
public void fax(String document, String number) {
System.out.println("Faxing " + document + " to " + number);
}

@Override
public void staple(int copies) { System.out.println("Stapling " + copies + " copies"); }
}
class BasicLaserPrinter implements Printable {
@Override
public void print(String document) { System.out.println("Printing: " + document); }
}

BasicLaserPrinter ei rakenda nüüd meetodeid, mida seal vaja pole. Iga kood, millel on viide Printable-ile, saab turvaliselt kutsuda print() meetodit nii sellel kui ka teisel seadmetel.

Sõltuvuste täpsustamine

Väiksemad liidesed võimaldavad kutsujatel täpselt määratleda, millest nad sõltuvad. Dokumendihaldussüsteem, mis saadab dokumente ainult printerisse, ei pea teadma, kas seade suudab neid ka faksi kaudu saatma:

class DocumentManager {
private final Printable printer;

public DocumentManager(Printable printer) {
this.printer = printer;
}

public void printReport(String content) {
printer.print(content);
}
}

DocumentManager võtab vastu ükskõik millist Printable tüüpi objekti, olgu selleks BasicLaserPrinter või täisfunktsionaalne AllInOnePrinter. Tüübist on samuti selgelt näha, et see komponent tegeleb ainult printimisega. Skaneerimine ja faksimine ei kuulu selle vastutusalasse.

DocumentManager manager = new DocumentManager(new BasicLaserPrinter());
manager.printReport("Q4 Financial Summary");

DocumentManager manager2 = new DocumentManager(new AllInOnePrinter());
manager2.printReport("Q4 Financial Summary");

Hoiatusmärgid ISP rikkumise kohta

  • Klass implementeerib liidest, kuid jätab mitmed meetodid tühjaks või viskab UnsupportedOperationException-i.
  • Liidest kirjeldatakse kui "kõike, mida masin / loom / kasutaja suudab teha", mitte kui üht sidusat võimekust.
  • Kutsujad, kes liidesest sõltuvad, kasutavad vaid ühte või kahte selle meetodit ja ignoreerivad ülejäänuid.
nõuanne

ISP ja SRP käsitlevad sarnaseid probleeme erinevatest vaatenurkadest. SRP küsib, kas klass teeb liiga palju. ISP küsib, kas liides nõuab liiga palju. Koos suunavad need väikeste ja fokusseeritud üksuste poole. Nii selle osas, mida klassid teevad, kui ka selle osas, mida tüübid lubavad.