这有点涉及Java(Java 8)内类的设计问题。所有示例代码都在我的文本下面
举个例子,假设我有一些机器,它涉及把燃料从一个喷泉泵到某种燃烧器,我可以使用一个叫做OilAPI的外部API来控制它。
我有一个Controller类,它负责完成工作,并决定哪个燃烧器需要从哪个间歇泉获取油,但我不希望使用API的类(如间歇泉和燃烧器)的逻辑泄漏到控制器中(也是因为API仍然会随着时间的推移发生一些变化)。
现在,为了封装它,我创建了一个名为FuelFacility的类,它包含OilAPI的所有逻辑。
问题是,我已经把类泵和发动机作为燃料设施的内部类。
首先,这是为了能够go pump.activate()而不是fuelFacility.activatePump(...)的语法或者别的什么。
进一步的是,为了连接油API中的间歇泉和燃烧器,您需要间歇泉和燃烧器对象,但我不想将它们暴露在外部,所以为了有某种“连接泵和发动机”的方法,我必须允许泵访问发动机的燃烧器变量,发动机访问泵的间歇泉+变量,或者FuelFacility访问这两个变量。在下面的示例中,我有一个Engine.ConnectTopUMP(pump)方法,这基本上是它在我的实际代码中的工作方式。
队友们觉得这有点奇怪;他们说,通过类访问私有变量打破了封装,特别是从“外部”(即从在Controller类中工作的角度)查看代码的程序员会假设,一旦您获得了Engine和Pump对象,它们就不再依赖于例如原始FuelFacility正在使用的OilAPI对象(尽管这应该是最终的,正如我在下面所做的那样),也不再依赖于彼此。
现在,从那以后,我设法改变了他们的想法--基本上,这只是一种他们不习惯的做事方式,但这并不是一种糟糕的练习。
然而,现在我正忙于修改一些其他代码,以与此类似的方式工作,我只想在继续之前确保我正在做的是良好的实践吗?有没有更好的处事方法?谢谢你的建议!
代码:
油类空气污染指数(不受本人控制):
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而创建的I类):
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);
}
}
}
控制器(由我拥有,位于我们的代码库中):
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);
}
}
让内部类访问彼此的私人领域本身并不是必要的坏事。您的主要目标似乎是保护Controller
免受对OilAPI
的更改。在此设计中,fuelfacility
、pump
和engine
与oilapi
、geyser
和burner
非常接近,所以我不确定您是否真正保护了controller
。fuelfacility
应该更多地针对controller
的需要而不是oilapi
的功能进行设计。在您的示例中,您没有在pump
或engine
上调用activate
,但我假设您最终希望这样做。首先,我要声明一些接口:
public interface PumpEngineConnection {
public void activate();
}
public interface FuelFacility {
public PumpEngineConnection connect(String pumpId, String engineId);
}
controller
通过这些接口工作,并且不知道它实际使用了什么实现。然后您可以对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
位于同一包中的包私有类可能更有意义。可能是OilAPI
的不同版本可以使用相同的GeyserBurnerConnection
类。
最后,控制器
可能如下所示:
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
的实现。在实践中,我们将通过依赖注入框架或外部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
检索的泵
。根据OilAPI
的内部结构,这可能是一个问题。
为什么这段代码不起作用 在这段代码工作的时候? 在第一段代码中,当我试图通过内部类“a”的对象引用内部类“a”的实例变量“x”时,我得到一个错误,说我是在静态上下文中使用内部类。在其他方法中执行相同操作时没有错误。
问题内容: 在内部类中,可以访问外部类的变量,但不能访问方法的局部变量。我了解了无法访问方法的局部变量的部分,但我想知道为什么外部类变量可以访问? 我的理解是,由于内部类与外部类绑定,因此只要父级可用,子级就可以访问其父级变量。我对么? 问题答案: 假设您的外部类在内部类的范围内(非静态)被称为,以获取该字段。 例如, 其中Outer是类的名称,并标识该字段。 您也可以直接抓取它,但是如果由于阴影
在内部类中,外部类的变量是可访问的,但方法的局部变量不是。我理解了关于方法的局部变量不可访问的部分,但我想知道为什么外部类变量是可访问的? 我的理解是,由于内部类与外部类绑定,因此只要父类可用,子类就可以访问其父变量。我说得对吗?
问题内容: 我确实阅读了许多讨论内部类的主题,并且给人的印象是内部类可以访问封闭类的变量和方法。在下面的代码中,我有一个外部类和一个内部类,在测试类中,我创建了一个外部类的实例,然后从中创建了一个内部类的实例。但是我无法通过内部类引用访问String变量。救命? 问题答案: 内部类可以通过自己的方法访问外部类的方法和属性。看下面的代码:
问题内容: 嗨,我正在浏览有关内部类的SCJP书,发现了这一说法,类似这样。 方法本地类只能引用已标记的本地变量 在解释中,指定的原因与本地类对象和堆上的局部变量的范围和生存期有关,但我无法理解。我在这里想念任何东西吗? 问题答案: 原因是,在创建方法本地类实例时,编译器实际上会将其引用的所有方法本地变量复制到其中。这就是为什么只能访问变量的原因。甲变量或参考是不变的,所以它停留在同步与其方法本地
问题内容: 如果我有一个包私有的Java类(用“ class”声明,而不是“ public class”),那么将内部方法声明为public或protected或package- private确实没有区别,对吗?那么我应该使用哪个,或者什么时候该使用呢?我有点困惑。 问题答案: 如果我有一个包私有的Java类(用“ class”声明,而不是“ public class”),那么将内部方法声明为p