Удаление дублирования кода в методах, которые выглядят одинаково, но работают с разными типами

У меня есть два метода с именем Run, которые выглядят почти одинаково, но работают с разными типами:

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) { ... }
}

Все части // ... Code идентичны в обоих методах. Wrapper<T> имеет экземпляр T, но кроме этого у них нет ничего общего.

Я хотел бы удалить дублирование, но я не уверен, как это сделать.

Какие-либо предложения?


person the-lights    schedule 24.11.2013    source источник


Ответы (2)


Если единственное, что отличается, это требуемый сериализатор, можете ли вы просто передать его как параметр? Что-то вроде этого:

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) { ... }
}

(У меня нет доступной Visual Studio, так что, вероятно, не на 100% правильно!)

person Fergus Bown    schedule 24.11.2013

Если вы посмотрите на свою логику, второй метод — это частный случай первого метода: если T — это что-то вроде Wrapper, сделайте что-нибудь еще (ObjectWrapperSerializer); в противном случае сделайте обычное дело (ObjectSerializer).

Итак, идея в том, что вы хотите динамически решать, что делать во время выполнения, глядя на T. Как вы это делаете? Отражение!

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
}

В качестве альтернативы вы можете изучить фабричный шаблон: создать фабричный класс, который создает экземпляр ObjectSerializer или ObjectWrapperSeralizer во время выполнения (конечно, вам потребуется какой-то контракт, например наследование, интерфейс или абстрактные классы).

Код не на 100% точен, но я надеюсь, что он укажет вам правильное направление.

person kevin    schedule 24.11.2013