4

I have a question about OOP implementation and design patterns.

I have a fixed class model which I cannot change (because it is generated automatically each time the application starts). There are many classes there with equals fields like in example below: as you can see the fields city and streets are contained in the both classes.

public class A{
   String city;
   String street;

   String name;

   ....//get methods
}

public class B{
   String city;
   String street;

   String age;

   ....//get methods
}

I need to extract an address form the both types of classes and I want to implement it with one method (because it seems to be silly to write the same code twice). If the class model were changeable, I could add a new interface Addressable which A and B could implement.

public interface Addressable{

     public String getStreet();
     public String getCity();
}

//somewhere in code
public Address getAddress(Addressable addressable){
      return new Address(addressable.getCity(), addressable.getStreet());
} 

What is the most elegant way to implement the same without interface and without coding the same for different classes?

user2957954
  • 1,221
  • 2
  • 18
  • 39
  • 1
    It is not clear what you can actually change in A and B. – davidxxx Aug 16 '17 at 09:04
  • There is no elegant way, you can just try to cast to the appropriate class, or use reflection (may be too heavy for this need). – Jean-Baptiste Yunès Aug 16 '17 at 09:06
  • If the classes can't be changed (if they are generated that might be possible though) and you can't generate wrappers/decorators for those classes that implement a common interface the only other way I'd see would be reflection. But before going down that route I'd think hard about what could be done without reflection. One way to generate (or at least fill) those wrappers might be [mapstruct](http://mapstruct.org/). – Thomas Aug 16 '17 at 09:06
  • @davidxxx nothing is changeable in this classes because they are generated automatically each time the application starts. so I cannot add an interface to them – user2957954 Aug 16 '17 at 09:07
  • @user2957954 but can you change the way those classes are generated? – Thomas Aug 16 '17 at 09:08
  • @user2957954 If you are not able to change A or B, you would have necessarily a degraded solution. A good designed solution relies of course on a common class/interface. – davidxxx Aug 16 '17 at 09:18
  • If you really want to do it without interface, I think you can use reflection to get this thing done. There are certainly some cons related to it. Please evaluate that. https://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful – Katiyman Aug 16 '17 at 10:31

3 Answers3

3

If you are not able to change A or B, you would have necessarily a degraded solution.
A simple and good designed solution would rely of course on a interface defining an Address retrieval method (Address getAddress()) that A and B would implement.

You could also define a wrapper class :

public class WrapperA implements Addressable {

  private final A a;

  public WrapperA(A a) {
    this.a = a;
  }

  @Override
  public Address getAddress(){
     return new Address(a.getCity(), a.getStreet(), etc...);
  } 

}

But it may be rather clumsy if you have to duplicate this kind code for many classes.
Besides the client will not manipulate any longer a A but a WrapperA class.
It may break the actual client code.
So also here, an interface is required if you want to implement a real adapter.

As said, without redesigning a minimum A or B, a really good solution is complicated.


As workaround, you may define an Address class that provides factory methods to create Address from a A or a B instance.

public class Address{

    ... 
   String city;
   String street;
    ... 

   private Address(){
   }

   public static Address of(A a){
     return new Address(a.getStreet(), a.getCity(), ....);
   }

   public static Address of(B b){
     return new Address(b.getStreet(), b.getCity(), ...);
   }

}

Then use these methods to create the Address on the demand as you need it.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
2

You could write adapters to provide a common interface.

public class AdpaterA implements Addressable {

  private final A a;

  public AdapterA(A a) {
    this.a = a;
  }

  @Override public String getStreet() {
    return this.a.street;
  }

  // other method is omitted as homework ;-)
}

Then you would use the adapter classes for further processing.

deamon
  • 89,107
  • 111
  • 320
  • 448
0

I had a similar situation, where classes are generated during the build process. (In my case, the build process would inspect the database, and generate one class per database table, with all the fields.)

You state that the classes are generated when your application starts. In case they are generated during the build process, you can add an extra element to the build process which alters the genreated files. In my case our build servers were only Linux, so I added a sed line to our ant script.

Adrian Smith
  • 17,236
  • 11
  • 71
  • 93