这是一个涉及 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
更改的影响。在此设计中,FuelFacility
、Pump
和 Engine
与 OilAPI
、Geyser
非常接近code> 和 Burner
,我不确定您是否真的保护了 Controller
那么多。 FuelFacility
的设计应该更多地满足 Controller
的需求,而不是 OilAPI
的需求。在您的示例中,您不会在 Pump
或 Engine
上调用 activate
,但我假设您最终希望这样做。首先,我首先声明一些接口(interface):
public interface PumpEngineConnection {
public void activate();
}
public interface FuelFacility {
public PumpEngineConnection connect(String pumpId, String engineId);
}
Controller
通过这些接口(interface)工作,并且不知道它实际使用的实现是什么。然后,您可以创建 FuelFacility
的 OilAPIFuelFacility
实现。它返回的 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();
}
}
请注意,它完全不知道它实际使用的 FuelFacility
和 PumpEngineConnection
的实现。在实践中,我们会使用依赖注入(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/