Веревки и пещеры и свободные концы.
В предыдущем посте утверждалось, что «… наследование — разделяемое как интерфейсом, так и классом, и, конечно, никакой особенностью интерфейса — не дает возможности обойти эту единственную категорию волновых эффектов. Здесь, наконец, заключается источник сопротивления интерфейса к волновому эффекту. » (Курсивом указано ключевое слово Java в отличие от общей концепции интерфейса.) То есть, несмотря на мудрость изречения «Программируйте интерфейс, а не реализацию», интерфейс Java вряд ли является интерфейсом, к которому относится исключительно пословица: с точки зрения волнового эффекта класс обычно служит так же хорошо, но немногие выбирают этот путь из-за ограничения одиночного наследования Java.
Однако очевидно, что в игру вступают другие силы. Дорогая сообщества Java, интерфейс доминирует в API Java. Тем не менее, это не должно быть так. Класс мог бы составлять API подавляющего большинства сервисов. Рассмотрим одноклассный API, Person
разработанный в двух вариантах: «Happy» и «Sad» так, что Person
— какие клиенты увидят — воспроизводит суперкласс для подклассов, HappyPerson
а SadPerson
какие — нет. Учитывая растущую враждебность к внедрению наследования за последнее десятилетие, у дизайнеров был бы стимул при рождении задушить любой другой суперкласс, от которого HappyPerson
илиSadPerson
мог бы унаследовать. Разработчики могли бы, действительно, молчаливо ограничить использование наследования для реализации API, тем самым воспользовавшись опасной передовой практикой, рост которой привел бы к глубокому оттенку ограничения одиночного наследования Java.
И все же этого явно не произошло. Зачем?
Ответ может быть таким, что люди не являются компиляторами.
В приведенном ниже фрагменте Person
, как часть API, он предлагает свои услуги клиентскому классу, welcome()
метод которого нечувствителен к Person
классу или интерфейсу . Программист, однако, нет.
public void welcome(Person person) { System.out.println("Hello, " + person.getName()); }
Рассмотрим оба случая. Если Person
это интерфейс, то программист может увидеть это:
public interface Person { . . . public String getName(); }
Если класс, то программист может увидеть:
public class Person { . . . public String getName() { return nameDirectory.getNameForIdentifier(identifier); } }
Из этих двух, казалось бы, похожих случаев следует четкое различие. В вычислительном отношении процедурная абстракция в обоих случаях полностью изолирует getName()
реализацию от всех вызывающих классов: компилятор сплетает ее фабрику из сигнатур методов, никогда не вызывая классы, «видя», внутренние методы, от которых они зависят. Но программист делает. В то время как интерфейс «S getName()
стоит голым, лишенный деталей реализации, класс в getName()
обнажает то , что называется nameDirectory
таким кровотечением реализацией через API. Это распространяется не на сторонний компилятор, а на изнуренный ум программиста, который едва ли может не задуматься: что это? Где nameDirectory
в API? Как это должно быть использовано? Конечно, подклассыPerson
может или не может переопределить этот конкретный метод и, следовательно, может или не может воспользоваться этим, nameDirectory
но ущерб уже был нанесен.
Таковы отвлекающие факторы, от которых уклоняются программисты, которые используют API только через интерфейс . Для программиста, в конце концов, класс — это норма, его участие в API — нерелевантность: это дыра в земле, в которую бросают зависимости только для того, чтобы зацепиться за скалистые выступы в двадцати футах вниз. Там, где такие зависимости приводят к «доморощенным» классам, в общем и целом, местность, по крайней мере, чувствует себя судоходной, но там, где они ведут через зияющие API-интерфейсы к чужим nameDirectory
хитросплетениям, находящимся за пределами разумного (исходного) контроля, царит тревога. Интерфейс , с другой стороны, выступает в качестве большой терминатора зависимости, огромный гранитный утес-слойного невосприимчив к психическому Долотное. Предлагая сигнатуры методов в одиночку и без помех в реализации, успокаивающий Person
интерфейс представляет ни малейшей точки опоры.
Тем не менее, программисты не дураки. Они знают, что под навесом реализации любое изменение службы может пробиться и исказить даже священную сигнатуру интерфейса . Мало кто верит, что разъединенные системы восстают из такой бессмысленной тактики, как отрыв от каждого класса интерфейса, через который затем может протекать всякая межклассовая связь. Лучшие системы интеллектуально разлагаются на организации, предоставляющие услуги, которые заслуживают интерфейсов на основе их стабильности, потенциального разнообразия и степени их публикации .
Привлекательность интерфейса сохраняется. Они культивируют разъединение и правильную структуру в духе, если не в действительности. Они, по-видимому, различают две отдельные плоскости волнового эффекта: одна — традиционная плоскость зависимостей, прослеживаемая через богоподобные способности программиста к нарушению процедурной абстракции, другая — плоскость, видимая с минималистской точки зрения компилятора, лишенная отвлечения. Будь то с помощью искусной хитрости или чистого желания, оба самолета пересекаются на границе раздела .
Резюме.
Программисты справедливо хвалят интерфейс . Если они преувеличивают его способность защищать от изменений волнового эффекта, они делают это с позиции, которая, хотя и не синтаксическая, кажется в основном оправданной. С точки зрения волнового эффекта, класс и интерфейс кажутся достаточно близкими, но программисту они разносят миры друг от друга.
Фото кредитная атрибуция.
CC image Пещера любезно предоставлена Иваром Абрахамсеном на Flickr.