В первую очередь ты должен предоставлять возможность расширения своего кода без его изменения.
Стандартный "книжный" пример:
Тебе необходимо улететь в другой город. Ты приезжаешь в аэропорт и садишься в самолет. Тебе не принципиально что это за самолет, тебе нужно чтоб он выполнял свою функцию как самолета, а какая конкретно это будет реализация не важно.
В коде это будет примерно так
Описание того что должен делать любой самолет:
public interface Plane {
void fly();
}
Описание конкретного самолета:
public class Airbus implements Plane {
int weight = 123;
void fly() {
// реализация полета для Airbus
}
public class Boeing implements Plane {
int personal = 5;
void fly() {
// реализация полета для Boeing
}
То есть любые дополнительные действия могут быть изменены в реализациях. И сама реализация метода fly() может быть любой, главное что класс, реализующий интерфейс Plane обязан предоставить такой метод.
Чтоб в коде можно было написать:
Plane plane = getPlane();
plane.fly();
public Plane getPlane() {
//здесь может вернуться и Airbus и Boeing, это не повлияет на работу программы
}
Так вот твоя задача как программиста - спроектировать свою программу так чтоб она не зависела от конкретных реализаций и их можно было заменить на другие в случае необходимости. Это и есть программирование на уровне интерфейса.