Java Filter Factory implementation

847 Views Asked by At

Inspired by FilenameFilter.java, I want to use similar approach/design pattern to solve my problem. I have select files from sftp server based on:

  1. If it is older than n days
  2. If it is older than n days and its name is in certain pattern.

I have defined a functional interface SemanticFileFilter as below:

public interface SftpFileFilter
{
    boolean accept(LsEntry sftpFile);
}

LsEntry for sftp is basically something like File in java.

Wanted to define SftpFileFilterFactory to get all implementation of SftpFileFilter at one place like below:

    public class SftpFileFilterFactory 
    {
        public static final SftpFileFilter OLD_FILE_FILTER =  new SftpFileFilter()
        {
//ERROR: because Interface function method should take only 1 parameter
            //@Override
            public boolean accept(LsEntry lsEntry,int nDays)
            {
                //checks if files if older than nDays
            }
        };

        public static final SftpFileFilter PATTERN_MATCH_OLD_FILE_FILTER =  new SftpFileFilter()
        {
//ERROR: because Interface function method should take only 1 parameter
            //@Override
            public boolean accept(LsEntry lsEntry,int nDays, String pattern)
            {
                //checks if files if older than nDays and matches pattern "pattern"
            }
        };
    }

How do I design my interface's function method or factory implementation so that in future if similar more filters needs to be defined, I don't need to bother much in code changes but just define new filter.

Also we should be able to chain filters. That is to say define one filter for older files and another for pattern matching. If both needs to used they should be able to chained together and hence both could be used.

1

There are 1 best solutions below

0
Michał Ziober On

Your problem reminds Command design pattern. You need to implement different conditions and to provide additional parameters you can use constructors and create classes or use Java 8 lambda expressions. See below example:

import java.util.ArrayList;
import java.util.List;

public class DesignPatterns {

    public static void main(String[] args) {
        List<SftpFileFilter> filters = new ArrayList<>();
        filters.add(new OlderThanNDaysFilter(10));
        filters.add(new NameSftpFileFilter("tmp.txt"));
        // you can use lambda as well
        filters.add((file) -> file.getName().length() > 0);
    }
}

interface SftpFileFilter {
    boolean accept(LsEntry sftpFile);
}

class OlderThanNDaysFilter implements SftpFileFilter {

    private final int days;

    public OlderThanNDaysFilter(int days) {
        this.days = days;
    }

    @Override
    public boolean accept(LsEntry sftpFile) {
        return sftpFile.isOlder(days);
    }
}

class NameSftpFileFilter implements SftpFileFilter {

    private final String name;

    public NameSftpFileFilter(String name) {
        this.name = name;
    }

    @Override
    public boolean accept(LsEntry sftpFile) {
        return sftpFile.getName().equals(name);
    }
}

These objects are too small and there is not need to create factory for it. You can create and use them if it is necessary. Of course, you can create factory which creates some predefined filters:

class ConditionFactory {
    private static final SftpFileFilter OLDER_THAN_TEN = new OlderThanNDaysFilter(10);
    private static final SftpFileFilter PASSWORDS_FILE = new NameSftpFileFilter("passwords.txt");

    public SftpFileFilter createOlderThan10Days() {
        return OLDER_THAN_TEN;
    }

    public SftpFileFilter createPasswordsFile() {
        return PASSWORDS_FILE;
    }

    public SftpFileFilter createNameFilter(final String name) {
        return new NameSftpFileFilter(Objects.requireNonNull(name));
    }

    public SftpFileFilter createOlderThan(final int days) {
        return new OlderThanNDaysFilter(days);
    }
}

It is a good separation between filter implementations and client code which does not know anything how filtering by name is implemented and can be easily exchanged.

In Java 8 you can use java.util.function.Predicate directly or extend it by your interface:

interface SftpFileFilter extends Predicate<LsEntry> {
    boolean accept(LsEntry sftpFile);

    @Override
    default boolean test(LsEntry lsEntry) {
        return accept(lsEntry);
    }
}