我想看到一个示例,该示例直接从使用公开声明的数据成员的类中获取代码,以查看封装不良的示例,因此,通过与不良示例进行对比,我可以理解OOP中封装的良好示例。(被告知要使用封装而没有不好的例子,就像被告知不要在不了解偷窃对我来说是偷窃一样。)谢谢。
答案 0 :(得分:2)
假设您有一个Counter
类,
increment
)封装不好的版本将直接公开内部计数器值:
class Counter {
public int value;
public Counter() {
this.value = 0;
}
public int increment() {
return ++this.value;
}
}
当然,问题在于该类的用户可以做到这一点:
Counter c = new Counter();
System.out.println(c.value); // 0
c.increment();
System.out.println(c.value); // 1
c.value = 42;
System.out.println(c.value); // 42
正确的封装可以纠正以下问题:
class Counter {
private int value; // *** Private
public Counter() {
this.value = 0;
}
public int increment() {
return ++this.value;
}
public int getValue() { // *** Accessor
return this.value;
}
}
现在,班级用户无法直接设置value
。
Counter c = new Counter();
System.out.println(c.getValue()); // 0
c.increment();
System.out.println(c.getValue()); // 1
// No equivalent to `c.value = 42` is possible here¹
¹(不使用反射)
答案 1 :(得分:0)
您的问题是一个有用的问题,因为了解封装很重要的原因将帮助您避免过于概括性地讲解原理,并帮助您充分理解封装原理。
您可以在此处找到封装效果不佳的示例:https://github.com/dotnet/training-tutorials/blob/master/content/csharp/getting-started/encapsulation-oop.md当示例中的类被其他代码用于执行常规操作时,由于未封装该类,因此会产生问题。 (其他示例可能会说明由 poor 封装而不是 lack 封装引起的问题,但我知道您需要一个基本概念的示例。)>
很多时候,由于没有封装代码而导致的问题是,当属性和/或对象是您实际希望更新或删除的对象的副本时,属性和/或对象将被更新或删除。
这是链接示例的一些相关部分。第一个引号描述了该类缺少封装时所产生的问题:
请注意,在此示例中,用于打印订单的技术是while循环,该循环会在打印每条记录时将其丢弃。这是一个实现细节,如果正确处理了此循环使用的集合,则不会造成任何问题。不幸的是,即使使用本地范围内的订单变量来表示集合,对RemoveAt的调用实际上仍在从基础Customer对象中删除记录。在计划结束时,两个客户都有0个订单。这不是预期的行为。
第二个引号中指出,可以通过不同的实现方式“解决”该问题,但可以完全避免使用封装:
有多种方法可以解决此问题,最简单的方法是将while循环更改为foreach,但是潜在的问题是Customer不能以任何方式封装其Orders属性。即使不允许其他类设置该属性,它公开的List类型也会破坏封装,并允许协作者任意删除甚至清除集合的内容。
此示例很好地说明了封装的需求不是绝对的,但肯定是最佳实践。