Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bulider Design Pattern to make generic method for methods having large number of parameters

I have an interface Itest and ClassA & ClassB are implementing this interface. testA & testB are methods in these classes respectively.

testA(String a, String b, String c, D d, E e)

testB(String a, String b, String c, F f, G g) 

Here D, E, F, G are custom data types (related to databases). I simplified the methods actually they have more number of parameters.

I need to make a generic method in testAB in Itest interface and implement that in both classes rather than having their own method.

testAB(String a, String b, String c, D d, E e, F f, G g)

As the number of parameters is more, generic method testAB will be pain for the user as he has to pass so many null values.

  • Is this a usecase for Bulider Design Pattern?

  • If yes, how to achieve this using this design pattern?

like image 425
Dev Avatar asked Jan 09 '23 05:01

Dev


2 Answers

Looks like the core requirement that you have is that you don't want the client to be passing additional parameters when they are not required. You can solve your problem using plain old method overloading :

Alter your ITest interface to have a single method called test

public interface ITest {
     public void test(String a,String b,String c,D d,E e,F f,G g);
}

Alter A as follows :

public class A implements ITest {

     //this is an overload - v1
     public void test(String a,String b,String c,D d,E e) {
            //dispatch the call to the overriden method
            test(a,b,c,d,e,null,null);
     }

     //this is an overload - v2
     public void test(String a,String b,String c,E e,F f) {
           //dispatch the call to the overriden method
           test(a,b,c,null,null,e,f);
     }

     @Override
     //this is an overriden method - v3
     public void test(String a,String b,String c,D d,E e,F f,G g) {
            if(d!=null && e!=null) {
                //use a,b,c,d,e and do something
            } 

            if(f!=null && g!=null) {
                //use a,b,c,f,g and do something
            }
     }
}

Now the client code can call whichever overloaded form they want without the need to pass null. Your overloaded methods will simply dispatch the call to a common method (which gives you the advantage of code reuse):

classAObj.test("1","2","3",new D(),new E());//calls overloaded method - v1
classAObj.test("1","2","3",new F(),new G());//calls overloaded method - v2
classAObj.test("1","2","3",new D(),new E(),new F(),new G());//calls overriden method - v3

Notice how the client code does not have to worry about passing additional parameters when they are not required. Also notice how clean the client calls look. Similar changes can be made in B as well.


1. You have an option to make ITest an abstract class. This will allow you to make the test method in it have the protected access specifier. The reason for wanting a protected access specifier is to restrict client classes from being able to access the method and always go through the overloaded forms instead. This is an add-on feature you can consider implementing in the future if you are stuck with using an interface at the moment.

2. You could also take advantage of Generics to avoid the need to write a new class every-time a new object type is introduced but as you can see from the other answers, this can easily complicate your code to a great extent. You could also add the overloaded methods to ITest interface to make it a part of its contract. However, I am deliberately leaving these parts out of my answer because the crux of your problem can be solved by using overloading.

3. The Builder pattern is a creational pattern. It's an overkill in this particular case since classes such as D,E,F and G are domain objects. Class A and B don't really depend on them in the real sense but use them as a source of data instead.

like image 85
Chetan Kinger Avatar answered Jan 10 '23 18:01

Chetan Kinger


Yep here you could use the builder pattern. You would like to have objects holding the information passed to your methods.

These objects could be created with an internal builder, while the fields may hold default values. Here a small example how this could look like.

ParamTestA<D, E> paramA = new ParamTestA<>.Builder(a, b, c).setD(d).setE(e).build();

testA(paramA);
like image 38
Timo Hanisch Avatar answered Jan 10 '23 18:01

Timo Hanisch