0

I've a question about DIP Principle. One of the guidelines says that we should not hold references to a concrete class (if it changes then I'll have to modify all clients that use it). So, what can I follow this guideline when I use POJOs ? For Example:

I have a Bean 'Foo' with some attributes (it could represent a Domain object)

class Foo {  
   private String one;   
   private String two;

  //getters and setters
}

Multiple clients instantiate this object, for example, to persist it in the Database

   class Client1 {   

      private FooDao dao;

      Client1(FooDao dao){     
         this.dao = dao;
      }  

      public void persist() {    
         //hard coding
         Foo foo = new Foo();   
         foo.setOne("something...");    
         dao.save(foo); }     
       } 

       class Client2 {   

         private FooDao dao;

          Client2(FooDao dao){   
            this.dao = dao;

           }  

        public void persist() {   
           Foo foo = new Foo();   
           foo.setOne("something...");   
           foo.setTwo("something...")    
           dao.save(foo); 
         }
       }

If I add or change any attribute to 'Foo' class every client would have to change, so follow this guideline how can I avoid that?

Thanks!

  • 1
    It's useful to distinguish between data classes (similar to structs in C) and service classes (which generally should implement some interface). Note that the *active record* pattern, which you show here, has some fundamental problems and is not *usually* used in Java. – chrylis -cautiouslyoptimistic- Mar 06 '19 at 00:01

3 Answers3

1

I think you're taking this a little too literally.

I had the pleasure of attending a talk given by Venkat Subramaniam, which talked about DIP.

You're right when you say that you should be relying on abstractions, not concretions, but in my notes from that talk, I have the footnote, "take this with a grain of salt."

In your case, you're going to want to take this with a grain of salt, since there's a fairly strong code smell here - you're exposing the use of this bean to all consumers who need it, which implicitly create a dependency on it. This violates the Single Responsibility Principle since this bean is being used in more places than it probably should be.

Since it seems like you're talking about a database abstraction, perhaps you would want to look into a DTO which would be exposed between services to carry information between them, and let your bean handle the internals.

To your point...

if it change[s] then I'll have to modify all clients that use it

...this is true if you remove functionality. If you add new functionality, you can let your downstream clients just ignore that functionality. If you want to change existing functionality, you have to allow the clients a path to migration.

Makoto
  • 104,088
  • 27
  • 192
  • 230
1

The comment from @chrylis is spot on. Robert Martin covers this in chapter 6 of Clean Code: Objects and Data Structures.

Objects hide their data behind abstractions and expose functions that operate on that data. Data structures expose their data and have no meaningful functions. (page 95)

The definition of OOP where everything is an object, and there are no data structures, is naive.

Mature programmers know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them. (page 97)

So what about classes that expose both data and behavior?

Confusion sometimes leads to unfortunate hybrid structures that are half object and half data structure. They have functions that do significant things, and they also have either public variables or public accessors and mutators that, for all intents and purposes, make the private variables public, tempting other external functions to use those variables the way a procedural program would use a data structure. Such hybrids make it hard to add new functions but also make it hard to add new data structures. They are the worst of both worlds. Avoid creating them. (page 99)

To the original question: the Dependency Inversion Principle applies to objects, not to data structures like Java Beans.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
  • So, is there some creational pattern I can use to avoid hard coding when I have to instantiate Java Beans (data structures)? – Gonzalo Prendes Mar 06 '19 at 13:39
  • The purpose of a data structure is to expose details, so a data structure should be a concrete dependency. The purpose of an object is to hide details, so an object should be an abstract dependency. [The expression problem](https://stackoverflow.com/q/3596366/1371329) describes this dichotomy in more detail, as does Martin in Clean Code. You don't want to avoid hard coding a data structure, because that's what a data structure is for (exposing details). It is the exact opposite of an object in that respect. – jaco0646 Mar 06 '19 at 18:01
0

You need to define the functionality of the method you would like to add.

interface Functionality {
public void persist();
}

Each class except the manager need to implement the interface:

 class Client1 implements Functionality{   
  //Your code..
}

Add a high level class high level classes that not working directly with low level classes:

Class ManageClients{
    Functionality func;

    public void setClient(Functionality f) {
        func= f;
    }


    public void manage() {
        func.persist();
    }
};
  • ManageClients class doesn't require changes when adding Clients.

  • Minimized risk to affect old functionality present in ManageClients class since we don't change it.

  • No need to redo the unit testing for ManageClients class.

Ido Segal
  • 430
  • 2
  • 7
  • 20
  • Thanks. I know that. But 'Foo' class, which is a concrete class, would be instantiated in Clients implementations, and if I change that I would have to change all clients implementations. – Gonzalo Prendes Mar 05 '19 at 23:41