什么时候应该使用封装?

时间:2013-12-24 00:49:38

标签: java oop encapsulation

我正在完成Sun / Oracle的Trail(http://docs.oracle.com/javase/tutorial/java/TOC.html),并且不断重申封装的重要性。

封装真的有多重要?我的意思是,如果我可能需要访问给定类字段的值,为什么我可以通过一种方法直接访问该字段?由于无论如何都会通过相应的对象访问该字段,这究竟会出错?

仅用于代码扩展性目的吗?换句话说,因为如果我决定在返回它之前想以某种方式改变或消毒该字段,我可以这样做吗?

我更喜欢一两个例子。

4 个答案:

答案 0 :(得分:3)

验证。

如果您不使用方法,则无法在该字段上添加任何验证,除非您在要访问该字段的每个位置验证它:不可持续。

它还会将您班级的数据与外界分开。通过隐藏方法后面的数据的实际实现,您可以按照您希望的方式(现在和将来)操作数据,并且不会破坏其他代码段。这允许您更改表示某些内容的方式而不会出现问题,只要您确保它仍然可以通过现有方法返回。

答案 1 :(得分:3)

封装不仅仅是为一个字段制作getter和setter。

关于:

  • 验证(以及一致性)
  • Hidding实现(编程到接口而不是实现)
  • 吸气剂和制定者不必反映真实的领域。对于按需计算值的字段,可能会有吸气剂(甚至是设定者)
  • 隐藏复杂性:getter / setter可以执行比设置值更复杂的事情
  • 高级:使用不同的实现/修改;如果您使用公共字段,则在ORM框架中使用的延迟加载等模式将无效

即使你如你所说“需要访问给定类字段的值”,你也不能确定这个要求不会改变(因为它会在大部分时间内发生)。

答案 2 :(得分:1)

实际上,我认为你正在以错误的方式思考这个问题。问题不在于封装本身,而是将对象的行为数据分离。

字段是数据 - 它们是对象内部状态的一部分。方法是对象的 API 的一部分。对象不应该只是字段集群 - 如果你的对象只是数据的愚蠢集合,那么这不是面向对象的编程,那只是结构化编程。

对象应该被设计为代表真实世界的实体,并且具有表示您可以对这些真实世界实体进行操作的方法。换句话说,你不要求一个对象的字段(例如getFoo()getBar())将这些字段传递给其他函数 - 而你应该把它相关操作(例如purchase()validate()等)直接作为对象的方法。

也就是说,使用访问器方法没有任何问题 - 有时您需要实际检索该值。但是通过创建那些访问者方法而不是直接暴露字段,您实现信息隐藏:您的类的用户不需要知道内部状态是什么样的能够使用它或从中获取数据。

基本上,在Java(或任何面向对象的语言)中,类是名词,方法是动词。如果你编写的课程没有任何动词,那么你就是programming in the kingdom of nouns

答案 3 :(得分:0)

封装允许您的对象通过为对象控制其自己的数据来进行保证(对象的契约的一部分),这恰好使调试变得更加容易。考虑这个课程:

public class TravelRoute {
    public int distance = 1000;
    public int travelSpeed = 60;

    public int calculateTravelTime() {
        return distance / travelSpeed;
    }
}

任何其他代码都可以将travelSpeed设置为零,这将导致将来对calculateTravelTime方法的所有调用都失败。更糟糕的是,你无法知道是谁将其设置为零,因此调试问题需要很长时间。

但是,通过封装,类可以完全控制值,并且保证它始终有效:

public class TravelRoute {
    private int distance = 1000;
    private int travelSpeed = 60;

    /**
     * This is GUARANTEED to return a positive value.
     */
    public int getTravelSpeed() {
        return travelSpeed;
    }

    /**
     * Sets this instance's travel speed.
     *
     * @throws IllegalArgumentException if argument is not positive
     */
    public void setTravelSpeed(int newSpeed) {
        if (newSpeed <= 0) {
            throw new IllegalArgumentException("Argument must be positive");
        }
        this.travelSpeed = newSpeed;
    }

    public int calculateTravelTime() {
        return distance / travelSpeed;
    }
}

现在,任何外部代码都绝对不可能将对象置于无效状态。如果有人试图这样做,那么产生的IllegalArgumentException将为您提供信息丰富的堆栈跟踪,它将立即暴露罪魁祸首。

作为奖励,使用此类的所有其他代码不再需要对其有效性进行任何检查,因为对象本身已经可以保证有效性。这使得每个人的整体开发速度更快。