Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining Strategies in a Java Strategy Pattern

Examples below are shamelessly ripped off of java.dzone.com, and modified to suit my needs:

Our interface:

public interface CompressionStrategy
{
  public void compressFiles(ArrayList<File> files);
}

Our First Implementation

public class GZipCompressionStrategy implements CompressionStrategy
{

   public File compressFiles(ArrayList<File> files)
   {
     //using GZIP approach
     return archive;
   }

}

Our Second Implementation:

public class TarCompressionStrategy implements CompressionStrategy
{

   public File compressFiles(ArrayList<File> files)
   {
     //using TAR approach
     return archive;
   }

}

And this is the use given:

public class CompressionContext
{
   private CompressionStrategy strategy;   

   //this can be set at runtime by the application preferences
   public void setCompressionStrategy(CompressionStrategy strategy) 
   {
       this.strategy = strategy;  
   }

  //use the strategy
   public File createArchive(ArrayList<File> files) 
   {
        strategy.compressFiles(files);
   }

}

Client Class with Main method

public class Client
{

   public static void main(String[] args)
   {
      CompressionContext ctx = new CompressionContext(); 
      File archive;
     //we could assume context is already set by preferences 
      ctx.setCompressionStrategy(new TarCompressionStrategy());     
     //get a list of files 
    ...
     archive = ctx.createArchive(fileList);    
     ctx. setCompressionStrategy(new GZipCompressionStrategy());
     archive = ctx.createArchive(archive);         
   }
}

Which feels messy, because:

  1. I'm having to reset the strategy each time
  2. The Two strategies may or may not be compatible (in that order, e.g. does it make sense to Tar a GZipped file?)
  3. Creating a third TARGZipStrategy class is ok in principle, but if we had 10 strategies allowing for every other one to be part of a valid XXXCombinedStrategy method, we'd have ~35 different classes.

Is there a neat way to arbitrarily run multiple strategies sequentially within this pattern? For instance if I wanted to create a .tar.gzip file at the end?

What I'm trying to say is there neat way to combine two strategies together into one?

I feel like what I'm doing should have some neat solution and I don't want to reinvent the wheel, and at the same time I don't want to fall into being too reliant on patterns.

like image 814
AncientSwordRage Avatar asked Jan 07 '14 13:01

AncientSwordRage


People also ask

Can Strategy pattern have multiple methods?

No you can have more than one method on your strategy interface. However, in order for your strategy object to actually use the Strategy pattern, at least one of the method implementations should differ between the different strategies.

Can strategy design pattern be used along with Java collections?

Yes, a combination of the Comparator, Comparable, and the Collections. sort() method is one of the best real-world examples of the Strategy design pattern.

What is the difference between pattern and strategies?

State pattern helps a class to exhibit different behaviors in a different state. Strategy Pattern encapsulates a set of related algorithms and allows the client to use interchangeable behaviors through composition and delegation at runtime.

What is Strategy pattern in Java?

Strategy design pattern is one of the behavioral design pattern. Strategy pattern is used when we have multiple algorithm for a specific task and client decides the actual implementation to be used at runtime.


2 Answers

You could create a JoinedCompressionStrategy

class JoinedCompressionStrategy implements CompressionStrategy {

    private final CompressionStrategy s0;
    private final CompressionStrategy s1;

    public JoinedCompressionStrategy(CompressionStrategy s0, CompressionStrategy s1) {
        this.s0 = s0;
        this.s1 = s1;
    }

    public File compressFiles(ArrayList<File> files) {
        File archive = s0.compressFiles(files);
        return s1.compressFiles(Arrays.asList(archive));
    }
}
like image 103
Blank Chisui Avatar answered Sep 18 '22 13:09

Blank Chisui


You probably are looking for a decorator pattern implementation instead. The intent of this pattern is to add additional responsibilities dynamically to an object. Tradeoff is that you'll get a explossion of subclasses too.

Example with code:

The common interface.

   public interface CompressionStrategy{
       File compressFiles(List<File> files);
   }

the base compression for all files.

public class CompressionBase implements CompressionStrategy{

    @Override
    public File compressFiles(List<File> files)) {
        //return default compression
    }

}

The decorator abstract class

public abstract class AbstractCompressionDecorator implements CompressionStrategy{

    private final CompressionStrategy decoratee;

    /**
     * @param decoratee
     */
    public AbstractCompressionDecorator(CompressionStrategy decoratee) {
        super();
        this.decoratee = decoratee;
    }

    @Override
    public File compressFiles(List<File> files) {
        File file = decoratee.compressFiles(files); 
        return compressFilesToAnotherFormat(file);
    }

    protected abstract File compressFilesToAnotherFormat(File file);


}

and the decorators concrete classes.

public class TarCompression extends AbstractCompressionDecorator {

    public TarCompression (CompressionStrategy compressionStrategy) {
            super(compressionStrategy);
    }

@Override
protected File compressFilesToAnotherFormat(File file) {
    // tar compression logic here;
}

}

Zip compression

 public class ZipCompression extends AbstractCompressionDecorator {

    public ZipCompression (CompressionStrategy compressionStrategy) {
            super(compressionStrategy);
    }

@Override
protected File compressFilesToAnotherFormat(File file) {
    // zip compression logic here;
}

and a simple Factory to create objects

public final class CompressionFactory {

    private CompressionFactory (){}

    public static CompressionStrategy create(String extension){

        CompressionStrategy compressionStrategy = new CompressionBase();        

            if(extension.contains("zip")){
               compressionStrategy = new ZipCompression(compressionStrategy);
            }else if(extension.contains("tar.gzip")){
               compressionStrategy = new TarCompression(new GzipCompression(compressionStrategy));
            }

        return compressionStrategy ;
    }
}

then in client code you only have to write this.

CompressionStrategy compressionStrategy = CompressionFactory.create("tar.gzip");
File file = compressionStrategy.compressFiles(files);
like image 22
nachokk Avatar answered Sep 21 '22 13:09

nachokk