如何在保留封装的同时将代码逻辑地组织到包中

How to organize code logically into package while preserving encapsulation

这是一道概念题。
我通常在逻辑上组织包中的代码。 例如:数学运算进入 my.package.math 或业务逻辑进入 my.package.business 等等。

最近我怀疑我是否想分享 package visibility in java.

强加的约束

让我展示一些代码来更好地解释我的情况: 我有以下包结构:

+-- org.example
    +-- greetings
    |   +-- RandomGreetings
    +-- GreetingsService
    |
    +-- Main

和以下代码:

GreetingsService.java

package org.example;

import org.example.greetings.RandomGreetings;

public class GreetingsService {
    public String greetingsToTheWorld(){
        RandomGreetings greetings = new RandomGreetings();
        return greetings.say() + " World";
    }
}

RandomGreetings.java

package org.example.greetings;

import java.util.Random;

class RandomGreetings {
    public String say() {
        Random rand = new Random();
        int value = rand.nextInt(2);
        return value==0 ? "Hello" : "Hi";
    }
}

Main.java

package org.example;

public class Main {

    public static void main(String[] args) {
        GreetingsService sayHelloService = new GreetingsService();
        System.out.println(sayHelloService.greetingsToTheWorld());
    }
}

正如我向您展示的,此代码无法编译,因为 class RandomGreetings 是包可见的,这意味着 GreetingsService 无法实例化它。 这个问题可以解决把public放在classRandomGreetings:

public class GreetingsService {
...
}

但是 RandomGreetings 对我来说是一个我想保持封装而不是 public 可见的实现。 在这种情况下,世界(任何导入我可能生成的 jar 工件的人)都能够看到并调用它 class 而这不是我想要的东西。

另一种可能的解决方案是将 GreetingsService 移动到与 RandomGreetings 相同的级别,如下所示:

+-- org.example
    +-- greetings
    |   +-- RandomGreetings
    |   |
    |   +-- GreetingsService
    |
    +-- Main

在这种情况下,代码编译成功并且 RandomGreetings 被封装,但我遇到了另一个问题。
我无法按逻辑顺序组织包中的 class。
在这种情况下,有几个 class 所以情况仍然是可控的,但是如果 class 的数量增加,我认为只有一个独特的大包。

我知道 Java 中没有 subpackage visibility,那么你是如何处理这种情况的?
你会如何解决我上面的代码的问题?

当你想使用一个包时,它必须有一些入口点,它必须是 public。否则您可以将包裹扔掉,因为它无法使用。这意味着您的 org.example.greetings 包必须包含某些东西,即 public 并且可以是 used/called 来自 "outside"。它不一定是你的 RandomGreetings class.

您可以定义一个接口(在 org.example 包中),在您的 RandomGreetings class 中实现它并使用另一个 public 方法创建和 return"package visible"RandomGreetingsclass。代码可能如下所示:

package org.example;

public interface Greeting {
    public String say();
}

这由 RandomGreetings class:

实现
package org.example.greetings;

import org.example.Greeting;

class RandomGreetings implements Greeting {
    // ...
}

然后定义一个 public 助手 class 到 return 来自 org.example.greetings 包的 RandomGreetings 对象:

package org.example.greetings;

import org.example.Greeting;

public GeneratorHelper {
    public static Greeting buildIt() {
         return new RandomGreetings();
    }
}

当您使用 GeneratorHelper.buildIt() 方法时,您会得到一些实现 Greeting 接口的对象。但是您无法访问 RandomGreetings class 本身,因为它仍然是 "package visible".