There has been a lot of discussion about getters and setters, spanning different programming languages.
For example in Java, there are programmers that write classes where all fields always have both public getters and setters. When you ask them for the reason, they say "It can be easily done with my IDE", or "I thought this is required".
In JavaScript, getters and setters are not a programming tradition, because JS is not an object-oriented language. Nevertheless people try to use it as such, and you find getters and setters also in JS code.
I believe the get / set tradition started with C++, which was the first real object-oriented language. There the getter is called "accessor", and the setter "mutator".
A getter is a method that returns the value of a "member"-field, and a setter lets change its value. It is a stereotype used in object-oriented languages. Here is an example for a Java Bean.
public class Person
{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Following the Java Beans Specification we call "name" a property, and an instance of Person
a bean. Thus a bean is a container for 1..n properties. A bean is meant to be the smallest building-block for composing reusable software.
The "name" property is a read/write property, because it has both getter and setter. Would it have only getter, it was read-only. Would it have neither getter nor setter, it was just a field, not a property.
In the bean-example above, getters and setters can be used for implementing read-only access. In other words, when you make a field private
in Java, you can neither read nor write it from outside the class. When you make the field protected
or public
, it would always have both read- and write-access in the same way. You can not implement read-only properties other than by
private
, There is no readonly
access-modifier in Java which would make it writeable to just the owning class.
Here is the Person
bean with a read-only property "name". Mind that "name" is still writeable within the owning class!
public class Person
{
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
So whether you need getters and setters seems to depend on the programming-language. Assuming there is a programming language that provides a readonly
access modifier, would we still need getters?
Let's say "No". But, we would still like to use setters!
public class Person
{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
fireChanged();
this.name = name;
}
private void fireChanged() {
.... // TODO
}
}
This bean has a little "logic" in it. It notifies listeners as soon as one of its properties was changed. How could we implement value-change logic safely when not using a setter?
There is also another common reason for using getters and setters, that is software maintenance. Imagine we made the "name" field public
and provided neither getter nor setter. All surrounding code that uses objects of that class now reads and writes to the field directly. (We reduced the lines of code for this class greatly :-)
public class Person
{
public String name;
}
So this is how a Person
looks like now.
Unfortunately a month later it turns out that the name of the person must be provided by another database class PersonData
, and Person
must delegate to that new class now.
public class PersonData
{
public String name;
}
public class Person
{
public PersonData data;
public String getName() {
return data.name;
}
}
Now all the classes that use person.name
must be rewritten. When you consider that domain-objects like Person
are the most widely used in an application, and that the field-access needs to be refactored to be a method-call, you might agree that using getters and setters makes sense, at least with a programming language like Java (which is still one of the most modern ones).
Of course the same applies to PersonData
, so here it is how they should look like:
public class PersonData
{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Person
{
private final PersonData data;
public Person(PersonData data) {
assert data != null;
this.data = data;
}
public String getName() {
return data.getName();
}
public void setName(String name) {
data.setName(name);
}
}
There is one last reason why I would like to have a getter. This reason may have expired in environments where debuggers let set watch-points to detect field modifications. But in environments where debuggers do not allow such, I would like to set a breakpoint into the getter to find out what the heck modifies that field so that this bug occurs - !
In the following example, the getFullName()
method accesses firstName
and lastName
directly, ignoring their getters. The question rises whether you are allowed to directly access a field, within its class, in case a getter exists (same for setter).
public class Person
{
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getFullName() {
return firstName+" "+lastName;
}
}
The answer is "No". As soon as there is a getter, only the getter should be used. Think of the maintenance example above. This also applies to the owning class itself. And, as soon as a setter exist, value-change logic could have been implemented, which may be ignored when accessing the field directly.
So, as soon as you introduce a getter or setter, refactor the class to not access the field directly any more. Exception is access in constructor, because you should not call overrideable methods from constructor.
public class Person
{
....
public String getFullName() {
return getFirstName()+" "+getLastName();
}
}
If this sounds now like me trying to convince you that setters and getters always make sense: No, I did not say that! Getters and setters sometimes make sense. I just showed some of the situations where you need to encapsulate fields.
private
, else package-visible or protected final
, worst is public
ɔ⃝ Fritz Ritzberger, 2016-10-16