我有一个程序,我需要从大多数其他对象读取和写入数据到一个公共(中央?)对象。从某种意义上说,它就像一个数据库,或者如果你愿意的话,它也可能是数据总线。我无法将其写入文件。目前我这样做:
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 中的所有对象通用。
答案 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 来管理此类实例,并通过依赖注入提供它们。
答案 2 :(得分:0)
这正是像 Guice 这样的“依赖注入”框架旨在解决的问题。许多 Java 应用程序都有一组类来为“应用程序”建模,而不是应用程序处理的数据。应用程序可能有一个 Server
、一个 WidgetService
、一个 Database
、一个 DatabaseWidgetReader
,等等。这些类都相互协作以实现应用程序逻辑,并且每个类通常都接受其协作者作为构造函数参数。
但是一旦你有多个这样的类,并且你有一个大的 initialize
方法来构造一切,并且每个测试都必须有它自己的大 initialize
方法版本,你开始认为一定有更好的方法。
使用 Guice 这样的框架,您只需使用 @Inject
注释构造函数参数,提供一些引导程序配置,然后让框架完成其余的工作。要使您的示例与 Guice 一起使用,您需要:
A
构造函数应该同时采用 Common
和 B
。Common
绑定到您创建的特定实例。 (我在这里假设 Common
需要一些它无法从 Guice 获取的信息,可能是数据库服务器的地址。)通常您在最后一步中指定的类将类似于 Server
或 Application
或作为整个应用程序入口点的东西。
假设您在最后一步要求 A
。 Guice 说,好吧,我需要一个 Common
(在上面的第三步中提供)和一个 B
的实例。为了得到 B
,我需要一个 Common
和一个 C
等等。显然这条链必须在某个地方结束。 Guice 将跟踪所有依赖项并构造所有内容,然后将 A
的实例返回给您。
如果您有一些不想成为单例的对象(例如,您想在每次需要时实例化一个新的 CPU
,但希望所有 CPU
实例共享相同的 { {1}}) 您可以注册 Guice 将用于创建新实例的“提供程序”。
还有其他框架或多或少以相同的方式工作;我只是最熟悉 Guice。