【问题标题】:Avoid passing parameter from high to low-level classes避免将参数从高级类传递到低级类
【发布时间】:2017-03-08 22:56:22
【问题描述】:

在我的 Java Web 服务器项目中,我的 Main.main 方法接受一个参数,指定它将在其中查找某些文件的目录,我稍后会使用这些文件。我的问题是,在我的高级课程中,我实际上并没有对这个论点做任何事情,但是我的一些低级课程需要这些信息。

自然,解析和存储我的命令行参数的类是第一个使用的类之一,即我的最高级别的类之一,所以我正在努力寻找一种方法来使命令行参数可以访问我的低级课程。

似乎我唯一的两个选择是要么一直向下传递它,要么通过从不触及参数的类,而不是将它传递到下一个级别,或者给它一个全局范围。从设计的角度来看,这些似乎都不是很好的选择,所以我想知道是否有我遗漏的替代方案,或者我是否只需要选择两个弊端中较小的一个——或者完全改变我的课程结构方式.

【问题讨论】:

  • 你的直觉是正确的,传递它或全局状态都不是很好的选择,但你接受了一个只建议这些的答案。

标签: java oop solid-principles


【解决方案1】:

避免通过不需要它们的类传递一些参数是正确的。这样做的诀窍在于,如果设计得当,低级对象仍然是在高级构建的,这意味着您通常在应用程序顶部的某个地方传递所需的参数。

一个有两个层次的例子。而不是这样做:

public class ObjectA {
    public ObjectA(String path) {
        ....
        b = new ObjectB(path);
    }
}

public class ObjectB {
    public ObjectB(String path) {
        ...
    }
}

public static void main(String[] args) {
    ...
    new ObjectA(path);
}

您将path 传递给ObjectA 只是因为它需要使用该参数构造ObjectB,您可以这样做:

public class ObjectA {
    public ObjectA(ObjectB b) {
        ....
    }
}

public class ObjectB {
    public ObjectB(String path) {
        ...
    }
}

public static void main(String[] args) {
    ...
    new ObjectA(new ObjectB(path));
}

这是避免传播依赖,通常是依赖注入的一部分。 DI 的声誉参差不齐,因为它被 Spring 和 JEE 等容器所采用,但在其核心 DI 实际上只意味着您不应该在其他对象中实例化对象(即使是较低级别的对象),而是应该已经配置的对象传递给其他对象。这有效地将对象构造和配置与使用它的对象分离。

以这种方式构建应用程序一开始可能听起来很奇怪,并且它对设计产生了各种奇怪的后果,但它是做事的正确 (OO) 方式,它可以解决您的问题。

【讨论】:

  • 但是如果 ObjectB 不是由 ObjectA 创建,而是由不知道 path 的 ObjectC 创建,该怎么办?也只能使用构造函数path 参数来制作ObjectC...所以与将path 作为方法的参数传递没有太大区别。它具有相同的要求 - 调用路径中的所有对象都必须定义为 path
  • 我不确定我是否理解正确。传递path 或某个对象之间的区别在于ObjectC 确实不需要 需要路径,但它确实 需要ObjectB(或其他)。所以你传递的东西是应该知道的。
  • 此外,依赖关系不会以这种方式复合。 ObjectC 需要 ObjectB 工作,但不需要 ObjectB 需要的任何东西。所以它只知道它需要直接的东西。
  • 这是一个简单的问题。在 ObjectC 中,您需要创建具有 path 作为构造函数参数的 ObjectB - 从哪里可以在 ObjectC 中获得它?这就是我试图问的......顺便说一句,这些方法都没有错。
  • 重点是,通常您真的不需要在ObjectC 中创建ObjectB。您只需要ObjectB 的实例,因为它具有您想要的一些功能。即使您确实需要创建一个新实例,您也可以传递一个能够创建ObjectBFactory,而无需知道创建它需要什么。这是避免发布原始问题的OO方式。
【解决方案2】:

是的,有很多选择:

  1. 正如您所说,一直向下传递 - 可能,但不太好,因为绝对所有调用路径中的所有方法都必须将其作为参数。
  2. 使用系统属性代替main方法参数和

    一个。将您的主要称为>java -Dmy.path=path_to_directory Main

    b.使用环境变量

    >set my.path=path_to_directory
    >java Main

    c。在main 方法中设置:

    public void main(String[] args) {
    System.getProperties().put("my.path",args[dirPathIndex]);

    在所有情况下,您都可以在代码中的任何位置获取值,就像:

    String dirPath=System.getProperty("my.path");

  3. 创建一个小的本地静态“缓存类型” 在主类中

    public class Main{

    public static String dirPath;

    public void main(String[] args) {
    dirPath= args[dirPathIndex];

然后,在代码中的任何地方你都可以得到它:

   `String dPath=Main.dirPath;`

当然,围绕“本地缓存”方法还有更多选择,但主要思想仍然相同。在某处保留一个值,您可以在代码中的任何位置获取它。

【讨论】:

  • 从 b 开始的所有解决方案都是相同的;全局数据。全局数据没有任何 SOLID。
  • @weston 只要它是一个 GLOBAL 参数,这没什么错。但是还有很多其他方法,例如引入 PropertyProvider、Context、注入、使用继承或接口等等……所有这些都取决于每个特定情况的确切需求。
  • 顺便说一句,我不是在喊,SOLID 是首字母缩略词。 OP 说:“似乎我唯一的两个选择是通过从不触及参数的类一直向下传递它,而不是将它传递到下一个级别,或者给它一个全局范围。两者都不是从设计的角度来看,其中一些似乎是不错的选择“您只给了他们这两个相同的选择。
  • 没错,但对于这种情况,这是一个合理的解决方案。如果需求表明下游对象的不同实例可能具有不同的 path 值,则从其他人那里得到 - 这是另一种情况,需要有不同的解决方案。
  • 读取全局数据使代码难以测试和扩展。
猜你喜欢
  • 2020-08-22
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 2022-08-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
  • 1970-01-01
相关资源
最近更新 更多