OOP에서 이미지 크기를 조정할 수 있어야합니까?


9

Image엔터티 가있는 앱을 작성 중이며 이미 각 작업의 책임을 결정하는 데 문제가 있습니다.

먼저 Image수업이 있습니다. 경로, 너비 및 기타 속성이 있습니다.

그런 다음 ImageRepository테스트 된 단일 메소드로 이미지를 검색하기위한 클래스를 작성했습니다 ( 예 :) findAllImagesWithoutThumbnail().

그러나 이제는 또한 할 수 있어야합니다 createThumbnail(). 누가 처리해야합니까? 나는 ImageManager응용 프로그램 특정 수업이 될 수업 을 생각하고 있었습니다 (제 3 자 이미지 조작 재사용 가능한 구성 요소가있을 것입니다. 휠을 재발 명하지 않습니다).

또는 Image크기 조정 자체 를 허용하는 것이 0K 입니까? 또는하자 ImageRepositoryImageManager같은 클래스가?

어떻게 생각해?


이미지는 지금까지 무엇을합니까?
윈스턴 에워 트

이미지 크기를 어떻게 조정할 수 있습니까? 이미지 만 축소하거나 전체 크기로 돌아가고 싶다고 상상할 수 있습니까?
로봇 고 르트

@WinstonEwert 형식이 지정된 해상도 (예 : 문자열 '1440x900'), 다른 URL과 같은 데이터를 생성하며 'views'또는 'votes'와 같은 일부 통계를 수정할 수 있습니다.
ChocoDeveloper

@StevenBurnap 원본 이미지를 가장 중요하게 생각합니다. 빠른 탐색을 위해 썸네일 만 사용하거나 사용자가 자신의 데스크탑이나 무언가에 맞게 크기를 조정하려는 경우 별도의 것입니다.
ChocoDeveloper

Image 클래스를 변경할 수 없도록 만들면 전달 할 때 방어 적으로 복사 할 필요가 없습니다.
dan_waterworth

답변:


8

요청 된 질문은 실제 사용 방법에 따라 실제 답변을하기에는 너무 모호합니다 Image.

한 가지 크기의 이미지 만 사용하고 소스 이미지의 크기가 잘못되어 크기를 조정하는 경우 읽기 코드에서 크기 조정을 수행하는 것이 가장 좋습니다. 당신이 createImage방법은 너비 / 높이를 가지고 다음 이미지가 그 너비 / 높이에서 크기가 조정 된 반환합니다.

여러 크기가 필요하고 메모리가 중요하지 않은 경우 이미지를 원래 읽은대로 메모리에 유지하고 표시시 크기를 조정하는 것이 가장 좋습니다. 이 경우 몇 가지 다른 디자인을 사용할 수 있습니다. 이미지 width와는 height해결 될 것입니다,하지만 당신은 중 하나 거라고 display위치와 타겟 높이 / 폭을했다 방법을 또는 당신이 사용하는 어떤 디스플레이 시스템 것을 객체를 반환 너비 / 높이를 가지고 몇 가지 방법이있을 것이다. 내가 사용한 대부분의 그리기 API를 사용하면 그리기 시간에 대상 크기를 지정할 수 있습니다.

요구 사항으로 인해 성능이 문제가되기에 충분한 크기로 다른 크기로 그릴 경우 원본을 기준으로 다른 크기의 새 이미지를 만드는 방법이있을 수 있습니다. 다른 방법은 Image클래스가 내부적으로 다른 표현을 캐시하도록 display하여 축소판 크기로 처음 호출 할 때 크기 조정을 수행하고 두 번째로 마지막에 저장 한 캐시 된 사본을 그리는 것입니다. 이것은 더 많은 메모리를 사용하지만, 몇 가지 일반적인 크기 조정을 갖는 경우는 드 rare니다.

다른 대안은 Image기본 이미지를 유지하고 해당 클래스에 하나 이상의 표현이 포함 된 단일 클래스를 갖는 것입니다. Image자체는 높이 / 폭이없는 것입니다. 대신 ImageRepresentation높이와 너비가있는 것으로 시작 합니다. 이 표현을 그릴 것입니다. 이미지의 크기를 조정하려면 Image특정 높이 / 너비 메트릭으로 표현을 요청해야 합니다. 그러면 원본과 함께이 새로운 표현이 포함됩니다. 이를 통해 추가 복잡성으로 인해 메모리에 걸려있는 내용을 정확하게 제어 할 수 있습니다.

Manager"관리자"는 클래스가 무엇을하는지 정확히 알려주지 않는 매우 모호한 단어 이기 때문에 개인적으로 단어가 포함 된 클래스를 싫어합니다 . 객체 수명을 관리합니까? 나머지 응용 프로그램과 관리 대상 사이에 있습니까?


"실제로 Image 객체가 어떻게 사용되는지에 달려 있습니다." 이 진술은 실제로 캡슐화 및 느슨한 결합의 개념과 일치하지 않습니다 (이 경우 원칙을 조금 더 멀리 적용하더라도). 더 나은 차별화 지점은 이미지의 상태에 의미있는 크기가 포함되는지 여부입니다.
Chris Bye

이미지 편집 애플리케이션 관점에 대해 이야기하고있는 것 같습니다. OP가 이것을 원했는지 아닌지 완전히 명확하지 않은가?
andho

6

몇 가지 요구 사항 만있는 한 일을 단순하게 유지하고 필요할 때 디자인을 개선하십시오. 대부분의 실제 경우에는 다음과 같은 디자인으로 시작하는 데 아무런 문제가 없다고 생각합니다.

class Image
{
    public Image createThumbnail(int sizeX, int sizeY)
    {
         // ...
         // later delegate the actual resize operation to a separate component
    }
}

더 많은 매개 변수를 전달해야 할 때 상황이 다를 수 있습니다 createThumbnail() 있으며 해당 매개 변수에는 고유 한 수명이 필요합니다. 예를 들어, 일부 대상 크기, 크기 조정 알고리즘 또는 품질을 사용하여 수백 개의 이미지에 대한 축소판을 생성한다고 가정 해 봅시다. 즉 , 매개 변수가 생성자에 의해 전달되어 객체 의 수명에 바인딩되는 createThumbnail다른 클래스 (예 : 관리자 클래스 또는 클래스) 로 이동할 수 있습니다.ImageResizerImageResizer

사실, 나는 첫 번째 접근 방식으로 시작하여 나중에 필요할 때 리팩터링합니다.


글쎄, 별도의 구성 요소에 대한 호출을 이미 위임하고 있다면 ImageResizer우선 클래스 의 책임이 아닌 이유 는 무엇입니까? 책임을 새 수업으로 옮기는 대신 언제 전화를 위임합니까?
Songo

2
@Songo : 2 가지 가능한 이유 : (1) 이미 존재하는 별도의 구성 요소에 위임하는 코드는 단순한 단일 라이너가 아니라 4-6 개의 명령 시퀀스 일 뿐이므로 저장할 장소가 필요합니다. . (2) 구문 설탕 / 사용의 용이성 : 작성하는 것이 매우 잘 될 것입니다 Image thumbnail = img.createThumbnail(x,y).
Doc Brown

+1 아 알겠습니다. 설명 주셔서 감사합니다 :)
Songo

4

Image외부 클래스의 크기를 조정하려면 resizer가의 구현을 알고 Image캡슐화를 위반 해야하므로 클래스의 일부가되어야한다고 생각합니다 . Image기본 클래스 라고 가정 하고 구체적인 이미지 유형 (PNG, JPEG, SVG 등)에 대해 별도의 하위 클래스를 작성합니다. 따라서 해당하는 크기 조정 클래스 또는 switch구현 클래스를 기반으로 크기가 조정되는 명령문이 있는 일반 크기 조정기 ( 클래식 디자인 냄새)가 있어야합니다.

한 가지 접근 방식은 Image생성자가 이미지, 높이 및 너비 매개 변수가 포함 된 리소스를 가져와 적절하게 만드는 것입니다. 그런 다음 크기 조정은 원본 리소스 (이미지 안에 캐시 됨)와 새로운 크기 매개 변수를 사용하여 새 객체를 만드는 것만 큼 간단 할 수 있습니다. 예를 들어 foo.createThumbnail()간단 return new Image(this.source, 250, 250)합니다. ( 물론 Image구체적인 유형으로 foo). 이렇게하면 이미지가 변경되지 않고 구현이 비공개로 유지됩니다.


나는이 솔루션을 좋아하지만 왜 resizer가의 내부 구현을 알아야한다고 말하는지 이해하지 못합니다 Image. 필요한 것은 소스와 목표 차원입니다.
ChocoDeveloper

외부 클래스를 갖는 것은 예상치 못한 일이기 때문에 의미가 없지만, 크기 조정을 수행 할 때 캡슐화를 위반하거나 switch () 문이 필요하지 않습니다. 궁극적으로 이미지는 2 차원 배열이며, 인터페이스에서 개별 픽셀을 가져오고 설정할 수있는 한 이미지 크기를 확실히 조정할 수 있습니다.
whatsisname

4

나는 OOP가 데이터와 행동을 함께 캡슐화하는 것에 관한 것임을 알고 있지만 Image가 필요 하지 않기 때문에 Image 가이 경우에 크기 조정 논리를 포함하는 것이 좋지 않다고 생각 합니다. 자체 크기 조정 방법을 알 이미지.

미리보기 이미지는 실제로 다른 이미지입니다. 아마도 사진과 섬네일 (둘 다 이미지) 사이의 관계를 유지하는 데이터 구조가있을 수 있습니다.

프로그램을 이미지 (사진, 사진, 썸네일 등)와 서비스 (사진 리포지토리, 썸네일 생성기 등)로 나누려고합니다. 데이터 구조를 올바르게 파악한 다음 해당 데이터 구조를 생성, 조작, 변환, 유지 및 복구 할 수있는 서비스를 정의하십시오. 데이터 구조가 올바르게 작성되고 올바르게 사용되는지 확인하는 것보다 데이터 구조에 더 이상 동작을 두지 않습니다.

따라서 아니요, 이미지에는 미리보기 이미지를 만드는 방법에 대한 논리가 포함되어서는 안됩니다. 다음과 같은 메소드가있는 ThumbnailGenerator 서비스가 있어야합니다.

Image GenerateThumbnailFrom(Image someImage);

더 큰 데이터 구조는 다음과 같습니다.

class Photograph : Image
{
    public Photograph(Image thumbnail)
    {
        if(thumbnail == null) throw new ArgumentNullException("thumbnail");
        this.Thumbnail = thumbnail;
    }

    public Image Thumbnail { get; private set; }
}

물론 이것은 객체를 구성하는 동안 원하지 않는 노력을 기울이고 있음을 의미하므로 다음과 같은 것도 고려할 것입니다.

class Photograph : Image
{
    private Image thumbnail = null;
    private readonly Func<Image,Image> generateThumbnail;

    public Photograph(Func<Image,Image> generateThumbnail)
    {
        this.generateThumbnail = generateThumbnail;
    }


    public Image Thumbnail
    {
        get
        {
            if(this.thumbnail == null)
            {
                this.thumbnail = this.generateThumbnail(this);
            }
            return this.thumbnail;
        }
    }
}

... 게으른 평가가있는 데이터 구조를 원하는 경우. (미안하지만 null 검사를 포함하지 않았고 스레드 안전으로 만들지 않았습니다. 불변의 데이터 구조를 모방하려는 경우 원하는 것입니다).

보시다시피, 이러한 클래스 중 하나는 일종의 PhotographRepository에 의해 작성되고 있으며, 아마도 의존성 주입을 통해 얻은 ThumbnailGenerator에 대한 참조가 있습니다.


나는 행동이없는 클래스를 만들면 안된다는 말을 들었습니다. '데이터 구조'를 말할 때 클래스 또는 C ++의 무언가를 언급하고 있는지 확실하지 않습니다 (이 언어입니까?). 내가 알고 사용하는 유일한 데이터 구조는 기본 요소입니다. 서비스와 DI 부분이 발견되었으므로 결국이 작업을 수행 할 수 있습니다.
ChocoDeveloper

@ChocoDeveloper : 상황에 따라 때때로 동작이없는 클래스가 유용하거나 필요합니다. 이를 가치 클래스 라고 합니다 . 일반적인 OOP 클래스는 하드 코딩 된 동작을 가진 클래스입니다. 컴포저 블 OOP 클래스는 하드 코딩 된 동작을 갖지만 컴포지션의 구조는 소프트웨어 응용 프로그램에 필요한 많은 동작을 유발할 수 있습니다.
rwong

3

구현하려는 단일 기능을 식별 했으므로 지금까지 식별 한 모든 기능과 분리되어서는 안되는 이유는 무엇입니까? 이것이 단일 책임 원칙 이 제안하는 것입니다.

IImageResizer이미지와 대상 크기를 전달할 수 있고 새 이미지를 반환 하는 인터페이스를 만듭니다 . 그런 다음 해당 인터페이스의 구현을 작성하십시오. 실제로 이미지 크기를 조정하는 방법은 매우 많으므로 둘 이상으로 끝날 수도 있습니다!


관련 SRP의 경우 +1이지만 실제 크기 조정을 구현하고 있지 않습니다. 이미 명시된 바와 같이 타사 라이브러리에 위임되었습니다.
ChocoDeveloper

3

이미지 크기를 조정하는 방법에 대한 사실을 가정합니다.

  • 새 이미지 사본을 반환해야합니다. 이 이미지를 참조하는 다른 코드가 손상되므로 이미지 자체를 수정할 수 없습니다.
  • Image 클래스의 내부 데이터에 액세스 할 필요가 없습니다. 이미지 클래스는 일반적으로 해당 데이터 (또는 사본)에 대한 공개 액세스를 제공해야합니다.
  • 이미지 크기 조정은 복잡하며 다양한 매개 변수가 필요합니다. 다른 크기 조정 알고리즘에 대한 확장 성 지점 일 수도 있습니다. 모든 것을 전달하면 메소드 서명이 커집니다.

이러한 사실을 바탕으로 Image resizing 메서드가 Image 클래스 자체의 일부가 될 이유가 없다고 말할 수 있습니다. 정적 도우미 메소드로 구현하는 것이 가장 좋습니다.


좋은 가정. 왜 정적 방법이어야하는지 모르겠지만 테스트 가능성을 피하기 위해 피하려고합니다.
ChocoDeveloper

2

이미지 처리 클래스가 적절할 수도 있습니다 (또는 호출 한 이미지 관리자). 예를 들어 축소판 이미지를 검색하려면 이미지를 이미지 프로세서의 CreateThumbnail 메서드에 전달하십시오.

이 경로를 제안하는 이유 중 하나는 타사 이미지 처리 라이브러리를 사용하고 있기 때문입니다. Image 클래스 자체에서 크기 조정 기능을 사용하면 플랫폼 별 또는 타사 코드를 쉽게 분리 할 수 ​​있습니다. 따라서 모든 플랫폼 / 앱에서 기본 이미지 클래스를 사용할 수 있다면 플랫폼 또는 라이브러리 특정 코드로 오염시킬 필요가 없습니다. 그것은 모두 이미지 프로세서에 위치 할 수 있습니다.


좋은 지적. 대부분의 사람들은 내가 이미 가장 복잡한 부분을 타사 라이브러리에 위임하고 있다는 것을 이해하지 못했습니다. 아마도 충분하지 않을 수 있습니다.
ChocoDeveloper

2

Doc Brown은 이미 말했듯이 :

getAsThumbnail()이미지 클래스에 대한 메소드를 작성하십시오. 그러나이 메소드는 실제로 작업을 일부 ImageUtils클래스에 위임해야합니다 . 따라서 다음과 같이 보일 것입니다.

 class Image{
   // ...
   public Thumbnail getAsThumbnail{
     return ImageUtils.convertToThumbnail(this);
   }
   // ...
 }

 class ImageUtils{
   // ...
   public static Thumbnail convertToThumbnail(Image i){
     // ...
   }
   // ...
 }

이것은보기 쉬운 코드를 허용합니다. 다음을 비교하십시오.

Image i = ...
someComponent.setThumbnail(i.getAsThumbnail());

또는

Image i = ...
Thumbnail t = ImageUtils.convertToThumbnail(i);
someComponent.setThumbnail(t); 

후자가 당신을 위해 좋아 보인다면, 당신은 또한 어딘가에이 도우미 메소드를 만드는 것을 고수 할 수 있습니다.


1

"이미지 도메인"에서는 불변이고 단조로운 Image 객체가 있다고 생각합니다. 이미지의 크기가 조정 된 버전을 요청하면 이미지의 크기가 조정됩니다. 그런 다음 원본을 제거할지 또는 둘 다 유지할지 결정할 수 있습니다.

이제 이미지의 썸네일, 아바타 등 버전은 완전히 다른 도메인으로, 이미지 도메인에 특정 이미지의 다른 버전을 요구하여 사용자에게 제공 할 수 있습니다. 일반적으로이 도메인은 그다지 크지 않거나 일반적인 것이 아니므로 응용 프로그램 논리에 유지할 수 있습니다.

소규모 응용 프로그램에서는 읽을 때 이미지 크기를 조정합니다. 예를 들어 이미지 'http://my.site.com/images/thumbnails/image1.png'인 경우 이미지를 스크립트로 PHP에 위임하는 아파치 다시 쓰기 규칙을 사용할 수 있습니다. 여기서 이미지는 image1.png라는 이름을 사용하여 검색됩니다. 'thumbnails / image1.png'에 크기를 조정하고 저장했습니다. 그런 다음이 동일한 이미지에 대한 다음 요청에서 아파치는 PHP 스크립트를 실행하지 않고 이미지를 직접 제공합니다. 통계를 수행 할 필요가 없으면 findAllImagesWithoutThumbnails에 대한 귀하의 질문에 자동으로 답변이 되었습니까?

대규모 응용 프로그램에서는 모든 새 이미지를 백그라운드 작업으로 전송하여 백그라운드 버전의 이미지를 생성하여 적절한 위치에 저장합니다. 이 도메인이 스파게티와 나쁜 소스의 끔찍한 혼란으로 자랄 것 같지 않기 때문에 전체 도메인이나 클래스를 만드는 것을 귀찮게하지 않을 것입니다.


0

짧은 답변:

내 추천은이 메소드에 이미지 클래스를 추가하는 것입니다.

public Image getResizedVersion(int width, int height);
public Image getResizedVersion(double percentage);

Image 객체는 여전히 변경 불가능하며,이 메소드는 새로운 이미지를 반환합니다.


0

이미 몇 가지 훌륭한 답변이 있으므로 객체와 해당 책임을 식별하는 방법 뒤에 휴리스틱에 대해 조금 자세히 설명하겠습니다.

실생활의 물체는 종종 수동적이며 OOP에서는 활동적이라는 점에서 OOP는 실생활과 다릅니다. 그리고 이것은 객체 사고 의 핵심입니다 . 예를 들어, 실생활에서 누가 이미지 크기를 조정합니까? 그런 점에서 똑똑한 인간. 그러나 OOP에는 사람이 없으므로 대신 물건이 똑똑합니다. OOP에서 이러한 "인간 중심"접근 방식을 구현하는 방법은 악명 높은 Manager클래스 와 같은 서비스 클래스를 이용하는 것 입니다. 따라서 객체는 수동 데이터 구조로 취급됩니다. OOP 방식이 아닙니다.

따라서 두 가지 옵션이 있습니다. 메서드를 만드는 첫 번째 방법 Image::createThumbnail()은 이미 고려되었습니다. 두 번째는 ResizedImage클래스 를 만드는 것 입니다. 의 소스 가 있어야하기 때문에 일부 캡슐화 문제가 발생하지만 (인터페이스 Image를 유지할지 여부에 따라 도메인에 따라 다름) 데코레이터가 될 수 있습니다 . 그러나 크기 조정 세부 사항에 압도되어 별도의 도메인 객체로 남겨두고 SRP에 따라 작동합니다.ImageResizedImageImageImage

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.