Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to achieve similar design (from Java) that use interface and implementing classes, using delegates in C#

Tags:

c#

I have a following design in Java (7) application:

There is a method where i pass collection of objects of some type and object that I call "predicate" that is used to filter given collection.

Predicate is an interface with one method called test - it takes an object and return a boolean value.

In such situation:

  • Anyone is able to write it's own implementation of Predicate interface by creating class that implements Predicate
  • Anyone is able to write it's own implementation of Predicate interface by creating anonymous implementation during call of filtering method
  • There predefined set of class implementing Predicate, that covers many standard situation, so in most cases there is no need to write new implementation.
  • Also there is a utility class that provides several static methods like and, or, allOf, anyOf, that allows to combine Predicates given as input into a new predicate (internally it will return anonymous implementation of new Predicate)

Now I would like to have similar design in C# (4.0) application. I can see two way of doing it - by mimic Java design, or by change Predicate into delegate:

I can try to mimic Java design, but:

  • As far as I know there is no such thing like anonymous implementation of interface in C# (so no creation of new predicate at the moment of calling filter method, and my util class have not to be based on anonymous implementations, but second is not the real problem)
  • I feel this is not a C#-way of doing such things

I can try achieve similar design using delegates. My filter method will take collection of object and Predicate delegate. In such case:

  • I don't know how to achieve situation when one is able to write some type that will "implements" delegate. I imagine situation when someone will just write class with method that match signature of delegate - I don't feel good about this strategy, because if I have interface to implement - compiler will force me to have correct signature - and since I cannot "extends" or "implements" delegate - compiler will tell that I'm wrong only when I will try to pass "reference" to this method to my filtering method (Of course it will still be compile time error).
  • Anyone is able to write it's own implementation of Predicate interface by passing lambda, or pointing to some method with signature that match Predicate delegate. So here will be the same as in Java
  • I don't know how to achieve situation when I have predefined sets of Predicates. I can see of course that is simple from technical point of view - I can for example write one class with static methods that will match signature of delegate - each of method will cover one Predicate. But I have a feeling that this approach is little bit inconsistent - because there will be one place with predefined predicates, and user's predicates will be defined in another places (In Java, both predefined and user-defined predicates will be classes that implements interface)
  • If Predicate will be delegate - I can also write some Util class that can combine predicates in many ways. Here is also the same as in Java

My question is - what in your opinion will be best way of achieve the same (or as similar as possible) design that I have in Java, that will be considered as correct, clean C#-approach of doing that?

I have few years of experience in programming in Java, but less that a year in C#, so I understand that maybe some problems that I see just doesn't exist in C# world or are not considered as problems at all.

EDIT: here is simplest (I think...) possible example of how my Java code work:

My "domain" object:

public class Person {

    private final String firstName;
    private final String secondName;

    public Person(String firstName, String secondName) {
        this.firstName = firstName;
        this.secondName = secondName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }
}

Filtering class:

public class Filter {
    public Collection<Person> filter(Collection<Person> collection, Predicate predicate) {
        Collection<Person> result = new LinkedList<Person>();
        for(Person person: collection) {
            if(predicate.test(person)) {
                result.add(person);
            }
        }
        return result;
    }
}

Predicate interface:

public interface Predicate {
    boolean test(Person person);
}

Two simple predefined implementations:

public class FirstNameStartsWithPredicate implements Predicate {

    private final String startsWith;

    public FirstNameStartsWithPredicate(String startsWith) {
        this.startsWith = startsWith;
    }

    public boolean test(Person person) {
        return person.getFirstName().startsWith(startsWith);
    }
}

public class LastNameEndsWithPredicate implements Predicate {

    private final String endsWith;

    public LastNameEndsWithPredicate(String endsWith) {
        this.endsWith = endsWith;
    }

    public boolean test(Person person) {
        return person.getSecondName().endsWith(endsWith);
    }
}

Utility class:

public final class PredicateUtils {

    public static Predicate and(final Predicate first, final Predicate second) {
        return new Predicate() {
            public boolean test(Person person) {
                return first.test(person) && second.test(person);
            }
        };
    }

    public static Predicate or(final Predicate first, final Predicate second) {
        return new Predicate() {
            public boolean test(Person person) {
                return first.test(person) || second.test(person);
            }
        };
    }

    public static Predicate allwaysTrue() {
        return new Predicate() {
            public boolean test(Person person) {
                return true;
            }
        };
    }
}

And finally, example of usage:

Collection<Person> persons = Arrays.asList(
        new Person("John", "Done"),
        new Person("Jane", "Done"),
        new Person("Adam", "Smith")
);

Filter filter = new Filter();

// Predefined predicates
filter.filter(persons, new FirstNameStartsWithPredicate("J"));
filter.filter(persons, new LastNameEndsWithPredicate("e"));

// anonymous implementation
filter.filter(persons, new Predicate() {
    public boolean test(Person person) {
        return person.getFirstName().equals("Adam") && person.getSecondName().equals("Smith");
    }
});

// utility class
filter.filter(persons, PredicateUtils.allwaysTrue());
filter.filter(persons, PredicateUtils.and(new FirstNameStartsWithPredicate("J"), new LastNameEndsWithPredicate("e")));
filter.filter(persons, PredicateUtils.or(new FirstNameStartsWithPredicate("J"), new FirstNameStartsWithPredicate("A")));
like image 809
Michał Przybylak Avatar asked May 16 '16 09:05

Michał Przybylak


1 Answers

This is totally doable in C#. Let's say we have a few users, and filters to filter them, some filters are built-in and some are implemented by users. Here we should use interfaces like Java instead of delegates.

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
}

public interface IPredicate<T>
{
    bool IsValid(T entity);
}

public class UserPredicate : IPredicate<User>
{
    /* built-in predicates */
    public static UserPredicate Adult = new UserPredicate(u => u.Age >= 18);
    public static UserPredicate NoAddress = new UserPredicate(u => string.IsNullOrEmpty(u.Address));

    public Func<User, bool> Predicate { get; private set; }
    public UserPredicate(Func<User, bool> predicate)
    {
        this.Predicate = predicate;
    }

    bool IPredicate<User>.IsValid(User entity)
    {
        return this.Predicate(entity);
    }
}

Users can easily add new predicates like this:

//user's code
var custom = new UserPredicate(MyCustomUserFilter);

bool MyCustomUserFilter(User u)
{
    //user's filter logic
}

It's not the same as Java, because in C# anonymous types can't implement an interface.

And also it's very easy to "combine" the predicates into a new one.

var AdultWithNoAddress = new UserPredicate(u => UserPredicate.Adult.Predicate(u) 
    && UserPredicate.NoAddress.Predicate(u));

EDIT To make the combination of predicates more clearer, you can put the combination logic into predicates itself.

public interface IPredicate<T>
{
    bool IsValid(T entity);

    IPredicate<T> And(IPredicate<T> another);

    IPredicate<T> Or(IPredicate<T> another);
}

public class UserPredicate : IPredicate<User>
{
    public static UserPredicate Adult = new UserPredicate(u => u.Age >= 18);
    public static UserPredicate NoAddress = new UserPredicate(u => string.IsNullOrEmpty(u.Address));

    private Func<User, bool> _predicate;
    public UserPredicate(Func<User, bool> predicate)
    {
        _predicate = predicate;
    }

    public bool IsValid(User entity)
    {
        return _predicate(entity);
    }

    public IPredicate<User> And(IPredicate<User> another)
    {
        return new UserPredicate(u => this.IsValid(u) && another.IsValid(u));
    }

    public IPredicate<User> Or(IPredicate<User> another)
    {
        return new UserPredicate(u => this.IsValid(u) || another.IsValid(u));
    }
}

//usage
var AdultWithNoAddress = UserPredicate.Adult.And(UserPredicate.NoAddress);
like image 123
Cheng Chen Avatar answered Sep 30 '22 19:09

Cheng Chen