5
\$\begingroup\$

I'm a developing a image processor, which reads a Base64 (compressed) image and writes its subimage to targets on threads.

(Constructors, exception handling, and resource management are omitted for brevity)

The base abstract class is AbstractImageProcessor and it implements Runnable to run on threads:

abstract class AbstractImageProcessor implements Runnable {
    protected final String base64; // input
    protected final List<Target> targetList; // list of target information

    protected abstract void read(byte[] data);
    protected abstract void write(Target t);

    @Override
    public void run() {
        byte[] data = decompress(decode()); // decode base64 and decompress
        read(data);
        for (Target t : targetList) {
            write(t)
        }
    }
}

Another abstract class, ImageIOProcessor extends AbstractImageProcessor to use ImageIO:

abstract class ImageIOProcessor extends AbstractImageProcessor {
    private BufferedImage image;

    protected abstract void writeBufferedImage(BufferedImage subimage, Target t)

    @Override
    protected void read(byte[] data) {
        image = ImageIO.read(new ByteArrayInputStream(data));
    }

    @Override
    protected void write(Target t) {
        writeBufferedImage(image.getSubimage(/* x, y, w, h from t */), t);
    }
}

There are two default implementations(concrete classes) of ImageIOProcessor, one writes to file and the other writes to base64 String:

class ImageIOFileWriter extends ImageIOProcessor {
    private File filepath;

    @Override
    protected void writeBufferedImage(BufferedImage subimage, Target t) {
        ImageIO.write(subimage, "png", filepath);
    }
}

class ImageIOBase64Writer extends ImageIOProcessor {
    @Override
    protected void writeBufferedImage(BufferedImage subimage, Target t) {
        ImageIO.write(subimage, "png", /* Base64-encoder-wrapped outputstream*/);
    }
}

The factory interface is responsible for creating appropriate image processing Runnable instance:

public interface ImageProcessorFactory {
    AbstractImageProcessor create(String base64, List<Target> t);
}

Is this the best way to implement them? Or is there better design pattern?

\$\endgroup\$
  • \$\begingroup\$ It's really hard to give recommendations without further details here. For example: Which of the classes are supposed to be public? What does the Target look like? E.g. why is it passed to writeBufferedImage? Does it provide a file path, or an output stream? Should clients be able to plug in own implementations at some point? What are the intended usage patterns in general? Right now, it looks like you could just let the factory return an anonymous, structure-free Runnable object, but I guess that's not the goal... \$\endgroup\$ – Marco13 Apr 3 '18 at 0:38
3
\$\begingroup\$

A couple of minor things:

  1. Some people like to always declare what interfaces a class implements, for the sake of clarity. Thus, you could have your subclasses of AbstractImageProcessor also specify implement Runnable.
  2. For consistency, I would rename ImageIOProcessor to AbstractImageIOProcessor.
  3. Not so minor, I would have the method create in ImageProcessorFactory to return an interface, not an abstract class.
  4. While naming is not easy, perhaps you could find a better alternative to ImageIOFileWriter and ImageIOBase64Writer, because they're not only image writers, they also read. They are processors as your other classes.
  5. Do you have any other class extending AbstractImageProcessor besides ImageIOProcessor? If not, maybe you don't need both.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.