`
鬼大来晚了
  • 浏览: 67843 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

【转载】为什么我们要使用私有的实例变量呢?

 
阅读更多
本文转载地址:http://www.importnew.com/9716.html

为什么我们要使用私有的实例变量呢?因为我们不希望其他类直接的依赖于这些变量。而且在心血来潮时,我们还可以灵活的修改变量类型和实现。然而,为什么程序员们都自动在对象中加入getter和setter方法,以此对外暴露私有变量,就如同这些变量是公有的一样。

存取方法

存取方法(又被称为getters和setters)是一些可以用来读写对象实例变量值的方法。


public class AccessorExample {
private String attribute;
 
public String getAttribute() {
 return attribute;
}
 
public void setAttribute(String attribute) {
 this.attribute = attribute;
}
}

为什么使用存取方法?

在类中使用存取方法而非直接暴露属性是有理由的。

Getter和Setter使得API更加的稳定。比如,假设类中有一个公共属性,它可以被其他类直接存取。一段时间后,你想要在读取或保存这个公共属性的时候添加额外的逻辑。这将影响到已经使用这个API的类。所以对这个公共属性的任何改变都会导致引用这个属性的其他类的改变。相反,使用存取方法,我们可以随后很容易的添加其他的一些逻辑,比如缓存数据,延迟加载。而且,如果新的属性值与旧的属性值不同,我们还可以触发属性改变事件。所有这些对于通过使用存取方法获取值的类来说都是透明的。

是否要对所有的属性都是使用存取方法?

属性可以被声明为包级私有或是私有嵌套类可见。在这些类中,相对于使用存取方法而言,对外直接暴露属性字段可以减少类定义和调用代码中的视觉混乱。

如果一个类是包级私有或是私有嵌套类可见,假设它的属性字段很好的描述了类所提供的数据,那么对外暴露这些属性字段本质上是没有问题的。

这样的类被限制在类所声明的包内,同时调用代码受限于类内部表示。我们可以修改这个类,而不用改变任何包外的代码。而且,对于私有嵌套类,改动的范围进一步的被缩小到被嵌套类里。

使用公共属性的另一个例子是JavaSpace 请求对象。Ken Arnold讲述了他们决定使用公共属性,而不是带存取方法的私有属性的经历(详情)

人们被告知不要使用公共属性,公共属性不好,有时这会让人们感觉不舒服,而且时常人们会使用不容置疑的语气来论述。但是我们不是非常虔诚的那些人。制定规则是有理由的。对于私有属性规则的理由并不适用于这个特例。这是一个特殊的例外,我也告诉人们不要在他们的类中使用公共属性,但也存在例外。这就是这个规则的一个例外,因为仅仅说它是一个属性会更加简单和安全。我们退一步想一想:既然这样,为什么要这条规则呢?它是否适用呢?在这个例外中,它并不适用。

私有属性 + 公共存取方法 == 封装

考虑下面的例子


public class A {
public int a;
}

我们通常都认为以上是糟糕的代码风格,因为它破坏了封装性。替代方法是:


public class A {
private int a;
 
public void setA(int a) {
 this.a =a;
}
 
public int getA() {
 return this.a;
}
}

有人认为这样封装了属性。这真的实现了封装吗?

实际上,Getter/Setter和封装性没有任何关系。数据并没有比使用公共属性获得更多隐蔽或封装。其他的类对这个类的内部细节仍然了如指掌。类的改动可能会蔓延,迫使依赖它的其他类做出相应的修改。以这种方式使用的Getter和Setter通常破坏了封装性。一个真正完整封装的类是没有setter方法的,而且最好也没有getter方法。类应该负责使用自身的数据计算并返回结果,而不是从某个类获得数据并计算这些数据。

看下面的例子,


public class Screens {
private Map screens = new HashMap();
 
public Map getScreens() {
 return screens;
}
 
public void setScreens(Map screens) {
 this.screens = screens;
}
// remaining code here
}

如果我们需要获得一个特殊的页面,我们会编写以下的代码,

1
Screen s = (Screen)screens.get(screenId);
这里值得注意的是:

客户端代码需要从Map里获得一个对象并把它转换为合适的类型。而且,更糟糕的是Map的任何客户端代码都可以清空这个Map,这通常是我们所不希望的。

相同逻辑的替代实现方法是:

public class Screens {
private Map screens = new HashMap();
 
public Screen getById(String id) {
 return (Screen) screens.get(id);
}
// remaining code here
}

这样隐藏了Map实例和交互接口(Map)。

Getters和Setters的过度使用

创建私有属性,随后通过IDE自动生成所有这些属性的getters和setters方法,这和直接使用公共属性是一样的糟糕。

过度使用的一个原因是现在在IDE中仅仅需要使用几个点击事件就可以创建这些存取方法。这些完全无意义的getter/setter代码有时会比类的逻辑代码本身还要长,你会多次阅读这些代码,虽然你并不想这么做。

所有的属性都应该保持私有,但对不可改变的属性仅仅增加setter方法。增加一个不必要的getter会暴露内部结构,这也增加了代码耦合的机会。避免方案是在每次增加存取方法的时候,我们应该分析是否可以通过封装行为来替代存取方法。

让我们看看另一个例子,


public class Money {
private double amount;
 
public double getAmount() {
 return amount;
}
 
public void setAmount(double amount) {
 this.amount = amount;
}
 
//client
Money pocketMoney = new Money();
pocketMoney.setAmount(15d);
double amount = pocketMoney.getAmount();  // we know its double
pocketMoney.setAmount(amount + 10d);
}

依据以上的逻辑,假设我们随后认为数据类型double不够合适,而是应该使用BigDecimal,这样那些已经使用了这个类的客户端代码也会失效。

让我们重建上面的例子,


public class Money {
private BigDecimal amount;
 
public Money(String amount) {
this.amount = new BigDecimal(amount);
}
 
public void add(Money toAdd) {
amount = amount.add(toAdd.amount);
}
 
// client
Money balance1 = new Money("10.0");
Money balance2 = new Money("6.0");
balance1.add(balance2);
 
}

与之前直接请求数据不同,类负责增加它自己的值。使用这种方式,将来任何改变数据类型的请求都不需要改变任何客户端代码。这样,不仅仅封装了数据,而且也封装了数据的保存方式甚至数据是否存在的事实。

结论

通过使用存取方法来限制对属性变量的访问要优于直接使用公共属性变量。但是,为每一个属性都创建getter和setter方法确实有些极端。而且这也要根据具体的情况来定,有些时候你仅仅希望有一个单纯的数据对象而已。应该为真正需要的属性添加存取方法。一个类应该使用它自身的属性,并对外提供强大的功能,而不是仅仅作为一个被其他类操作的存储状态属性的存储池。
分享到:
评论

相关推荐

    C# 公有变量 私有变量 静态变量

    本文将深入探讨C#中的公有变量(public)、私有变量(private)和静态变量(static),并结合20171028的C#编程实践进行说明。 **公有变量(public)** 公有变量是可以被程序任何部分访问的成员,无论是在同一类中...

    反射修改私有成员变量例子

    在本例中,我们将使用 Java 反射机制来修改私有成员变量的值。 什么是 Java 反射机制? Java 反射机制是 Java 语言中一个内置的机制,它允许程序在运行时检查和修改类的结构和行为。反射机制提供了一种方式来在...

    ios demo,自定义一个类,实例变量和成员变量的实现

    为了声明实例变量,我们可以使用`@private`或`@protected`(默认为`@private`),然后跟随变量名和类型: ```objc @interface CustomClass : NSObject { NSString *instanceVariable; } @end ``` 在这个例子中,`...

    类变量、全局变量、实例变量, 多态、为什么ruby、ruby编码规范

    实例变量以`@`开头,它们是对象的私有属性,只能在对象内部访问。每个实例都有自己独立的一套实例变量,互不影响。 ```ruby class Person def initialize(name) @name = name end def introduce puts "Hi, I...

    Java反射机制修改私有成员变量的实现_Reflection

    3. **设置访问权限**:由于私有成员变量默认不允许外部访问,因此我们需要使用Field对象的`setAccessible(true)`方法,将其可访问性设置为true,这样就可以绕过访问控制检查。 4. **修改成员变量值**:最后,通过...

    C#net反射实现访问类中的私有变量或者方法

    在本篇文章中,我们将深入探讨如何利用C#中的反射机制来访问类中的私有变量或方法。通过一个具体的示例代码,我们将详细了解反射的基本概念、使用场景以及如何具体操作。 ### 一、C#反射机制简介 #### 1.1 什么是...

    java入门教程:数据类型_实例变量.docx

    例如,在`Employee`类中,`name`变量被初始化为`String`类型,而`salary`变量则被声明为私有(`private`),意味着它只能在类内部访问。为了对外提供访问这些实例变量的途径,通常我们会创建getter和setter方法。`...

    java入门教程:数据类型_实例变量.pdf

    在给出的`Employee`类示例中,我们看到`Employee`类如何使用实例变量来存储员工的姓名和薪水。通过构造器`Employee(String empName)`,我们可以为`name`变量初始化值,而`setSalary(double empSal)`方法允许我们改变...

    Java反射访问私有变量和私有方法.doc

    Java 反射访问私有变量和私有方法 Java 反射机制是 Java 语言中的一种强大的工具,使得我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在实际测试中,我们经常需要访问类的非...

    JavaScript私有变量实例详解

    需要注意的是,使用静态私有变量模式时,由于所有实例共享私有变量,因此当多个实例同时调用特权方法时,可能会引发同步问题。此外,如果作用域链过长,可能会导致内存消耗增加,影响性能。 总的来说,JavaScript的...

    Objective-C 2.0 with Cocoa Foundation--- 7,对象的初始化以及实例变量的作用域

    然而,为了保证一致性,Objective-C引入了`@synthesize`关键字,它自动为实例变量生成getter和setter方法。如果你选择不手动初始化,那么实例变量的初始值通常是`nil`或者对应类型的零值。 在Objective-C 2.0中,还...

    类私有变量的“偷窃”以及 构造函数“私有化”的实验

    1、类中声明的私有变量 都有谁可以访问?“私有”是对谁而言的?是类本身还是类的实例? msdn 中说: private Class members declared as private can be used only by member functions and friends (classes or ...

    c++访问私有private成员变量的常用方法

    例如,在上面的代码中,我们定义了 `getX` 和 `getY` 函数,返回私有成员变量 `x` 和 `y` 的值,然后在 `main` 函数中使用这些函数来访问私有成员变量。 4. 利用引用访问私有数据成员 这个方法利用引用来访问私有...

    部署安装WebPageTest私有实例_linux

    部署安装 WebPageTest 私有实例 Linux WebPageTest 是一款非常优秀的网页前端性能测试工具,原本是由 AOL 开发内部使用的工具,后来在 Google Code 上开源。WebPageTest 可以对网站的前端性能进行测试和分析,提供...

    js类中的公有变量和私有变量

    当我们创建一个 `Car` 的实例时,私有变量 `wheel` 在构造函数执行时被访问,而公有变量 `this.wheel` 可以在外部通过实例 `car1` 访问。 ##### 示例2:只声明不调用构造函数的情况 ```javascript function Car() ...

    Ruby中类变量和实例变量的比较

    ### Ruby中类变量和实例变量的比较 在Ruby编程语言中,类变量和实例变量都是用来存储数据的重要机制。它们虽然都是变量,但在用途、作用范围、生命周期等方面有着明显的区别。接下来,我们将详细介绍这两者之间的四...

    Python私有变量的用法共1页.pdf.zip

    在上面的例子中,`Employee`类有一个名为`__employee_id`的私有变量,它与`Person`类的`__name`私有变量是完全独立的,即使它们在名字上看起来相似。 总结来说,Python私有变量的用法主要包括: 1. 使用双下划线`__...

    java中静态变量和实例变量的区别详细介绍

    在Java编程语言中,静态变量和实例变量是两种不同类型的成员变量,它们在内存中的分配、生命周期、以及使用方式上有着显著的区别。本篇文章将详细探讨这两种变量的差异。 1. 内存分配: - 静态变量:静态变量是...

    C#静态变量与实例变量实例分析

    在C#编程语言中,了解和熟练掌握静态变量与实例变量是至关重要的。它们在程序设计中扮演着不同的角色,理解和正确使用它们可以优化代码结构并提高程序效率。以下是关于这两种变量的详细分析: 首先,从语法定义上看...

Global site tag (gtag.js) - Google Analytics