Подтвердить что ты не робот

Разница в стиле: IDictionary vs Dictionary

У меня есть друг, который просто вступает в .NET-разработку после разработки в Java целую вечность, и, посмотрев на некоторые из его кода, я заметил, что он довольно часто делает следующее:

IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();

Он объявляет словарь как интерфейс, а не класс. Обычно я бы сделал следующее:

Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();

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

Мой вопрос: есть ли какие-либо достоинства в его способе делать что-то? Является ли это обычной практикой в ​​Java?

4b9b3361

Ответ 1

Если IDictionary является "более общим" типом, чем словарь, тогда имеет смысл использовать более общий тип при объявлении переменных. Таким образом, вам не нужно заботиться о классе реализации, назначаемом переменной, и вы можете легко изменить тип в будущем, не изменяя много следующего кода. Например, в Java часто считается, что лучше делать

List<Integer> intList=new LinkedList<Integer>();

чем это сделать

LinkedList<Integer> intList=new LinkedList<Integer>();

Таким образом, я уверен, что весь следующий код относится к списку как к списку, а не к LinkedList, что упрощает в будущем выключение LinkedList для Vector или любого другого класса, который реализует List. Я бы сказал, что это обычное явление для Java и хорошего программирования в целом.

Ответ 2

Эта практика не ограничивается только Java.

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

Вы также увидите, что эта практика сильно используется при работе с контейнерами IoC и instanciation с использованием шаблона Factory.

Ответ 3

Ваш друг придерживается очень полезного принципа:

"Откажитесь от деталей реализации"

Ответ 4

Вы всегда должны пытаться программировать интерфейс, а не конкретный класс.

В Java или любом другом объектно-ориентированном языке программирования.

В .NET мире обычно используется I, чтобы указать, что это интерфейс, который вы используете. Я думаю, что это более распространено, потому что в С# у них нет implements и extends для ссылки на наследование интерфейса класса vs.

Я думаю, сыворотка будет печатать

 class MyClass:ISome,Other,IMore 
 { 
 }

И вы можете сказать ISome a IMore интерфейсы, а Other - класс

В Java нет необходимости в такой вещи

 class MyClass extends Other implements Some, More {
 }

Концепция по-прежнему применяется, вы должны попытаться выполнить код для интерфейса.

Ответ 5

Насколько я видел, разработчики Java чаще используют абстракции (и шаблоны проектирования) чаще, чем разработчики .NET. Это может показаться еще одним примером: зачем объявлять конкретный класс, когда он будет работать только с членами интерфейса?

Ответ 6

Чаще всего вы видите тип интерфейса (IDictionary), используемый, когда член подвергается внешнему коду, независимо от того, находится ли он вне сборки или находится вне класса. Как правило, большинство разработчиков используют конкретный тип внутри класса для определения класса, в то время как они отображают свойство encapsulated с использованием типа интерфейса. Таким образом, они могут использовать возможности конкретного типа, но если они изменяют конкретный тип, объявление класса класса не нужно изменять.

public class Widget
{
    private Dictionary<string, string> map = new Dictionary<string, string>();
    public IDictionary<string, string> Map
    {
        get { return map; }
    }
}

позже может стать:

class SpecialMap<TKey, TValue> : IDictionary<TKey, TValue> { ... }

public class Widget
{
    private SpecialMap<string, string> map = new SpecialMap<string, string>();
    public IDictionary<string, string> Map
    {
        get { return map; }
    }
}

без изменения интерфейса Widget и необходимости менять другой код, уже использующий его.

Ответ 7

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

Ответ 8

В описанной ситуации почти каждый разработчик Java использовал интерфейс для объявления переменной. Способ использования коллекций Java, вероятно, является одним из лучших примеров:

Map map = new HashMap();
List list = new ArrayList();

Угадайте, что он просто выполняет свободное сцепление во многих ситуациях.

Ответ 9

Из мира Java я согласен с тем, что в вас просверлена мантра "программа для интерфейса". Путем программирования интерфейса, а не реализации, вы делаете ваши методы более расширяемыми для будущих потребностей.

Ответ 10

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

В отличие от подписей классов или подписи метода, очень мало усилий по рефакторингу, если вы решите изменить типы, а также переменная, видимая вне ее сайта использования. Фактически, когда вы используете var для объявления locals, вы не получаете тип интерфейса, а скорее тип класса (если только вы явно не применили к интерфейсу).

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

Ответ 11

Использование интерфейсов означает, что "словарь" в следующем коде может представлять собой любую реализацию IDictionary.

Dictionary1 dictionary = new Dictionary1();
dictionary.operation1(); // if operation1 is implemented only in Dictionary1() this will fail for every other implementation

Лучше всего это видно, когда вы скрываете конструкцию объекта:

IDictionary dictionary = DictionaryFactory.getDictionary(...);

Ответ 12

Java Collections включает множество реализаций. Поэтому мне гораздо легче использовать

List<String> myList = new ArrayList<String>();

Тогда в будущем, когда я пойму, мне нужно, чтобы "myList" был потокобезопасным, просто изменив эту единственную строку на

List<String> myList = new Vector<String>();

И не меняйте другую строку кода. Сюда также входят геттеры/сеттеры. Если вы посмотрите на число реализаций Карты, например, вы можете себе представить, почему это может быть хорошей практикой. На других языках, где есть только пара реализаций для чего-то (извините, не большой .NET-парень), но в Objective-C есть действительно только NSDictionary и NSMutableDictionary. Таким образом, это не имеет особого смысла.

Edit:

Не удалось нажать на один из моих ключевых пунктов (просто ссылался на него с помощью getter/seters).

Вышеупомянутое позволяет:

public void setMyList(List<String> myList) {
    this.myList = myList;
}

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

Ответ 13

Я столкнулся с такой же ситуацией с разработчиком Java. Он таким же образом реализует объекты коллекции AND и их интерфейс. Например,

IAccount account = new Account();

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