Encapsulation and Getters

2019-01-27 00:49发布

问题:

I was reading this article on why getter and setters are evil. The article doesn't say not to use them ever, but, it's telling you to think in a way that limits the use of those methods, or to quote the article:

Don't ask for the information you need to do the work; ask the object that has the information to do the work for you.

what happens when you need to display data in a GUI, but don't have getter methods? The article covers this briefly, but not fully. It mentions passing a JComponent to the class, but if you're GUI changes, it could lead to a lot of work to fix.

Take for example, you have a Book class (making this example limited to keep it readable).

public final class Book {

    private String title;
    //Authors is class with the attributes authorFirstname, authorLastname
    private List<Author> listofAuthors;


    public Book(String title, List<Author> listofAuthors)
    {
        //initialization
    }

    //other methods that do work
}

If I have a GUI that has a JTextField to display the book title and a JTable to display the list of authors, how would I write my method to "do the work" for me and display the result? Is this one of those times where a getter is necessary?

回答1:

Allen Holub's article (the one you mentioned) is completely right, you shouldn't ask for data, at least when you're doing Object-Orientation. And no, displaying things is not a valid excuse to open up an object.

If you have a Book, just ask for the Book to display itself! It shouldn't matter whether that uses a JTextField or JTable or whatever. Depending on your requirements of course, you could do:

public final class Book {
    ...
    JComponent display() {
        ...
    }
}

The point of Object-Orientation is of course, that you are trying to localize changes (restrict to one class as much as possible). The only way to do that is to localize the functionality that depends on the same things into (preferably) the same class. Also called increasing "cohesion".

So now, if the Book internals change, all of the things, including how the Book is displayed is in the Book itself, so there is no need to "hunt" for code that uses the Book.

Now, for the answer that this is not "clean", because you are mixing presentation code with "business logic". It may be interesting to note, that the whole idea of not mixing presentation with "business logic" comes from earlier times, when we still thought that presentation might be "remote" to the "business objects", where "business objects" might be used by multiple applications for different things. Ie. multi-tier designs. YAGNI. Most of the time there is no real reason to have artificial technical boundaries inside a single application. There is no harm done if the Book knows it's part of a GUI application, and there are real benefits (maintainability) to have.

Edit: this is how the `display() method could look like in detail, with displaying the title and authors (pseudocode for Swing):

public final class Book {
    private final String title;
    private final List<Author> authors;
    ...
    public JComponent display() {
        JPanel bookPanel = new JPanel();
        bookPanel.add(new JLabel(title));
        JList authorsList = new JList(); // Or similar
        for (Author author: authors) {
            authorsList.add(author.display());
        }
        bookPanel.add(authorsList);
        return bookPanel;
    }
}

And then you can simply add() that component to whatever swing container you want to display the book in.



回答2:

Think of it this way: Getters (public functions) are a bridge for private attributes.

I'll write you a simple example to modify your TextField using OOP.

Book class:

public final class Book {
private String title;
//Authors is class with the attributes authorFirstname, authorLastname
private List<Author> listofAuthors;


public Book(String title, List<Author> listofAuthors)
{
    //initialization
}
public String getTitle() {
return this.title; }
}

GUI:

author1 = new Author("jhon");
author 2 = new Author("alsojhon");
list = new ArrayList();
list.add(author1);
list.add(author2)
b = new Book("stack",list);
JTextField field;
field.setText(b.getTitle());


回答3:

You can create three kind of classes:

  1. Entity, classes that represents a business concept and have only one unique id, like Client class with Id the username. It is usually a mutable class. You should have all the business logic here. You should not open his data with getters and setters.

  2. Value object, classes that represents a business concept but not have an unique id, like Email class. It is usually an imm.utable class. You should have all the business logic here.

  3. Data structure (kind of DTO), classes to save data only, without behavior, maybe you have setters and getters to access those datas.

What can I do if I need to access all Client data if I do not have accesors? Well, you should transform Client to a DTO. You can use a framework like Orika. Or you can create a method into Client class to ask for information (mediator pattern).

I like second option but it implies more work:

class Client{
    private String name;
    ...
   public void publishInfo(ClientInfo c){
      c.setName(name);
      ...
    }
}

class ClientInfo{
    private String name;
    //GETTERS
    //SETTERS
}


回答4:

Are you asking about a method to display all the information for you? I'm not following.

If that is what you're asking, here's my recommendation: Don't write it in the class with the window code in it. It won't be very clean. Make a new class with that method in it (Yes, getters are necessary, they make OOP easier, but that's biased).

What I do if I want to iterate through a list is make a StringBuilder, along with a for loop, that adds the name of the author. Then, have the method return the list of authors.

List<Authors> a;
StringBuilder d = new StringBuilder();
for (int i = 0; i < a.size(); i++) {
    d.append(a.get(i).getName() + ", ");
}
return d.toString();
//It's just sudo code, but still.