2

I was reading this page and came across this sentence in the accepted answer:

I don't like A directly knowing about B. But that's a DIP thing not a POJO thing.

What if you can't abstract out B so that A isn't aware of it?

Suppose if I have the following Book class:

public final class Book {

    private final Author author;

    // constructor and methods left out :D. 
} 


public final Author {

     private final String firstName;
     private final String lastName;

     // constructor and methods left out :D.
}

I'm aware of the fact that a book might have a list of authors, so it would be better if it was List<Author> authors, but I want to focus on a specific part of the above code sample.

Some might point out that the Book class knows about the Author object.

I don't see why Author would be an interface or abstract class.

Question:

Would you abstract out Author? If so, how?

2 Answers 2

2

The way that in your example the Book class depends on Author, it's quite plausible that Author depends on Country, Town and Language (describing home town and native language), and so on.

For the productive use of the program, you'll probably need Books with Authors having all these properties. So, there's nothing really wrong about that approach.

But if you want to unit-test some functionality about Books, you'll appreciate not having to construct full-fledged Authors with their Country, Town and Language instances (and their dependencies and so on). Instead you'll want to use mockups with just enough functionality to support the Book class.

To make that easy, define as an interface the API that an Author offers to other classes (hopefully Book and e.g. PublishingCompany need similar methods from Author, otherwise you should create multiple interfaces). Then for testing purposes, you (or your preferred mocking framework) can supply Author-like objects that are sufficient for Book, but don't need all the other classes.

Of course, there are situations where this is just over-engineering, e.g. if Author really is nothing more than a holder for first and last name.

2

Whether we want to use an abstraction(interfaces and abstract classes) depends on whether we want the following advantages :

  • We want to be able to comfortably switch between different implementations of Author like ScientificJournalAuthor or FictionAuthor or a AutoBiographyAuthor. If all these different authors follow the contract of the interface Author, you will be able to use them interchangeably in certain scenarios, reaping the benefits of code reuse.

  • By thinking in terms of an Abstraction, you will be able to narrow down on the minimal amount of functionality you need from an Author for the Book class. This knowledge will help you keep your implementation of Book decoupled from implementations of Author.

Warning : Make sure you don't spawn too many abstractions as this can reduce code readability and make you spend time thinking about things that aren't necessary and provide little code reuse.

4
  • 3
    This answer provides a good example of why in this case, the more general reasoning it that this approach make the software more flexible with regard to enhancement and is closely related to the Open Closed principle en.wikipedia.org/wiki/Open%E2%80%93closed_principle Commented Oct 14, 2018 at 20:00
  • The problem is, I don't see why different subclasses of Author need to exist. The types you provide in your first point could just as easily be stored in a List collection called genres, to indicate that one author might write multiple types of books.
    – user315575
    Commented Oct 14, 2018 at 21:18
  • 1
    @BasementJoe This is getting contrived because this example is conflating data and functionality, but imagine that the company's ID for an author has a unique prefix depending on their main genre. If you have a mainGenre property, you need one function for GetPrefix that has a big switch-case statement depending on their mainGenre, that has to be updated anytime a new genre comes out or any prefix is changed. On the other hand, with different subclasses all implementing their own GetPrefix, it's easy to add a new genre or update an existing one with minimal chance of impact. Commented Oct 14, 2018 at 22:23
  • @BasementJoe I see your concern. Let me elaborate on the scenario I was trying to highlight. Assume you have a method findLastElement(List list). Here List is an interface that can either be substituted by an ArrayList or a LinkedList, making this method more general than if it was findLastElement(ArrayList list) instead. Commented Oct 15, 2018 at 1:53

Your Answer

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