Search code examples
javaoopencapsulation

Getters for Display


I was researching on getters/setters, and the general idea is that they are evil and should be avoided. You should let the object do the work, and produce the result.

Reading Material:

Why getter and setter methods are evil

Are getters and setters poor design? Contradictory advice seen

Why use getters and setters?

With all that in mind, suppose I have a Book class that looked like this:

publc final Book{

    private final String title;
    private final List<Authors> listofAuthors;
    private final int numberofpages;

    //constructor not included in example.

    //getters for title, listofAuthors, and numberofpages

    public int numberofpagesleft(int pagesRead){
        //code to determine number of pages left
    }

    public boolean isWrittenBy(Author author){
        //code to determine if book contains author
    }

}

If I had a UI(ex. JavaFX, webpage, etc..) and wanted my class to be flexible, would including the getters for title, listofAuthors and numberofpages for display purposes break encapsulation?

Example:

//library is an List<Book> collection, and author is an object of the Author class.
for (Book book : library){
    if(book.isWrittenBy(author){
      System.out.println("Books in your collection written by " + author.toString() + " include:" );
      System.out.println(book.getTitle());
    }
}

or

for (Book book : library){
    //returns a Collections.unmodifiableList().
    if(book.getAuthors(){
        //code to build a set of authors in my library
    }
}

Questions:

  1. Is calling getTitle() or getAuthors() in the loop breaking encapsulation?

  2. If the answer to the above question is yes, how would I display the book if isWrittenBy() returns true? and How would I gather all the authors in my library?


Solution

  • There is a difference between attributes of an object, and implementation details of an object.

    • A book has a title - that is not an implementation detail.
    • A book has authors - that is not an implementation detail.
    • How authors of a book are stored - this can be an implementation detail.

    Getters are not evil, but you must use them carefully, since they can expose implementation details which restrict changes to your implementation.

    class Book {
        private ArrayList<Author> authors = new ArrayList<>();
    
        public ArrayList<Author> getAuthors() { return authors; }
    }
    

    The above is bad, for two reasons. First, it returns a modifiable collection to the caller, allowing the caller to change the collection. Second, it promises the caller that the returned collection is an ArrayList.

    Book book = ...;
    ArrayList<Author> authors = book.getAuthors();
    

    We fixing the first problem by wrapping the collection:

    class Book {
        private ArrayList<Author> authors = new ArrayList<>();
    
        public List<Author> getAuthors() {
            return Collection.unmodifiableList(authors);
        }
    }
    
    Book book = ...;
    List<Author> authors = book.getAuthors();
    

    Now the caller can't modify the collection, but they are still promised the collection is a List. If we find we want to store our authors as a Set (because an author doesn't author a book more than once), we cannot simply change the internal storage.

    class Book {
        private HashSet<Author> authors = new HashSet<>();
    
        public List<Author> getAuthors() {
            return Collection.unmodifiableList(authors); // ERROR - authors not a list
        }
    }
    

    We would have to collect the authors into a new List, or change the signature of the getAuthors(), but that would impact the callers who expected a List to be returned.

    Instead, the code should simply return a Collection. This does not expose the implementation detail of the storage of authors.

    class Book {
        public Collection<Author> getAuthors() {
            return Collection.unmodifiableCollection(authors);
        }
    }
    

    Note: it could be that the authors are ordered, with the primary author listed first. In that case, you may want to return a List to promise the caller that the data is, in fact, ordered. In that case, the List is not an implementation detail, but rather part of the interface contract.

    So, does getTitle() break encapsulation? No. Absolutely not.

    Does getAuthors() break encapsulation? Depends what it returns, and what you are promising. If it returns a Collection, then no. If it returns a List and you are not promising ordered results, then yes.