【问题标题】:Avoiding global state without cluttering constructors在不使构造函数混乱的情况下避免全局状态
【发布时间】:2012-11-13 00:45:36
【问题描述】:

我有一个需要重构的项目。它是一个 Java 桌面 SWT/JFace 应用程序,具有大约 40 个 GUI 组件,主要组件控制次要组件的生命周期。我想拥有低耦合的良好模块化设计,但是如果没有很长的构造函数,我找不到避免全局状态的方法。

例如,当前软件国际化的方法是这样的:

public class Main {
    public static void main(String[] args) {
        MyMessages.loadMessages();
    }
}
public class MyMessages {
    private static ResourceBundle messages;
    public static void loadMessages() {
        messagesBundle = ResourceBundle.getBundle("messages", "en");
    }

    public static void getMessage(String m) {
        return messagesBundle.getString(m);
    }
}

public class SomeComponent extends Component() {
    public void init() {
        String textboxLabel = MyMessages.getMessage("someMessage");
    }
}

代码中的其他地方使用单例(用于数据模型),这使得随着代码库的增长很难分析依赖关系。

另一种方法是使MyMessages 有状态,并通过GUI 组件的层次结构一直传递它的实例。对我来说,这似乎很混乱,容易出错,与感知到的好处相比,不值得麻烦。

还有什么其他方法可以设计成这样:

  • 不依赖于本质上是全局变量。
  • 明确依赖关系。
  • 不会用冗长的构造函数弄乱代码。

我应该考虑依赖注入框架,还是有另一种方法对于小型桌面应用程序来说似乎并不过分?

【问题讨论】:

    标签: java architecture singleton dependencies decoupling


    【解决方案1】:

    我应该考虑依赖注入框架,还是有另一种方法对于小型桌面应用程序来说似乎并不过分?

    对于小型桌面应用程序,我建议使用依赖注入设计模式,但不要使用工业级 DI 框架。

    在您的代码中,消息助手类是可以的,但是您的 SomeComponent 类绝对不是 DI 友好的:

    public class SomeComponent extends Component() {
        public void init() {
            String textboxLabel = MyMessages.getMessage("someMessage");
        }
    }
    

    SomeComponent 现在绑定到 MyMessages。

    改为使用类似的东西:

    public class SomeComponent extends Component() {
    
        public void setTextBoxLabel(String value) {
            // implementation depends on requirements
        }
    
        public void init() {
            // do something with all the properties that were set
        }
    }
    

    基本上只需将设置器添加到您的组件。这是 DI 的第一部分。让你的所有类都是松散耦合的,每个类都相信有人会负责设置它的所有属性。不需要全局状态,因为组件所需的一切都将被注入其中。

    当然,一旦您这样做了,您的启动代码将成为一场噩梦,因为您必须创建所有这些对象并设置它们的所有属性,并且它们都与您的应用程序相关联。此时您开始重构您的启动代码并创建负责为您创建对象的构建器和/或工厂。

    假设其中一个组件由属性文件初始化。您创建一个读取属性文件、实例化组件、设置组件属性并返回实例的构建器。该组件对属性文件一无所知。它只是有一些它知道将在启动时设置的 getter 和 setter。

    稍后您决定使用 XML 作为文件格式 - 但对于不同的应用程序,同时仍将属性文件用于第一个应用程序。没问题 - 创建一个新的 XML 构建器来读取 XML 文件并构建组件 - 但实际的 GUI 组件保持不变,因为它与初始化的方式完全分离。

    关键是查看creational design patterns 并找出哪些模式(构建器、工厂等)可以帮助您可靠地创建组件,同时避免全局状态。

    【讨论】:

    • 我更喜欢注入 ResourceBundle,因为在真正的应用程序中,除了基本标签之外,还会有工具提示等。
    【解决方案2】:

    我会使用全局状态。特别是如果您正在重用视图。恕我直言,对将在整个应用程序中大量使用的各种组件进行某种查找并没有错。

    请注意,如果您使用 DI 框架,那么可能会隐式使用某种全局状态(当然,这是依赖于 DI 框架的实现)。以 Spring 为例,ApplicationContext 是一个全局对象,它本身将保留各种组件和/或管理组件的生命周期,具体取决于各种 bean 的配置。

    拥有全局组件不一定是坏事。如果权衡是巨大的构造函数签名和传递引用,我会随时使用全局引用(我会把它们放在某种Application 对象中)。

    【讨论】:

    • 在测试过程中,一切都依赖于一两个类,会不会导致问题? (顺便说一句,投票给你的不是我)
    • 没有。您仍然可以按照松散耦合原则编写组件。总会有一些依赖关系,但举个例子,你不能打破应用程序级组件的松散耦合。
    【解决方案3】:

    使用java logger api怎么样...另外可以扩展StreamHandler类并将输出流设置为组件。您还可以跟踪消息以及相关的类和方法。

    【讨论】:

      猜你喜欢
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 2018-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多