views:

176

answers:

9

Hello and thanks for any assistance.

using .Net 3.5 C#;

Say i have about 10 methods that all follow the same pattern

Using 3 as an example:

public Customer CreateCustomer(Customer c) { .. }
public Car CreateCar(Car c) { .. }
public Planet CreatePlanet(Planet p) { ..}

the internal logic of each method has the exact same pattern.

IE:

public Customer CreateCustomer(Customer c)
{
Log.BeginRequest(c, ActionType.Create); 
Validate(customer);
WebService.Send(Convert(c));
Log.EndRequest(c, ActionType.Create); 
}

public Car CreateCar(Car c)
{
Log.BeginRequest(c, ActionType.Create); 

Validate(c);

WebService.Send(Convert(c));

Log.EndRequest(c, ActionType.Create); 
}

The same is true with CreatePlanet and the other 7 methods.

Can these methods be re-written, they all follow the same pattern, and I feel like I am missing something... Is there is another level of abstraction that could be derived?

Question: How should this be re-written to take advantage of proper architecture pattens?

Thanks, Steven

A: 

Is Generic possible to solve you problem?

Benny
+6  A: 

It seems like a case that fits the template pattern. You can make all of the entities implement the same interface/base and execute the action against the interface.

I assume the only part that has to know the actual type is Validate(). It can be solved by two ways:

  1. Having the interface/base declare Validate and then implement it in each concrete entity.
  2. Define a strategy mapping between type of concrete entity and the actual validate strategy.

Example using base class abstract validation -

Base of entity, where it has internal service for create and abstract definition for validate:

public abstract class EntityBase
{
    protected abstract void Validate();

    protected void Create(EntityBase c)
    {
        Log.BeginRequest(c, ActionType.Create);
        c.Validate();
        WebService.Send(Convert(c));
        Log.EndRequest(c, ActionType.Create); 
    }
}

Concrete implementor with validate functionality:

public class Customer : EntityBase
{
    private int year;

    public Customer(int year)
    {
        this.year = year;
    }

    public void CreateCustomer(Customer c)
    {
        Create(c);
    }

    protected override void Validate()
    {
        if (year < 1900)
        {
            throw new Exception("Too old");
        }
    }
}

I didn't see in the original code what Create returns so I changed it to void to make the example clear

Elisha
Hello Elisha. Thanks for this idea, I'm implementing it now.Question: You mentioned: Define a strategy mapping between type of concrete entity and the actual validate strategy. Do you mean with something similar to: if(c.GetType() == typeof(Customer)) ?? Also, do you know if this will cost in performance ? Thanks Stevne
stevenrosscampbell
By mapping strategies it means that.You can also do it with if(c is Customer) instead of type comparisons. I am not sure how bad will it be for performance, I guess it wouldn't be much since there is no heavy reflection here. You'll pay also for every downcast, casting costs a little. The performance of virtual validate will be better.
Elisha
+2  A: 

I think you're looking for a generic method solution.

public T Create<T>(T t)
{
    Log.BeginRequest(t, ActionType.Create); 
    Validate(t);
    WebService.Send(Convert(t));
    Log.EndRequest(t, ActionType.Create);
    return t;
}
Matt Davis
A: 

You can use generic methods:

T Create<T>(T arg)

Or just System.Object:

object Create(object arg);
{
   Log.BeginRequest(arg, ActionType.Create); 
   Validate(arg);
   WebService.Send(Convert(arg));
   Log.EndRequest(arg, ActionType.Create); 
   return arg; //It must have a return
}

Car CreateCar(Car c)
{
   return (Car)Create(c);
}
Victor Hurdugaci
A: 

You could extract the common interface for those ten types if only the type is different or you could create a generic method.

EricSchaefer
+1  A: 

Yes, with Generic function:

  public T TrackInstantiation<T>(T entity)
  {
    Log.BeginRequest(entity, ActionType.Create); 
    Validate(entity);
    WebService.Send(Convert(entity));
    Log.EndRequest(entity, ActionType.Create);
    // Don't you also need to return the thing to fulfill the method siugnature ?
    return entity;
  }

I changed the name of the method because you are not Creating the object in this method, (you are passing in an already created instance) you are just validating, persisting and logging it's creation. BTW, why not actually create the object in here as well? Then this would be close to the pattern called an Abstract Factory.

You could also do the same thing using an interface.

public interface ICanBeTracked { /* No methods */ }

Modify each of the types you want to pass to this method so that they also umplement this interface, Then just write your method

  public ICanBeTracked TrackInstantiation(ICanBeTracked  entity)
  {
    Log.BeginRequest(entity, ActionType.Create); 
    Validate(entity);
    WebService.Send(Convert(entity));
    Log.EndRequest(entity, ActionType.Create);
    // Don't you also need to return the thing to fulfill the method siugnature ?
    return entity;
  }

... And make an overload for each of the three methods called by the method above with the input parameter typed as ICanBeTracked reference

Charles Bretana
+1  A: 
 public T Create<T>(T c)
 {
    Log.BeginRequest(c, ActionType.Create); 
    Validate(customer);
    WebService.Send(Convert(c));
    Log.EndRequest(c, ActionType.Create); 
 }
mfeingold
A: 

I see several recommendations for a template function. This is fine, but it doesn't tell the whole story. I'm guessing part of the issue is that each of your Validate and Convert function work a little differently, which means that a simple generic function may not be adequate.

If that's the case, you have a few options. One is to just overload the Validate/Convert functions and let the type system's overload resolution work out which one to call. Another (and this is preferred) is to use the template pattern recommended earlier and just have each of your types implement a common interface. And a third option is to require a delegate parameter for the function for each of your methods. You already have examples of the first two, so here is a code example for how to write the method to accept delegates:

public T Create<T>(T c, Action<T> validate, Func<T, string> convert)
{
    Log.BeginRequest(c, ActionType.Create);

    validate(c);
    WebService.Send(convert(c));

    Log.EndRequest(c, ActionType.Create);
}
Joel Coehoorn
A: 

You could use Aspect oriented programming to apply the logging and validation. Using PIAB (the Policy Injection Application Block) (part of the Enterprise Library) from Microsoft Patterns & Practices team (and assuming the configuration was in place) you could end up with something like this:

[LogRequest(ActionType.Create)]
[DoValidate()]
public T Create<T>(T item)
{
   WebService.Send(convert(item));
}

where LogRequest is a custom attribute which instatiates a new ICallHandler in which would Log the start of the request, call getNext() (which sends control to the next ICallHandler down the chain, or if this was the last one in the chain, sends control to the method being called), then when control was returned to the ICallHandler it would log the end of the request.

The validation might be handled by the Validation Application Block which works seamlessly with the PIAB or you could write your own validation handlers

AOP can substantially cut down on the amount of code you need, just be careful to not go overboard with it.

you can read up on the Enterpris Library here: http://www.codeplex.com/entlib/

AndreasKnudsen