问题:
下面提供的Class SmartPhone,违反了封装规则。重新编写它以便保留封装。
class Battery{
private String type;
private int voltage;
public Battery(String t, int v){type = t; voltage = v}
public String getType(){return type;}
public int getVoltage(){return voltage;}
public void setType(String t){type = t;}
public void setVoltage(int v){voltage = v;}
}
class SmartPhone {
private Battery battery;
private String make, model;
public SmartPhone(String make, String model, Battery battery){
this.make = make; this.model = model;
this.battery = battery;
}
public String getMake(){return make;}
public String getModel(){return model;}
//I think here it is breaking encapsulation
public Battery getBattery(){return battery;}
}
我看不到任何其他破坏封装的规则,我无法忘记为什么这破坏了封装。
是因为在此示例中,我们正在从该类外部返回另一个类的实例,所以它的battery
从而破坏了封装?
封装意味着隐藏内部表示。需要删除任何揭示实现的方法(尤其是getBattery()
)。我将从删除所有吸气剂开始。
class SmartPhone {
private final Battery battery;
private final String make, model;
public SmartPhone(String make, String model, Battery battery){
this.make = make; this.model = model;
this.battery = battery;
}
}
这封装了电话实现的所有细节。
然后您应该添加电话应显示的表示behaviors的任何方法,例如打开/关闭的功能。
[在我看来,吸气剂破坏了封装,但是有时候没关系(例如getMake()
和getModel()
方法对内部的了解不多)。您可以提供一种显示电压的方法,而不显示Battery
对象本身的详细信息。 Law of Demeter之后。
class SmartPhone {
private final Battery battery;
private final String make, model;
public SmartPhone(String make, String model, Battery battery){
this.make = make; this.model = model;
this.battery = battery;
}
public String getMake(){
return make;
}
public String getModel(){
return model;
}
public void turnOn() {
// TODO
}
public void turnOff() {
// TODO
}
public int getVoltage() {
return battery.getVoltage();
}
}
[对于“封装规则”的含义没有更清晰的定义,这个问题有点模棱两可。但是我认为规则可能是这样的:
一个对象应该负责维护自己的状态,包括作为其组成部分的任何其他对象的状态。其他代码应该不可能直接更改对象的状态,而不使用对象在其公共接口中可用的mutator方法。
根据这样的规则,SmartPhone
类违反了封装,因为它允许访问其内部具有设置方法的battery
组件。如果我们认为电池的状态是电话内部状态的一部分(因为电池是电话的组成部分),则电池的设置方法允许对该内部状态进行突变,而不是通过电话的mutator方法。 >
有多个潜在的解决方案:我的首选是使Battery
类不可变:
class Battery { private final String type; private final int voltage; public Battery(String t, int v) { type = t; voltage = v; } public String getType() { return type; } public int getVoltage() { return voltage; } }
通过这种方式,
getBattery
方法可以自由地返回对内部battery
组件的引用,可以避免被其他代码所突变,因为电池没有任何mutator方法。
但是,这种问题暗示SmartPhone
类是您应该更改的类,而不是Battery
类。也许出于某些其他原因,要求电池具有设置方法。在这种情况下,问题会更加微妙。按照您当前编写类的方式,不仅可以通过battery
方法来引用内部getBattery
对象,还可以使用在构造对象时首先提供电池的人:
> Battery b = new Battery("Lithium ion", 5); > SmartPhone s = new SmartPhone("Banana", "bPhone 2", b); > s.getBattery().getVoltage() 5 (int) > b.setVoltage(240); // danger! high voltage! > s.getBattery().getVoltage() 240 (int)
请注意,由于我们保留了所提供电池的参考
s
,因此我们完全不需要通过b
就可以对电池进行突变。
有两种解决方法,两种方法都不能像使电池不可变那样整洁。第一种方法是完全隐藏Battery
类的内部细节,并使SmartPhone
类本身具有所有四个属性:
class SmartPhone { private final Battery battery; private final String make, model; public SmartPhone(String make, String model, String bType, int bVoltage) { this.make = make; this.model = model; this.battery = new Battery(bType, bVoltage); } public String getMake() { return make; } public String getModel() { return model; } public String getBatteryType() { return battery.getType(); } public int getBatteryVoltage() { return battery.getVoltage(); } }
请注意,由于该类无法提供更改其值的方法,因此我将字段设为
final
。
这绝对不允许外部访问电池的mutator方法。另外,我们可以制作防御性副本,以确保我们的私有引用永远不会对其他代码可用:
电池的增变方法会做一些有用的事情,但是这些没有广告效果:class SmartPhone { private final Battery battery; private final String make, model; public SmartPhone(String make, String model, Battery battery) { this.make = make; this.model = model; // defensive copy this.battery = new Battery(battery.getType(), battery.getVoltage()); } public String getMake() { return make; } public String getModel() { return model; } public Battery getBattery() { // defensive copy return new Battery(battery.getType(), battery.getVoltage()); } }
我不喜欢这种解决方案,因为它误导了它[[外观
> Battery b = new Battery("Lithium ion", 5);
> SmartPhone s = new SmartPhone("Banana", "bPhone 2", b);
> b.setVoltage(240);
> s.getBattery().getVoltage()
5 (int)
> s.getBattery().setVoltage(240);
> s.getBattery().getVoltage()
5 (int)