Основная причина в том, что это облегчает такие методы, как внедрение зависимостей . Это, в свою очередь, обеспечивает большую гибкость программного обеспечения и упрощает повторное использование и рекомбинацию существующего кода. Примеры того, где это полезно, включают различные формы модульного тестирования (как вы упомянули), а также большинство других форм «регулярного» повторного использования кода.
Простой пример:
Скажем, у вас есть метод расчета зарплаты emplyoee. В качестве части своей подписи он принимает объект, который вычисляет их преимущества, например экземпляр BenefitCalculator:
calculateSalary(... BenefitCalculator bc, ...)
Изначально ваш дизайн имеет только один класс BenefitCalculator. Но позже выясняется, что вам нужно более одного класса, например, потому что разные части программного обеспечения должны использовать разные алгоритмы (возможно, для поддержки разных стран, или потому что алгоритм должен настраиваться пользователем ...). В этом случае вместо того, чтобы раздуть существующую реализацию BenefitCalculator, имеет смысл создать новый класс (ы), например, BenefitCalculatorFrance или BenefitCalculatorSimple и т. Д.
Теперь, если вы используете подпись
calculateSalary(... BenefitCalculator bc, ...)
, вы как бы облажались, потому что вы не можете предоставить разные реализации. Однако, если вы используете
Рассчитать зарплату (... IBenefitCalculator
до н.э., ...)
вы можете просто сделать так, чтобы все классы реализовывали интерфейс.
Это на самом деле просто особый случай "слабой связи": требовать как можно меньше от других частей кода. В этом случае не требуйте определенного класса; вместо этого просто требуйте, чтобы определенные методы существовали, что и делает интерфейс.