Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I've got a little problem in choosing the best design. I have some (5 at the moment) image processing operations in my coe (Java). Every processing step independent from the other ones and consoists of a small amount of code, 5-15 lines in general. These are mostly wrappers to JAI, AWT and so on.

My current design is an abstract class:

public abstract class AbstractPreProcessingStep {
    protected Logger logger = Logger.getLogger(this.getClass().getName());

    protected AbstractPreProcessingStep() {
    };

    public abstract BufferedImage startProcessing(BufferedImage input);

}

Every processing step implements this class and can be called uniform. (like the Strategy-Pattern).

Since every processing step may have a different set of parameters, I created a factory class, like this:

public class PreProcessingFactory {


    public static AbstractPreProcessingStep createColorInverter() {
        return new ColorInverter();
    }


    public static AbstractPreProcessingStep createRescaler(float rescaleFactor) {
        return new Rescaler(rescaleFactor);
    }


    public static AbstractPreProcessingStep createEdgeDetector() {
        return new EdgeDetector();
    }

    public static AbstractPreProcessingStep createBlackAndWhite() {
        return new BlackAndWhite();
    }
...

So, if you want to create a black and white image with inverted colors, you need to do this (pseude code)

bw = PreProcessingFactory.createBlackAndWhite();
inv = PreProcessingFactory.createInverter();
result = bw.startProcessing(result);
result = inv.startProcessing(result);

In my mind, this is a good design since the implementation details are hidden and new processing steps can easily be add by adding a new class.

The other approach which was in my mind was to create a single class with every step as a static method - however, I've got dislike of 'static'

Do you have any suggestions to improve the code?

share|improve this question
2  
result repeated 4 times in 2 statements hurts my eyes. Also, when I see couple of classes with interface like SomeType doSomething(SomeType), first thing that jumps to mind is that's the case for Builder. output=PreProcessingBuilder.consume(input).blackAndWhite().invert().build() –  gnat Jul 17 '13 at 10:51
    
This is a good point. For now I am not very familar with this pattern, but I'll consider that! –  verbose-mode Jul 17 '13 at 10:54
    
...But that's just feelings, mere guesses. To see the line, line that lies between good and bad, I'd write a unit test suite covering usage scenarios for my design and study how it looks like from the side of client code that will use it. As an additional benefit, this suite would also help me avoid regressions when modifying / maintaining my design –  gnat Jul 17 '13 at 10:55

2 Answers 2

up vote 0 down vote accepted

You are consufing terms.

The problem is not Factory Class vs Static Class, but a method for each process vs a class for each process.

The "a class for each process" method that you seem to be considering has many advantages, mainly flexibility and scalability.

But in order to implement that, you will have to use some kind of Creational Pattern to help you keep your code decoupled from the concrete classes.

The Factory class approach you are using is OK.

The order or the way you call those processes is a matter of Behavioral Patterns like Chain of Responsability, Iterator, State, Command etc. These behavioral patterns are used for decoupling the requestor ( the class that needs to call a process ) from the concrete class that actually does the work.

share|improve this answer
1  
I am always consufed. –  Robert Harvey Jul 17 '13 at 17:15
    
Thank you! I should learn more patterns –  verbose-mode Jul 18 '13 at 8:03
    
-1. How is this helping him? You've only corrected terms and "ok"-ed what he's currently doing. You're the quintessential grammar nazi for programmers. –  Neil Jul 18 '13 at 8:06

I'll make the following assumptions about your process:

  1. One step does not need to know about the inner workings of another step.
  2. Order of these steps is significant (you don't obtain the same results performing these operations in the opposite order).

In this situation, I can only really tell you what I would do. I would avoid Factory pattern, simply because it implies you must have a factory method for every type of phase, and if you wanted to invent new stages, you'd also have to modify the factory as well. Plus while it may be manageable in bite-sized pieces, if you had to deal with 50 phases, even if it didn't change often, it would still be very messy. Not to mention that factory would seem to imply that order is irrelevant. It makes you think that you can make any number of AbstractPreProcessingSteps and apply them in however order you wish. Even if that is not the case, I think it is generally better to present an interface which does not mislead a programmer.

Consider the following alternative pattern: Chain of responsibility

Suppose you had class AbstractProcessor as such:

public abstract class AbstractProcessor {
    protected AbstractProcessor step;

    public void setNextStep(AbstractProcessor step) {
        this.step = step;
    }

    public abstract BufferedImage process(BufferedImage input);
}

Then you could have your steps as such:

public class ColorInverter extends AbstractProcessor {
    public ColorInverter(AbstractProcessor step) {
        setNextStep(step);
    }

    public BufferedImage process(BufferedImage input) {
        // Perform color inverter process here
        return step != null ? step.process(output) : output;
    }
}
public class Rescaler extends AbstractProcessor {
    public Rescaler(AbstractProcessor step) {
        setNextStep(step);
    }

    public BufferedImage process(BufferedImage input) {
        // Perform rescalar process here
        return step != null ? step.process(output) : output;
    }
}

Just like this, it is beautiful in its simplicity, however, you could override ordering for steps which must be performed at the end simply by calling "step.process(input)" and performing some operation on the returned result.

Also, if there were specific phases, you could override AbstractProcessor's setNextStep to require a certain type of AbstractProcessor to guarantee that certain phases are achieved. Take for example the following:

public abstract class RasteringProcessingStep extends AbstractProcessor { 
    public void setNextStep(RasteringProcessingStep step) {
        super.setNextStep(step);
    }
    public void setNextStep(PostProcessingStep step) {
        super.setNextStep(step);
    }
}

In this way, you ensure that any step implementing RasteringProcessingStep must either be followed by another RasteringProcessingStep or it must be followed by a PostProcessingStep which also derives from AbstractPreProcessingStep.

To use it, you would see something like:

AbstractProcessor processor = new ColorInverter(new Rescaler(null));
File image = new File("strawberry.jpg");
BufferedImage output = processor.process(ImageIO.read(image));

Please let me know what you think about this approach.

share|improve this answer
1  
You advice OP to use Chain of Responsability instead of Factory pattern. But Factory is a creational pattern whereas Chain is a behavioral pattern. They are not interchangeable. You can substitute Factory with other creational pattern, like Builder, Abstract Factory, Factory Method, etc –  user61852 Jul 17 '13 at 16:08
    
@user61852 He's asking for suggestions to improve his code, and so I provided a suggestion. I don't think that merits a downvote simply because I didn't limit my idea of improvement to interchangeable creational patterns. Otherwise, I'm sure there is a very lovely list of creational patterns which can be found via Google without having to wait for a response to his question here. Besides, he's creating instances for the sole purpose of immediately applying it afterwards. This is not just "a creational pattern" that he's trying to achieve here. –  Neil Jul 17 '13 at 16:25
    
Very interesting, thanks! –  verbose-mode Jul 18 '13 at 8:02

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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