显示的吸气剂

时间:2017-10-10 17:13:23

标签: java oop encapsulation

我正在研究getters/setters,一般的想法是它们是邪恶的,应该避免。您应该让对象完成工作,并生成结果。

阅读材料:

Why getter and setter methods are evil

Are getters and setters poor design? Contradictory advice seen

Why use getters and setters?

考虑到所有这些,假设我有一本看起来像这样的书class

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
    }

}

如果我有一个UI(例如JavaFXwebpage等),并希望我的课程变得灵活,那么getters包括title 1}},listofAuthorsnumberofpages用于显示目的,请中断encapsulation

示例:

//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());
    }
}

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

问题:

  1. 在循环中调用getTitle()getAuthors() encapsulation

  2. 如果上述问题的答案是肯定的,那么如果isWrittenBy()返回,我将如何显示该书 true?我如何收集我图书馆的所有作者?

3 个答案:

答案 0 :(得分:5)

有一种思想流派声称大量的get / set方法显示出粗心的面向对象设计。如果您的Book对象没有相应的getXXX方法,您会询问如何显示“图书”数据。大概是答案是你告诉你的书要展示自己:

MyUserInterface interface=...;
...
book.renderToUserInterface (interface);

我认为在争论中有一些力量过度使用get / set方法 - 我认为一个好的设计会使程序化类成为他们所代表的真实世界事物的良好模型,而现实世界的事物通常都不是这只是可以设置和查询的离散数据项的存储库。我认为我们确实倾向于认为类(在Java和其他语言中)只不过是一些数据结构,而且它们可能不止于此。

然而,在我看来,获取/设置方法是“邪恶”的想法太过分了。这些方法的教条避免不太可能有用,特别是当大多数OO程序中发生的很多事情真正支持围绕模拟域对象的类的基础结构时。此外,在考虑应用程序的完整OO模型时,“使对象完成工作”是最有意义的。例如,在用于通用库的个人对象的设计级别,我认为我们必须有点务实。

我似乎记得“让对象做好工作”是十年前的一个热门想法,但我不相信这是一个如此重要的事情。

答案 1 :(得分:1)

对象的属性与对象的实现细节之间存在差异。

  • 一本书有一个标题 - 这不是一个实现细节。
  • 一本书有作者 - 这不是一个实现细节。
  • 如何存储图书的作者 - 这可以是一个实现细节。

Getters不是邪恶的,但你必须小心使用它们,因为它们可以暴露实施细节,限制你的实现的变化。

class Book {
    private ArrayList<Author> authors = new ArrayList<>();

    public ArrayList<Author> getAuthors() { return authors; }
}

由于两个原因,上述情况很糟糕。首先,它向调用者返回一个可修改的集合,允许调用者更改集合。其次,它向调用者承诺返回的集合是ArrayList

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

我们通过包装集合来解决第一个问题:

class Book {
    private ArrayList<Author> authors = new ArrayList<>();

    public List<Author> getAuthors() {
        return Collection.unmodifiableList(authors);
    }
}

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

现在调用者无法修改集合,但他们仍然承诺集合是List。如果我们发现我们想要将作者存储为Set(因为作者不会多次创作一本书),我们就不能简单地更改内部存储。

class Book {
    private HashSet<Author> authors = new HashSet<>();

    public List<Author> getAuthors() {
        return Collection.unmodifiableList(authors); // ERROR - authors not a list
    }
}

我们必须将作者收集到新的List中,或更改getAuthors()的签名,但这会影响期望返回List的来电者。< / p>

相反,代码应该只返回Collection。这不会公开authors

存储的实现细节
class Book {
    public Collection<Author> getAuthors() {
        return Collection.unmodifiableCollection(authors);
    }
}

注意:可能是作者被命令,首先列出主要作者。在这种情况下,您可能希望返回List以向调用者承诺数据实际上是有序的。在这种情况下,List不是实现细节,而是接口契约的一部分。

那么,getTitle()是否打破了封装?不,绝对不是。

getAuthors()会破坏封装吗?取决于它的回报,以及你所承诺的。如果它返回Collection,那么没有。如果它返回List并且你不承诺有序的结果,那么是。

答案 2 :(得分:1)

这只是对一些不同但相关(有时是高级)主题的辅助评论,并不是问题的直接答案(在其他答案中已经得到了很好的回答)。也许这里的信息与此处或您的问题无关,如果是这样,请忽略它: - )

On Immutability,即Just Not Adding Setters

Getter通常很好,因为调用者无法更改对象,但是不必要的setter会使得更难以推理对象,尤其是在编写并发代码时。

当你有setter时,对象更改的来源可能来自更广泛的区域,另一个类甚至另一个线程。

请参阅Java教程中的Immutable Objectssynchronized sample with setters,这与immutable version without setters相比更加复杂且容易出错。

JavaFX Color类是JDK中不可变类的一个示例。

通常,当我创建一个类时,我通常会尝试将所有输入放入构造函数参数中,使类成为final,而不是添加setter并使类的所有成员都成为final。你不能总是这样做,但是当你可以的时候,它似乎让我更容易编程。

请注意,设置器通常是有用且必要的,因此如果您需要它们,请不要担心添加它们,不变性只是有时需要的属性而不是规则。

关于桥接数据模型和显示

如果您真的对从显示对象获取数据的不同模式和惯例感兴趣,请阅读Martin Fowler的GUI Architectures,因为它对此有了很好的概述。

通常,您需要来自数据和域对象以及UI的separation of concerns。对于小型应用程序,您不需要这种分离,这只是更多的开销。但是,只要您开始开发更大的应用程序,如果您将UI与域模型完全分开,通常会更好地组织。通常,observables有助于执行此操作,通常与MVCMVPMVVM等设计模式相结合。

关于JavaFX属性和绑定到显示

如果您使用JavaFX,正如您在问题中提到的那样,properties and binding提供了一个可观察的界面,因此您的域对象中的更改可以自动反映在您的UI中。您可以直接在数据和域类中定义属性。您可以使用FXML and an FXML Controller之类的内容定义UI。在控制器中,您可以将UI的属性(例如the text string of a Label)绑定到数据和域对象中的属性。这样,如果您在内部更改域中属性的值,例如在库中的书籍数量,您可以将该数字公开为属性,无论何时添加新书,都会在UI中自动更新(无需在添加书籍时明确告知UI更新标签)。

例如,在您的LibraryController中,您可能有:

@FXML
Label numberOfBooksInLibraryLabel;
Library library = new Library();

...

numberOfBooksInLibraryLabel.textProperty().bind(
    library.books().sizeProperty().asString()
);     

关于JavaFX属性和封装

JavaFX有一些围绕JavaFX属性的约定,它们比没有JavaFX的普通Java更复杂和微妙。要帮助封装和JavaFX属性,请考虑使用FXCollections.unmodifiableObservableList()read only properties

private final ReadOnlyDoubleWrapper size = new ReadOnlyDoubleWrapper();

public final double getSize() {
    return size.get();
}

public final ReadOnlyDoubleProperty sizeProperty() {
    return size.getReadOnlyProperty();
}