Какова лучшая практика для возврата подкласса или реализации интерфейса из метода? - PullRequest
2 голосов
/ 23 сентября 2010

Отказ от ответственности

Пожалуйста, не голосуйте, чтобы закрыть это, потому что заголовок выглядит субъективно, и, если его уже спрашивали, пожалуйста, укажите мне на предыдущий вопрос в комментариях, и я удалю этот - я действительно посмотрел высоко и низко, пытаясь найти предыдущий вопрос по теме.

Фон

Учитывая, что у меня есть следующий интерфейс и конкретная реализация:

public interface IFoo 
{
   // Some stuff
}
public class Foo : IFoo
{
   // Concrete implementations of stuff
}

И где-то у меня есть следующий метод:

public Foo GiveMeAFoo()
{ 
    return new Foo();
}

Я всегда всегда возвращал Foo, видя , так как он по сути IFoo в любом случае , так что его можно использовать на другом конце как IFoo:

IFoo foo = GiveMeAFoo();

Основное преимущество, которое я вижу, состоит в том, что если мне действительно нужно потреблять Foo где-то в качестве конкретной реализации , я могу.

Актуальный вопрос

Недавно я натолкнулся на некоторые комментарии по другому вопросу, которые затрудняют это, предлагая тип возвращаемого значения IFoo.

Я ошибаюсь или они? Кто-нибудь может дать мне причину, почему возврат конкретной реализации - плохая идея?

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

Редактировать

Использование IFoo и Foo могло быть слишком расплывчатым. Вот конкретный пример из .NET (в .NET массивы реализуют IEnumerable - то есть string[] : IEnumerable<string>)

public string[] GetMeSomeStrings()
{
    return new string[] { "first", "second", "third" };
}

Конечно, плохая идея вернуть IEnumerable здесь? Чтобы получить свойство длины, вам теперь нужно вызвать Enumerable.Count (). Я не уверен насчет того, сколько оптимизировано в фоновом режиме, но логика подсказывает, что теперь придется подсчитывать элементы, перечисляя их, что должно сказаться на производительности. Если я просто верну массив как массив, то это прямой поиск свойства.

Ответы [ 4 ]

3 голосов
/ 23 сентября 2010

Если вы хотите, чтобы ваш метод был максимально гибким, вы должны вернуть наименее производный тип (в вашем случае, IFoo):

public interface IFoo { }

public class Foo : IFoo { }

public IFoo GiveMeAFoo() { return new Foo(); }

Это позволит вам изменить конкретную реализацию IFoo, встроенную в ваш метод, не нарушая никого, кто потребляет ваш метод:

public interface IFoo { }

public class Foo : IFoo { }

public class Foo2 : IFoo { }

public IFoo GiveMeAFoo() { return new Foo2(); }
1 голос
/ 23 сентября 2010

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

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

Также вы можете удалить зависимости .Код вызывающей стороны не должен ничего знать о фактическом типе, который вы выбрали для создания.

Мое последнее замечание - цитата из Эрика Липперта: " Вы, вероятно, не должны возвращать массив как значениеоткрытый метод или свойство".(Его статья сосредоточена на C #, но общая идея не зависит от языка)

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

Вы можете создать группу интерфейсов и передать их различным командам.Возьмем ваш собственный пример

public interface IFoo 
{
   // Some stuff
}

public interface IBar
{
    public IFoo getMeAFoo();
}

Затем вы можете передать эти интерфейсы человеку, разрабатывающему приложение переднего плана, и он не должен знать, какова конкретная реализация.Парень, разрабатывающий интерфейс, может использовать метод getMeAFoo (), зная, что он возвращает объект IFoo, тогда как конкретная реализация может быть разработана отдельно как:

public class Foo implements IFoo
{
    // more stuff
}

public class MoreFoo implements IFoo
{
    //
}

public class WunderBar implements IBar
{
    public IFoo getMeAFoo()
    {
        case 1:
            return new Foo();
        case 2:
            return new MoreFoo();
    }
}

Надеюсь, это имеет смысл:)

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

Хорошо, везде, где вы его используете, вы используете IFoo foo = GiveMeAFoo ();

Тогда кто-то другой использует ваш код и не использует его таким образом, по любой причине, он использует Foo foo = GiveMeAFoo ();

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

Не хорошо.

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