Working with class attributes directly
I heard it is not really good idea to work with class attributes directly, instead using setters and getters. Ca开发者_JS百科n somebody tell me why?
Funnily enough the wikipedia article on this subject gives good guidance: http://en.wikipedia.org/wiki/Accessor
In short, a class should use mutator methods (getters, setters etc) to validate the input before it is stored privately. Doing so ensures the class can always be relied upon elsewhere in the code.
By contrast, if the attribute is globably available, anybody could modify it. If you have, for example, a string that needs to be a specific format and somebody adds a value that isn't correct, bugs may result.
In a nutshell, if you code to interfaces (which contain getters and setters to a private field) then you can swap out any object that conforms to the interface. External references to fields of the object mean that the external users know about the implementation. This creates coupling between the consumer of the object and the implementation of the object, which in this case is a bad thing.
Today your name
field might be stored as a String
but next year you might want to update it to some more interesting type or get the information from somewhere else or anything ... who knows? Using a field prevents you from doing that. Using an accessor lets you change the implementation however you want. This is a more important principle than you might think.
You may find this of interest: Coding to interfaces?
It just depends; For simple changes within the class (bool member), it's probably faster to just change it. If its a slightly more complicated (MyDate), then you may want to implement some validation.
Most people keep member data private, thus get/set or methods must be used to modify those data from outside the class.
It is a convention, albeit a strong one.
It is used by the JavaBean specification to offer a workaround to the fact that Java has no properties.
It allows to express the intent of read-only/write-only or read-write attributes.
It is very handy to place a breakpoint in a setter to see why an attribute has unexplanedly changed.
As already mentioned, interfaces cannot contain attributes, but can define accessors.
Sometimes you will want to create 'virtual' attributes, the classical example being complex numbers where you have the attributes x and y and offer mod and angle through calculation in the setters and getters.
Many API's rely on this convention to infer meaning of your class through reflection.
All IDE's now can generate them automatically so they do not hurt my fingers so much as in the past.
There are two classic reasons why you want to forbid outside access to fields (attributes). These are typically discussed in introductory OO programming classes.
- If the underlying data is private, you are free to change the implementation in the class. For example, let's say you store a complex number as real and imaginary part. Then later you decided you wanted to change that to store Z and theta (magnitude and angle). If you exposed the fields, then the change would break any classes that interacted with it. If you had used accessors then you could implement getReal() and getImaginary() in terms of Z and theta and not break anything.
- You can add other operations to the getters and (especially) setters. For example you could keep track of how many changes are made to a field, or update another field in sync with it, but only if you've forced any class that wants to change the field to go through an accessor to do it. With a public field you have no chance of doing these things.
What it really comes down to is that Java does not respect the Uniform Access Principle. That is, the client code for changing a field value (e.g. obj.field = newValue
) is very different from the code for invoking a setter (e.g. obj.setField(newValue)
).
If you start with a public field and later decide you really need a private member (e.g. to add validation or to change the underlying implementation of the member), all of your client code will break. As a result, you need to use a setter rather than a public field if there's any chance that you'll need the additional capabilities provided by accessors. The end-result is lots of unnecessary code:
public class Person {
private String _firstName;
private String _lastName;
public Person(String firstName, String lastName) {
_firstName = firstName;
_lastName = lastName;
}
public String getFirstName() {
return _firstName;
}
public void setFirstName(String name) {
_firstName = name;
}
public String getLastName() {
return _lastName;
}
public void setLastName(String name) {
_lastName = name;
}
}
In languages that respect the UAP, such as Scala, it is quite common to use public fields instead of getters/setters. The above class in Scala, for example, is written:
class Person(var firstName: String, var lastName: String)
// example client code:
val p = new Person("John", "Smith");
p.lastName = "Brown";
If I decide I need validation, I can replace this with:
class Person(firstName: String, lastName: String)
private var _firstName = validate(firstName);
private var _lastName = validate(lastName);
// getters
def firstName: String = _firstName
def lastName: String = _lastName
// setters
def firstName_=(name: String): Unit = {
_firstName = validate(name);
}
def lastName_=(name: String): Unit = {
_lastName = validate(name);
}
// validation
@throws(classOf[IllegalArgumentException])
private def validate(name: String): String = {
// ... validation code ...
name
}
}
// and client code doesn't break!
val p = new Person("John", "Smith");
p.lastName = "Brown";
精彩评论