Интерфейси и абстрактни класове (в Java)

Често ми се случва да се чудя дали да ползвам абстрактен клас или интерфейс докато пиша на Java. И двете идват от леко остарелия ООП апарат на C++, в който наследяването се ползва предимно за две неща – предотвратяване на повторение на код и абстракция. Та, хрумна ми една проста схемичка как да определя кога какво да ползвам.

Интерфейси се ползват, когато искате да постигнете някаква абстракция. Имате няколко вида фигури, но повечето ви алгоритми се нуждаят единствено от това да достъпват лицето им и горния ляв ъгъл на най-малкия правоъгълник, в който се намират. Съответно, правите интерфейс Shape с методи getArea() и getUpperLeftPoint() и ги ползвате навсякъде вместо Triangle, Square и Circle.

Абстрактни класове се ползват, когато искате да избегнете повторение на код. Два класа, обща функционалност, правите трети който я съдържа, двата го наследяват и така. Общото е на едно място, а вариациите са по наследниците.

А ако искаме да направим и двете? Според мен добро решение е да имаме както абстрактен клас, така и интерфейс. Това често може да го видим по разни Java библиотеки. Въпреки че абстракния клас е достатъчен, интерфейса може да допринесе както с четимост, така и с изчистване на идеята. Прочита на интерфейса е много по-лек – не натоварва с имплементационни детайли и клиентите виждат прозрачно какво очакват (или да имплементират).

Друго което може да избегнете с двойка интерфейс и абстрактен клас са проблеми при непредвидени разширения на йерархията. Ако започнете с Triangle и Square, може да ви хрумне в Shape да добавите и логика, която пази върховете на фигурите. Пишете си спокойно кода един ден ви се налага да вкарате и кръг. Тогава в Shape ще имате логика, която няма да е съвместима с идеята със Circle и ще трябва да правите по-драстичен редизайн.

И допълнително, като кода ползва интерфейси се тества по-лесно, понеже по-лесно се stub-ва.

2 thoughts on “Интерфейси и абстрактни класове (в Java)

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *