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

2024-03-17

我会尽量保持简短。

我的类有很多实例变量(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 枚举?

SOLUTION

我补充了一些更多的想法。我现在在编译时具有类型安全性。这是我的 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) 将这种概念称为.


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

三年来我们取得了长足的进步。我们现在有更好的语言。 Swift、Rust、Kotlin、Go 等。我们了解数据/值类型和操作它的代码之间的区别。正确的企业 CLEAN 架构在 Java 领域提倡这一点。但 Java 并不为此类模式提供语言级支持。最终结果是大量使用(仍然很棒)诸如 RxJava 之类的东西,以及进行代码生成的注释处理器等。但是现在很难高兴地拖着 Java 的遗产。 Kotlin 倾向于以 Java 无法解决的方式解决 Java 的问题very最低代价:失去严格的源兼容性(不是 Groovy)。

原始答案,底部有更新。

有两个答案。

One:

无用的样板!

如果类代表一些大数据对象,那么听起来大多数成员变量只是数据的容器。当出现这种情况时,严格遵循信息隐藏的 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();
    }
}

只需直接访问成员即可节省数百行代码(就像您建议您尝试做的那样)。

这引出了这个问题的第二个答案。

Two:

设计臭..

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

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

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

使用 Clojure

对象建模

虽然你可以使用闭包来建模对象 http://pramode.net/clojure/2010/05/26/creating-objects-in-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结构图 http://clojure.org/data_structures#Data%20Structures-StructMaps and Records http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/defrecord.

2017年更新

只需使用Kotlin https://kotlinlang.org/。它正在全面治愈爪哇病。它对所有这些事情都有一流的语言支持,编译器甚至可以帮助您摆脱无用的样板文件。它已经存在了 7 年多了,自 2016 年 2 月起就发布了稳定版本。如果我一开始就知道它,我可能不会包含 Closure。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何避免在具有许多实例变量的类中使用 getter/setter 的相关文章

随机推荐

  • 如何将操作链接包含到按钮的标签中?

    我的目标是在标签中包含一个操作链接 显示帮助文本 selectInput button library shiny ui lt fluidPage br selectInput inputId some id label Please ch
  • 自更新到 Swift 1.2 以来,字典现在给出错误“无法转换为 BooleanLiteralConvertible”

    我刚刚开始了解 Swift 然后出现了 Swift 1 2 破坏了我的工作代码 我有一个基于代码示例的函数NSHipster CGImageSourceCreateThumbnailAtIndex http nshipster com im
  • 使用 XSLT 更改 XML 文件中的一个标签名称

    我是否可以在 XSLT 中设置条件 以便仅查找并替换特定标记名称的第一个标记 例如 我有一个 XML 文件 其中包含许多内容
  • 使用 php artisanserve 无法加载公共文件夹中的任何 js / css?

    我有xampp控制面板v3 2 2 php版本5 6 24 我的laravel版本是5 4 32 在 Windows 7 32 位中 我正在使用 Laravel 创建项目composer create project laravel lar
  • 是否使用JTA事务?

    我正在开发一个部署在 JBoss 应用程序服务器上的 J2EE 应用程序 该应用程序由 EJB 2 x 组件和 Web 组件组成 运行在本地计算机或远程服务器上 数据库是 Oracle 或 SQL Server 并且不处于分布式环境中 我使
  • 通过 Istio 入口网关的 TLS 握手失败 (tlsMode=passthrough)

    从外部客户端到 Kubernetes 集群内服务器的 TLS 握手失败 这是关于理解原因 我已将 Istio 入口网关配置为通过端口 15433 上收到的 TLS 并将其路由到端口 433 上的服务器 入口网关日志显示客户端尝试 TLS 握
  • 在 VSTS 中执行运行功能测试任务时出错

    在 VSTS 2015 中 我们在使用 NUnit 测试框架的项目中构建和发布管道设置 我们正在尝试使用 运行功能任务 配置集成测试用例执行 任务配置如下面的屏幕截图所示 可视化测试代理部署任务用于在目标计算机中部署 NUnit 测试代理
  • 每 5 秒使用 jquery/javascript 读取 XML

    我有一个html 索引 html Tasks span 0 span Messages span 0 span Notifications span 0 span
  • Spring Rest 客户端异常处理

    我正在使用弹簧RestTemplate消耗休息服务 在春季休息中暴露 我能够消费成功场景 但对于负面情况 服务会返回错误消息和错误代码 我需要在我的网页中显示这些错误消息 例如对于无效请求 服务抛出异常HttpStatus BAD REQU
  • 如何根据 X509Certificate2Collection 链验证 X509Certificate2

    我正在编写一个 SAML 2 0 响应解析器来处理 ASP Net 中的 POST 身份验证 在 C 和 MVC 中 但这不太相关 所以我有一个 p7b要验证的文件并且可以读入X509Certificate2Collection以及示例断言
  • 像any.do一样弹出窗口

    我正在编写一个应用程序 在弹出窗口中显示未接来电和未读短信 它还具有提醒功能 关闭弹出窗口并在指定时间后打开它 它类似于any do的弹出窗口 我能够使用 WindowManger 创建这样一个窗口 但由于某些我到目前为止不明白的原因 弹出
  • 如何在 ClickOnce 安装中仅更新一个 DLL?

    我正在开发一个大型单击一次应用程序 150MB gt 200 个 DLL 作为交互式调试过程的一部分 我想仅更新 1 个 DLL 并重新启动应用程序 而无需重建和重新部署整个应用程序 那可能吗 如果是这样 怎么办 问题澄清 2009年9月2
  • ImageButton标题标签

    在 ImageButton 上使用 AlternateText 属性向浏览器呈现 alt 标签
  • SQL Server 触发器隔离/范围文档

    我一直在寻找确定的有关 SQL Server 中触发器的隔离级别 或并发性或范围 我不确定确切的称呼它 的文档 我发现以下来源表明我认为是正确的 也就是说 两个用户对同一个表 甚至是相同的行 执行更新 然后将执行独立且隔离的触发器 http
  • Thymeleaf 中 th:each 语句中的 If-Else

    我想要的是 Thymeleaf 中 th each 语句中的 if else If currentSkill null 然后显示包含内容的表格 否则 您没有任何技能 这是没有 if else 的代码 div table tr td td t
  • 递归函数 vs setInterval vs setTimeout javascript

    我正在使用 NodeJs 并需要调用无限函数 但我不知道什么是最佳性能 递归函数 function test my code test 设置时间间隔 setInterval function my code 60 设置超时时间 functi
  • 如何在 Android 中从网络浏览器 (Chrome) 打开任何应用程序?我与 A Href 链接有什么关系?

    我想从我的网络浏览器打开第三方应用程序 所以 我没有任何清单文件或任何东西 我有一个网页 我想要一个可以打开第三方应用程序的链接 例如 Twitter 或 Opera 如何构建锚链接来打开该应用程序 谢谢 基于意图的 URI 的基本语法如下
  • 如何在 PHP 中设置自定义标头

    JAVA 开发人员在标头中向我发送数据 我这样认为 SESSION HTTP COUNTRYNAME 如何使用标头返回响应 它尝试过header countryname USA 但是PHPfunction headers list没有显示它
  • mysql根据之前的记录增加值

    我有一张桌子 Id Parent Counter 1 A NULL 2 A NULL 3 A NULL 4 B NULL 5 B NULL 6 C NULL 7 D NULL 8 D NULL 我想更新表 使计数器列更新 1 与之前的一样长
  • 如何避免在具有许多实例变量的类中使用 getter/setter

    我会尽量保持简短 我的类有很多实例变量 30 因此有很多 getter setter 这些类本身很简单 但由于 getter setter LOC 爆炸了 而且还有太多的代码重复 所以我删除了属性并将它们存储在地图中 如下所示 public