Вопросы создания объектов в java [duplicate]

Чтобы решить проблему автономного вывода, см. решение даниэлей. Но, как вы упомянули в своем вопросе, сироты не являются лучшей практикой, и ghc будет генерировать предупреждения. Чтобы подавить предупреждения сиротских сирот ghc , вы можете использовать флаг -fno-warn-orphans. Вы также можете добавить его в свой файл cabal:

...
library
  exposed-modules: ...
  ...
  ghc-options: -fno-warn-orphans
...
714
задан Sameer 8 June 2017 в 12:41
поделиться

30 ответов

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

Когда я впервые начал работать с интерфейсами, я тоже был смущен об их значимости. Я не понимал, зачем вам это нужно. Если мы используем такой язык, как Java или C #, у нас уже есть наследование, и я рассматривал интерфейсы как более слабую форму наследования и мысли «зачем беспокоиться»? В некотором смысле я был прав, вы можете думать о интерфейсах как о какой-то слабой форме наследования, но помимо этого я, наконец, понял их использование в качестве языковой конструкции, считая их как средство классификации общих черт или моделей поведения, которые были представлены потенциально много несвязанных классов объектов.

Например, скажем, у вас есть игра с SIM-картой, и у вас есть следующие классы:

 class HouseFly inherits Insect {
   void FlyAroundYourHead(){}
   void LandOnThings(){}
 }

 class Telemarketer inherits Person {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}
 }

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

Предположим, что в нашей игре должно быть какое-то случайное вещь , которая раздражает игрока, когда они едят обед. Это могут быть HouseFly или Telemarketer или оба, но как вы можете использовать обе функции с одной функцией? И как вы просите каждый из разных типов объектов «делать свою раздражающую вещь» таким же образом?

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

 interface IPest {
    void BeAnnoying();
 }

 class HouseFly inherits Insect implements IPest {
   void FlyAroundYourHead(){}
   void LandOnThings(){}

   void BeAnnoying() {
     FlyAroundYourHead();
     LandOnThings();
   }
 }

 class Telemarketer inherits Person implements IPest {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}

   void BeAnnoying() {
      CallDuringDinner();
      ContinueTalkingWhenYouSayNo();
   }
 }

Теперь у нас есть два класса, каждый из которых может раздражать по-своему. И им не нужно выводить из одного базового класса и обладать общими присущими характеристиками - им просто нужно удовлетворить контракт IPest - этот контракт прост. Вам просто нужно BeAnnoying. В этой связи мы можем моделировать следующее:

 class DiningRoom {

   DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

   void ServeDinner() {
     when diningPeople are eating,

       foreach pest in pests
         pest.BeAnnoying();
   }
 }

Здесь у нас есть столовая, в которой принимают несколько посетителей и несколько вредителей - обратите внимание на использование интерфейса. Это означает, что в нашем маленьком мире членом массива pests может быть объект Telemarketer или объект HouseFly.

Метод ServeDinner вызывается при подаче обеда и наши люди в столовой должны есть. В нашей маленькой игре, вот когда наши вредители выполняют свою работу - каждый вредитель инструктируется быть раздражающим через интерфейс IPest. Таким образом, мы с легкостью можем раздражать как Telemarketers, так и HouseFlys в каждом из своих способов - мы заботимся только о том, что у нас есть что-то в объекте DiningRoom, являющемся вредителем, нам все равно что это такое, и они не могли иметь ничего общего с другими.

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

1411
ответ дан JohnAllen 15 August 2018 в 14:35
поделиться
  • 1
    Другое дело, что в некоторых случаях было бы полезно иметь интерфейс для вещей, которые могут быть «доступны», быть раздражающим, и иметь varierty объектов реализовать BeAnnoying как no-op; этот интерфейс может существовать вместо или в дополнение к интерфейсу для вещей, которые раздражают (если оба интерфейса существуют, «вещи, которые являются раздражающими» интерфейсами, должны унаследовать от «вещей, которые может быть раздражающим "интерфейсом). Недостатком использования таких интерфейсов является то, что реализации могут быть обременены внедрением "раздражающего" количество методов заглушки. Преимуществом является то, что ... – supercat 20 July 2012 в 15:39
  • 2
    ... часто быстрее вызывать ненужный метод, чем определять, следует ли вызывать метод. Это часть причины IEnumerator<T> наследует IDisposable. В подавляющем большинстве случаев потребителю перечислителя будет дешевле безоговорочно называть Dispose на нем, чем для потребителя, чтобы проверить, нужен ли вызов Dispose. Слишком плохо, что ни .NET, ни Java не позволяют указывать реализации по умолчанию для методов интерфейса, поскольку существует множество ситуаций, когда интерфейсы могут извлечь выгоду из «необязательных» методов, где бы ... – supercat 20 July 2012 в 15:48
  • 3
    Инкапсулирующее поведение, такое как IPest, известно как шаблон стратегии на всякий случай, если кто-то заинтересован в следующем с большим количеством материала по этому вопросу ... – nckbrz 9 March 2014 в 21:15
  • 4
    Интересно, что вы не указываете, что, поскольку объекты в IPest[] являются ссылками IPest, вы можете вызвать BeAnnoying(), потому что у них есть этот метод, в то время как вы не можете вызывать другие методы без трансляции. Тем не менее, каждый объект индивидуального метода BeAnnoying() будет вызван. – D. Ben Knoble 26 May 2015 в 01:16
  • 5
    Это поистине фантастический ответ - один из лучших, что я когда-либо видел в StackOverflow. – Nick Hodges 18 March 2017 в 14:08

Простыми словами ... Если я пишу новый класс Swimmer для добавления функциональности swim () и вам нужно использовать объект класса say Dog, и этот класс Dog реализует интерфейс Animal, который объявляет swim () [To лучше понять ... вы можете нарисовать диаграмму о том, о чем я говорю]. В верхней части иерархии (Animal) он очень абстрактный, а внизу (Dog) он очень конкретный. То, как я думаю о «программировании для интерфейсов», заключается в том, что, когда я пишу класс Swimmer, я хочу написать свой код против интерфейса, который находится так далеко от иерархии, которая в этом случае является объектом Animal. Интерфейс свободен от деталей реализации и, таким образом, делает ваш код слабосвязанным. Детали реализации могут быть изменены со временем, однако это не повлияет на оставшийся код, поскольку все, с чем вы взаимодействуете, связано с интерфейсом, а не с реализацией. Вам все равно, что такое реализация ... все, что вы знаете, это то, что будет класс, который будет реализовывать интерфейс.

2
ответ дан Abhishek Shrivastava 15 August 2018 в 14:35
поделиться

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

List myList = new ArrayList(); // programming to the List interface

вместо

ArrayList myList = new ArrayList(); // this is bad

. Они выглядят точно так же в короткой программе, но если вы продолжаете использовать myList 100 раз в своей программе, вы можете начать видеть разницу. Первая декларация гарантирует, что вы вызываете только методы на myList, которые определены интерфейсом List (поэтому нет специальных методов ArrayList). Если вы запрограммировали интерфейс таким образом, позже вы можете решить, что вам действительно нужно

List myList = new TreeList();

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

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

public ArrayList doSomething(HashMap map);

Это объявление метода связывает вас с двумя конкретными реализациями (ArrayList и HashMap). Как только этот метод вызывается из другого кода, любые изменения этих типов, вероятно, означают, что вам также придется изменить код вызова. Было бы лучше запрограммировать интерфейсы.

public List doSomething(Map map);

Теперь не имеет значения, какой тип List вы возвращаете или какой Map передается в качестве параметра. Изменения, внесенные вами в метод doSomething, не заставят вас изменить код вызова.

228
ответ дан Bill the Lizard 15 August 2018 в 14:35
поделиться
  • 1
    В Java это интерфейс. Но у вас есть идея, вы хотите, какой бы интерфейс ни был, а не конкретная реализация. – Bill the Lizard 21 December 2008 в 02:41
  • 2
    @EdS. Большинство ответов на этот вопрос имеют общее заблуждение, что «программирование на интерфейс» означает использование языковой конструкции интерфейса interface ; что совершенно неправильно! Это первый ответ, который я видел, который правильно иллюстрирует, что «программирование для интерфейса» означает: необязательно привязывать свой «клиентский код» к конкретным / конкретным реализациям подклассов, потому что, если вы позже решите изменить его, используйте другую реализацию, у вас есть намного больше работы, отменяющей все ненужные привязки. То есть программа / привязка к вещам без деталей реализации. Например. Абстрактные базовые классы;) – Craig Young 22 November 2011 в 22:45
  • 3
    +1 к комментарию @CraigYoung. Наследование предназначено для того, чтобы один из них мог выразить все подклассы таким же образом. Это фундаментальное описание интерфейса. – radarbob 23 May 2013 в 17:04
  • 4
    Я полностью думаю, что ЭТО КОММЕНТАРИЙ - это ПРАВЫЙ ОТВЕТ к вопросу. Как группировать различные объекты с помощью интерфейса (как объяснил Питер Мейер выше) - это основная вещь. Ключевой концепцией ответа на титульный вопрос является взаимозаменяемость классов, реализующих интерфейс, что облегчает внесение изменений в код. Чем ниже уровень сцепления между вашими модулями / классами, тем легче вносить изменения. Плохо, что те люди, которые просто идут за голосующим ответом и думают, что поняли, никогда не смогут понять, они не получили ответа, который они искали. – Kovács Imre 9 December 2013 в 14:29
  • 5
    Это действительно единственный ответ, который дал мне «ага». момент, по которому следует использовать Drawable square = new Square() над Square square = new Square() (если применимо). – Trace 9 September 2015 в 16:22

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

Например, я мог бы интерфейс движения. Метод, который делает что-то «движение», и любой объект (Person, Car, Cat), который реализует интерфейс движения, может быть передан и передан для перемещения. Без метода, каждый из которых знает тип класса.

3
ответ дан Damien 15 August 2018 в 14:35
поделиться

Я не сохраняю interface s - самое важное в языке: он чаще всего используется для наследования класса. Но в любом случае они важны! Например (это Java код, но он может просто адаптироваться к C# или многим другим языкам):

interface Convertable<T> {

    T convert();
}

public class NumerableText implements Convertable<Integer> {

    private String text = "";

    public NumerableText() { }

    public NumerableText(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Integer convert() {
        return this.text.hashCode();
    }
}

public class NumerableTextArray implements Convertable<Integer> {

    private String[] textArray = "";

    public NumerableTextArray() { }

    public NumerableTextArray(String[] textArray) {
        this.textArray = textArray;
    }

    public String[] getTextArray() {
        return this.textArray;
    }

    public void setTextArray(String[] text) {
        this.textArray = textArray;
    }

    public Integer convert() {
        Integer value = 0;
        for (String text : textArray)
            value += text.hashCode();
        return value;
    }
}

public class Foo {

    public static void main() {
        Convertable<Integer> num1 = new NumerableText("hello");
        Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
        System.out.println(String.valueOf(num1.convert()));
        System.out.println(String.valueOf(num2.convert()));
        //Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
    }
}
0
ответ дан Davide Cannizzo 15 August 2018 в 14:35
поделиться

Программирование на интерфейсы является удивительным, оно способствует ослаблению связи. Как упоминал @lassevk, очень важно использовать Inversion of Control.

Кроме того, ознакомьтесь с принципами SOLID. здесь представляет собой серию видео

. Он проходит через жесткий кодированный (сильно связанный пример), затем просматривает интерфейсы, и, наконец, переходит к инструменту IoC / DI (NInject)

8
ответ дан dbones 15 August 2018 в 14:35
поделиться

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

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

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

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

10
ответ дан Ed S. 15 August 2018 в 14:35
поделиться

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

4
ответ дан edgi 15 August 2018 в 14:35
поделиться

Много объяснений там, но сделать его еще проще. Возьмем, к примеру, List. Можно реализовать список с помощью:

  1. внутреннего массива
  2. Связанный список
  3. другая реализация

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

Вы можете использовать любой тип реализации, который внутренне говорит о реализации array. Но предположим, что вы хотите изменить реализацию по какой-то причине, скажем, ошибку или производительность. Затем вам просто нужно изменить объявление List<String> ls = new ArrayList<String>() на List<String> ls = new LinkedList<String>().

Нет, где еще в коде, вам придется что-то изменить; Потому что все остальное было построено на определении List.

6
ответ дан Jatin 15 August 2018 в 14:35
поделиться

Программирование на интерфейс абсолютно не связано с абстрактными интерфейсами, как мы видим на Java или .NET. Это даже не концепция ООП.

На самом деле это не значит, что вы не обходитесь с внутренними элементами объекта или структуры данных. Используйте интерфейс абстрактной программы или API для взаимодействия с вашими данными. В Java или C # это означает использование общедоступных свойств и методов вместо необработанного доступа к полям. Для C это означает использование функций вместо необработанных указателей.

EDIT: И с базами данных это означает использование представлений и хранимых процедур вместо прямого доступа к таблице.

28
ответ дан Jonathan Allen 15 August 2018 в 14:35
поделиться

Если вы программируете на Java, JDBC - хороший пример. JDBC определяет набор интерфейсов, но ничего не говорит о реализации. Ваши приложения могут быть написаны против этого набора интерфейсов. Теоретически вы выбираете драйвер JDBC, и ваше приложение будет работать. Если вы обнаружите, что есть более быстрый или «лучший» или более дешевый драйвер JDBC или по какой-либо причине, вы можете снова теоретически переконфигурировать свой файл свойств и без необходимости внесения каких-либо изменений в ваше приложение, ваше приложение все равно будет работать.

8
ответ дан Kevin Le - Khnle 15 August 2018 в 14:35
поделиться
  • 1
    Это не просто полезно, если появится лучший драйвер, что позволяет полностью изменить поставщиков баз данных. – Ken Liu 21 December 2008 в 05:57
  • 2
    +1, JDBC - пример, который я собирался опубликовать. – Karl 21 December 2008 в 17:09
  • 3
    JDBC настолько плох, что его нужно заменить. Найдите другой пример. – Joshua 4 January 2011 в 22:43

Программирование на интерфейс говорит: «Мне нужна эта функциональность, и мне все равно, откуда она».

Рассмотрим (на Java) интерфейс List по сравнению с ArrayList и LinkedList конкретных классов. Если все, о чем я забочусь, это то, что у меня есть структура данных, содержащая несколько элементов данных, к которым я должен обращаться через итерацию, я бы выбрал List (и это 99% времени). Если я знаю, что мне нужно вставить / удалить постоянное время с любого конца списка, я бы выбрал конкретную реализацию LinkedList (или, более вероятно, использовать интерфейс Queue ). Если я знаю, что мне нужен произвольный доступ по индексу, я бы выбрал конкретный класс ArrayList.

62
ответ дан Lucky 15 August 2018 в 14:35
поделиться
  • 1
    полностью согласны с тем, что независимость между тем, что делается, и тем, как это делается. Разбирая систему вдоль независимых компонентов, вы получаете систему, которая проста и многоразовая (см. Simple Made Easy от парня, создавшего Clojure) – beluchin 29 December 2013 в 09:56

Представьте, что у вас есть продукт под названием «Zebra», который может быть дополнен плагинами. Он находит плагины, ища библиотеки DLL в некоторых каталогах. Он загружает все эти DLL и использует отражение для поиска любых классов, которые реализуют IZebraPlugin, а затем вызывает методы этого интерфейса для связи с плагинами.

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

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

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

3
ответ дан Matt Fenwick 15 August 2018 в 14:35
поделиться

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

Тестирование модуля

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

Поскольку мне приходилось делать много экземпляров классов, используемых для ввода в качестве переменных класса и / или параметров. Таким образом, тесты больше походят на интеграционные тесты (нужно сделать полную «инфраструктуру» классов, поскольку все было связано друг с другом).

Страх интерфейсов Итак, я решил использовать интерфейсы. Мой страх состоял в том, что мне приходилось выполнять всю функциональность везде (во всех используемых классах) несколько раз. В некотором роде это верно, однако, используя наследование, его можно значительно уменьшить.

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

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

Таким образом, копирование кода не требуется, но при этом полезно использовать автомобиль в качестве интерфейса (ICar).

0
ответ дан Michel Keijzers 15 August 2018 в 14:35
поделиться

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

0
ответ дан ndoty 15 August 2018 в 14:35
поделиться

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

IInterface classRef = new ObjectWhatever()

Вы можете использовать любой класс, который реализует IInterface? Когда вам это нужно?

Взгляните на этот вопрос SE на хорошем примере.

Почему должен быть предпочтительным интерфейс для класса Java?

использует ли производительность интерфейса?

if так сколько?

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

как вы можете избежать этого, не имея необходимости поддерживать два бита кода?

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

Один хороший вариант использования: реализация шаблона стратегии:

Реальный мир Пример шаблона стратегии

0
ответ дан Ravindra babu 15 August 2018 в 14:35
поделиться

Это также хорошо для Unit Testing, вы можете вводить свои собственные классы (отвечающие требованиям интерфейса) в класс, который зависит от него

4
ответ дан Richard 15 August 2018 в 14:35
поделиться

Сначала начнем с некоторых определений:

Интерфейс n. Набор всех сигнатур, определенных операциями объекта, называется интерфейсом к объекту

Тип n. Конкретный интерфейс

. Простым примером интерфейса, как определено выше, были бы все методы объекта PDO, такие как query(), commit(), close() и т. д., в целом, не отдельно. Эти методы, т. Е. Его интерфейс, определяют полный набор сообщений, запросы, которые могут быть отправлены объекту.

Тип, определенный выше, является конкретным интерфейсом. Я буду использовать созданный интерфейс формы, чтобы продемонстрировать: draw(), getArea(), getPerimeter() и т. Д.

Если объект относится к типу базы данных, мы подразумеваем, что он принимает сообщения / запросы интерфейс базы данных, query(), commit() и т. д. Объекты могут быть разных типов. У вас может быть объект базы данных типа формы, если он реализует свой интерфейс, и в этом случае это будет подтип.

Многие объекты могут иметь много разных интерфейсов / типов и реализовывать этот интерфейс иначе. Это позволяет нам заменять объекты, позволяя нам выбирать, какой из них использовать. Также известен как полиморфизм.

Клиент будет знать интерфейс, а не реализацию.

Таким образом, по сути программирование на интерфейс потребует создания некоторого типа абстрактного класса, такого как Shape, только с указанным интерфейсом, т.е. draw(), getCoordinates(), getArea() и т. д. И затем различные конкретные классы реализуют такие интерфейсы, как класс Circle, Square class, Triangle class. Следовательно, программа для интерфейса не является реализацией.

0
ответ дан Robert Rocha 15 August 2018 в 14:35
поделиться

В Java эти конкретные классы реализуют интерфейс CharSequence:

CharBuffer, String, StringBuffer, StringBuilder

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

Однако каждый из этих классов способен соответствующим образом реализовать методы интерфейса CharSequence :

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

В некоторых случаях классы классов классов Java, которые использовали для приема String, были пересмотрены, чтобы теперь принять интерфейс CharSequence. Поэтому, если у вас есть экземпляр StringBuilder, вместо извлечения объекта String (что означает создание экземпляра нового объекта) вместо этого можно просто передать сам StringBuilder, поскольку он реализует интерфейс CharSequence.

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

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer

2
ответ дан RogerV 15 August 2018 в 14:35
поделиться
  • 1
    Очень плохие интерфейсы, такие как CharSequence, настолько анемичны. Я хочу, чтобы Java и .NET позволяли интерфейсам иметь реализацию по умолчанию, так что люди не будут парировать интерфейсы исключительно с целью минимизации кода шаблона. Учитывая любую законную реализацию CharSequence, можно было бы эмулировать большинство функций String, используя только вышеуказанные четыре метода, но многие реализации могли выполнять эти функции гораздо эффективнее другими способами. К сожалению, даже если конкретная реализация CharSequence содержит все в одном char[] и может выполнять много ... – supercat 18 October 2014 в 17:58
  • 2
    ... операций, подобных indexOf быстро, нет способа, чтобы вызывающий, который не знаком с конкретной реализацией CharSequence, мог попросить его сделать это, а не использовать charAt для изучения каждого отдельного символа. – supercat 18 October 2014 в 18:00

Код для интерфейса. Не реализация не имеет отношения к Java, а также к конструкции интерфейса.

Эта концепция была выведена в известность в книгах «Шаблоны / Банды четырех», но, скорее всего, была вокруг нее задолго до этого. Концепция, безусловно, существовала задолго до того, как существовала Java.

Конструкция интерфейса Java была создана для помощи в этой идее (среди прочего), и люди стали слишком сосредоточены на конструкции как центр смысла, а не оригинальное намерение. Тем не менее, это причина, по которой у нас есть общедоступные и частные методы и атрибуты в Java, C ++, C # и т. Д.

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

И вы можете запрограммировать интерфейс, а не реализацию, не используя конструкцию интерфейса. Вы можете запрограммировать интерфейс, а не реализацию на C ++, которая не имеет конструкцию интерфейса. Вы можете интегрировать две огромные корпоративные системы гораздо надежнее, если они взаимодействуют через публичные интерфейсы (контракты), а не вызывают методы для объектов, внутренних для систем. Ожидается, что интерфейсы всегда будут реагировать на один и тот же ожидаемый путь с учетом тех же входных параметров; если реализован интерфейс, а не реализация. Концепция работает во многих местах.

Встряхните мысль о том, что Java-интерфейсы имеют что-то, что связано с концепцией «Программа для интерфейса, а не реализация». Они могут помочь применить концепцию, но это не концепция.

34
ответ дан Sameer 15 August 2018 в 14:35
поделиться
  • 1
    Почему упоминание IoC в самом начале, поскольку это только добавит больше путаницы. – Kevin Le - Khnle 21 December 2008 в 02:13
  • 2
    Согласитесь, я бы сказал, что программирование против интерфейсов - это просто способ сделать IoC проще и надежнее. – terjetyl 21 December 2008 в 03:36
  • 3
    В первом предложении говорится все. Это должен быть принятый ответ. – madumlao 23 April 2017 в 17:41

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

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

, так что лучше оберните его обложкой (в нашей истории это интерфейс), тогда он

Теперь почтальон должен получать и доставлять только обложки .. (он не беспокоился о том, что внутри обложки).

Создайте тип interface не действительный тип, но реализуется с фактическим типом.

Создать для интерфейса означает, что ваши компоненты легко вставляются в остальную часть кода

Я приведу вам пример.

у вас есть интерфейс AirPlane, как показано ниже.

interface Airplane{
    parkPlane();
    servicePlane();
}

Предположим, что у вас есть методы в вашем классе контроллеров Planes, например

parkPlane(Airplane plane)

и

servicePlane(Airplane plane)

реализованы в вашей программе. Это не BREAK ваш код. Я имею в виду, что он не должен меняться, пока он принимает аргументы как AirPlane.

Поскольку он будет принимать любой самолет, несмотря на фактический тип, flyer, highflyr, fighter и т. Д. .

Кроме того, в коллекции:

List<Airplane> plane; // Возьмем все ваши самолеты.

Следующий пример очистит ваше понимание.


У вас есть истребитель, который его реализует, поэтому

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

То же самое для HighFlyer и другой классы:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Теперь подумайте, что ваш контроллер классы, использующие AirPlane несколько раз,

Предположим, что ваш класс Controller - это ControlPlane, как показано ниже,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

здесь магия появляется как

, вы можете сделать свой новый AirPlane, и вы не меняете

код класса ControlPlane.

вы можете добавить экземпляр ..

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

вы также можете удалить экземпляры ранее созданных типов.

2
ответ дан Sanjay Rabari 15 August 2018 в 14:35
поделиться

Может быть полезно программировать интерфейсы, даже если мы не зависим от абстракций.

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

  1. мешает нам делать контекстуально неприемлемые вещи, а
  2. позволяет нам безопасно изменить реализацию в будущем.

Например, рассмотрим класс Person, который реализует интерфейс Friend и Employee.

class Person implements AbstractEmployee, AbstractFriend {

}

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

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

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

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

Отлично. Мы ведем себя соответственно в разных контекстах, и наше программное обеспечение работает хорошо.

В будущем, если наш бизнес изменится для работы с собаками, мы сможем довольно легко изменить программное обеспечение. Сначала мы создаем класс Dog, который реализует как Friend, так и Employee. Затем мы благополучно меняем new Person() на new Dog(). Даже если обе функции имеют тысячи строк кода, это простое редактирование будет работать, потому что мы знаем следующее:

  1. Функция party использует только подмножество Friend в Person.
  2. Функция workplace использует только подмножество Employee в Person.
  3. Класс Dog реализует интерфейсы Friend и Employee.

С другой стороны, если либо party, либо workplace должны были запрограммироваться против Person, существует риск того, что оба имеют Person -специфический код. Переход от Person в Dog потребовал бы, чтобы мы расчесывали код, чтобы искоренить любой Person -специфический код, который Dog не поддерживает.

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

1
ответ дан Shaun Luttin 15 August 2018 в 14:35
поделиться
  • 1
    Предполагая, что у вас нет чрезмерно широких интерфейсов. – Casey 18 July 2017 в 20:41

Интерфейс подобен контракту, в котором вы хотите, чтобы ваш класс реализации реализовал методы, написанные в контракте (интерфейс). Поскольку Java не предоставляет множественное наследование, программирование для интерфейса является хорошим способом достижения цели множественного наследования. Если у вас есть класс A, который уже расширяет какой-либо другой класс B, но вы хотите, чтобы класс A также следовал определенным рекомендациям или реализовывал определенный контракт, тогда вы можете сделать это путем программирования стратегии интерфейса.

1
ответ дан Shivam Sugandhi 15 August 2018 в 14:35
поделиться

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

// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

, тогда я мог бы создать GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider и т. Д.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

, а затем создать JpegImageLoader, GifImageLoader, PngImageLoader и т. Д.

Большинство надстроек и систем плагинов работают с интерфейсами.

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

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

, тогда я мог бы создать XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository и т. Д. Для моих веб-приложений я часто создаю хранилища XML на ранней стадии, поэтому я могу получить что-то и запустить до того, как база данных Sql будет готова. Когда база данных будет готова, я напишу SQLRepository для замены версии XML. Остальная часть моего кода остается неизменной, так как она работает с интерфейсами.

Методы могут принимать интерфейсы, такие как:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}
12
ответ дан Todd Smith 15 August 2018 в 14:35
поделиться

Объяснение C ++.

Подумайте о интерфейсе как об общедоступных методах классов.

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

Любой класс алгоритмов, который определяет функции / интерфейс Vector, вызывает вызовы, которые удовлетворяют «контракту», (как объяснил кто-то в первоначальном ответе). Алгоритмы даже не должны быть одного базового класса; единственное требование состоит в том, что функции / методы, которые вектор зависит от (интерфейса), определены в вашем алгоритме.

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

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

Это экономит время (повторное использование кода ООП), так как вы можете написать алгоритм один раз, а не снова и снова, каждый новый объект, который вы создаете, без чрезмерного усложнения проблемы с заросшим деревом наследования.

Что касается «отсутствия» в отношении того, как все работает; (по крайней мере, на C ++), так как это работает в основном из стандартной среды библиотеки TEMPLATE.

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

Это огромная тема и один из краеугольных принципов дизайна шаблонов.

3
ответ дан Trae Barlow 15 August 2018 в 14:35
поделиться

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

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

EDIT: Ниже приведена ссылка на статью, где Эрих Гамма обсуждает свою цитату «Программа для интерфейса, а не реализация».

http://www.artima.com/lejava/articles/designprinciples.html

35
ответ дан tvanfosson 15 August 2018 в 14:35
поделиться
  • 1
    большое интервью linj, спасибо! – PawelP 15 November 2013 в 12:47
  • 2
    Пожалуйста, еще раз прочитайте это интервью: Гамма, конечно, говорила о концепции интерфейса OO, а не о JAVA или классе C # (ISomething). Проблема в том, что большинство людей, хотя он и говорил о ключевом слове, так что теперь у нас есть много необработанных интерфейсов (ISomething). – Sylvain Rodrigue 13 March 2015 в 13:17
  • 3
    Очень хорошее интервью. Пожалуйста, будьте внимательны к будущим читателям, в интервью есть четыре страницы. Я почти закрыл браузер, прежде чем увидеть его. – Ad Infinitum 21 April 2017 в 12:07

Q: - ... "Можно использовать любой класс, реализующий интерфейс?" A: - Да.

Q: -... «Когда вам это нужно?» A: - Каждый раз, когда вам нужен класс (ы), который реализует интерфейс (ы).

Примечание: мы не смогли создать интерфейс, не реализованный классом - Правда.

  • почему?
  • , потому что интерфейс имеет только прототипы методов, а не определения (просто имена функций, а не их логика)

AnIntf ​​anInst = new Aclass (); // мы могли бы сделать это, только если Aclass реализует AnIntf. // anInst будет иметь ссылку на Aclass.


Примечание: Теперь мы могли бы понять что происходит, если Bclass и Cclass реализуют один и тот же Dintf.

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

Что мы имеем: те же интерфейсные прототипы (имена функций в интерфейсе) и вызываем разные реализации.

Библиография: Прототипы - википедия

1
ответ дан user 15 August 2018 в 14:35
поделиться

В дополнение к уже выбранному ответу (и различным информационным сообщениям здесь) я настоятельно рекомендую захватить копию Head First Design Patterns . Это очень простое чтение и ответит на ваш вопрос напрямую, объясните, почему это важно, и покажите вам много шаблонов программирования, которые вы можете использовать, чтобы использовать этот принцип (и другие).

6
ответ дан whaley 15 August 2018 в 14:35
поделиться

Я поздний участник этого вопроса, но я хочу упомянуть здесь, что строка «Программа для интерфейса, а не реализация» имела хорошее обсуждение в книге шаблонов дизайна GoF (Gang of Four).

На стр. 18:

Программа для интерфейса, а не для реализации

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

и выше этого, это началось с:

Есть два преимущества манипулировать объектами исключительно с точки зрения интерфейса, определенного абстрактными классами:

  1. Клиенты не знают о конкретных типах объектов, которые они используют, если объекты соответствуют интерфейсу, который ожидают клиенты.
  2. Клиенты не знают о классах, которые реализуют эти объекты. Клиенты знают только об абстрактном классе (ов), определяющем интерфейс.

Так, другими словами, не записывайте его в свои классы, чтобы он имел метод quack() для уток, а затем метод bark() для собак, поскольку они слишком специфичны для конкретной реализации класса (или подкласса). Вместо этого напишите метод, используя имена, которые являются достаточно общими для использования в базовом классе, например giveSound() или move(), чтобы их можно было использовать для уток, собак или даже автомобилей, а затем для клиента вашего классы могут просто сказать .giveSound(), а не думать о том, использовать ли quack() или bark() или даже определить тип перед выдачей правильного сообщения, которое должно быть отправлено объекту.

6
ответ дан 太極者無極而生 15 August 2018 в 14:35
поделиться

Вы должны посмотреть на инверсию управления:

В таком сценарии вы не будете писать это:

IInterface classRef = new ObjectWhatever();

Вы бы написали что-то вроде этого:

IInterface classRef = container.Resolve<IInterface>();

Это приведет к установке на основе правил в объекте container и построит для вас фактический объект, который может быть ObjectWhatever. Важно то, что вы могли бы заменить это правило тем, что вообще использовали другой тип объекта, и ваш код все равно будет работать.

Если мы покинем IoC из таблицы, вы можете написать код, который знает, что это может разговаривать с объектом , который делает что-то конкретное , но не тот тип объекта или как он это делает.

Это было бы полезно при передаче параметров.

Что касается вашего вопроса в скобках «Также, как вы могли написать метод, который принимает объект, реализующий интерфейс? Возможно ли это?», на C # вы просто используете тип интерфейса для типа параметра, например:

public void DoSomethingToAnObject(IInterface whatever) { ... }

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

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

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

Позвольте мне привести конкретный пример.

У нас есть пользовательская система перевода окон. Эта система проходит через элементы управления в форме и преобразует текст в каждый. Система знает, как обрабатывать базовые элементы управления, такие как свойство type-of-of-control-that-a-Text-свойство и аналогичные базовые элементы, но для чего-то базового оно не подходит.

Теперь , поскольку элементы управления наследуют от заранее определенных классов, которые мы не контролируем, мы могли бы сделать одну из трех вещей:

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

Итак, мы сделали nr. 3. Все наши элементы управления реализуют ILocalizable, который является интерфейсом, который дает нам один метод, способность переводить «сам» в контейнер текста перевода / правил. Таким образом, форма не должна знать, какой вид управления она обнаружила, только то, что она реализует конкретный интерфейс, и знает, что существует метод, в котором он может вызвать локализацию элемента управления.

34
ответ дан Sameer 15 August 2018 в 14:36
поделиться
  • 1
    Почему упоминание IoC в самом начале, поскольку это только добавит больше путаницы. – Kevin Le - Khnle 21 December 2008 в 02:13
  • 2
    Согласитесь, я бы сказал, что программирование против интерфейсов - это просто способ сделать IoC проще и надежнее. – terjetyl 21 December 2008 в 03:36
Другие вопросы по тегам:

Похожие вопросы: