Я пишу приложение, в котором будет Image
сущность, и у меня уже возникают проблемы с определением, за кого должна отвечать каждая задача.
Сначала у меня есть Image
класс. У него есть путь, ширина и другие атрибуты.
Затем я создал ImageRepository
класс, для получения изображений с помощью одного и проверенного метода, например: findAllImagesWithoutThumbnail()
.
Но теперь мне тоже нужно уметь createThumbnail()
. Кто должен иметь дело с этим? Я думал о том, чтобы иметь ImageManager
класс, который был бы классом для конкретного приложения (был бы также сторонний компонент многократного использования для манипулирования изображениями, я не изобретаю колесо).
Или, может быть, было бы 0K, чтобы позволить Image
изменить сам размер? Или позвольте ImageRepository
и ImageManager
быть того же класса?
Что вы думаете?
источник
Ответы:
Заданный вопрос слишком расплывчат, чтобы иметь реальный ответ, поскольку он действительно зависит от того, как
Image
объекты будут использоваться.Если вы используете изображение только одного размера и изменяете размер, потому что исходное изображение имеет неправильный размер, может быть лучше, чтобы считывающий код выполнял изменение размера. Пусть ваш
createImage
метод определит ширину / высоту, а затем вернет изображение, размер которого был изменен на эту ширину / высоту.Если вам нужны несколько размеров, и если память не имеет значения, лучше сохранить изображение в памяти, как оно было первоначально прочитано, и выполнить любое изменение размера во время отображения. В таком случае, вы можете использовать несколько разных дизайнов. Изображение
width
иheight
будет фиксированным, но у вас будет либоdisplay
метод, который занял позицию и целевую высоту / ширину, либо у вас был бы метод, принимающий ширину / высоту, который бы возвращал объект, какой бы системой отображения вы ни пользовались. Большинство API рисования, которые я использовал, позволяют указывать целевой размер во время рисования.Если требования приводят к тому, что рисование с разными размерами достаточно часто, чтобы производительность была проблемой, у вас может быть метод, который создает новое изображение другого размера на основе оригинала. В качестве альтернативы можно использовать
Image
внутренние классы для кэширования различных представлений, чтобы при первом вызовеdisplay
с размером миниатюры он изменял размер, а во второй раз просто рисовал кэшированную копию, сохраненную в прошлый раз. Это использует больше памяти, но редко у вас будет больше, чем несколько общих размеров.Другая альтернатива состоит в том, чтобы иметь один
Image
класс, который сохраняет базовое изображение, и этот класс содержит одно или несколько представлений.Image
Само по себе не будет иметь высоту / ширину. Вместо этого это началось бы с тогоImageRepresentation
, у которого были высота и ширина. Вы бы нарисовали это представление. Чтобы «изменить размер» изображения, вы должны запроситьImage
представление с определенной метрикой высоты / ширины. Это приведет к тому, что он будет содержать это новое представление, а также оригинал. Это дает вам большой контроль над тем, что находится в памяти за счет дополнительной сложности.Мне лично не нравятся классы, в которых есть это слово,
Manager
потому что «менеджер» - очень расплывчатое слово, которое мало что говорит о том, что именно делает класс. Управляет ли он временем жизни объекта? Стоит ли оно между остальной частью приложения и тем, чем оно управляет?источник
Сохраняйте простоту, пока существует только несколько требований, и улучшайте свой дизайн, когда это необходимо. Я думаю, что в большинстве реальных случаев нет ничего плохого в том, чтобы начать с такого дизайна:
Вещи могут измениться, когда вам нужно передать больше параметров
createThumbnail()
, и эти параметры нуждаются в собственном времени жизни. Например, предположим, что вы собираетесь создавать миниатюры для нескольких сотен изображений, все с заданным размером назначения, алгоритмом изменения размера или качеством. Это будет означать, что вы можете переместитьcreateThumbnail
объект в другой класс, например, класс менеджера илиImageResizer
класс, в котором эти параметры передаются конструктором и, таким образом, связаны с временем жизниImageResizer
объекта.На самом деле, я бы начал с первого подхода и рефакторинга позже, когда он мне действительно понадобится.
источник
ImageResizer
класс в первую очередь? Когда вы делегируете вызов вместо того, чтобы переместить ответственность в новый класс?Image thumbnail = img.createThumbnail(x,y)
.Я думаю, что это должно быть частью
Image
класса, так как изменение размера во внешнем классе потребовало бы, чтобы преобразователь знал реализациюImage
, тем самым нарушая инкапсуляцию. Я предполагаю,Image
что это базовый класс, и вы получите отдельные подклассы для конкретных типов изображений (PNG, JPEG, SVG и т. Д.). Следовательно, вам нужно будет иметь соответствующие классы изменения размера или универсальный шаблонswitch
изменения размера с оператором, который изменяет размер в зависимости от класса реализации - классический запах дизайна.Один из подходов может заключаться в том, чтобы ваш
Image
конструктор использовал ресурс, содержащий изображение, параметры высоты и ширины, и создавал себя соответствующим образом. Тогда изменение размера может быть таким же простым, как создание нового объекта с использованием исходного ресурса (кэшированного внутри изображения) и параметров нового размера. Напримерfoo.createThumbnail()
, простоreturn new Image(this.source, 250, 250)
. (сImage
конкретным типомfoo
, конечно). Это делает ваши изображения неизменными, а их реализации - частными.источник
Image
. Все, что ему нужно, это исходный и целевой размеры.Я знаю, что ООП - это инкапсуляция данных и поведения вместе, но я не думаю, что для Image будет хорошей идеей встроить логику изменения размера в этом случае, потому что Image не нужно знать, как изменить размер себя, чтобы быть картинка.
Миниатюра на самом деле другое изображение. Возможно, у вас есть структура данных, которая содержит связь между фотографией и ее миниатюрой (обе из которых являются изображениями).
Я пытаюсь разделить мои программы на такие вещи, как изображения, фотографии, миниатюры и т. Д., И службы (например, PhotographRepository, ThumbnailGenerator и т. Д.). Получите правильные структуры данных, а затем определите сервисы, которые позволят вам создавать, манипулировать, преобразовывать, сохранять и восстанавливать эти структуры данных. Я не добавляю больше поведения в свои структуры данных, чем проверяю, правильно ли они созданы и используются соответствующим образом.
Следовательно, нет, изображение не должно содержать логику о том, как сделать эскиз. Там должен быть сервис ThumbnailGenerator, который имеет такой метод:
Моя большая структура данных может выглядеть так:
Конечно, это может означать, что вы прилагаете усилия, которые не хотите делать при создании объекта, поэтому я бы тоже подумал, что это нормально:
... в случае, если вы хотите структуру данных с ленивой оценкой. (Извините, я не включил свои нулевые проверки, и я не сделал его потокобезопасным, чего вы бы хотели, если бы вы пытались имитировать неизменную структуру данных).
Как видите, любой из этих классов создается неким PhotographRepository, который, вероятно, имеет ссылку на ThumbnailGenerator, который он получил посредством внедрения зависимостей.
источник
Вы определили одну функциональность, которую хотите реализовать, так почему бы не выделить ее отдельно от всего, что вы определили до сих пор? Вот что предлагает принцип единой ответственности - это решение.
Создайте
IImageResizer
интерфейс, который позволяет передавать изображение и целевой размер и который возвращает новое изображение. Затем создайте реализацию этого интерфейса. На самом деле существует множество способов изменить размер изображения, так что вы даже можете получить более одного!источник
Я предполагаю те факты о методе, который изменяет размеры изображений:
Исходя из этих фактов, я бы сказал, что нет никакой причины для метода изменения размера изображения быть частью самого класса Image. Реализация этого как статического вспомогательного метода класса была бы лучшей.
источник
Класс Image Processing может быть подходящим (или Image Manager, как вы его назвали). Передайте ваше изображение в метод CreateThumbnail обработчика изображений, например, чтобы получить уменьшенное изображение.
Одна из причин, по которой я бы предложил этот маршрут, заключается в том, что вы говорите, что используете стороннюю библиотеку обработки изображений. Исключение функциональности изменения размера из самого класса Image может облегчить вам выделение кода для любой платформы или стороннего кода. Поэтому, если вы можете использовать свой базовый класс Image на всех платформах / приложениях, вам не нужно загрязнять его кодом, специфичным для платформы или библиотеки. Все это может быть расположено в процессоре изображений.
источник
В основном, как уже сказал Док Браун:
Создайте
getAsThumbnail()
метод для класса изображений, но этот метод должен просто делегировать работу некоторомуImageUtils
классу. Так это будет выглядеть примерно так:А также
Это позволит легче просматривать код. Сравните следующее:
Или
Если последний вариант вам подходит, вы также можете просто создать где-нибудь этот вспомогательный метод.
источник
Я думаю, что в «Области изображения» у вас есть только объект Image, который является неизменным и монадическим. Вы запрашиваете изображение для измененной версии, и оно возвращает измененную версию самой себя. Затем вы можете решить, хотите ли вы избавиться от оригинала или оставить оба.
Теперь версии изображения, аватара и т. Д. - это совершенно другой домен, который может запрашивать у домена изображения разные версии определенного изображения для предоставления пользователю. Обычно этот домен также не такой большой или общий, так что вы, вероятно, можете сохранить его в логике приложения.
В небольшом приложении я бы изменял размеры изображений во время чтения. Например, у меня может быть правило перезаписи apache, которое делегирует php скрипт, если изображение 'http://my.site.com/images/thumbnails/image1.png', где файл будет извлечен с использованием имени image1.png и изменить размер и сохранить в «thumbnails / image1.png». Затем при следующем запросе к этому же изображению apache будет обслуживать изображение напрямую, без запуска сценария php. Apache автоматически отвечает на ваш вопрос findAllImagesWithoutThumbnails, если вам не нужна статистика?
В крупномасштабном приложении я отправляю все новые изображения в фоновое задание, которое заботится о создании различных версий изображения и сохраняет его в соответствующих местах. Я бы не стал создавать целый домен или класс, так как этот домен вряд ли превратится в ужасный беспорядок спагетти и плохого соуса.
источник
Короткий ответ:
Я рекомендую добавить эти методы в класс изображений:
Объект Image по-прежнему неизменен, эти методы возвращают новое изображение.
источник
Уже есть довольно много отличных ответов, поэтому я немного подробнее расскажу об эвристике, лежащей в основе определения объектов и их обязанностей.
ООП отличается от реальной жизни тем, что объекты в реальной жизни часто пассивны, а в ООП они активны. И это ядро объектного мышления . Например, кто изменит размер изображения в реальной жизни? Человек, который умен в этом отношении. Но в ООП нет людей, поэтому объекты умнее. Одним из способов реализации этого «ориентированного на человека» подхода в ООП является использование классов обслуживания, например пресловутых
Manager
классов. Таким образом, объекты рассматриваются как пассивные структуры данных. Это не ООП способ.Так что есть два варианта. Первый, создающий метод
Image::createThumbnail()
, уже рассмотрен. Второй - создатьResizedImage
класс. Он может быть декораторомImage
(зависит от вашего домена, сохранять лиImage
интерфейс или нет), хотя это приводит к некоторым проблемам инкапсуляции, посколькуResizedImage
должен иметьImage
исходный текст. Но anImage
не будет перегружен деталями изменения размера, оставляя это отдельному объекту домена, действующему в соответствии с SRP.источник