Removing code duplication in methods that looks the same but works with different types

StackOverflow https://stackoverflow.com/questions/20175111

  •  04-08-2022
  •  | 
  •  

Domanda

I have two methods called Run that look almost identical but they work with different types:

public string Run<T>(IEnumerable<T> items) 
{
    // ... Code

    var serializer = new ObjectSerializer<T>();
    var headers = serializer.SerializeHeaders(items);

    // ... Code

    foreach (var item in items)
    {
        var values = serializer.SerializeValues(item);

        // ... Code
    }

    // ... Code
}


public string Run<T>(IEnumerable<Wrapper<T>> items) 
{
    // ... Code

    var serializer = new ObjectWrapperSerializer<T>();
    var headers = serializer.SerializeHeaders(items);

    // ... Code

    foreach (var item in items)
    {
        var values = serializer.SerializeValues(item);

        // ... Code
    }

    // ... Code
}

public class ObjectSerializer<T>
{
    public string[] SerializeHeaders(IEnumerable<T> items) { ... }
    public string SerializeValues(T item) { ... }
}

public class ObjectWrapperSerializer<T>
{
    public string[] SerializeHeaders(IEnumerable<Wrapper<T>> items) { ... }
    public string SerializeValues(Wrapper<T> item) { ... }
}

All the // ... Code parts are identical in both methods. Wrapper<T> has an instance of T, but other than that they don't have anything in common.

I'd like to remove the duplication, but I'm not sure how to do it.

Any suggestions?

È stato utile?

Soluzione

If the only thing that is different is the serializer that is required can you just pass it as a parameter? Something like this:

public class Runner    
{
    private string Run<T>(IEnumerable<T> items, IObjectSerializer<T> serializer) 
    {
        // ... Code

        var headers = serializer.SerializeHeaders(items);

        // ... Code

        foreach (var item in items)
        {
            var values = serializer.SerializeValues(item);

            // ... Code
        }

        // ... Code
    }

    public string Run<T>(IEnumerable<T> items)
    {
        return Run(items, new ObjectSerializer<T>());
    }

    public string Run<T>(IEnumerable<Wrapper<T>> items)
    {
        return Run(items, new ObjectWrapperSerializer<T>());
    }
}        
public interface IObjectSerializer<T>
{
    string[] SerializeHeaders(IEnumerable<T> items);
    string SerializeValues(T item);
}

public class ObjectSerializer<T>: IObjectSerializer<T>
{
    public string[] SerializeHeaders(IEnumerable<T> items) { ... }
    public string SerializeValues(T item) { ... }
}

public class ObjectWrapperSerializer<T> : IObjectSerializer<Wrapper<T>>
{
    public string[] SerializeHeaders(IEnumerable<Wrapper<T>> items) { ... }
    public string SerializeValues(Wrapper<T> item) { ... }
}

(Haven't got Visual Studio available, so probably not 100% right!)

Altri suggerimenti

If you look at your logic, the second method is a special case of the first method: if T is something like Wrapper, do something else (ObjectWrapperSerializer); otherwise do the normal thing (ObjectSerializer).

So the idea is, you want to dynamically decide what to do at runtime by looking at T. How do you do that? Reflection!

if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Wrapper<>)){
    Type u = typeof(T).GetGenericArguments()[0]; //u is the type in Wrapper<U>
    MethodInfo method = TheGenericWrapperMethod;
    MethodInfo gMethod = method.MakeGenericMethod(new Type[] { u });
    gMethod.Invoke();
} else {
    //do the normal thing
}

Alternatively, you may look into the factory pattern: make a factory class which produces either a ObjectSerializer or ObjectWrapperSeralizer instance at runtime (of course you will have to have some sort of contract, like inheritance or interface or abstract classes alike).

The code is not 100% accurate but I hope it will point you to the right direction.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top