1

Which is considered a generally accepted practice?

class Image {
public void decode();
};
//main
Image image;
image.decode();

vs

class ImageDecoder {
public Image run(Image image);
};
//main
Image image;
ImageDecoder decoder;
Image decoded = decoder.run(image);

In other words, are these "worker classes" that accept objects as parameters a good practice, or should methods be called directly on objects whenever possible? Is this just a matter of opinion, or is there any general agreement which design pattern is better? How are these patterns called and where can I read more?

If I wanted to manipulate some essential property of the Image such as its size (width, height), Image::resize() would definitely be the way because implementing an ImageResizer class would not make sense. In the example above I am not so sure. After all, decoding an image for further use (converting it to another type, for example) is nowhere near the inherent functionality of an Image.

Please note the code above is just an example. My question is about a good OOP design.

2 Answers 2

9

Is there a single, obvious, well-known way to decode an image? Is this a substantial part of what handling an image is about? Then its implementation should go inside the Image class.

Are there multiple possible ways of decoding an image, or options that change the processing? Does it depend on context which one is appropriate? Can the same decoding be applied to content other than images? All those would be indicators for having a separate decoder class.

As always, the answer will be somewhere in the middle, and depending on the particulars of your code base, one or the other solution will make more sense. But neither is inherently "good OOP design".

3

When considering the "best" solution in terms of OOP - bearing in mind that there are very few true "best" ways - you can get some great guidance from the SOLID principles, specifically in this case the Single Responsibility Principle which basically states that an object should have a single reason to change (also stated as an object should do one thing)

So in this case, the best "single" responsibility of the Image class is to represent an image in terms of the data that makes up that image and the behavior using that data that is inherent to an Image. i.e. the SRP of the Image class is to be well, an Image!

It then follows that operations on the image which do not modify the internal state of that image but rather produce something else which is based on the image as an input (a resized version, a compressed / encoded version, etc) belong as single responsibilities on other classes such as your ImageDecoder class..

So if you were to follow the SOLID guidelines then your second approach is "best". However that's not to say that other approaches are wrong but for example, even if there were only one way right now to decode an image, placing the code for that in the Image class would mean that in future, if and when a second decoding method exists, you will have a harder time refactoring the code to support that, than if you had adhered to the SRP in the first place.

Even for something like resizing, which at first glance does seem like it ought to belong to the Image class: there is likely to be some non-trivial logic in there and also likely to be more than one way to resize an image (depending on what tradeoffs you want to make in terms of upsampling, width/height proportion adjustment, etc). So again it makes more sense for this functionality to be a separate responsibility.

2
  • Another class won't have the knowledge needed for example to scale or to rotate an image.
    – gnasher729
    Commented Oct 25, 2019 at 20:40
  • 1
    @gnasher729 - not sure I understand what you mean; if one decided to use the SRP and create an ImageResizer or ImageRotater class, then those classes would know how to do whatever it is they are supposed to do, taking an instance of the Image class as input and returning a new Image with the operation done to it. The implementation of the method that does the work of course has to have some information from the Image class but that's to be expected. Commented Oct 26, 2019 at 21:09

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.