Created
April 21, 2024 22:26
-
-
Save ramsayleung/cb5bda3416cf605dcb00737449853961 to your computer and use it in GitHub Desktop.
Solution for Unix Find command OOD design
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ramsayleung.github.io.ood; | |
// Problem statement: | |
// Design Unix File Search API to search file with different arguments as "extension", "name", "size" ... | |
// The design should be maintainable to add new contraints. | |
// | |
// Follow up: How would you handle if some contraints should support AND, OR conditionals. | |
import java.util.ArrayList; | |
import java.util.List; | |
class File { | |
String name; | |
int size; | |
int type; | |
boolean isDirectory; | |
List<File> children; | |
public File(String name, int size, int type, boolean isDirectory, List<File> children) { | |
this.name = name; | |
this.size = size; | |
this.type = type; | |
this.isDirectory = isDirectory; | |
this.children = children; | |
} | |
} | |
interface Filter { | |
boolean apply(final File file); | |
} | |
class ExtensionFilter implements Filter{ | |
private String extension; | |
public ExtensionFilter(String extension) { | |
this.extension = extension; | |
} | |
@Override | |
public boolean apply(File file) { | |
return file.name.endsWith(extension); | |
} | |
} | |
class NameFilter implements Filter { | |
private String name; | |
public NameFilter(String name) { | |
this.name = name; | |
} | |
@Override | |
public boolean apply(File file) { | |
return file.name.equals(name); | |
} | |
} | |
class MinSizeFilter implements Filter { | |
private final int minSize; | |
public MinSizeFilter(int minSize) { | |
this.minSize = minSize; | |
} | |
@Override | |
public boolean apply(File file) { | |
return file.size > minSize; | |
} | |
} | |
class MaxSizeFilter implements Filter { | |
private final int maxSize; | |
public MaxSizeFilter(int maxSize) { | |
this.maxSize = maxSize; | |
} | |
@Override | |
public boolean apply(File file) { | |
return file.size < maxSize; | |
} | |
} | |
class TypeFilter implements Filter { | |
private final int type; | |
public TypeFilter(int type) { | |
this.type = type; | |
} | |
@Override | |
public boolean apply(File file) { | |
return file.type == type; | |
} | |
} | |
abstract class LogicalFilter implements Filter{ | |
protected final List<Filter> filterList; | |
LogicalFilter(List<Filter> filterList) { | |
this.filterList = filterList; | |
} | |
} | |
class AndFilter extends LogicalFilter { | |
AndFilter(List<Filter> filterList) { | |
super(filterList); | |
} | |
@Override | |
public boolean apply(File file) { | |
return filterList.stream().allMatch(filter -> filter.apply(file)); | |
} | |
} | |
class OrFilter extends LogicalFilter { | |
OrFilter(List<Filter> filterList) { | |
super(filterList); | |
} | |
@Override | |
public boolean apply(File file) { | |
return filterList.stream().anyMatch(filter -> filter.apply(file)); | |
} | |
} | |
public class FindCommand { | |
public static List<File> find(File root, List<Filter> filters){ | |
List<File> result = new ArrayList<>(); | |
if(filters.stream().allMatch(filter -> filter.apply(root))) { | |
result.add(root); | |
} | |
find(root, filters, result); | |
return result; | |
} | |
private static void find(File directory, List<Filter> filters, List<File> result) { | |
if(directory.children.isEmpty()){ | |
return; | |
} | |
for(File file: directory.children){ | |
if(file.isDirectory){ | |
find(file, filters, result); | |
}else { | |
boolean isMatched = filters.stream().allMatch(filter -> filter.apply(file)); | |
if(isMatched){ | |
result.add(file); | |
} | |
} | |
} | |
} | |
public static void main(String[] args) { | |
File root = new File("home", 100, 0, true, new ArrayList<>()); | |
File subFile1 = new File("foo", 140, 1, false, new ArrayList<>()); | |
File subFile2 = new File("bar.bak", 180, 2, false, new ArrayList<>()); | |
root.children.addAll(List.of(subFile1, subFile2)); | |
List<Filter> filters1 = List.of(new AndFilter(List.of(new NameFilter("home"), new MinSizeFilter(110)))); | |
List<File> result1 = FindCommand.find(root, filters1); // should be empty | |
System.out.println(result1.size()); | |
List<Filter> filters2 = List.of(new OrFilter(List.of(new NameFilter("home"), new TypeFilter(0)))); | |
List<File> result2 = FindCommand.find(root, filters2); // should return "home" | |
System.out.println(result2.size()); | |
System.out.println(result2.getFirst().name); | |
List<Filter> filters3 = List.of(new AndFilter(List.of(new ExtensionFilter("bak"), new MinSizeFilter(150)))); | |
List<File> result3 = FindCommand.find(root, filters3); // should return "bar.bak" | |
System.out.println(result3.size()); | |
System.out.println(result3.getFirst().name); | |
List<Filter> filters4 = List.of(new AndFilter(List.of(new MinSizeFilter(130), new MaxSizeFilter(150)))); | |
List<File> result4 = FindCommand.find(root, filters4); // should return "foo" | |
System.out.println(result4.size()); | |
System.out.println(result4.getFirst().name); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment