DEV Community

Amir Ehsan Ahmadzadeh
Amir Ehsan Ahmadzadeh

Posted on

Why Use Getters and Setters?!

If you’ve worked with Python or JavaScript or any other language that supports OOP, you’ve likely come across getters and setters—special methods used to access or modify an object’s attributes. But if we can directly change attributes like this:

class Person:
    def __init__(self, name):
        self.name = name  # Directly accessible attribute

# Usage
p = Person("Alice")
print(p.name)  # Access directly
p.name = "Bob"  # Modify directly
print(p.name)
Enter fullscreen mode Exit fullscreen mode

Why would we ever need getters and setters? Isn’t this redundant?

It’s a valid question. In this article, we’ll break down why getters and setters are useful, when you should use them, and when you can skip them.


What Are Getters and Setters?

In object-oriented programming, getters and setters are methods used to access and modify an object's attributes. Here's a simple example in Python:

class Person:
    def __init__(self, name):
        self._name = name  # Protected attribute

    @property
    def name(self):
        return self._name  # Getter

    @name.setter
    def name(self, value):
        if not value:
            raise ValueError("Name cannot be empty")  # Validation
        self._name = value  # Setter
Enter fullscreen mode Exit fullscreen mode

In JavaScript, it looks like this:

class Person {
    constructor(name) {
        this._name = name; // Protected attribute
    }

    get name() {
        return this._name; // Getter
    }

    set name(value) {
        if (!value) {
            throw new Error("Name cannot be empty"); // Validation
        }
        this._name = value; // Setter
    }
}
Enter fullscreen mode Exit fullscreen mode

By wrapping attributes in methods, you gain control over how they’re accessed or modified.


Why Not Access Attributes Directly?

The argument for direct access is simplicity. Why add extra layers when you can just do this?

class Person:
    def __init__(self, name):
        self.name = name  # Use a regular attribute


# Try to changing it directly
p = Person("Alice")
print(p.name)
p.name = "Bob"
print(p.name)

# Output: 
# Alice
# Bob
Enter fullscreen mode Exit fullscreen mode

This works, but it leaves your attributes vulnerable to unintended or invalid changes. Let’s explore why getters and setters matter:

Validation and Control

Direct access gives you no way to enforce rules for the attribute’s value. For instance, consider a Person class where the name attribute must never be empty. With a setter, you can validate the value:
Python:

@name.setter
def name(self, value):
    if not value:
        raise ValueError("Name cannot be empty")

Enter fullscreen mode Exit fullscreen mode

JavaScript:

set name(value) {
    if (!value) {
        throw new Error("Name cannot be empty");
    }
    this._name = value;
}

Enter fullscreen mode Exit fullscreen mode

Without this, invalid values can sneak in:

p.name = ""  # No error without a setter!

Enter fullscreen mode Exit fullscreen mode

Read-Only Properties

Some attributes should only be readable, not writable. Getters allow you to expose data without allowing modifications.

Example: Calculating age from birth_year:
Python:

from datetime import date

class Person:
    def __init__(self, name, birth_year):
        self.name = name
        self.birth_year = birth_year

    @property
    def age(self):
        return date.today().year - self.birth_year  # Read-only attribute
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class Person {
    constructor(name, birthYear) {
        this.name = name;
        this.birthYear = birthYear;
    }

    get age() {
        return new Date().getFullYear() - this.birthYear; // Read-only attribute
    }
}
Enter fullscreen mode Exit fullscreen mode

Users can read age but not modify it directly:

p.age = 30  # Throws an error
Enter fullscreen mode Exit fullscreen mode

Future-Proofing

Exposing an attribute directly ties your internal implementation to your class's public interface. If you later decide to add logic (e.g., validating or formatting values), you’ll need to refactor all code that directly accesses the attribute.

Using getters and setters from the start makes future changes seamless.

Example: Changing how name is stored:

Python:

@name.setter
def name(self, value):
    self._name = value.capitalize()  # Always capitalize names
Enter fullscreen mode Exit fullscreen mode

JavaScript:

set name(value) {
    this._name = value.toUpperCase(); // Always store in uppercase
}
Enter fullscreen mode Exit fullscreen mode

With direct access, these changes would break existing code.

Lazy Computation

Python:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self._area = None

    @property
    def area(self):
        if self._area is None:
            self._area = self.width * self.height
        return self._area
Enter fullscreen mode Exit fullscreen mode

JavaScript:

get area() {
    if (!this._area) {
        this._area = this.width * this.height;
    }
    return this._area;
}
Enter fullscreen mode Exit fullscreen mode

Benefits of Using Getters and Setters

1. Better Control
Getters and setters give you fine-grained control over how attributes are accessed and modified. They allow you to enforce rules, validate input, or transform data as needed, ensuring consistency and reliability.

2. Encapsulation
They support encapsulation by hiding the internal implementation of attributes and exposing only what is necessary. This helps maintain a clean, modular, and maintainable class design.

3. Reusability
By implementing logic directly within setters, you eliminate the need for external validation or transformation code. This centralization ensures that users of the class don’t need to write additional code to handle common operations, such as validation or formatting. As a result, your class becomes easier to use and more consistent across different parts of your project.

4. Security
Restricting direct access to attributes provides an added layer of security. Sensitive or critical data is protected from accidental or unauthorized modifications, enhancing the robustness of your code.

5. Future-Proofing
Getters and setters decouple the attribute’s external access from its internal implementation. This makes it easier to adapt to future changes, such as adding new features, validations, or logic, without breaking existing code.


What Do You Think?

Have you used getters and setters in your projects? Are they overkill, or do they help you write better code? Share your thoughts in the comments!

Top comments (0)