如何避免具有许多实例变量的类中的getter / setter

时间:2013-04-25 22:12:51

标签: java setter getter code-duplication

我会尽量保持简短。

我有许多实例变量(30+)的类,因此有许多getter / setter。这些类本身很简单,但由于getter / setter,LOC刚刚爆炸(而且代码也存在太多差异)。

所以我删除了属性并将它们存储在地图中,就像这样

public class MyTechnicalToolClassX
{

//...constructor

private Map<String, Object> data;

public Object getAttributeByKey(AttributeKey key)
{
    // ...doStuff, check data associated with key, etc
    // if (predicate == true) return otherData;
    return data.get(key.toString());
}

public void setAttributeByKey(AttributeKey key, Object value) throws IllegalArgumentException
{
    if(key.getType().isInstance(value))
    {
        data.put(key.toString(), value);
    }
    else
    {
        throw IllegalArgumentException("The passed value has the wrong type, we expect: "
        + key.getType().getName());
    }
}

public enum AttributeKey
{
    someKey1("key1", String.class),
    someKey2("key2", Date.class),
    //...
    someKeyN("keyN", SomeType.class);

    private String key;
    private Class valueType;

AttributeKey(String key, Class valueType)
{
    this.key = key;
    this.valueType = valueType;
}

@Override
public String toString()
{
    return key;
}

public Class getType()
{
    return valueType;
}

} // AttributeKey

} // MyTechnicalToolClassX

AttributeKey曾经只是一个字符串,但这样我可以确保setter中的类型安全。 现在我的问题是,我删除了类中的代码重复性,但我有其他类也有很多属性(因为它们代表了一些技术对象......),这里最好的方法是什么?为每个类提供自己的AttributeKey枚举?

我补充了一些想法。我现在在编译时有类型安全性。这是我的getter和setter的新界面。

public <Type, SetVal extends Type> void setAttributeByName(IAttribute<Key, Type> attribute, SetVal value);

public <Type> Type getAttributeByName(IAttribute<Key, Type> attribute);

Joshua Bloch称这种概念为typesafe heterogeneous container

2 个答案:

答案 0 :(得分:7)

忘记你在OOP学校学到的东西!

我们已经有3年了。我们现在有更好的语言。 Swift,Rust,Kotlin,Go等。我们理解数据/值类型与操作它的代码之间的区别。适当的企业CLEAN架构在Java领域提倡这一点。但Java只是不为这种模式提供语言级支持。最终的结果是大量使用(仍然很棒)RxJava,以及进行代码生成等的注释处理器。但是现在很难高兴地拖延Java的遗产。 Kotlin倾向于以一种非常非常最低价格的方式解决Java的问题:缺乏严格的源兼容性(它不是Groovy)。

原始答案,底部有更新。

有两个答案。

一:

无用的锅炉!

如果类表示某个大型数据对象,则听起来大多数成员变量只是数据的容器。在这种情况下,严格遵循OOP信息隐藏惯例并不重要。人们经常混淆这个惯例的目的,最终滥用它。它只是为了防止程序员不得不处理对象的复杂和不必要的内部工作。而不是掩盖整个对象,只是掩盖不应该被搞乱的部分。如果您只是从数据库映射信息或充当存储容器,请执行以下代码:

import java.util.Date;

public class Article {

    protected int id;

    protected String guid;
    protected String title;
    protected String link;
    protected Date pubDate;
    protected String category;
    protected String description;
    protected String body;
    protected String comments;

    protected Article (String articleTitle, String articleBody) {
        title = articleTitle;
        body = articleBody;
    }

    protected Article (String guid, String articleTitle, String articleLink,
            long publicationDate, String category, String description,
            String articleBody, String commentsLink) {
        this(articleTitle, articleBody);
        this.guid = guid;
        this.link = articleLink;
        this.pubDate = new Date(publicationDate);
        this.category = category;
        this.description = description;
        this.comments = commentsLink;
    }

    protected Article (int id, String guid, String articleTitle, String articleLink,
            long publicationDate, String category, String description,
            String articleBody, String commentsLink) {
        this(guid, articleTitle, articleLink, publicationDate, 
                category, description, articleBody, commentsLink);
        this.id = id;
    }

    protected int getId() {
        return id;
    }

    protected String getTitle() {
        return title;
    }

    protected String getGuid() {
        return guid;
    }

    protected String getLink() {
        return link;
    }

    protected String getComments() {
        return comments;
    }

    protected String getCategory() {
        return category;
    }

    protected String getDescription() {
        return description;
    }

    protected String getBody() {
        return body;
    }

    protected void setId(int id) {
        this.id = id;
    }

    protected void setGuid(String guid) {
        this.guid = guid;
    }

    protected void setTitle(String title) {
        this.title = title;
    }

    protected void setLink(String link) {
        this.link = link;
    }

    protected void setPubDate(Date pubDate) {
        this.pubDate = pubDate;
    }

    protected void setCategory(String category) {
        this.category = category;
    }

    protected void setDescription(String description) {
        this.description = description;
    }

    protected void setBody(String body) {
        this.body = body;
    }

    protected void setComments(String comments) {
        this.comments = comments;
    }

}

..完全可恶。

在这种情况下,实际上没有理由只是为了访问数据对象的成员而完成所有额外的工作。特别是如果你只是在一些外部代码行中使用它们:

public OtherClass {

    private Article article;

    public OtherClass(Article data) {
        article = data;
    }

    public String getArticleContents() {

        return (new StringBuilder())
        .append(article.getTitle())
        .append(article.getCategory())
        .append(dateToString(article.getPubDate())
        .append(article.getBody())
        .toString();
    }
}

只需直接访问会员并自行保存数百行代码(就像您建议您尝试的那样)。

这导致了这个问题的第二个答案。

两个

Design Stank ..

您的代码可能正在腐烂。拉姆齐厨师会感到羞耻。原因如下:

显然上面的OtherClass完全没用,因为它的功能可以(并且应该)放在Article类中,而不是包含在其他一些无用的,不需要的文件系统中。{{{ 1}}。如果你这样做,你甚至可以忘记甚至需要getter和setter OtherClass,因为与它连接的东西可能只需要文章内容而不是标题,身体等,分开。在这种方法中,OtherClass类隐藏了来自外部世界的所有内容,只提供绝对需要的信息。

由于这些是您问题的两个完全可行的答案,因此必须有一个解决方案 的Ĵ 上。

使用Clojure

建模对象

虽然你可以use closures to model objects,但还有更好的方法。有趣的是,在一种将功能视为一等公民的语言中,您可以完全使用地图对传统的面向对象范式进行建模 - 当您开始在重构30多个成员字段类系统时弄清楚被给予。

将此与原Article + Article方法进行比较:

OtherClass

以上考试是一个系统,它从两个答案中获取分数,并将它们组合成一个隐藏不需要的成员,合并逻辑功能(获取'内容'的功能),并使用不需要令人难以置信的金额的语言的系统闪亮的样板代码..

更换类/对象系统

虽然这是如何使用函数式语言对对象进行建模的一个很好的例子,但它对于Clojure和函数式编程来说并不完全是惯用的。要更简单地处理结构化数据,请查看Clojure StructMapsRecords

2017年更新

只需使用Kotlin即可。它正在全面治愈Java疾病。它为所有这些东西提供了一流的语言支持,编译器甚至可以帮助你摆脱无用的样板。自2016年2月以来,它已经存在超过7年并且处于稳定版本。如果我最初了解它,我可能不会包括Closure。

答案 1 :(得分:3)

也许您正在寻找类似Lambok的内容。