简体   繁体   中英

Avoiding code duplication — best approach

I've got two methods with the same list and types of arguments and almost the same body but each of them calls another function to fetch list of elements. To be more precise:



    public void method1 (int a, int b) {
            //body (the same in both of methods)
            List<SomeObject> list = service.getListA(int c, int d);
            //rest of the body (the same in both of methods)
        }

        public void method2 (int a, int b) {
            //body (the same in both of methods)
            List<SomeObject> list = service.getListB(int c, int d, int e);
            //rest of the body (the same in both of methods)
        }

What is the best approach to the problem avoiding code duplication in that case? I thought about Strategy pattern, but there is a problem with difference in argument list.

UPDATE:



    public void method1 (int a, int b) {
            //body (the same in both of methods)
            int c = some_value;
            List<SomeObject> list = service.getListA(a, b, c);
            //rest of the body (the same in both of methods)
        }

        public void method2 (int a, int b) {
            //body (the same in both of methods)
            int c = some_value;
            int d = another_value;
            List<SomeObject> list = service.getListB(a, b, c, d);
            //rest of the body (the same in both of methods)
        }

So some variables are local and some are passed through arguments.

Factor them out into additional methods.

public void method1 (int a, int b) {
        MyClass myClass = method3(a, b);
        List<SomeObject> list = service.getListA(myClass.getC(), myClass.getD());
        method4(list);
}

public void method2 (int a, int b) {
        MyClass myClass = method3(a, b);
        List<SomeObject> list = service.getListB(myClass.getC(), myClass.getD(), myClass.getE());
        method4(list);
}

public MyClass {
    private final int c;
    private final int d;
    private final int e;
    ...
}

public MyClass method3(int a, int b) {
    // body
    return new MyClass(c, d, e)
}

public void method4(List<SomeObject> list) {
    // rest of body
}

One way of avoiding code duplication in your case could be to introduce an extra parameter that is used to decide which method to retrieve the list is going to be used:

public void method (int a, int b, int method) {
    //body (the same in both of methods)
    List<SomeObject> list = null;
    switch (method) {
        case 1: 
            list = service.getListA(int c, int d);
            break;
        case 2: 
            list = service.getListB(int c, int d, int e);
            break;
    }
    //rest of the body (the same in both of methods)
}

instead of using int method as the additional parameter I would use an new enum type and define a default case in the switch statement.

Encapsulate the invocation of service.getListA or service.getListB in an ListSource class/interface, implement each version in concrete classes and pass a concrete instance as a third argument. This is basically the object-oriented version of the answer proposed by jlordo.

interface ListSource {
    List<SomeObject> getList(int c, int d, int e);
}

class ListSourceA implements ListSource {
    // constructor etc.
    @Override
    public getList(int c, int d, int e) {
      return service.getListB(c, d);
    }
}

class ListSourceB implements ListSource {
    // constructor etc.
    @Override
    public getList(int c, int d, int e) {
      return service.getListA(c, d, e);
    }
}

public void method (int a, int b, ListSource source) {
    //body (the same in both of methods)
    List<SomeObject> list = source.getList(int c, int d, int e);
    //rest of the body (the same in both of methods)
}
public void method (int a, int b, List<SomeObject> theList) {
    //body (the same in both of methods)
    List<SomeObject> list = theList;
    //rest of the body (the same in both of methods)
}

This to me removes ALL the code duplication, means the method NEVER has to be modified each time we want to operate on a list derived using a different method signature.

I believe you could further this if the type SomeObject is not known using generics ie (and I am not a java programmer so you will have to read the docs )

public void method (int a, int b, List<T> theList) {
    //body (the same in both of methods)
    List<T> list = theList;
    //rest of the body (the same in both of methods)
}

You could also use an enum:

public void method(int a, int b, Service service) {
    // body
    List<SomeObject> list = service.getList(myClass);
    // rest
}

public enum Service {
    METHOD_1 {
        @Override
        public List<SomeObject> getList(MyClass myClass) {}
    },
    METHOD_2 {
        @Override
        public List<SomeObject> getList(MyClass myClass) {}
    };

    public abstract List<SomeObject> getList(MyClass myClass);
}

public MyClass {
    private final int c;
    private final int d;
    private final int e;
    ...
}

Essentially the same as @proskor except in a different form.

If the body parts depend upon one another so you can't do as @dicarlo2's answer:

private interface GetObjects {
    List<SomeObject> get();
}

public void method1(int a, int b) {
    impl(a, b, new GetObjects() { public List<SomeObject> get() {
        return service.getListA(c, d);
    }});
}

public void method2(int a, int b) {
    impl(a, b, new GetObjects() { public List<SomeObject> get() {
        return service.getListB(c, d, e);
    }});
}

private void impl(int a, int b, GetObjects getObjects) {
    //body (the same in both of methods)
    List<SomeObject> list = getObjects.get();
    //rest of the body (the same in both of methods)
}

You can use an enum in place of GetObjects if you are concerned about the new , but don't mind getting the order mixed up, missing out on outer this and don't want to open this up (although it could implement an public interface).

Better syntax coming in Java SE 8, possibly. IIRC, along the lines of:

public void method1(int a, int b) {
    impl(a, b, { -> service.getListA(c, d) });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM