Question

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.

Was it helpful?

Solution

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".

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
scroll top