显示的吸气剂
我正在研究getters/setters
,一般的想法是它们是邪恶的,应该避免。 您应该让对象完成工作,并生成结果。
阅读材料:
为什么getter和setter方法是邪恶的
吸气剂和二传手设计不佳吗? 看到矛盾的建议
为什么要使用getter和setter?
考虑到所有这些,假设我有一个看起来像这样的Book class
:
publc final Book{ private final String title; private final List 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
(例如JavaFX
, webpage
等等)并希望我的课程变得灵活,那么包括title
的getters
, listofAuthors
和用于显示目的的numberofpages
是否会打破encapsulation
?
例:
//library is an List 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 } }
问题:
-
在循环中调用
getTitle()
或getAuthors()
打破encapsulation
? -
如果上述问题的答案是肯定的,如果
isWrittenBy()
返回true
,我将如何显示该书? 我如何收集我图书馆的所有作者?
对象的属性与对象的实现细节之间存在差异。
- 一本书有一个标题 – 这不是一个实现细节。
- 一本书有作者 – 这不是一个实现细节。
- 如何存储图书的作者 – 这可以是一个实现细节。
吸气剂并不邪恶,但您必须小心使用它们,因为它们可能会暴露实施细节,从而限制您的实施变更。
class Book { private ArrayList authors = new ArrayList<>(); public ArrayList getAuthors() { return authors; } }
由于两个原因,上述情况很糟糕。 首先,它向调用者返回一个可修改的集合,允许调用者更改集合。 其次,它向调用者承诺返回的集合是ArrayList
。
Book book = ...; ArrayList authors = book.getAuthors();
我们通过包装集合来解决第一个问题:
class Book { private ArrayList authors = new ArrayList<>(); public List getAuthors() { return Collection.unmodifiableList(authors); } } Book book = ...; List authors = book.getAuthors();
现在调用者无法修改集合,但他们仍然承诺集合是一个List
。 如果我们发现我们想要将我们的作者存储为Set
(因为作者不会多次创作一本书),我们不能简单地更改内部存储。
class Book { private HashSet authors = new HashSet<>(); public List getAuthors() { return Collection.unmodifiableList(authors); // ERROR - authors not a list } }
我们必须将作者收集到一个新List
,或者更改getAuthors()
的签名,但这会影响期望返回List
的调用者。
相反,代码应该只返回一个Collection
。 这不会暴露authors
存储的实现细节。
class Book { public Collection getAuthors() { return Collection.unmodifiableCollection(authors); } }
注意:可能是作者被命令,首先列出主要作者。 在这种情况下,您可能希望返回一个List
以向调用者承诺数据实际上是有序的。 在这种情况下, List
不是实现细节,而是接口契约的一部分。
那么, getTitle()
打破了封装? 不,绝对不是。
getAuthors()
是否会破坏封装? 取决于它的回报,以及你的承诺。 如果它返回一个Collection
,那么没有。 如果它返回一个List
并且你不承诺有序的结果,那么是的。
有一种思想流派认为,大量的get / set方法显示出一种粗心的面向对象设计。 如果您的Book
对象没有相应的getXXX
方法,您会问如何显示“book”数据。 据推测,答案是你告诉你的书要展示自己:
MyUserInterface interface=...; ... book.renderToUserInterface (interface);
我认为在争论中有一些力量过度使用get / set方法 – 我认为一个好的设计会使程序化类成为他们所代表的真实世界事物的良好模型,现实世界的事物通常不仅仅是可以设置和查询的离散数据项的存储库。 我认为我们确实倾向于认为类(在Java和其他语言中)只不过是用于数据结构,而且它们可能不止于此。
然而,在我看来,获取/设置方法是“邪恶”的想法太过分了。 这些方法的教条避免不太可能有用,特别是当大多数OO程序中发生的很多事情真正支持围绕模拟域对象的类的基础设施时。 此外,在考虑应用程序的完整OO模型时,“使对象完成工作”是最有意义的。 例如,在单个对象的设计层面上,为了在通用库中使用,我认为我们必须有点务实。
我似乎记得“让对象做好工作”是十年前的一个热门想法,但我不相信这是一个如此重要的事情。
这只是对一些不同但相关(有时是高级)主题的辅助评论,并不是问题的直接答案(在其他答案中已经得到了很好的回答)。 也许这里的信息与此处或您的问题无关,如果是,请忽略它:-)
关于不变性,即不添加塞特犬
Getter通常很好,因为调用者无法更改对象,但是不必要的setter会使得更难以推理对象,尤其是在编写并发代码时。
当你有setter时,对象更改的来源可能来自更广泛的区域,另一个类甚至另一个线程。
请参阅Java教程中的不可变对象和带有setter的同步示例,这些示例显然更复杂,并且可能更容易出错而不具有setter的不可变版本 。
JavaFX Color类是JDK中不可变类的示例。
通常,当我创建一个类时,我通常会尝试将所有输入放入构造函数参数中,使类成为final,而不是添加setter并使类的所有成员都成为final。 你不能总是这样做,但是当你可以的时候,它似乎让我更容易编程。
请注意,setter通常是有用且必要的,因此如果您需要它们,请不要担心添加它们,不变性只是有时需要的属性而不是规则。
关于桥接数据模型和显示
如果您真的对从对象中显示数据的不同模式和约定感兴趣,那么请阅读Martin Fowler的GUI架构,因为它对此给出了非常好的概述。
通常,您希望从数据和域对象以及UI中分离关注点 。 对于小型应用程序,您不需要这种分离,这只是更多的开销。 但是,只要您开始开发更大的应用程序,如果您完全将UI与域模型分开,它通常会变得更好。 通常, observable有助于做到这一点,通常与MVC , MVP或MVVM等设计模式相结合。
在JavaFX属性和绑定到显示
如果您在问题中提到使用JavaFX,则属性和绑定会提供可观察的界面,以便您的域对象中的更改可以自动反映在UI中。 您可以直接在数据和域类中定义属性。 您可以使用FXML和FXML Controller等定义UI。 在控制器中,您可以将UI的属性(例如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()
和只读属性 。
private final ReadOnlyDoubleWrapper size = new ReadOnlyDoubleWrapper(); public final double getSize() { return size.get(); } public final ReadOnlyDoubleProperty sizeProperty() { return size.getReadOnlyProperty(); }