4

我会尽量保持简短。

我有很多实例变量(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 将这种概念称为类型安全的异构容器

4

2 回答 2

8

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

我们在 3 年内取得了进展。我们现在有更好的语言。Swift、Rust、Kotlin、Go 等。我们了解数据/值类型与操作它的代码之间的区别。适当的企业 CLEAN 架构在 Java 领域提倡这一点。但是 Java 只是没有为这种模式提供语言级别的支持。最终结果是大量使用(仍然很棒)诸如 RxJava 之类的东西,以及进行代码生成的注解处理器等。但是这些天很难愉快地拖着 Java 的遗产。Kotlin 倾向于以 Java 无法以极低的代价解决 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();
    }
}

只需直接访问成员并为自己节省数百行代码(就像您建议的那样)。

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

二:

设计坦克..

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

显然,上面OtherClass的内容完全没用,因为它的功能可以(并且应该)放在Article类中,而不是包含在其他一些无用的、不需要的文件系统乱扔垃圾OtherClass中。如果你这样做了,你甚至可以忘记需要 getter 和 setter OtherClass因为与之交互的东西可能只需要文章内容而不是单独的标题、正文等。在这种方法中,Article类对外界隐藏了一切,只提供绝对需要的信息。

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

使用 Clojure

建模对象

尽管您可以使用闭包来为对象建模,但还有更好的方法。有趣的是,在将函数视为一等公民的语言中,您可以完全使用映射对传统的面向对象范式进行建模——正如您在重构 30 多个成员字段类系统时开始发现的那样被给予。

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

(defn Article []
  (let [id (atom nil)
        guid  (atom nil)
        title  (atom nil)
        link (atom nil)
        pubdate (atom nil)
        category (atom nil)
        description (atom nil)
        body (atom nil)
        comments (atom nil)

        set (fn [g t l p cg d b cm]
              (do (reset! guid g)
                  (reset! title t)
                  (reset! link l)
                  (reset! pubdate p)
                  (reset! category cg)
                  (reset! description d)
                  (reset! body b)
                  (reset! commments cm)))
        get (fn [] [@guid
                    @link
                    @description
                    @comments
                    :content (content)])

        content #(str title category pubdate body)]
    {:get get, :set set}))

上面的这个例子是一个系统,它从两个答案中获取点并将它们组合成一个隐藏不需要的成员,结合逻辑功能(获取“内容”的功能),并使用不需要大量闪亮样板的语言代码..

替换类/对象系统

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

2017 更新

只需使用Kotlin 即可。它正在全面治愈 Java 病。它对所有这些东西都有一流的语言支持,编译器甚至会帮助你摆脱无用的样板。它已经存在了 7 年多,并且自 2016 年 2 月以来一直在稳定发布。如果我一开始就知道它,我可能不会包括 Closure。

于 2013-04-26T08:22:46.200 回答
3

也许您正在寻找类似Lambok的东西。

于 2013-04-25T22:36:43.903 回答