0

有一次我在一家大型科技公司工作。我们用 Java 编程。那里的人非常聪明,他们喜欢用 Java 中的 C/C++ 模拟结构。为了清楚起见,他们提倡创建充当“数据持有者”的类:

public class BookInformation {
    public final List<String> authors;
    public final String bookTitle;
    public BookInformation(List<String> authors, String bookTitle) {
        this.authors = authors;
        this.bookTitle = bookTitle;
    }
    @Override
    public String toString() { ... }

    @Override
    public int hashCode() { ... }

    @Override
    public boolean equals(Object obj) { ... }
}

正如我们所见,这个类的目的只是保存数据。尽管我们违反了许多企业编程规则,例如不直接暴露类字段和防御性复制,但这种类确实获得了简洁编码等好处。我们可以直接调用字段名称,而不是调用 getter 和 setter。

让我烦恼的是这种类很难维护和扩展。很难维护,因为无法确保对象的状态是合法的。业务逻辑可能会说,一本书必须有一个标题和至少一个作者。但在现实世界中,对象可以有空标题、空标题、空作者列表或空作者列表,我们根本无法控制它。很难扩展。例如,如果将数据源更改为为作者姓名分别提供名字、姓氏怎么办?我必须使用两个字符串列表而不是一个。更糟糕的是,数据结构的变化会影响和接口。由于我没有 getAuthorNames() 接口,我可能不得不在很多地方更改代码。

我不得不承认,上述情况并没有发生。我不得不承认,使用该类的所有代码都在团队的控制之下,因此界面更改听起来不像为其他团队/公司编写使用那样糟糕。那么即使我们使用Java这种纯OO语言进行企业级编码也可以有这样的编码标准吗?

我知道可能没有“正确”的答案。我想听听个人意见。代表自己大声说话!

编辑:

好的。我应该重新表述我的问题的核心:牺牲一些教科书的编码规则来获得简单性是否明智?当代码库增长和团队增长时,牺牲会咬你吗?个人意见很重要,尤其是来自智者,在很多情况下,没有对错之分,我们都只是遵循令人信服的意见。很抱歉,Stackoverflow 仅针对正确和错误的问题而设计。在那种情况下,这个问题应该被关闭。

4

2 回答 2

4

我认为有一个正确的答案:如果它适合你,那就去吧。没有时尚警察等着逮捕你。

了解规则及其背后的原因。当你打破它们时,了解后果。为你所做的事有充分的理由。忍受发生的事情。

例如,我认为从不公开数据的规则不需要是绝对的——只要你做得正确。

你需要意识到你做错了。

您将 List 引用设为 final 的事实只是意味着无法更改引用。引用所指的 List 可以添加和删除元素。你必须让它不可变才能实现你想要的。

如果您不使其不可变,则您对传递给构造函数的引用所做的更改将反映在您的对象中。即使您将该引用设为私有,这也是正确的。您必须复制您传入的 List 以使您的对象的状态真正独立。与其他对可变类型的引用相同,例如 Date。

它适用于字符串,因为它是不可变的。

还有一件事:应该是equals(),不是Equals()。Java 中的大小写很重要。

public final class BookInformation {
    public final List<String> authors;
    public final String bookTitle;
    public final Date publicationDate;

    public BookInformation(List<String> authors, String bookTitle, Date publicationDate) {
        this.authors = Collections.unmodifiableList((authors == null) ? new ArrayList<String>() : authors);
        this.bookTitle = (StringUtils.isBlank(bookTitle) ? "" : bookTitle);
        this.publicationDate = ((publicationDate == null) ? new Date() : new Date(publicationDate.getTime()));
    }
    @Override
    public String toString() { ... }

    @Override
    public int hashCode() { ... }

    @Override

   public boolean equals(Object obj) { ... }
}
于 2013-01-09T01:42:06.077 回答
0

我发现标准的 getter/setter 有时是不必要的冗长,但有一些方法可以解决这个问题。

例如,您可以使用与字段同名的 getter 或 setter,并且可以创建比直接访问字段更简洁的操作方法。

例如

public class BookInformation {
    private final Set<String> authors = new LinkedHashSet<>();
    private final String bookTitle;

    public BookInformation(List<String> authors, String bookTitle) {
        assert authors != null;
        assert bookTitle != null;

        for(String author: authors) addAuthor(author);
        this.bookTitle = bookTitle;
    }

    public String bookTitle() { return bookTitle; }

    // get all the authors without needing a defensive copy.
    // for(int i = 0, len = bookInfo.authorCount(); i < len; i++) {
    //     String name = bookInfo.author(i);

    public int authorCount() { return authors.size(); }
    public String author(int n) { return authors.get(n); }

    public void addAuthor(String name) {
        assert name != null
        authors.add(name);
    }

class AuthorCounter {
    private final ConcurrentMap<String, AtomicInteger> authorCountMap =
                  new ConcurrentHashMap<>();

    public void addAuthor(String name) {
        authorCountMap.putIfAbsent(name, new AtomicInteger());
    }

    public void incrCountFor(String name) {
        authorCountMap.get(name).incrementAndGet();
    }

    public int countForAuthor(String name) {
        AtomicInteger ai = authorCountMap.get(name);
        return ai == null ? 0 : ai.get();
    }
}
于 2013-01-09T09:02:00.793 回答