Comments 76
Подводя итог этой части, если какой-то интерфейс реализуется только в одном единственном классе, то не тратьте время на него. Он просто не нужен.
Это, заметим, уже неправда. Если у вас потребитель и реализация находятся в разных сборках, то использование интерфейса может позволить упростить управление зависимостями, начиная с того, что потребитель не будет иметь никакой информации о реализации, и заканчивая тем, что потребитель интерфейса может собираться под .net 4, а реализация — под 4.7.
У объявления через интерфейс есть один недостаток: вам будут доступны только методы и свойства, объявленные в интерфейсе и самом классе
… только в интерфейсе. И это не недостаток, это смысл использования интерфейсов.
возможность реализовать в одном классе два интерфейса с одинаковыми по сигнатуре, но разными по содержанию методами. Я не знаю какая от этого может быть практическая польза кроме уже упомянутой
Во-первых, иногда вам надо иметь разные типы возвращаемых значений (которые являются частью сигнатуры, но не позволяют делать перегрузку). Самый яркий пример — GetEnumerator
.
Во-вторых, когда интерфейсы разрабатывают разные люди (а класс должен реализовать их все), пересечение имен методов — вполне типовая ситуация.
Интересно, что при реализации таких интерфейсов методам не могут быть назначены модификаторы доступа
… потому что метод, реализующий метод интерфейса, всегда публичный.
То есть когда вы написали "если какой-то интерфейс реализуется только в одном единственном классе, то [...] он просто не нужен" — вы имели в виду не это, а что-то другое?
Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.
Так это же неправда.
Люди удаляют интерфейс и не видят разницы, а значит, полагают они, интерфейс — это что-то избыточное.
Если люди удалили интерфейс и не увидели разницы, значит в их ситуации интерфейс правда избыточен.
Интерфейсы проявляют себя либо в командных проектах, либо при несколько нестандартном взгляде на них.
Интерфейсы прекрасно себя проявляют при использовании по назначению даже в сольных проектах.
Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.
Так это же неправда.
Что здесь не так? Расскажите, как добавление интерфейса к работающему проекту из пары классов что-то изменит.
Статья для тех, кто еще не столкнулся с необходимостью использовать интерфейсы и не может понять зачем они, если все и так работает.
Расскажите, как добавление интерфейса к работающему проекту из пары классов что-то изменит.
Это от "пары классов" зависит.
Например: у вас есть сборка Impl, содержащая классы A и B, и есть сборка Client, содержащая класс C, зависящий от класса A. В тот момент, когда вы добавляете IA (A: IA), вы разрываете прямую зависимость, и позволяете разработчику C делать то, что он считает нужным, и так, как он считает нужным, и одновременно упрощаете разработчку A жизнь, уменьшая количество публичных обязательств его класса. Собственно, после этого A можно сделать internal, и вообще не думать, что кто-то на него завяжется.
А есть ведь и более сложные ситуации: вот у вас есть A и B в одной сборке, и вам надо разбить эту сборку на две для упрощения поддержки. Но вот беда: A и B, которые должны оказаться в разных сборках, зависят друг от друга. Выделение интерфейсов (хотя каждый интерфейс будет реализован ровно одним классом) позволяет решить эту задачу.
Статья для тех, кто еще не столкнулся с необходимостью использовать интерфейсы и не может понять зачем они, если все и так работает.
А из вашей статьи так и не понятно, зачем они, если и так все работает.
Эээ… нет. Второй пример не имеет никакого отношения к командной разработке.
Но он и к сути вопроса не имеет отношения
К сути какого вопроса? "Зачем нужны интерфейсы"? Имеет, и прямое.
Совсем не обязательно, что все вокруг построено на сложных проектах с многочисленными зависимостями.
… так вот интерфейсы нужны как раз тогда, когда у вас сложность проекта переваливает за некий предел (если не брать в расчет те случаи, когда вам интерфейсы навязаны требованиями). Не переваливает — не нужны.
Вот тут ниже заминусили мое предложение добавить интерфейс к HelloWord — это потому что придется согласиться со мной?
Нет, потому, что это бессмысленный пример.
Да банально для юнит-тестов.
Есть у вас предположим в проекте одна реализация сервиса А. Вы предлагаете без интерфейса внедрять её в другие классы.
Да, есть фреймворки которые умеют мокать даже такие внедрения, но гораздо проще замокать интерфейс, а не конкретную реализацию.
Я объяснил, что если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.
SOLID, «Слабая связанность», DI, IoC.
Погуглите эти фразы, чтобы не делать столь громких и, одновременно, неуместных заявлений.
Я допускаю, что вы неправильно ФОРМУЛИРУЕТЕ эту мысль даже для самого себя. Это должно звучать примерно так:
Применение интерфейса имеет, как положительный эффект — снижение уровня связанности объектов, что положительно сказывается на возможности расширения функционала разрабатываемого ПО, масштабирования и внесения в него правок. Так и негативные — некоторое увеличение расхода системных ресурсов, увеличение объёма кода и времени на разработку. По этому программист должен определять необходимость применения интерфейса в зависимости от конкретной ситуации.
Мысль можно развить, дополнить, добавить уточнения, но, согласитесь, это уже намного лучше, чем
если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нужен.
Тогда бы стоило написать, зачем они действительно нужны. То есть SOLID, DI, IOC, полиморфизм и вот это все.
А у вас "можно методы с одинаковыми именами сделать, если никак не придумать уникальное". Статья наполовину про то, что "по назначению использовать интерфейсы вам пока не надо, поэтому можно вот такую штуку сделать, потому что можем".
Проблема вашего поста в том, что вы не объясняете, когда интерфейсы нужны, и заодно ошибочно утверждаете, когда они не нужны.
interface IFont
{
void Draw(...);
}
Этот интерфейс реализован одним лишь классом
class SpriteFont : IFont
{
public void Draw(...);
...
}
По вашим словам, он тут не нужен. Однако, все другие классы, которые как-то завязаны на шрифтах (например Label) используют не SpriteFont, а IFont. Догадаетесь, почему?
Ответ очень прост — чтобы человек, который решит поиспользовать мой движок, смог сам написать такую реализацию шрифта, которую сочтет нужной. Это если его моя реализация не устроит.
Вы рассказали о том, что интерфейсы создают гибкий код, но не сказали, почему. Потому что интерфейсы — это описание протокола взаимодействия между классами. И заявлять, что
если кроме этого проекта ваш класс нигде больше не существует, то и интерфейс не нуженочень сомнительно.
А, все рано же не напишите…
Но сейчас я совершенно не согласен, потому что начал использовать интерфейсы правильно, мои библиотеки начали использовать другие разработчики, которые наверняка попытаются вернуть int там, где ожидается string, например. Так что, советую пост удалить и попытаться еще раз построить код правильно.
public class A {
public void Run() => Console.WriteLine("A");
}
public class B {
public void Run() => Console.WriteLine("B");
}
public class C {
public void Run() => Console.WriteLine("C");
}
object GetSmth() => new B();
void Main()
{
var smth = GetSmth();
if (smth is A a)
a.Run();
if (smth is B b)
b.Run();
if (smth is C c)
c.Run();
}
Тут ничего не изменится и не улучшится с добавлением интерфейса?
Хотя интерфейс здесь конечно просто просится.
Мне этого рассказывать не надо, я про все это знаю. Я бы на ревью этот код завернул. И в собственных проектах тоже без весомой причины такое никогда бы не написал.
Вы уж определитесь, "интерфейс просится" или "не изменится и лучше не станет"?
что касается ревью, то пожалуй:
interface Ix {
void Run();
}
public class A: Ix
{
public void Run() => Console.WriteLine("A");
}
public class B: Ix
{
public void Run() => Console.WriteLine("B");
}
public class C: Ix
{
public void Run() => Console.WriteLine("C");
}
class Prg
{
object GetSmth() => new B();
public Prg()
{
var smth = (Ix)GetSmth();
smth.Run();
Console.ReadKey();
}
}
Это по сути то, что я написал сразу после дополнения, если вы читали (многие по-моему не читали дальше ката)
Что тут не понятно? Три класса имеют одинаковые методы. Было бы красиво прикрутить интерфейс, который они реализую.
Да, пожалуй, мне стоило назвать классы чуть иначе. Например, FooTaskRunner, BarTaskRunner и BazTaskRunner. Было бы чуть очевиднее.
Понимаете, было бы не "красиво" прикрутить интерфейс. Это необходимо, потому что ООП и далее по тексту.
Ваша логика примерно такая: можно не делать интерфейсы, если можно написать без них. Это примерно тот же уровень логики, как "можно не писать грамотно, мы не в школе". Что угодно можно написать без интерфейсов. Но это не значит, что это нужно делать.
Я вот просто открыл гитхаб и набрал в поиске «C#», перешел по первой же ссылке — какая-то игра «JumpAttacker». Довольно объемный проект. Походил по файлам. Нет интерфейсов… Может плохо искал. Пошел по другой ссылке (что-то китайское «GavinYellow»). В некоторых классах были интерфейсы, в некоторых нет. Идем далее «statianzo» — где-есть, где-то нет интерфейса.
Я уже ниже писал, что если погуглить англоязычные форумы, то там пишут, что интерфейсы не надо липить везде подряд вот или вот
И вы уж извините, но я склонен больше доверять тем, кто придумал C#.
Do define an interface if you need to provide a polymorphic hiercharcy of value types, а если полиморфизм не нужен, то не захламяйте код.
Ок, у вас в проектах все без исключения классы имеют интерфейсы потому что ООП? И еще наверно абстрактный класс до кучи?
Нет. Интерфейсы у меня там, где они необходимы, а не везде. Например, там, где есть отношение "является", или где необходимо предоставлять классу какие-то зависимости.
И абстрактные классы, не поверите, тоже там, где они нужны. Но вообще редко приходится их использовать.
Никто это и не утверждает. Все говорят вам, что ваши утверждения про то, что если реализация одна или что без интерфейса тоже заработает, то он не нужен — бред.
Я уже ниже писал, что если погуглить англоязычные форумы, то там пишут, что интерфейсы не надо липить везде подряд вот или вот
Вам здесь еще никто и не сказал, что их надо лепить везде подряд.
что если реализация одна или что без интерфейса тоже заработает, то он не нужен — бред.Почему?
Вам здесь еще никто и не сказал, что их надо лепить везде подряд.
Да все только про это и говорят. Я пишу, что интерфейс нужен вовсе не везде а только там, где он нужен, а мне отвечают, что я не прав. Вы же программист? Утверждение обратное «Интерфейс нужен не везде» — это «Интерфейс нужен везде». И при этом все отмечают, что у них как бы не все классы под интерфейсами, но я все равно не прав.
Я пишу, что интерфейс нужен вовсе не везде а только там, где он нужен
… как определить, где нужен интерфейс?
Ну то есть на вопрос "я пишу проект, зачем мне делать в нем интерфейсы?" у вас ответа нет?
Если вас просят вскопать огород, вы приходите с лопатой, если забить гвоздь, то с молотком. Вы же не потащите «на объект» все возможные инструменты. Так и здесь.
Вы уже поняли, что зря тут на меня наехали, но гордость не позволяет отступить. Начали уже за все подряд цепляться лишь бы последнее слово сказать.
Do define an interface if you need to provide a polymorphic hiercharcy of value types
Плохая, плохая цитата. Если ей верить, то интерфейсы надо использовать тогда, когда у вас value types (ни одного примера в вашем посте), а все остальное время надо использовать классы:
Do favor defining classes over interfaces.
Do use abstract [...] classes instead of interfaces to decouple the contract from implementations.
… и только если нужно множественное наследование, тогда:
Consider defining interfaces to achieve an effect similar to that of multiple inheritance.
К счастью, это не "те, кто придумал C#". И сейчас разрабатывают не так: в современных публичных API намного пропорция "интерфейсы — абстрактные классы" — по крайней мере, по моим наблюдениям — существенно сильнее сместилась в сторону интерфейсов.
Я пишу, что если все работает без интерфейсов (понимаете, уже работает, все, задача исполнена), то добавив интерфейс вы ни чего не измените и не улучшите.
Это зависит от того, что считать улучшением.
Да нет, это вполне нормальный жизненный вопрос. Когда вы делаете рефакторинг, вы (обычно) улучшаете код. Поэтому даже если что-то работает без интерфейсов, но можно отрефакторить, применив интерфейсы, станет лучше (читаемее, например).
Реальный вопрос возникает дальше: а нужно ли это улучшение? Потому что если этот проект отдали и забыли, и поддерживать его не надо — то, возможно, возиться с рефакторингом тоже не надо, потому что не окупится. А вот если оно работает, но завтра могут прийти и попросить поправить...
Собственно, это все рассуждения, который собственно к интерфейсам отношения не имеют, это общие мысли про рефакторинг. В моем опыте случаев, когда что-то сначала писали "просто чтобы работало", а потом рефакторили на "более правильно", очень и очень много.
Я сегодня добавлял интерфейс там, где его не было несколько лет (и все прекрасно работало без него).
Я храбро разрабатываю в одиночку. У меня есть коллекция, по которой я хочу иметь возможность итерировать, однако это, будучи коллекцией, не знаю, каких-то узлов в дереве, а именно — трехмерных объектов на сцене, наследует определенные свойства абстрактного класса, реализующего отрисовку геометрии. Абстрактный класс и его наследники предназначены для вывода, перебор для физического движка. В итоге мне нужно реализовать два интерфейса. Как это сделать? В C# нет множественного наследования.
Или у меня есть интерфейс Event. На какой черт мне нужен абстрактный класс, в котором не реализован ни один метод? Я просто сделаю интерфейс IEvent (вдобавок некоторые события опять же могут реализовывать несколько интерфейсов).
Все в рамках одного проекта и без команды. Нужны интерфейсы? Ну можно было нагородить дерево наследования как-то хитро, избегая таких случаев, создавая непонятные проблемы. А можно просто реализовать интерфейс.
en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system
Ну даже если он разрабатывает например сам, возьмем разработку сайтов. Вот он написал, первое что пришло в голову — логгер, который пишет лог в файл
class MyFileLogger
{
public void LogMessage(string message) {}
}
И всё у него было хорошо, пока ему не потребовалось писать лог в базу. Ему придется переписать или реализацию метода, или все ссылки на MyFileLogger исправить на MyDBLogger. А если он юзает свои наработки, возможно, что логгер в какой-то подключаемой библиотеке. Одни недостатки. То ли дело, если бы наш программист заранее подумал, «а мало ли», и сделал интерфейс:
interface IMyLogger
{
void LogMessage(string message);
}
и использовал ссылки этого типа, чтобы отвязаться от реализации.
Мой пример утрирован, но думаю, он должен дать пищу для размышлений.
Так же есть такое мнение, с которым лично я полностью согласен: «программировать нужно на уровне интерфейсов, а не на уровне реализации». Интерфейсы дают более чистое представление о структуре и логике проекта.
Покажите мне где я написал, что интерфейсы не нужны?
Показываю:
Подводя итог этой части, если какой-то интерфейс реализуется только в одном единственном классе, то не тратьте время на него. Он просто не нужен.
Вам выше рассказали почему это утверждение не соответствует действительности.
Я уверен, что в вашем проекте, которым сейчас вы занимаетесь (если вы реально программист) найдется не мало классов к которым нет интерфейсов.
Я выше спросил надо написать интерфейс к HelloWord, но мне не ответили по существу. Да и не ответят. Общий тренд уже задан. Комментариев в поддержку не будет, чтобы минусов не нахватать от вас.
Разве я здесь написал, что интерфейсы не нужны вообще?
Конечно нет. Вы написали что "интерфейсы не нужны к классам с единственной реализацией".
А я вот не согласен с этим утверждением.
Есть случаи когда к классу с единственной реализацией нужен интерфейс. Тестируемый код должен быть слабосвязанным.
Добиться этого проще через интерфейсы, поэтому даже если у Вас есть всего одна (и больше не планируется) реализация сервиса работы с очередью для получения данных, проще выделить интерфейс IQueueDataService чтобы замокать её в юнит-тестах с полпинка. Попутно получаем все прочие плюсы слабой связанности.
Расскажите где я неправ, очень будет любопытно.
Я вам по существу отвечу про hello world здесь, ладно? А то вы много раз про это напоминали. Обрадую: к hello world интерфейс не нужен. Но это несостоятельный пример ровно по той причине, что hello world вы пишете примерно один раз в жизни.
Да, на каждый класс вешать интерфейс не нужно, это факт. Интерфейсы нужны там, где они могут обеспечить полифморфизм, сокрытие и возможность замены реализации и все такое. Но у вас про это нет ни слова, вы не объясняете, когда они нужны. Из вашей статьи и комментариев следует только то, что "если вы пилите свой небольшой проект, то зачем эти интерфейсы, если можно как-нибудь и без них накодить, чтоб работало". И это совершенно бесполезно и даже вредно. Независимо от масштаба проекта и количества разработчиков код должен быть хорошим и поддерживаемым, в том числе за счет использования интерфейсов там, где это нужно. Но где это нужно, вы так и не сказали.
В первых же абзацах вы приводите какой-то случайный интерфейс и пишете "Практическая ценность такого договора минимальная". Неужели? Вы сразу бросаете серьезнейшее утверждение, при этом не приводя никакого контекста. Как используется этот интерфейс? Кто его реализует?
Вы пишете про реализацию интерфейса и говорите, что сами не знаете, зачем это, кроме как чтобы методы с одинаковой сигнатурой сделать. Может, если вы не знаете и не можете пояснить, зачем это, не стоит писать статью про интерфейсы?
У вас вроде обучающая статья. Вы ориентируетесь на тех, кто только учится, у кого нет знания терминологии. И у вас в статье с терминологией просто ужас. Интерфейсы не наследуются, а реализуются. Объявляют не "экземпляр класса", а "переменную типа". Классы с общими признаками, и возможность их использовать, не зная конкретного типа, называется полиморфизмом. Такие ошибки просто недопустимо делать, если вы хотите чему-то кого-то научить.
Ни кто не хочет смириться с мыслью, что бывают простые проекты.
По поводу реализации похожих интерфейсов, я написал, что есть механизм для этого, исключающий неоднозначность, что не знаю как еще это можно использовать, кроме того, о чем написал, но можно проделать интересный фокус, который возможно кому-то покажется полезным. Учитесь читать.
По поводу терминологии. Один раз случайно «унаследовал» интерфейс и все. Если верить МСДНу, то создают экземпляр класса, а не «переменную типа». Да, не объявляют. 1:1
Прочее читаю придиками не по существу.
Ни кто не хочет смириться с мыслью, что бывают простые проекты.
Знаете, у меня за последние лет пять-восемь не было ни одного проекта, исключая чисто учебные или тестовые запускалки, где не всплывали бы интерфейсы.
По ссылке из вашей единственной статьи (да, я любопытный) перешел на гитхаб в «unitycontainer», походил по классам. Интерфейсы есть далеко не везде. Может уже пора признать, что если интерфейс не нужен, то он и не нужен?
Может уже пора признать, что если интерфейс не нужен, то он и не нужен?
А я, вроде бы, пять часов назад написал вам: "интерфейсы нужны как раз тогда, когда у вас сложность проекта переваливает за некий предел [...] не переваливает — не нужны"
Учебные и тестовые программы за этот предел не переваливают. Простая, казалось бы, консольная программка для заливки/выгрузки схем с удаленного сайта — переваливает.
По ссылке из вашей единственной статьи (да, я любопытный) перешел на гитхаб в «unitycontainer», походил по классам. Интерфейсы есть далеко не везде.
… при этом весь смысл Automapper.Unity
(а совсем не UnityContainer, за который я никак не отвечаю) ровно в том, чтобы сделать удобным взаимодействие с интерфейсом (вместо статического класса). И, что характерно, у этого интерфейса всего одна представляющая интерес реализация. Так что вы выбрали неудачный пример (ну или удачный, смотря какую точку зрения доказывать).
Все 100% классов в ваших проектах имели интерфейсы?
Если взять MVC, например, то, скажем, модели (те классы, которые просто представляют сущности) — могут не использовать интерфейсы. Хотя я в моделях реализовывал интерфейсы, типа IDeletable, ISorted и подобные.
Вы лично можете не использовать интерфейсы в контроллерах, хотя опять же, зависит от ситуации, да и тот же ApiController реализует IDisposable.
Так же в C# существуют статические классы (хелперы, либо классы с расширяющими методами).
А еще вложенные типы, которые тоже, на моей практике, часто не реализуют интерфейсы.
А еще, у меня сейчас открыт проект, в котором классы Program и Startup не реализуют интерфейсы.
Это просто примеры навскидку. Уверен, найдется еще множество причин не использовать интерфейсы
И, кстати, Вам не сказал lair, что «все классы должны реализовывать интерфейсы».
Если верить МСДНу, то создают экземпляр класса, а не «переменную типа». Да, не объявляют. 1:1
О, вы счет ведете? Круто. Так вот:
Экземпляр класса создают. Переменную типа объявляют. Это разные вещи.
А у вас в статье "объявляют экземпляр класса":
В такой случае можно объявить экземпляр класса, реализующего интерфейс:
…
IForecast any;
Открываем microsoft. Членами класса являются 1,2,3,… Переменных нет, но есть поля класса, которые содержат переменные класса. Идем дальше на страницу полей
Поля объявляются в блоке класса путем указания уровня доступа поля, за которым следует тип поля, а затем имя поля.
Т.е. в данном случае все таки объявление, но конечно не класса, но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.
Я не понимаю, зачем это все здесь обсуждать. Очевидно у вас тоже не уверенные знания.
Переменные есть в другой статье, про локальные переменные.
У вас IForecast any; — это что? Локальная переменная или поле? Приведите полный кусок кода, без пропусков.
Из текста следует, что это локальная переменная. Если вы подразумевали, что это поле, то ладно, суди это не меняет: объявляется поле класса, создается экземпляр класса.
И не увлекайтесь сильно русским мсдн-ом, там часто машинный перевод, и он откровенно кривой.
но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.
Простите, что? Под полем подразумевается класс?
Я не понимаю, зачем это все здесь обсуждать. Очевидно у вас тоже не уверенные знания.
Затем, что ваша обучающая статья сдержит ошибки и обучаться кому-то по ней противопоказано.
Т.е. в данном случае все таки объявление, но конечно не класса, но и не переменной, а поля. Под которым в дальнейшем подразумевается класс.
Под полем не может подразумеваться класс. Поле — это поле, у него есть тип (который может быть классом, структурой или интерфейсом).
Разве я здесь написал, что интерфейсы не нужны вообще?
Нет, вы написали, что интерфейсы не нужны в конкретном частном случае, и это утверждение неверно.
Устал повторять, но что вы все зациклились на командной работе?
Потому что ориентироваться надо именно на нее. И, что занятно, практики, позволяющие одному человеку (без феноменальной памяти) работать с большим и сложным проектом, — те же самые, как если бы там работала команда.
Интерфейсы в C#