1

So I have two identical classes, say ClassA and ClassB. In both classes, they operate on the same kind of data classes, and the method bodies are exactly the same. The only difference is that the imported classes, which identically named in both ClassA and `ClassB, come from different packages/projects. That is,

//Class A

package packageA;

import com.project.google.cool.Message;
import com.project.google.cool.CoolClass;

public class ClassA {

   //this is a static class

   public static void method(Message m) {
       m.setType("cool message");
       m.setObj(new CoolClass());
   }
   .
   .
   .
   //etc same for other  methods
}

//Class B


package packageB;

import com.project.microsoft.cool.Message;
import com.project.microsoft.cool.CoolClass;

public class ClassB {

   // this is a static class

   public static void method(Message m) {
       m.setType("cool message");
       m.setObj(new CoolClass());
   }
   .
   .
   .
    //etc same for other  methods
}

The classes from com.project.google/microsoft are autogenerated and there's nothing I can do to change them, including renaming.

I've explored creating a generic interface, where both ClassA and ClassB implement said interface, but I dont think I got an implementation working. As is, SonarQube reported "code duplication", and I was wondering if there's a design pattern available to tackle situations like this.

The actual caller of the methods (e.g. method) has the responsibility of handing off the correct data type, either from project "google" or "microsoft".

3 Answers 3

4

In this situation, I would create an interface which would have the same signatures as the Message and CoolClass. Then you would simply create two implementations, one for Google and one for Microsoft. ClassA and ClassB then collapse into a single class that takes an instance of this interface and calls the same methods it does now with the exception of calling the CoolClass constructor. That could be handled inside the wrapper classes i.e., it would create the object and passes it to the message instance.

The main potential dowside of this is if there are many methods that your ClassA/B need to call, you will need a lot of boiler-plate to wrap all of those. You can probably generate them with an IDE.

Here's a rough example as requested:

package packageA;

public interface MessageX {
    void setType(String typeStr);
    void createCoolObj();
   //etc same for other  methods
}

// ................... //

import com.project.google.cool.Message;
import com.project.google.cool.CoolClass;
import packageA.MessageX;

class GoogleMessage implements MessageX {
    private final Message message;

    GoogleMessage(Message msg) {
        this.message = msg;
    }

    void setType(typeStr) {
        this.message.setType(typeStr);
        
    }
    
    void createCoolObj() {
        this.message.setObj(new CoolClass());
    }
    
    //etc same for other  methods
}

// ................... //

import com.project.microsoft.cool.Message;
import com.project.microsoft.cool.CoolClass;
import packageA.MessageX;

class MicrosoftMessage implements MessageX {
    private final Message message;

    MicrosoftMessage(Message msg) {
        this.message = msg;
    }

    void setType(typeStr) {
        this.message.setType(typeStr);
        
    }
    
    void createCoolObj() {
        this.message.setObj(new CoolClass());
    }
    
    //etc same for other  methods
}

// ................... //

package packageB;

import packageA.MessageX;

public class ClassX {

   // this is a static class

   public static void method(MessageX m) {
       m.setType("cool message");
       m.createCoolObj();
   }
}
5
  • with this approach, how would the autogenerated code deal with "implementation" keyword?
    – kroger9
    Commented Dec 15, 2023 at 22:36
  • @kroger9 I am not familiar with the 'implementation' keyword. Do you mean implements? If so, why would you worry about that?
    – JimmyJames
    Commented Dec 15, 2023 at 22:40
  • Yes sorry. Would you mind giving an example please?
    – kroger9
    Commented Dec 15, 2023 at 23:25
  • @kroger9 I added a basic example.
    – JimmyJames
    Commented Dec 18, 2023 at 15:22
  • \@JimmyJames Thank you!
    – kroger9
    Commented Dec 18, 2023 at 17:02
3

The classes from com.project.google/microsoft are autogenerated and there's nothing I can do to change them, including renaming.

I am sure this is too pessimistic. You can surely write your own tiny generator script which takes the classes' source code after they were autogenerated and applies some changes to the code, like adding different namespaces around them, or changing the name. Then integrate that script into the build process.

That should to the trick.

5
  • 2
    Honestly, if you are unable to change the SonarQube configuration, the old "fix it with a shell script" trick is a fine solution indeed. Commented Dec 17, 2023 at 0:36
  • 1
    @GregBurghardt: yes, and as a bonus, this approach is not bound to specific tool like SonarQube.
    – Doc Brown
    Commented Dec 17, 2023 at 4:57
  • You mean you would use a simple search and replace operation which every decent edior would be capable of (when it is only about changing the said import statements). No script required. But ofc scripts are much cooler - esp. Perl ;) Commented Dec 21, 2023 at 9:00
  • @ThomasJunk: no, the OP wrotes "autogeneration" and "cannot change the classes" - which means doing one-time changes using an editor would be overwritten by the next regeneration of the classes. The solution to this is to automate afterwards changes and integrate that into the "regeneration" step in the build process. This has nothing to do with bein "cooler".
    – Doc Brown
    Commented Dec 21, 2023 at 9:24
  • @DocBrown Okay. Misunderstanding about "autogeneration". I thought about a singular event of once autogenerated code files. If that happens periodically an automated version is ofc the way to go. Commented Dec 21, 2023 at 11:38
2

Take a step back, and ask yourself:

Is the code unrelated, and thus the automation reporting it as evil code duplication the problem?

Or do both specimens solve the same problem and thus are identical, and they belong to the same project, meaning they should be folded?

It's clearly the former, as the google and microsoft projects are independent. On top of that, auto-generated code being similar up to identical should not be too surprising.

In the end, you just have to curb the automation. See https://docs.sonarsource.com/sonarqube/8.9/project-administration/narrowing-the-focus/#ignore-duplications for reigning it in.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.