代码之家  ›  专栏  ›  技术社区  ›  bancer

封装。精心设计的班级

  •  5
  • bancer  · 技术社区  · 14 年前

    今天我读了一本书,作者写道,在一个设计良好的类中,访问属性的唯一方法是通过其中一个类方法。这是一个被广泛接受的思想吗?为什么封装属性如此重要?不这样做的后果是什么?我之前在某个地方读到过,这提高了安全性或类似的东西。PHP或Java中的任何示例都会非常有用。

    6 回复  |  直到 11 年前
        1
  •  10
  •   Feanor    14 年前

    这是一个被广泛接受的思想吗?

    在面向对象的世界里,是的。

    为什么封装属性如此重要?不这样做的后果是什么?

    对象旨在成为包含数据和行为的内聚实体,其他对象可以通过公共接口以受控方式访问这些数据和行为。如果一个类不封装它的数据和行为,它就不再控制被访问的数据,并且不能与公共接口所隐含的其他对象完成它的契约。

    这方面的一个大问题是,如果一个类必须在内部进行更改,那么公共接口就不必更改。这样它就不会破坏任何代码,其他类也可以像以前一样继续使用它。

    PHP或Java中的任何示例都会非常有用。

    下面是一个Java示例:

    public class MyClass {
        // Should not be < 0
        public int importantValue;
        ...
        public void setImportantValue(int newValue) {
            if (newValue < 0) {
               throw new IllegalArgumentException("value cannot be < 0");
            }
        }
        ...
    }
    

    这里的问题是因为我还没有封装 importantValue 通过制作 private 不是公开的,任何人都可以绕过我在setter中设置的检查,以防止对象处于无效状态。 重要价值 不应小于0,但由于缺少封装,因此不可能阻止这样做。

        2
  •  3
  •   Blrfl    14 年前

    不这样做的后果是什么? 这样做了吗?

    封装背后的全部思想是,与类相关的所有知识(接口除外)都在类本身中。例如,允许直接访问属性将使您有责任确保执行分配的代码上的任何分配都是有效的。如果什么是有效的定义发生了变化,则必须使用类检查并审核所有内容,以确保它们一致。将规则封装在“setter”方法中意味着您只需在一个地方对其进行更改,任何尝试任何有趣操作的调用方都会得到一个异常。当属性更改时,您可能还需要做很多其他的事情,而setter就是您要做的地方。

    是否允许直接访问没有任何规则来绑定它们的属性(例如,任何适合于整数的属性都可以),这是一个很好的实践,值得商榷。为了一致性,我认为使用getter和setter是一个好主意,也就是说,您总是知道可以调用 setFoo() 改变 foo 属性,无需查看是否可以直接执行。它们还允许您将来验证类,这样如果您有额外的代码要执行,那么放置它的位置就已经存在了。

    就我个人而言,我认为必须使用getter和setter看起来很笨拙。我宁愿写信 x.foo = 34 x.setFoo(34) 并且期待有一天,当某些语言为成员提供相当于数据库触发器的代码时,这些成员允许您定义在分配之前、之后或而不是分配之后触发的代码。

        3
  •  1
  •   knitti freethinker    14 年前

    关于如何实现“好的ood”的意见不计其数,而且经验丰富的程序员和设计师往往在设计选择和理念上存在分歧。如果你问不同语言背景和范例的人,这可能是一场火焰战的开始。

    是的,理论和实践是一样的,所以语言的选择不应该对高层次的设计产生很大的影响。但在实践中,它们确实存在,因此会发生好的和坏的事情。

    我再加一句: 这要看情况而定。封装(在支持语言中)使您可以控制如何使用类,因此 可以 告诉人们:这是API,你必须使用它。在其他语言(如python)中,官方API和非正式(可能会发生变化)接口之间的区别仅在于命名约定。( after all, we're all consenting adults here )

    封装不是安全功能。

        4
  •  1
  •   Jon    14 年前

    另一个思考

    带访问器的封装在将来也提供了更好的可维护性。在上面的Feanor的回答中,执行安全检查(假设您的instvar是私有的)非常有效,但是它可以有更多的好处。

    考虑以下情况:
    1)您完成应用程序,并将其分发给一些用户(内部、外部,无论什么)。
    2)BigCustomera接近您的团队,希望在产品中添加审计跟踪。

    如果每个人都在代码中使用访问器方法,那么实现这一点就变得非常简单。像这样:

    MyAPI 1.0版

    public class MyClass {
        private int importantValue;
        ...
        public void setImportantValue(int newValue) {
            if (newValue < 0) {
               throw new IllegalArgumentException("value cannot be < 0");
            }
            importantValue = newValue;
        }
        ...
    }
    



    myapi v1.1(现在有审计跟踪)

    public class MyClass {
        private int importantValue;
        ...
        public void setImportantValue(int newValue) {
            if (newValue < 0) {
               throw new IllegalArgumentException("value cannot be < 0");
            }
            this.addAuditTrail("importantValue", importantValue, newValue);
            importantValue = newValue;
        }
        ...
    }
    

    API的现有用户对其代码不做任何更改,新功能(审计跟踪)现在可用。
    如果不使用访问器进行封装,您将面临巨大的迁移工作。


    当第一次编码时,它看起来像很多工作。打字速度快得多: class.varName = something VS class.setVarName(something); 但如果每个人都采取了简单的方法,为BigCustomera的功能请求付费将是一项巨大的努力。

        5
  •  1
  •   ArBR    12 年前

    对象定向编程 有一个原则被称为(http://en.wikipedia.org/wiki/open/closed_principle): PoC 开闭原理 . 这一原则适用于: 一个好的类设计应该为可扩展性(继承)打开,但为修改内部成员(封装)关闭。 . 这意味着,如果不注意对象的状态,就无法对其进行修改。

    因此,新的语言只通过属性(C++/Java中的GETTER和SETTES方法)修改内部变量(字段)。在C属性中,编译为MSIL中的方法。

    C:

    int _myproperty = 0;
    public int MyProperty
    {
        get { return _myproperty; }
        set { if (_someVarieble = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; }  }    
    }
    

    C++/Java:

    int _myproperty = 0;
    public void setMyProperty(int value) 
    {
      if (value = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; }
    }
    public int getMyProperty() 
    {
        return _myproperty;
    }
    
        6
  •  0
  •   InfZero    11 年前

    把这些想法(从 头一C ):

    • 想想田地可能被滥用的方法。如果设置不正确会出什么问题。
    • 你班上的一切都公开了吗?花些时间考虑封装。
    • 哪些字段需要处理或计算?他们是主要的候选人。
    • 只有在需要时才将字段和方法公开。如果你没有理由公开某件事,就不要公开。