hungryMind
hungryMind

Reputation: 6999

Add behavior to existing implementation - C# / Design Pattern

My current implementation for service and business layer is straight forward as below.

public class MyEntity { }

// Business layer
public interface IBusiness { IList<MyEntity> GetEntities(); }
public class MyBusinessOne : IBusiness
{
    public IList<MyEntity> GetEntities()
    {
        return new List<MyEntity>();
    }
}

//factory
public static class Factory
{
    public static T Create<T>() where T :  class
    {
        return new MyBusinessOne() as T; // returns instance based on T
    }
}

//Service layer
public class MyService
{
    public IList<MyEntity> GetEntities()
    {
        return Factory.Create<IBusiness>().GetEntities();
    }
}

We needed some changes in current implementation. Reason being data grew over the time and service & client cannot handle the volume of data. we needed to implement pagination to the current service. We also expect some more features (like return fault when data is more that threshold, apply filters etc), so the design needs to be updated.

Following is my new proposal.

public interface IBusiness
{
    IList<MyEntity> GetEntities();
}

public interface IBehavior
{
    IEnumerable<T> Apply<T>(IEnumerable<T> data);
}

public abstract class MyBusiness
{
    protected List<IBehavior> Behaviors = new List<IBehavior>();
    public void AddBehavior(IBehavior behavior)
    {
        Behaviors.Add(behavior);
    }
}

public class PaginationBehavior : IBehavior
{
    public int PageSize = 10;
    public int PageNumber = 2;
    public IEnumerable<T> Apply<T>(IEnumerable<T> data)
    {
        //apply behavior here
        return data
            .Skip(PageNumber * PageSize)
            .Take(PageSize);
    }
}

public class MyEntity { }

public class MyBusinessOne : MyBusiness, IBusiness
{
    public IList<MyEntity> GetEntities()
    {
        IEnumerable<MyEntity> result = new List<MyEntity>();
        this.Behaviors.ForEach(rs =>
        {
            result = rs.Apply<MyEntity>(result);
        });
        return result.ToList();
    }
}

public static class Factory
{
    public static T Create<T>(List<IBehavior> behaviors) where T : class
    {
        // returns instance based on T
        var instance = new MyBusinessOne();
        behaviors.ForEach(rs => instance.AddBehavior(rs));
        return instance as T;
    }
}

public class MyService
{
    public IList<MyEntity> GetEntities(int currentPage)
    {
        List<IBehavior> behaviors = new List<IBehavior>() { 
            new PaginationBehavior() { PageNumber = currentPage, }
        };
        return Factory.Create<IBusiness>(behaviors).GetEntities();
    }
}

Experts please suggest me if my implementation is correct or I am over killing it. If it correct what design pattern it is - Decorator or Visitor.

Also my service returns JSON string. How can I use this behavior collections to serialize only selected properties rather than entire entity. List of properties comes from user as request. (Kind of column picker)

Upvotes: 0

Views: 1283

Answers (1)

Arun J
Arun J

Reputation: 557

Looks like I don't have enough points to comment on your question. So, I am gonna make some assumption as I am not a C# expert.

Assumption 1: Looks like you are getting the data first and then applying the pagination using behavior object. If so, this is a wrong approach. Lets say there are 500 records and you are showing 50 records per fetch. Instead of simply fetching 50 records from DB, you are fetching 500 records for 10 times and on top of it you are adding a costly filter. DB is better equipped to do this job that C# or Java.

I would not consider pagination as a behavior with respect to the service. Its the behavior of the presentation layer. Your service should only worry about 'Data Granularity'. Looks like one of your customer wants all the data in one go and others might want a subset of that data.

Option 1: In DAO layer, have two methods: one for pagination and other for regular fetch. Based on the incoming params decide which method to call.

Option 2: Create two methods at service level. One for a small subset of data and the other for the whole set of data. Since you said JSON, this should be Restful service. Then based on the incoming URL, properly call the correct method. If you use Jersey, this should be easy.

In a service, new behaviors can be added by simply exposing new methods or adding new params to existing methods/functionalities (just make sure those changes are backward compatible). We really don't need Decorator or Visitor pattern. The only concern is no existing user should be affected.

Upvotes: 1

Related Questions