Java:内部类访问彼此的私有(private)变量 - 封装外部 API 的良好实践?

标签 java api private inner-classes encapsulation

这是一个涉及 Java (Java 8) 中内部类的设计问题。所有示例代码都在我的文本下方

举个例子,假设我有一些机械,涉及将燃料从间歇泉泵送到某种燃烧器,我可以使用称为 OilAPI 的外部 API 来控制这些机械。

我有一个 Controller 类,它正在执行工作并决定哪个燃烧器需要从哪个间歇泉获取油,但我不希望使用 API 类(如 Geyser 和 Burner)的逻辑泄漏到 Controller 中(也因为随着时间的推移,API 仍然会发生一些变化)。

现在,为了封装它,我创建了一个名为 FuelFacility 的类,其中包含 OilAPI 的所有逻辑。

问题是,我已将 Pump 和 Engine 类作为 FuelFacility 内部的内部类。

首先,这是为了能够使用 Pump.activate() 而不是 FuelFacility.activatePump(...) 或其他内容的语法。

此外,为了在 Oil API 中连接间歇泉和燃烧器,您需要间歇泉和燃烧器对象,但我不想将它们暴露在外部,因此为了拥有某种“连接泵”和引擎”方法中,我必须允许泵访问引擎的燃烧器变量,引擎访问泵的间歇泉+变量,或者允许燃料设施访问这两个变量。在下面的示例中,我有一个 Engine.connectToPump(pump) 方法,这基本上是它在我的实际代码中的工作方式。

我的队友觉得这有点奇怪;他们说,跨类访问私有(private)变量会破坏封装,尤其是从“外部”(即从在 Controller 类中工作的角度)查看代码的程序员会假设一旦获得了发动机和泵对象,它们将不再依赖于例如原始 FuelFacility 正在使用哪个 OilAPI 对象(尽管这应该保持最终状态,正如我在下面所做的那样),也不是相互影响。

现在,从那时起,我已经成功地改变了他们的想法 - 基本上这只是一种他们不习惯的做事方式,但这并不是一个坏习惯。

但是,现在我正忙于更改其他一些代码以与此类似的方式工作,我只想在继续之前确定我正在做的事情是好的做法吗?有更好的做事方式吗?非常感谢您的建议!

代码:

石油 API(不受我控制):

public class OilAPI {
    private final Pipes pipes = new Pipes();

    public static class Geyser {}
    public static class Burner {}

    public static class Pipes {
        public void createConnectionBetweenGeyserAndBurner(Geyser g, Burner b) {
            // Connects geyser and burner
        }   
    }

    public Geyser getGeyserWithId(String id) {
        // Actually retrieves a specific instance
        return new Geyser();
    }

    public Burner getBurnerWithId(String id) {
        // Actually retrieves a specific instance
        return new Burner();
    }

    public void activateGeyser(Geyser g) {
        // do stuff
    }

    public void activateBurner(Burner b) {
        // do stuff
    }       

    public void createConnectionBetweenGeyserAndBurner(Geyser g, Burner b) {
        pipes.createConnectionBetweenGeyserAndBurner(g,b);
    }
}

燃料设施(我创建的用于封装石油 API 的类):

public class FuelFacility { 
    private final OilAPI oil;

    FuelFacility(OilAPI oil) {
        this.oil = oil;
    }

    public Pump getPumpForId(String id) {
        OilAPI.Geyser geyser = oil.getGeyserWithId(id);
        return new Pump(geyser);
    }

    public Engine getEngineForId(String id) {
        OilAPI.Burner burner = oil.getBurnerWithId(id);
        return new Engine(burner);
    }

    public class Pump {
        private final OilAPI.Geyser geyser;
        private Pump(OilAPI.Geyser geyser) {
            this.geyser = geyser;
        }

        public void activate() {
            oil.activateGeyser(geyser);
        }
    }

    public class Engine {
        private final OilAPI.Burner burner;
        private Engine(OilAPI.Burner burner) {
            this.burner = burner;
        }

        public void connectToPump(Pump pump) {
            oil.createConnectionBetweenGeyserAndBurner(pump.geyser, burner);
        }   

        public void activate() {
            oil.activateBurner(burner);
        }
    }
}

Controller (由我所有,位于我们的代码库中):

public class Controller {

    public static void main(String[] args) {
        // We actually get these from a database
        String engineId = "engineId";
        String pumpId = "pumpId";

        OilAPI oil = new OilAPI();

        FuelFacility facility = new FuelFacility(oil);
        FuelFacility.Engine engine = facility.getEngineForId(engineId);
        FuelFacility.Pump pump = facility.getPumpForId(pumpId);
        engine.connectToPump(pump);
    }
}

最佳答案

让内部类访问彼此的私有(private)字段本身并不一定是坏事。看来您的主要目标是保护 Controller 免受 OilAPI 更改的影响。在此设计中,FuelFacilityPumpEngineOilAPIGeyser 非常接近code> 和 Burner ,我不确定您是否真的保护了 Controller 那么多。 FuelFacility 的设计应该更多地满足 Controller 的需求,而不是 OilAPI 的需求。在您的示例中,您不会在 PumpEngine 上调用 activate,但我假设您最终希望这样做。首先,我首先声明一些接口(interface):

public interface PumpEngineConnection {
  public void activate();
}

public interface FuelFacility {
  public PumpEngineConnection connect(String pumpId, String engineId);
}

Controller 通过这些接口(interface)工作,并且不知道它实际使用的实现是什么。然后,您可以创建 FuelFacilityOilAPIFuelFacility 实现。它返回的 PumpEngineConnection 实现将是专门设计用于与 OilAPIFuelFacility 配合使用的实现。您可以使用内部类来做到这一点:

public class OilAPIFuelFacility implements FuelFacility {
  private final OilAPI oil;
  public OilAPIFuelFacility(OilAPI oil){ this.oil = oil; }

  @Override
  public PumpEngineConnection connect(String pumpId, String engineId){
     Geyser geyser = oil.getGeyserWithId(pumpId);
     Burner burner = oil.getBurnerWithId(engineId);
     oil.createConnectionBetweenGeyserAndBurner(geyser, burner);
     return this.new GeyserBurnerConnection(geyser, burner);
  }

  private class GeyserBurnerConnection implements PumpEngineConnection {
     private final Geyser geyser;
     private final Burner burner;

     private GeyserBurnerConnection(Geyser geyser, Burner burner){
       this.geyser = geyser;
       this.burner = burner;
     }

     @Override
     public void activate() {
        OilAPIFuelFacility.this.oil.activateGeyser(this.geyser);
        OilAPIFuelFacility.this.oil.activateBurner(this.burner);
     }
  }
}

每个GeyserBurnerConnection 隐式获取对创建它的OilAPIFuelFacility 的引用。这是合理的,因为只有将 PumpEngineConnection 与创建它的 FuelFacility 一起使用才有意义。同样,GeyserBurnerConnection 引用 OilAPIFuelFacility 中的 oil 成员也是完全合理的。

也就是说,将 GeyserBurnerConnection 作为与 OilAPIFuelFacility 位于同一包中的包私有(private)类可能更有意义。不同版本的 OilAPI 可能可以使用相同的 GeyserBurnerConnection 类。

最后, Controller 可能看起来像这样:

import com.example.fuelfacility.FuelFacility;
import com.example.fuelfacility.PumpEngineConnection;
public Controller {
  private final FuelFacility fuelFacility;

  public Controller(FuelFacility fuelFacility){
    this.fuelFacility = fuelFacility;
  }

  public void example(){
     String pumpId = "pumpId";
     String engineId = "engineId";

     PumpEngineConnection connection = fuelFacility.connect("pumpId", "engineId");
     connection.activate();
  }
}

请注意,它完全不知道它实际使用的 FuelFacilityPumpEngineConnection 的实现。在实践中,我们会使用依赖注入(inject)框架或外部 Main 类传入 OilAPIFuelFacility

我意识到您的示例可能是您实际需要做的事情的简化。尽管如此,您确实应该考虑 Controller 需要什么,而不是 OilAPI 做什么。

最后,我应该指出,我基本上同意您的同事对您的设计的担忧。考虑这个片段:

OilAPI oil1 = new OilAPI();
OilAPI oil2 = new OilAPI();
FuelFacility fuel1 = new FuelFacility(oil1);
FuelFacility fuel2 = new FuelFacility(oil2);
Engine engine = fuel1.getEngineForId("engineId");
Pump pump = fuel2.getPumpForId("pumpId");
engine.connectToPump(pump);

会发生什么?然后使用 oil1 连接通过 oil2 检索的 Pump。根据 OilAPI 的内部结构,这可能是一个问题。

关于Java:内部类访问彼此的私有(private)变量 - 封装外部 API 的良好实践?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45323644/

相关文章:

python - 从 C 调用 IPython.embed() 方法时为 "ValueError: call stack is not deep enough"

api - 构建 XACML 策略的最佳实践是什么?

c++ - 为什么会发生此编译器错误?

java - 可以将 Google 模块与 Eclipse 的 Google 插件一起使用吗?

java - 使用 XMLUnit 进行 XML 比较

java - 如何编写Spring Boot Controller 类单元测试用例

api - GO单元测试结构化REST API项目

java - Netty 握手问题

c - 隐藏结构成员

php - 在 PHP 中将变量传递给私有(private)类时出现问题