Должен ли возвращаемый тип объявления метода быть интерфейсом или конкретным классом? - PullRequest
13 голосов
/ 07 марта 2012

В общем случае интерфейс или абстрактный класс часто являются подходящим решением, я прав?

Но в некоторых случаях кажется, что конкретный класс лучше.Например,

public string Replace(string old, string new)

Метод Replace для String возвращает конкретный класс.(Это всего лишь пример, хотя String не реализует никаких интерфейсов.)

Мой вопрос

  1. Когда я должен вернуть интерфейс, а когда я должен вернутьконкретный класс?

  2. Является ли частью program to an interface, not an implementation для возврата интерфейса?

Ответы [ 4 ]

5 голосов
/ 07 марта 2012

Это зависит.

Я видел, как этот вопрос задавался пару раз, и вот хороший пример, иллюстрирующий ответ "все зависит".

Рассмотрим следующий класс:

public class MyClass
{
    public static IEnumerable<int> Test()
    {
        return new List<int> { 2, 3, 4 };
    }

    public static List<int> Test2()
    {
        return new List<int> { 2, 3, 4 };
    }
}

Test возвращает IEnumerable, а Test2 возвращает конкретную реализацию интерфейса IEnumerable (List в этом случае). Какой метод самый лучший? Test или Test2?

На самом деле, оба семантически различны:

  • Поскольку Test возвращает только IEnumerable, подразумевает , что это часть контракта метода , что разработчик использует возвращенный объект в перечислении (foreach ).
  • Поскольку Test2 возвращает экземпляр List, он позволяет пользователю получить доступ к объектам List по индексу. Это совершенно другое использование возвращаемого объекта.

private static void Main(string[] args)
{
    foreach (var z in MyClass.Test())
    {
        Console.WriteLine(z);
    }

    var f = MyClass.Test2()[0];

    Console.ReadKey();
}

Если вы ожидаете, что разработчик будет использовать возвращенный объект только в перечислении, то вы можете использовать интерфейс в качестве возвращаемого типа. Если вы ожидаете, что разработчик будет использовать методы / свойства конкретной реализации интерфейса (в приведенном выше примере доступ к объекту по индексу), тогда вы можете вернуть конкретный тип.

Также помните, что иногда у вас нет выбора. Например, если вы хотите предоставить общедоступную коллекцию, которая должна использоваться для привязки Silverlight, вы должны вернуть ObservableCollection<T>, а не IEnumerable<T>, потому что системе привязок на самом деле требуется метод / свойства / поведение ObservableCollection класс (IEnumerable будет недостаточно для привязки к работе).

Чего следует избегать, так это метода, который возвращает IEnumerable<T> и используется каждый раз с ToList().

3 голосов
/ 07 марта 2012

По моему мнению, это зависит, скажем, если у вас есть метод, который используется тесно связанным способом, то есть класс 1 вызывает метод A в классе 2 и всегда хочет определенный тип и является единственным, когда метод вызывается, тогда выМожно утверждать, что нет никакого смысла.Примером этого может быть возвращение IEnumerable, где метод создает коллекцию в виде списка, затем возвращает ее как IEnumerable, а класс 1 все равно использует его как список, поэтому для возврата в любом случае потребуется вызов ToList ().

Однако, когда я пишу интерфейс между классами, такой как уровень данных, более разделенный, я всегда предпочитаю возвращать наименьший общий знаменатель (например, IEnumarable) и разрешать потребителю, который может не знать, что делатьделать с этим.

1 голос
/ 07 марта 2012

Вы получаете Program to an interface, not an implementation неправильно, я верю.Что GOF подразумевает под программой для интерфейса, является интерфейсом типа.

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

Что касается вашего вопроса, я думаю, что, вероятно, лучше вернуть абстрактный класс.Будет ли это работать, если вы вернете какой-либо объект, полученный из этого абстрактного класса?Если да, то вернуть абстрактный класс.Если нет, перейдите более конкретно и верните производный тип.

0 голосов
/ 07 марта 2012

Зависит от того, сколько доступа вы хотите предоставить пользователю своего кода!

Я имею в виду, что возвращать List<AccountStatement> пользователю не имеет никакого смысла. Потому что вы не хотите, чтобы пользователь вашего кода ДОБАВЛЯЛ новую выписку в этой коллекции (вы должны создать инструкцию при бронировании конкретной транзакции)

Итак, в этом случае вы можете просто вернуть IEnumerable<AccountStatement> - только доступ для чтения.

В общем, Я поддерживаю идею возврата Интерфейсов из методов по следующим причинам:

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

Кстати, ваш пример возврата "string" не очень полезен. Это примитивный тип данных (я имею в виду собственный тип данных в mscorlib), и скрывать / показывать в этом объекте нечего.

...