Я работаю над коллекцией классов, используемых для воспроизведения и записи видео. У меня есть один главный класс , который действует как открытый интерфейс, с методами , как play()
, stop()
, pause()
, и record()
т.д. ... Тогда у меня есть рабочая лошадка классы , которые делают видео декодирования и кодирования видео.
Я только что узнал о существовании вложенных классов в C ++, и мне любопытно узнать, что программисты думают об их использовании. Я немного осторожен и не совсем уверен, какие преимущества / недостатки есть, но они (в соответствии с книгой, которую я читаю), похоже, используются в таких случаях, как мой.
Книга предполагает, что в сценарии, подобном моему, хорошим решением было бы вложить рабочие классы в класс интерфейса, чтобы не было отдельных файлов для классов, которые клиент не должен использовать, и чтобы избежать возможных конфликтов имен? Я не знаю об этих оправданиях. Вложенные классы - это новая концепция для меня. Просто хочу посмотреть, что программисты думают об этой проблеме.
Я бы неохотно использовал здесь вложенные классы. Что, если вы создали абстрактный базовый класс для «драйвера мультимедиа» для обработки серверной части (рабочей лошадки) и отдельный класс для внешней работы? Внешний интерфейс может взять указатель / ссылку на реализованный класс драйвера (для соответствующего типа носителя и ситуации) и выполнить абстрактные операции над структурой рабочей лошадки.
Моя философия заключалась бы в том, чтобы пойти дальше и сделать обе структуры доступными для клиента безупречным образом, только при условии, что они будут использоваться в тандеме.
Я бы сослался на что-то вроде QTextDocument в Qt. Вы предоставляете прямой интерфейс для обработки данных с нуля, но передаете полномочия объекту, подобному QTextEdit, для выполнения манипуляции.
Вы бы использовали вложенный класс, чтобы создать (маленький) вспомогательный класс, необходимый для реализации основного класса. Или, например, определить интерфейс (класс с абстрактными методами).
В этом случае основным недостатком вложенных классов является то, что это затрудняет их повторное использование. Возможно, вы захотите использовать свой класс VideoDecoder в другом проекте. Если вы сделаете это вложенным классом VideoPlayer, вы не сможете сделать это элегантным способом.
Вместо этого поместите другие классы в отдельные файлы .h / .cpp, которые затем можно использовать в своем классе VideoPlayer. Клиенту VideoPlayer теперь нужно только включить файл, который объявляет VideoPlayer, и все же не нужно знать, как вы его реализовали.
Один из способов решить, использовать ли вложенные классы или нет, - подумать, играет ли этот класс вспомогательную роль или свою собственную.
Если он существует исключительно для помощи другому классу, я обычно делаю его вложенным. Существует целый ряд предостережений, некоторые из которых кажутся противоречивыми, но все сводится к опыту и интуитивным ощущениям.
звучит как случай, когда вы могли бы использовать шаблон стратегии
Иногда целесообразно скрыть классы реализации от пользователя - в этих случаях лучше поместить их в файл foo_internal.h, чем в определение открытого класса. Таким образом, читатели вашего foo.h не увидят того, с чем вы бы предпочли, они не будут обеспокоены, но вы все равно сможете написать тесты для каждой конкретной реализации вашего интерфейса.
Мы столкнулись с проблемой полустарелого компилятора Sun C ++ и видимости вложенных классов, поведение которых изменилось в стандарте. Конечно, это не причина, по которой вы не выполняете вложенный класс, а просто то, о чем нужно знать, если вы планируете компилировать свое программное обеспечение на многих платформах, включая старые компиляторы.
Что ж, если вы используете указатели на ваши классы рабочих лошадей в вашем классе Interface и не выставляете их как параметры или возвращаемые типы в ваших интерфейсных методах, вам не нужно будет включать определения для этих рабочих лошадей в ваш заголовочный файл интерфейса (вы просто вперед объявляю их вместо). Таким образом, пользователям вашего интерфейса не нужно будет знать о классах в фоновом режиме.
Вам определенно не нужно вкладывать классы для этого. Фактически, отдельные файлы классов на самом деле сделают ваш код более читабельным и простым в управлении по мере роста вашего проекта. это также поможет вам позже, если вам нужно создать подкласс (например, для разных типов контента / кодеков).
Вот больше информации о шаблоне PIMPL (раздел 3.1.1).
Вы должны использовать внутренний класс только тогда, когда вы не можете реализовать его как отдельный класс, используя открытый интерфейс потенциального внешнего класса. Внутренние классы увеличивают размер, сложность и ответственность класса, поэтому их следует использовать с осторожностью.
Ваш класс кодировщика / декодера звучит так, как будто он лучше соответствует шаблону стратегии
Одна из причин, по которой следует избегать вложенных классов, - это если вы когда-нибудь намереваетесь обернуть код swig ( http://www.swig.org ) для использования с другими языками. Swig в настоящее время имеет проблемы с вложенными классами, поэтому взаимодействие с библиотеками, которые предоставляют любые вложенные классы, становится настоящей болью.
Следует также помнить, представляете ли вы когда-нибудь разные реализации своих рабочих функций (например, декодирование и кодирование). В этом случае вам определенно понадобится абстрактный базовый класс с различными конкретными классами, которые реализуют функции. На самом деле было бы неуместно вкладывать отдельный подкласс для каждого типа реализации.