Почему вы должны заботиться о том, является ли ссылка на объект интерфейсом или классом? - PullRequest
9 голосов
/ 30 сентября 2010

Мне часто кажется, что я сталкиваюсь с обсуждением вопроса о том, применять или нет какое-то соглашение о префиксах / суффиксах к именам типов интерфейса, обычно добавляя «I» в начало имени.

Лично я в лагере, который не поддерживает префикса, но это не тот вопрос, о котором идет речь.Скорее, речь идет об одном из аргументов, которые я часто слышу в этой дискуссии:

Вы не можете сразу увидеть, является ли что-то интерфейсом или классом.

Вопрос, который сразу же возникает у меня в голове: кроме создания объекта, зачем вам когда-нибудь заботиться о том, является ли ссылка на объект классом или интерфейсом?

Я отметил этот вопрос как языкагностик, но, как было указано, это не так.Я утверждаю, что это потому, что, хотя конкретные детали реализации языка могут быть интересны, я хотел бы сохранить это на концептуальном уровне.Другими словами, я думаю , что концептуально вам никогда не придется заботиться о том, указана ли ссылка на объект в виде класса или интерфейса, но я не уверен , поэтомувопрос.

Это не обсуждение IDE и того, что они делают или не делают при визуализации различных типов;Забота о типе объекта, безусловно, необходима при просмотре кода (пакеты / исходники / любая форма).Это также не дискуссия о плюсах и минусах соглашения об именах.Я просто не могу понять, в каком сценарии, кроме создания объекта, вы действительно заботитесь о том, ссылаетесь ли вы на конкретный тип или интерфейс.

Ответы [ 9 ]

10 голосов
/ 30 сентября 2010

В большинстве случаев вам, вероятно, все равно. Но вот несколько примеров, которые я могу придумать, где бы вы были. Их несколько, и они немного различаются в зависимости от языка. Некоторые языки не так против, как другие.

В случае инверсии управления (когда кто-то передает вам параметр) вам, вероятно, все равно, является ли это интерфейсом или объектом, если он вызывает его методы и т. Д. Но при работе с типами он определенно может сделать разница.

  • В управляемых языках, таких как языки .NET, интерфейсы обычно могут наследовать только один интерфейс, тогда как класс может наследовать один класс, но реализовывать много интерфейсов. Порядок классов и интерфейсов также может иметь значение в объявлении класса или интерфейса. Так что вам нужно знать, что есть что при определении нового класса или интерфейса.

  • В Delphi / VCL интерфейсы подсчитываются и автоматически собираются, тогда как классы должны быть явно освобождены, так что это влияет на управление жизненным циклом в целом, а не только на создание.

  • Интерфейсы не могут быть жизнеспособными источниками для ссылок на классы.

  • Интерфейсы могут быть преобразованы в совместимые интерфейсы, но во многих языках они не могут быть преобразованы в совместимые классы. Классы могут быть приведены к любому.

  • Интерфейсы могут передаваться параметрам типа IID или IUnknown, тогда как классы не могут (без преобразования и вспомогательного интерфейса).

  • Реализация интерфейса неизвестна. Его ввод и вывод определены, но реализация, которая создает вывод, абстрагируется. В общем, можно сказать, что при работе с классом можно знать, как работает класс. Но при работе с интерфейсом такого предположения делать не следует. В идеальном мире это не имеет значения. Но на самом деле это наверняка может повлиять на ваш дизайн.

1 голос
/ 30 сентября 2010

Я согласен с тем, что соглашение об именовании I * просто не подходит для современных ОО-языков, но правда в том, что этот вопрос на самом деле не зависит от языка. Существуют законные случаи, когда у вас есть интерфейс не по какой-либо архитектурной причине, а потому, что у вас просто нет реализации или нет доступа к реализации. В этих случаях вы можете прочитать I * как * Stub или подобное, и в этих случаях может иметь смысл иметь классы IBlah и Blah

В наши дни, однако, вы редко сталкиваетесь с этим, и в современных ОО-языках, когда вы говорите «Интерфейс», вы на самом деле имеете в виду «Интерфейс», не только у меня нет кода для этого. Таким образом, нет необходимости в I *, и на самом деле он поощряет действительно плохой дизайн ОО, поскольку вы не получите естественных конфликтов имен, которые могли бы сказать вам, что что-то пошло не так в вашей архитектуре. Скажем, у вас есть список и IList ... какая разница? когда бы вы использовали один поверх другого? если бы вы хотели реализовать IList, вы были бы ограничены (по крайней мере концептуально) тем, что делает List? Я скажу вам, что ... если бы я нашел класс IBlah и Blah в любой из моих кодовых баз, я бы удалил один наугад и забрал права коммита этого человека.

1 голос
/ 30 сентября 2010

Вы хотите сразу увидеть, какие «интерфейсы» и «конкретные классы», чтобы вы могли сосредоточить свое внимание на абстракциях в дизайне, а не на деталях.

Хорошие дизайныоснованы на абстракциях - если вы знаете и понимаете их, вы понимаете систему, не зная ни одной детали.Итак, вы знаете, что вы можете пропустить классы без префикса I и сосредоточиться на тех, которые есть, пока вы понимаете код, и вы также знаете, как избежать создания нового кода вокруг неинтерфейсных классов без обращения к некоторым другимпроектная документация.

1 голос
/ 30 сентября 2010

Я согласен с вами (и, следовательно, не используйте префикс "I" для интерфейсов). Нам не нужно заботиться о том, является ли это абстрактным классом или интерфейсом.

Стоит отметить, что Java должна иметь представление об интерфейсе исключительно потому, что она не поддерживает множественное наследование. В противном случае было бы достаточно понятия «абстрактный класс» (которое может быть «все» абстрактным, или частично абстрактным, или почти конкретным, и всего лишь 1 крошечным абстрактным, что угодно).

1 голос
/ 30 сентября 2010

Когда используется класс, я могу предположить, что я получу объекты из относительно небольшого и почти четко определенного диапазона подклассов. Это потому, что подклассы есть - или, по крайней мере, так должно быть - решение, которое не принимается слишком легко, особенно в языках, которые не поддерживают множественное наследование. Напротив, интерфейсы могут быть реализованы любым классом, и реализация может быть добавлена ​​позже к любому классу.

Таким образом, информация полезна, особенно при просмотре кода и попытке понять, что автор кода намеревался сделать, - но я думаю, что этого будет достаточно, если IDE показывает интерфейсы / классы в качестве отличительных значков. *

1 голос
/ 30 сентября 2010

Вещи, которые может иметь конкретный класс, а интерфейсы не могут:

  1. Конструкторы
  2. Поля экземпляра
  3. Статические методы и статические поля

Таким образом, если вы используете соглашение, начинающее все имена интерфейсов с «I», то это указывает пользователю вашей библиотеки, что конкретный тип не будет иметь ничего из перечисленного выше.

Но личноЯ чувствую, что это не достаточная причина, чтобы начинать все имена интерфейсов с «I».Современные IDE достаточно мощны, чтобы указывать, является ли какой-то тип интерфейсом.Также он скрывает истинное значение имени интерфейса: представьте, если Runnable и List интерфейсы были названы IRunnable и IList соответственно.

0 голосов
/ 30 сентября 2010

Вы должны заботиться, потому что:

  1. Интерфейс с большой буквы "I" позволяет вам, а именно вам или вашим коллегам использовать любую реализацию, которая реализует интерфейс. Если в будущем вы придумаете лучший способ что-то сделать, скажем, лучший алгоритм сортировки списка, вы застрянете с необходимостью изменить ВСЕ методы вызова.

  2. Это помогает в понимании кода - например, вам не нужно запоминать все 10 реализаций, скажем, I_SortableList, вам просто нужно, чтобы он сортировал список (или что-то в этом роде) Ваш код становится практически самодокументируемым.

Чтобы завершить обсуждение, вот пример псевдокода, иллюстрирующий вышеприведенное:

//Pseudocode - define implementations of ISortableList
Class SortList1 : ISortableLIst, SortList2:IsortableList, SortList3:IsortableList

//PseudoCode - the interface way
void Populate(ISortableList list, int[] nums)
{
    list.set(nums)
}

//PseudoCode - the "i dont care way"

void Populate2( SortList1 list, int[] nums )
{
    list.set(nums)
}

...
//Pseudocode - create instances

SortList1 list1 = new SortList1();
SortList2 list2 = new SortList2();
SortList3 list3 = new SortList3();

//Invoke Populate() - The "interface way"
Populate(list1,nums);//OK, list1 is ISortableList implementation
Populate(list2,nums);//OK, list2 is ISortableList implementation
Populate(list3,nums);//OK, list3 is ISortableList implementation

//Invoke Populate2() - the "I don't care way"
Populate(list1,nums);//OK, list1 is an instance of SortList1
Populate(list2,nums);//Not OK, list2 is not of required argument type, won't compile
Populate(list3,nums);//the same as above

Надеюсь, это поможет,

Иак.

0 голосов
/ 30 сентября 2010

Различие между интерфейсами и классами может быть полезно в любом месте, на которое ссылается тип, в IDE или вне, для определения:

  • Могу ли я создать новую реализацию этого типа?
  • Могу ли я реализовать этот интерфейс на языке, который не поддерживает множественное наследование классов реализации (например, Java).
  • Может ли быть несколько реализаций этого типа?
  • Могу ли я легко смоделировать этот интерфейс в произвольной среде для имитации?

Стоит отметить, что UML различает интерфейсы и классы реализации.Кроме того, префикс «I» используется в примерах в «Руководстве пользователя по языку унифицированного моделирования» тремя amigos Booch, Jacobson и Rumbaugh.(Кстати, это также дает пример того, почему одной раскраски синтаксиса IDE недостаточно для различения во всех контекстах.)

0 голосов
/ 30 сентября 2010

Интерфейсы не имеют полей, поэтому, когда вы используете IDisposable (или что-то еще), вы знаете, что только объявляете, что вы можете сделать.Это кажется мне главным в этом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...