避免全局状态而不会使构造函数混乱

时间:2012-11-13 00:45:37

标签: java architecture singleton dependencies decoupling

我有一个需要重构的项目。它是一个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组件的层次结构中。对我来说,这似乎是混乱的,容易出错,与感知的好处相比,不值得麻烦。

还有哪些方法可以通过以下方式设计:

  • 不依赖于本质上是全局变量。
  • 使依赖关系显式化。
  • 不会使用冗长的构造函数来混淆代码。

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

3 个答案:

答案 0 :(得分:2)

  

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

对于小型桌面应用程序,我建议使用依赖注入设计模式,但不要使用工业级的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
    }
}

基本上只需在组件中添加setter即可。这是DI的第一部分。这样你的所有课程都松散耦合,每个班级都相信有人会照顾好所有的属性。不需要全局状态,因为组件需要的所有内容都将被注入其中。

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

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

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

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

答案 1 :(得分:1)

我会使用全球状态。特别是如果您重新使用视图。恕我直言,对各种组件进行某种查找没有任何问题,这些组件将从整个应用程序中获得大量使用。

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

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

答案 2 :(得分:0)

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