Комментарии 35
Это как список ToDo? Просто для удобства? Программист-руководитель написал интерфейс, другой программист им пользуется пока третий пишет реализацию? В чем "реализуется истинная инкапсуляция, невозможная при использовании обычного класса"? Это без относительно С++, в Java я так понимаю они тоже есть.
Как там в простых примерах — есть у меня интерфейс Animal — в нем виртуальные функции Jump, Run, Eat — которые где то реализованы в производном классе. Зачем этот промежуточный этап в виде интерфейса?
Интерфейс позволяет зависеть от абстракции, а не от реализации.
Лучше пример с логированием подойдёт, например
У Вас есть интерфейс ILogger с методом Log(message: string)
Всё кто используют этот метод не знают конкретной реализации, может логгер пишет в БД, а может в файл. А может вообще отправляет на почту админу.
При зависимости от интерфейса Вы легко можете поменять реализацию, не трогая потребителей интерфейса
У интерфейсов есть и другие достоинства. Один класс может реализовывать несколько разных интерфейсов. Интерфейсы хорошо версионируются:
class IFoo_v1
{
public:
virtual void bar() = 0;
};
class IFoo_v2 : public IFoo_v1
{
public:
virtual void baz() = 0; // Метод, появившийся только во второй версии
[[deprecated]] void bar() override = 0; // Устаревший во второй версии метод
};
using IFoo = IFoo_v2; // Алиас на последнюю версию. Пользователи могут использовать его или конкретную
// implementation
class Foo : public IFoo
{
//...
};
К уже написанному добавлю, что интерфейсы и реализации подчиняются принципу подстановки Лисков. Это значит, что везде, где ожидается некий базовый класс, можно передать объект любого дочернего класса, и всё должно работать верно. Интерфейсы и есть такие базовые классы, а их реализации — дочерние, поэтому если функция ожидает аргумент класса интерфейса, в неё можно передать любой дочерний класс-реализацию. Если бы функция ожидала конкретный класс-реализацию, в неё нельзя было бы передать другую реализацию, т.к. она является не дочерней, а сиблингом (т.е. братом-сестрой, по аналогии с родством среди людей). Таким образом, гибкость архитектуры становится сильно хуже, и программа теряет расширяемость и модифицируемость.
Извиняюсь за некропост, но, думаю, кому то это будет полезно.
Интерфейс описывает требования, чтобы объектом можно было пользоваться. Причём тому, кто им пользуется совсем не важно знать, как оно работает.
Вот представь, что ты выучился на управление машиной. С этого момента ты фактически можешь управлять абсолютно любой машиной, тебе не важно знать как она работает, чтобы это делать, а важно только, что у нее есть определенные методы взаимодействия с ней. Если бы ты использовал именно конкретную реализацию вмето интерфейса, то в примере выше получилось бы, что ты владеешь управлением толлко одной конкретной модели машины.
В этом контексте интерфейс - это машина, реализация интерфейса - это конкретная модель машины, а управление ею - вызов методов интерфейса.
Спасибо за комментарий, очень образное описание сути интерфейса. Многие языки программирования поддерживают интерфейсы на уровне языка (например C#, Java), но вот C++ нет. Приходится эмулировать интерфейсы с помощью чисто виртуальных функций, но при этом возникают достаточно много тонкостей и потенциальных проблем. В своей статье я как раз и попытался разобраться с этими проблемами.
Chapter 4: Smart Pointers
Smart pointers are one way to address these issues. Smart pointers are wrappers
around raw pointers that act much like the raw pointers they wrap, but that avoid
many of their pitfalls. You should therefore prefer smart pointers to raw pointers.
Smart pointers can do virtually everything raw pointers can, but with far fewer
opportunities for error.
Мне кажется, что термин умные указатели никуда не делся. Как они были smart pointer так и остались.
Похоже на студенческую курсовую работу. Какая здесь новая информация?
Очень хотелось бы увидеть примеры «комфортной» реализации COM интерфейсов (interface+dispinterface) на C++.
books.google.com/books/about/Essential_COM.html?id=kfRWvKSePmAC
С другой стороны, в Delphi, VB, и скриптовых языках от MS (jscript, vbs) за счёт IDispatch уже довольно удобно пользоваться COM, и потому никаких изменений не нужно.
Удобная вещь когда нужно подготовить «контекст» перед вызовом виртуальной ф-ции
Отличная подборка материала про интерфейсы, и подводные камни в реализации и использовании.
Следует с осторожностью объявлять функции-члены интерфейсных классов как const.
Вы могли бы раскрыть здесь подробнее, какие сложности нас могут ожидать на этом пути.
Например, доступ к свойству может быть реализован с использованием lazy evaluation.
вы неправильно понимаете константность. Это всё-таки про неизменяемость наблюдаемого состояния объекта, а не внутреннего. Яркий пример — COW структуры данных, thread-safe классы с mutex'ами и пр. И если ваш getter реализован через lazy evaluation, но он никак не может поменять наблюдаемое состояние класса, его наоборот будет правильно пометить как const
А константность, на мой взгляд, нужна для облегчения использования интерфейса, а не наоборот. Она только подчёркивает свойства метода, позволяет избежать ошибок ещё во время компиляции.
Злоупотреблять, расставляя const где попало, конечно не стоит.
Моё мнение таково, что константность должна нести смысловую нагрузку. «Не делать методы константными потому что из-за этого кому-то может быть придется делать mutable поле в реализации» — откровенно неправильный подход к архитектуре.
Разработка интерфейсных классов на С++