В настоящее время я работаю над программным проектом, который выполняет сжатие и индексацию видеозаписей видеонаблюдения. Сжатие выполняется путем разделения объектов фона и переднего плана, а затем сохранения фона в виде статического изображения и переднего плана в виде спрайта.
Недавно я приступил к рассмотрению некоторых классов, которые я разработал для этого проекта.
Я заметил, что есть много классов, которые имеют только один публичный метод. Некоторые из этих классов:
- VideoCompressor (с
compress
методом, который принимает входное видео типаRawVideo
и возвращает выходное видео типаCompressedVideo
). - VideoSplitter (с
split
методом, который принимает входное видео типаRawVideo
и возвращает вектор из 2 выходных видео, каждого типаRawVideo
). - VideoIndexer (с
index
методом, который принимает входное видео типаRawVideo
и возвращает индекс видео типаVideoIndex
).
Я считаю себя инстанцированием каждого класса только совершать звонки , как VideoCompressor.compress(...)
, VideoSplitter.split(...)
, VideoIndexer.index(...)
.
На первый взгляд, я думаю, что имена классов достаточно описывают их предназначенную функцию, и они на самом деле являются существительными. Соответственно их методы также являются глаголами.
Это на самом деле проблема?
источник
Ответы:
Нет, это не проблема, скорее наоборот. Это признак модульности и четкой ответственности класса. Скудный интерфейс легко понять с точки зрения пользователя этого класса, и он будет способствовать слабой связи. Это имеет много преимуществ, но почти не имеет недостатков. Я хотел бы, чтобы больше компонентов было разработано таким образом!
источник
Это больше не объектно-ориентированное. Поскольку эти классы ничего не представляют, они просто сосуды для функций.
Это не значит, что это неправильно. Если функциональность достаточно сложна или когда она является общей (то есть аргументы являются интерфейсами, а не конкретными конечными типами), имеет смысл поместить эту функциональность в отдельный модуль .
Оттуда это зависит от вашего языка. Если в языке есть свободные функции, это должны быть модули, экспортирующие функции. Зачем притворяться, что это класс, когда это не так. Если в языке нет свободных функций, таких как, например, Java, вы создаете классы с помощью одного открытого метода. Ну, это просто показывает пределы объектно-ориентированного дизайна. Иногда функционал просто лучше подходит.
В одном случае вам может понадобиться класс с одним открытым методом, потому что он должен реализовывать интерфейс с одним открытым методом. Будь то для наблюдателя или внедрения зависимости или чего-то еще. Здесь это снова зависит от языка. В языках с функторами первого класса (C ++ (
std::function
или параметр шаблона), C # (делегат), Python, Perl, Ruby (proc
), Lisp, Haskell, ...) эти шаблоны используют типы функций и не нуждаются в классах. В Java нет (пока не будет версии 8) функциональных типов, поэтому вы используете интерфейсы с одним методом и соответствующие классы с одним методом.Конечно, я не защищаю написание одной огромной функции. Он должен иметь частные подпрограммы, но они могут быть частными для файла реализации (статическое или анонимное пространство имен на уровне файлов в C ++) или в частном вспомогательном классе, который создается только внутри публичной функции ( для хранения данных или нет? ).
источник
Могут быть причины извлечь данный метод в выделенный класс. Одна из этих причин - разрешить инъекцию зависимостей.
Представьте, что у вас есть класс с именем,
VideoExporter
который, в конце концов, сможет сжимать видео. Чистым способом было бы иметь интерфейс:который будет реализован так:
и используется так:
Плохая альтернатива была бы иметь ,
VideoExporter
который имеет много общих методы и делает всю работу, в том числе в сжимающем. Это быстро превратилось в кошмар обслуживания, что затруднило бы добавление поддержки других видеоформатов.источник
Это признак того, что вы хотите передать функции в качестве аргументов другим функциям. Я предполагаю, что ваш язык (Java?) Не поддерживает его; если это так, то это не столько неудача в вашем дизайне, сколько недостаток на выбранном вами языке. Это одна из самых больших проблем с языками, которые настаивают на том, что все должно быть классом.
Если вы на самом деле не передаете эти искусственные функции, тогда вам просто нужна свободная / статическая функция.
источник
Я знаю, что опаздываю на вечеринку, но, как кажется, все упустили, чтобы указать на это:
Это хорошо известный шаблон дизайна, который называется « Стратегический шаблон» .
Шаблон стратегии используется, когда есть несколько возможных стратегий для решения подзадачи. Обычно вы определяете интерфейс, который обеспечивает выполнение контракта для всех реализаций, а затем используете некоторую форму внедрения зависимостей, чтобы предоставить вам конкретную стратегию.
Например, в этом случае вы можете иметь,
interface VideoCompressor
а затем иметь несколько альтернативных реализаций, напримерclass H264Compressor implements VideoCompressor
иclass XVidCompressor implements VideoCompressor
. Из OP не ясно, что задействован какой-либо интерфейс, даже если его нет, может быть, просто первоначальный автор оставил дверь открытой, чтобы реализовать шаблон стратегии, если это необходимо. Что само по себе тоже хороший дизайн.Проблема в том, что OP постоянно создает экземпляры классов для вызова метода, это проблема, связанная с тем, что она неправильно использует внедрение зависимостей и шаблон стратегии. Вместо того, чтобы создавать его там, где он вам нужен, содержащий класс должен иметь член с объектом стратегии. И этот член должен быть введен, например, в конструктор.
Во многих случаях шаблон стратегии приводит к интерфейсным классам (как вы показываете) только с одним
doStuff(...)
методом.источник
То, что у вас есть, является модульным, и это хорошо, но если бы вы сгруппировали эти обязанности в IVideoProcessor, это, вероятно, имело бы больше смысла с точки зрения DDD.
С другой стороны, если разбиение, сжатие и индексация никак не связаны, я бы оставил их как отдельные компоненты.
источник
Это проблема - вы работаете с функциональной стороны дизайна, а не с данными. На самом деле у вас есть 3 автономные функции, которые были OO-ified.
Например, у вас есть класс VideoCompressor. Почему вы работаете с классом, предназначенным для сжатия видео - почему у вас нет класса Video с методами для сжатия (видео) данных, которые содержит каждый объект этого типа?
При разработке ОО-систем лучше всего создавать классы, представляющие объекты, а не классы, представляющие действия, которые вы можете применить. В старые времена классы назывались типами - ОО был способом расширения языка с поддержкой новых типов данных. Если вы думаете об ОО, как это, вы получаете лучший способ разработки ваших классов.
РЕДАКТИРОВАТЬ:
позвольте мне попытаться объяснить себя немного лучше, представьте себе строковый класс с методом concat. Вы можете реализовать такую вещь, где каждый объект, созданный из класса, содержит строковые данные, так что вы можете сказать,
но ОП хочет, чтобы это работало так:
теперь есть места, где класс может использоваться для хранения набора связанных функций, но это не OO, это удобный способ использования функций OO языка, чтобы помочь лучше управлять вашей кодовой базой, но это никоим образом "ОО Дизайн". Объект в таких случаях является полностью искусственным, просто используется таким образом, потому что язык не предлагает ничего лучшего для решения такого рода проблем. например. В таких языках, как C #, вы бы использовали статический класс для обеспечения этой функциональности - он повторно использует механизм классов, но вам больше не нужно создавать экземпляр объекта просто для вызова его методов. Вы в конечном итоге с методами, как
string.IsNullOrEmpty(mystring)
я думаю, плохо по сравнению сmystring.isNullOrEmpty()
.Итак, если кто-то спрашивает «как мне спроектировать мои классы», я рекомендую подумать о данных, которые будет содержать класс, а не о функциях, которые он содержит. Если вы выберете «класс - это набор методов», то вы в конечном итоге будете писать код в стиле «лучше С». (что не обязательно плохо, если вы улучшаете код на C), но это не даст вам лучшую программу, разработанную ОО.
источник
Video
и имеет тенденцию перегружать такие классы функциональностью, которая часто заканчивается беспорядочным кодом с> 10K LOC на класс. Усовершенствованный дизайн ОО разбивает функциональность на более мелкие единицы, такие какVideoCompressor
(и позволяетVideo
классу быть просто классом данных или фасадом дляVideoCompressor
).VideoCompressor
не представляет объект . В этом нет ничего плохого, просто показан предел объектно-ориентированного дизайна.Video
класс, но методология сжатия ни в коем случае не тривиальна, поэтому я бы на самом деле разбилcompress
метод на несколько других частных методов. ЭтоVideo
класс, но он будет состоять изVideoCompressor
, аVideoSplitter
, и другие связанные классы, которые должны, в хорошей форме OO, быть высоко сплоченные индивидуальные занятия.ISP (интерфейс принцип сегрегации) говорит , что ни один клиент не должен быть вынужден зависеть от методов его не использует. Преимущества многочисленны и очевидны. Ваш подход полностью уважает провайдера, и это хорошо.
Другой подход, также относящийся к ISP, заключается в том, чтобы, например, создать интерфейс для каждого метода (или набора методов с высокой степенью согласованности), а затем создать единый класс, реализующий все эти интерфейсы. Является ли это лучшим решением, зависит от сценария. Преимущество этого заключается в том, что при использовании внедрения зависимостей у вас может быть клиент с разными соавторами (по одному на каждый интерфейс), но в конце все соавторы будут указывать на один и тот же экземпляр объекта.
Кстати, вы сказали
эти классы, по-видимому, являются службами (и, следовательно, не имеют состояния). Ты думал о том, чтобы сделать их одиночками?
источник
Да, есть проблема. Но не серьезный. Я имею в виду, что вы можете структурировать свой код следующим образом, и ничего плохого не произойдет, его можно поддерживать. Но есть некоторые слабые стороны такого рода структурирования. Например, подумайте, если представление вашего видео (в вашем случае оно сгруппировано в классе RawVideo) изменится, вам нужно будет обновить все ваши классы операций. Или учтите, что у вас может быть несколько представлений видео, которые различаются во время выполнения. Тогда вам нужно будет сопоставить представление с конкретным классом «операция». Также субъективно раздражает перетаскивание зависимости для каждой операции, которую вы хотите выполнить с чем-либо. и обновлять список зависимостей, передаваемых каждый раз, когда вы решаете, что операция больше не нужна или вам нужна новая операция.
Также это на самом деле является нарушением ПСП. Некоторые люди просто воспринимают SRP как руководство по разделению обязанностей (и далеко зашли, рассматривая каждую операцию как отдельную ответственность), но они забывают, что SRP также является руководством по группированию обязанностей. И в соответствии с обязанностями ПСП, которые меняются по одной и той же причине, следует группировать их так, чтобы в случае изменения они были ограничены как можно меньшим количеством классов / модулей. Что касается больших классов, то это не проблема наличия нескольких алгоритмов в одном классе, если эти алгоритмы связаны между собой (то есть делятся некоторыми знаниями, которые не должны быть известны за пределами этого класса). Проблема в больших классах, которые имеют алгоритмы, которые никак не связаны и меняются / меняются по разным причинам.
источник