3

I've a JSON file which I'm trying to de-serialize into POJOs.

public abstract BaseClass {
    private String baseClassField;
    abstract String execute();
}

ClassA extends BaseClass

public ClassA extends BaseClass {
   private String classA;
   private SomeInterface interface;

   @Override
   public String execute() {
      return interface.execute();
   }
}

I've simplified my use case, but as you can see in the way I've structured my classes, I've will have business logic within my core POJO definition. I don't think that's a good idea because soon, I will start trying to bring in dependency injection of other member instances to help and perform the execution.

How should I model the classes, so that I'm able to main the inheritance hierarchy and have the ability of child classes executing the contracts defined by the base class?

4 Answers 4

3

I've will have business logic within my core POJO definition. I don't think that's a good idea because soon, I will start trying to bring in dependency injection of other member instances to help and perform the execution.

You have really hit on the main problem with intermingling data transfer objects and business classes (also called domain models). Your "POJO" plays the role of a data transfer object. This is an important thing to realize. Once one references the other, they are coupled. So long as one holds a reference to another you cannot decouple them.

The fact is your business classes need the data in the POJO. To keep them decoupled you need to introduce an object whose sole purpose is to be coupled to these two different kinds of classes. This leads you to the various data access patterns. One of the more popular ones is the repository pattern. It knows how to load the data from file and delegates to the proper parser. It takes the resulting POJOs and maps them over to your business classes.

This keeps your POJOs devoid of references to your business classes, and your business classes remain ignorant of the POJOs. This is persistence ignorance and is a very important concept allowing your business classes to remain unchanged when you change the way data is stored and retrieved.

Each repository should implement an interface defined in the same module as your business classes. This allows business and other core service classes to retrieve data by calling methods on the repository interface, so they too can remain ignorant of the persistence mechanism.

Domain model: https://en.m.wikipedia.org/wiki/Domain_model

Data transfer object: https://en.m.wikipedia.org/wiki/Data_transfer_object

Persistence ignorance: https://deviq.com/persistence-ignorance/

1
  • 2
    Disagree. "Persistence ignorance" is not an important concept. The need to be "clean", or have "pure" business objects is nonsense. In the pursuit of these goals you arrive at exactly the opposite outcome you want, that is, you couple everything to the data inside of objects, instead relying on the behavior of objects. Not to mention that separating data and function on purpose can hardly be called object-oriented. May 5, 2020 at 15:58
-1

Having business logic in the object where the data for it resides is a good idea in general (I don't know about your specific case enough). In fact, that is a basic idea of object-orientation itself.

Now, I think I know what you mean. Most, if not all JSON de/serialization libraries that work with reflection don't let you use your constructors properly. So if you have some dependencies, you can't really pass them to your object. This is a problem.

It is however not a problem with the concept of having business-logic in objects, it is a problem that these libraries are just not made for object-oriented designs in mind.

You have to decide whether you let your design be forced by some json library, or whether you get a library that supports your design properly.

My usual disclaimer: If you pursue proper object-orientation you might find that a lot of libraries / tools suddenly don't fit. You have been warned :)

-1

Objects don't have to know how to construct themselves. You can keep their factories anywhere you like. So even if you want to use json to do it the objects don't have to know json. Just the factory does.

public BaseClass BaseFromJsonFactory(String json) {
    ... //json parsing logic
    SomeInterface someInterface = new SomeInterface();
    return new ClassA(baseClassField, classA, someInterface);
}
-2

There are.. ways:

  1. Most IoC containers are smart enough to allow wiring dependencies manually, in case of Spring looks like this:

 

public ClassA extends BaseClass {

   private String classA;

   @Autowired
   private SomeInterface interface;

   public void wireme(AutowireCapableBeanFactory beanFactory) {
     beanFactory.autowireBean(this);
   } 

   @Override
   public String execute() {
      return interface.execute();
   }

}

So case with POJOs created outside of container will be like:

  ClassA c = new ClassA();

   c.wireme(beanFactory);

   //after call to wireme(), the 'interface' field will be non-null

   c.execute();

AutowireCapableBeanFactory is part of Spring Core, can be easily injected everywhere: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/AutowireCapableBeanFactory.html

  1. Use internal Builder pattern and keep all business logic there:

 

public ClassA extends BaseClass {

   private String classA;
   private SomeInterface interface;

   public static class Builder {

   private ClassA source;

   protected Builder(ClassA source) {
     this.source=source;          }

   public String execute() {
      return source.interface.execute();
   }

   public static Builder create(ClassA source) {
     return new Builder(source);           }

   }   

}

Usage:

ClassA c = new ClassA();
ClassA.Builder.create(c).execute();

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