关于带有示例问题的封装问题
Question about encapsulation with example question
问题:
Class SmartPhone, given below, violates the rule on encapsulation.
Re-write it so that encapsulation is preserved.
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;}
}
我看不到任何其他破坏封装的规则,我不记得为什么要破坏它。
是否因为我们从 class 外部返回另一个 class 的实例,在这个例子中它 battery
因此破坏了封装?
封装意味着隐藏内部表示。需要删除任何揭示实现的方法(特别是 getBattery()
)。我将从删除所有 getters.
开始
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;
}
}
这封装了 phone.
实现的所有细节
然后您应该添加表示 行为 的任何方法,phone 应该揭示这些行为,例如被转向的能力 on/off。
在我看来,getters 打破了封装,但有时这没关系(比如 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 int getVoltage() {
return battery.getVoltage();
}
}
更新
封装就是隐藏实现细节---"preventing unauthorized parties' direct access to them" (Wikipedia)。
接受的答案是错误的。通过封装使电池不可变没有任何作用,因为getter仍然使其对外界可见。
您需要删除 getBattery()
方法,以便对 SmartPhone
的用户隐藏实现细节。
封装的好处是它允许您稍后更改实现细节。如果你透露了Battery
,你以后就不能换成不同的电源,比如SolarPanel
,因为外界知道电池。
"the rule on encapsulation"没有更明确的定义,这个问题有点模棱两可。但我认为规则可能是something like this(来自维基百科):
Under the definition that encapsulation "can be used to hide data members and member functions", the internal representation of an object is generally hidden from view outside of the object's definition. Typically, only the object's own methods can directly inspect or manipulate its fields. Hiding the internals of the object protects its integrity by preventing users from setting the internal data of the component into an invalid or inconsistent state.
根据这个规则,一个对象应该负责维护它自己的状态,包括作为它的组件的任何其他对象的状态。如果不使用对象在其 public 接口中可用的增变器方法,其他代码应该不可能直接更改状态。
因此,SmartPhone
class 违反了封装,因为它提供了对其内部 battery
组件的访问权限,该组件具有 setter 方法。如果我们将电池的状态视为 phone 内部状态的一部分(因为电池是 phone 的一个组件),那么电池的 setter 方法允许变异该内部状态,而不是通过 phone 的修改器方法。
可能的解决方案不止一种。我的首选是使 Battery
class 不可变:
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
方法可以自由 return 对内部 battery
组件的引用,不会被其他代码修改,因为电池没有修改器方法。
然而,问题有点暗示 SmartPhone
class 是你应该改变的,而不是 Battery
class。也许出于某些其他原因,电池需要具有 setter 方法。在这种情况下,问题有点微妙;您的 class 当前编写的方式,对内部 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
class 的内部细节,并使 SmartPhone
class 看起来只具有所有四个属性:
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
,因为 class 无论如何都无法更改它们的值。
或者,我们可以制作防御性副本以确保我们的私有引用永远不会被其他代码使用:
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)
问题:
Class SmartPhone, given below, violates the rule on encapsulation. Re-write it so that encapsulation is preserved.
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;}
}
我看不到任何其他破坏封装的规则,我不记得为什么要破坏它。
是否因为我们从 class 外部返回另一个 class 的实例,在这个例子中它 battery
因此破坏了封装?
封装意味着隐藏内部表示。需要删除任何揭示实现的方法(特别是 getBattery()
)。我将从删除所有 getters.
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;
}
}
这封装了 phone.
实现的所有细节然后您应该添加表示 行为 的任何方法,phone 应该揭示这些行为,例如被转向的能力 on/off。
在我看来,getters 打破了封装,但有时这没关系(比如 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 int getVoltage() {
return battery.getVoltage();
}
}
更新
封装就是隐藏实现细节---"preventing unauthorized parties' direct access to them" (Wikipedia)。
接受的答案是错误的。通过封装使电池不可变没有任何作用,因为getter仍然使其对外界可见。
您需要删除 getBattery()
方法,以便对 SmartPhone
的用户隐藏实现细节。
封装的好处是它允许您稍后更改实现细节。如果你透露了Battery
,你以后就不能换成不同的电源,比如SolarPanel
,因为外界知道电池。
"the rule on encapsulation"没有更明确的定义,这个问题有点模棱两可。但我认为规则可能是something like this(来自维基百科):
Under the definition that encapsulation "can be used to hide data members and member functions", the internal representation of an object is generally hidden from view outside of the object's definition. Typically, only the object's own methods can directly inspect or manipulate its fields. Hiding the internals of the object protects its integrity by preventing users from setting the internal data of the component into an invalid or inconsistent state.
根据这个规则,一个对象应该负责维护它自己的状态,包括作为它的组件的任何其他对象的状态。如果不使用对象在其 public 接口中可用的增变器方法,其他代码应该不可能直接更改状态。
因此,SmartPhone
class 违反了封装,因为它提供了对其内部 battery
组件的访问权限,该组件具有 setter 方法。如果我们将电池的状态视为 phone 内部状态的一部分(因为电池是 phone 的一个组件),那么电池的 setter 方法允许变异该内部状态,而不是通过 phone 的修改器方法。
可能的解决方案不止一种。我的首选是使 Battery
class 不可变:
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
方法可以自由 return 对内部 battery
组件的引用,不会被其他代码修改,因为电池没有修改器方法。
然而,问题有点暗示 SmartPhone
class 是你应该改变的,而不是 Battery
class。也许出于某些其他原因,电池需要具有 setter 方法。在这种情况下,问题有点微妙;您的 class 当前编写的方式,对内部 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
class 的内部细节,并使 SmartPhone
class 看起来只具有所有四个属性:
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
,因为 class 无论如何都无法更改它们的值。
或者,我们可以制作防御性副本以确保我们的私有引用永远不会被其他代码使用:
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)