处理公共/中心对象的最佳方式

时间:2021-02-18 14:16:24

标签: java

我有一个程序,我需要从大多数其他对象读取和写入数据到一个公共(中央?)对象。从某种意义上说,它就像一个数据库,或者如果你愿意的话,它也可能是数据总线。我无法将其写入文件。目前我这样做:

class A {
    private Common common;
    private B b;
    
    public A() {
        common = new Common();
        b = new B(common);
    }
}

class B {
    private Common common;
    private C c;

    public B(Common common) {
        this.common = common;
        c = new C(common);
    }
}

class C {
    private Common common;
    private D d;

    public C(Common common) {
        this.common = common;
        d = new D(common);
    }
}


...etc.

所以我正在“沿着线”传递对象,但这样做感觉不对(考虑到我要接触的对象数量)。有没有更好的方法来做到这一点?

编辑:A 类可以被实例化多次,所以 Common 只对每个单独 A 中的所有对象通用。

3 个答案:

答案 0 :(得分:4)

真正的答案是“不要”。

您可以使用 Singleton 模式;但是,这样做可以避免“面向对象编程”,这是 Java 用来使程序可维护和可修复的方法。

一旦您有了一个单一的中心对象,如果不调查中心对象是如何受到影响的,然后调查同样使用中心对象的所有其他对象如何通过中心对象。

而是为每个场景创建一个对象。这允许您一次处理两个场景,当您开始使程序多线程时,这将使生活更轻松,这是您迟早要做的事情,因为否则您将只使用 8 个内核中的 1 个内核核心处理器。

您的语言假定一个中心对象,因此我无法在您的场景中提供示例。我会将 Commmon 修改为 Context,其中 Context 表示“我现在正在处理的场景”。

请注意,对于这样的更改,您可能不应该拥有上下文,而应该将其传递。

class A {
    private B b;
    
    public A() {
        b = new B();
    }

    public handle(Context context) {
        context.add(...whatever...);
        b.handle(context);
    }
}

class B {
    private C c;

    public B() {
        c = new C();
    }

    public handle(Context context) {
        context.remove(...whatever...);
        context.add(...something else...);
        c.handle(context);
    }
}

class C {
    private D d;

    public C() {
        d = new D();
    }

    public handle(Context context) {
        context.do(...whatever...);
        d.handle(context);
    }
}

现在调用的方式是

Context c1 = new Context();
Context c2 = new Context();
A a = new A();
a.handle(c1);
a.handle(c2);

如您所见,不再需要单一的“中心”类。

将问题融入语言,或为问题选择语言是一种习得的技能。请避免单例模式,因为它是一种很难学习的反模式,会损害软件的可维护性。

如果您使用单例编写所有代码,则可以轻松地将代码移植到非面向对象的语言(如 C、FORTRAN 等)中,因为单例是“全局范围,名称空间稍好一些”的语法糖. C++、Java 等流行的原因是将代码结构化为对象更容易代码维护。

答案 1 :(得分:0)

在共享此类对象时,而不是将它们作为参数作为方法传递时,您需要为您提供实例。东西可以

  • 初始或按需创建实例
  • 使用单个实例,或提供上下文敏感实例(例如,每个线程一个,或按租户、按用户等)

单例模式

此类对象的基本设计模式Singleton pattern,其中(单个)实例由该实例的类创建和提供。

用法如下:

Common common = Common.instance();

注册表模式

当此类对象的更高级模式是注册表模式(请参阅 Martin Fowler 撰写的企业应用程序架构模式)时,专用单例(注册表)创建并提供这些实例。

用法如下:

Common common = Registry.instance().get(Common.class);

依赖注入

JavaEE (CDI)Spring 等框架利用注册表模式,并使用 Bean Registry 来管理此类实例,并通过依赖注入提供它们

推荐

  • 对于简单的案例/项目,请使用单例模式
  • 考虑使用 Spring Framework、CDI 或类似工具(Quarkus、Micronaut)来处理更复杂的情况。

答案 2 :(得分:0)

这正是像 Guice 这样的“依赖注入”框架旨在解决的问题。许多 Java 应用程序都有一组类来为“应用程序”建模,而不是应用程序处理的数据。应用程序可能有一个 Server、一个 WidgetService、一个 Database、一个 DatabaseWidgetReader,等等。这些类都相互协作以实现应用程序逻辑,并且每个类通常都接受其协作者作为构造函数参数。

但是一旦你有多个这样的类,并且你有一个大的 initialize 方法来构造一切,并且每个测试都必须有它自己的大 initialize 方法版本,你开始认为一定有更好的方法。

使用 Guice 这样的框架,您只需使用 @Inject 注释构造函数参数,提供一些引导程序配置,然后让框架完成其余的工作。要使您的示例与 Guice 一起使用,您需要:

  1. 修改类构造函数以接受所有他们的合作者作为参数。例如,A 构造函数应该同时采用 CommonB
  2. 创建“注射器”,这基本上是一个工厂。
  3. 告诉注入器将类 Common 绑定到您创建的特定实例。 (我在这里假设 Common 需要一些它无法从 Guice 获取的信息,可能是数据库服务器的地址。)
  4. 向注入器询问其他类之一的实例。

通常您在最后一步中指定的类将类似于 ServerApplication 或作为整个应用程序入口点的东西。

假设您在最后一步要求 A。 Guice 说,好吧,我需要一个 Common(在上面的第三步中提供)和一个 B 的实例。为了得到 B,我需要一个 Common 和一个 C 等等。显然这条链必须在某个地方结束。 Guice 将跟踪所有依赖项并构造所有内容,然后将 A 的实例返回给您。

如果您有一些不想成为单例的对象(例如,您想在每次需要时实例化一个新的 CPU,但希望所有 CPU 实例共享相同的 { {1}}) 您可以注册 Guice 将用于创建新实例的“提供程序”。

还有其他框架或多或少以相同的方式工作;我只是最熟悉 Guice。

相关问题