loading...
Cover image for More about Setter and Getter and OOP

More about Setter and Getter and OOP

jorgecc profile image Jorge Castro Updated on ・7 min read

It is for business projects.

Architecture

Business Object

In the past, our architecture was composed of classes called "Business Object" (it also has some other names but the idea is the same), it was a type of class that contains logic (functions) and store values (aka fields).

For example, let's say we have a system that manages Customers.

public class Customer {
    private String age;
    private String name;
    private String prefix;
    // setter and getters goes here...

    public void showMe() { // some logic
        System.out.println("My name is "+name);
    }
}

It works fine for small projects. It's not rare to find books and manuals talking about this kind of classes. But they are not scalable and it presents some troubles. So, we split this class in two:

Anemic Object aka Model

It's a class that stores values it should lack logic.

public class Customer {
    private String age;
    private String name;
    private String prefix;
    // setter and getters goes here...

}

It is our class in the database

age name prefix
10 John Phd
20 Annah Dr.
30 Paul Junior

And it is our class via rest service

{"age":10,"name":"John","prefix":"Phd"}

It could have logic but it is not recommended.

Service Class

Contrary to the Model class, the service class lacks fields while it contains the logic (method/functions).

public class CustomerService {    
    public void showMe() {
        System.out.println("My name is "+name);
    }
}

Why we split the class?.

It is because is easy to debug and to maintain the code. It also helps to escalate the project and to add new functionalities without breaking other code.

For example, let's say we want to add methods that connect to the database

public class CustomerMySql {   // usually it is called CustomerRepo etc.
    public List<Customer> listAll() {
        // our code
    }
}

What is it?. A service class. In fact, we could have as many service class as we want to.

So, we also separate the service classes in categories, commonly:

  • Repository, DAO, DAL classes (that connects to the database)
  • Web Service classes.
  • Logic Classes (such as validations, factory, and such)
  • Others.

And again, why? Because it's easy to debug and to maintain the code. Let's say we have a problem, our system is unable to insert a customer into the database. So, we find the namespace (MySql), then we find the class that starts with Customer and we found the method.

Question: But what about setter and getter?.

Well, in our Model class, we are adding logic, logic that most of the time is useless!.

It is logic:

public class Customer {
    // ....
    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

We could use Lombok (it is highly recommended) to HIDE it but it is still logic.

@Getter @Setter // lombok
public class Customer {
    private String age;
    private String name;
    private String prefix;
    // we don't need setter and getter.
}

Lombok works at compile time, so it is the same to add code before it is compiled (but the code is added anyways).

Question: But OOP

But OOP requires encapsulation.

https://memegenerator.net/img/instances/65956115.jpg

OOP is not a law, its a guideline.

Question: What is the problem with setter and getter?

The clarity of the code.

It is how we use the model with setter and getter.

Customer cus=new Customer();
cus.setName("John");
System.out.println("The age is "+cus.getAge());

And it is the version with public fields.

Customer cus=new Customer();
cus.name="John";
System.out.println("The age is "+cus.age);

Question: We couldn't add logic with public fields, are we?

Let's say the next code

    public void setName(String name) {
        this.name = name;
        this.prefix="Junior";
    }

So if we modify the name then we also modify the prefix. The problem to add logic in the setter and getter it is not obvious.

For example:

Customer cus=new Customer();
cus.setPrefix("Dr.");
cus.setName("John");

// it will return Junior John instead of Dr. John. 

Adding logic to the setter and getter is usually evil.

Also, we could write the logic in the service class, where it is easy to debug and test.

Question: We should encapsulate the values.

Yes, but it is not solved by setter and getter!.

For example, let's say our model contains a new field (it's an object)

public class Customer {
    private String age;
    private String name;
    private String prefix;
    private Category category;
    // setter and getters
}
public class Category {
    private String nameCategory;
    // setter and getters
}

For example, let's say we want to modify the name inside the category:

Customer cus=new Customer();
cus.setName("John");
cus.getCategory().setNameCategory(""); // Using get to set a value @_@.

So our getter not only lacks encapsulation (we are modifying a field using a get) but it is also not clean.

Question: Then, how we could encapsulate values?.

First, every (JAVA but it also applies for other languages) developer must understand the difference between primitive versus object and the difference between by value and by reference.

We could also encapsulate the value by creating a clone but we don't do for obvious reasons.

Also, if we read a value that it must be immutable then why we are modifying it?. And it is here where we apply SRP (Simple Responsibility Principle), we do one task at the same time: or we modify the value or we read the value, not both.

We couldn't do Unit test without setter and getter.

I hear it often

https://thumbs.gfycat.com/ExhaustedObviousHapuka-size_restricted.gif

We must not do Unit test of code generated (because it's cheating!). Also, we mustn't do unit test of trivial code. It affects the code coverage however, the code coverage is just a number, not a measure of something worth (really). Many developers play the system by increasing the code coverage artificially.

We couldn't use Interface without setter and getter.

Interface is useful because we could reuse code.

Some java coders create one Interface = One class and it is SO WRONG but I'm not talking about it.

And about SOLID (I = Interface)

https://memegenerator.net/img/instances/65956115.jpg
SOLID is a guideline, not a rule.

But about Interface, let's say we have the next models:

  • Customer (age, name, prefix)
  • Employee (age, name, company)

So it could make sense to create an interface if not an inheritance. (tips: not really).

  • Person (age, name)

So our classes are implemented as

public class Customer implements Person {
   // implementation
}
public class Employee implements Person {
   // implementation
}

public interface Person {
    String getName();
    void setName(String name);
    String getAge();
    void setAge(String age);
}

However, the use of Interface adds DEPENDENCY.

Now, let's say we don't want to hold the age of the Customers. If we modify the interface, then we also modify the class Employee, ergo we found a problem because we added a dependency.

Interfaces are right if they are used with a clear purpose but it is also a double edge sword.

Also, it's hard to debug code with Interface.

Let's say this code fails:

   SomeInterface object;
   // it fails somewhere here.

Question: what is the class of the object?

However

Not every object is similar. For example, a visual object, a button:

https://d2d3qesrx8xj6s.cloudfront.net/img/screenshots/898a5a0793fe8369472aa3a8c9018b0ad7e6170d.jpeg

In the case of a visual object, we don't use a Model+Service classes pair but a Bussiness Object class (a class with fields and functions). Why?. Because they fit better than an anemic model + service class.

public class MyButton {
    private String Title;
    private int width;
    private int height;
    private int top;
    private int left;
    // setter and getter also functions
}

We could also add logic to the setter and getter.

public class MyButton {
    //....
    public int getXRight() {
        return left+width; // we can use a getter with logic without a field.
    }
    public void setWidth(int width) {
        if(width<100) {
            width=100;
        }        
        this.width = width;
    }
}

Also

Sometimes we want to standardize the code, i.e. we don't want to see one style for one class and another style for another class.

For example, it is right (depending on the context:

class Model1 {
    public int field1;
    public int field2;
}
class Model2 {
    public int field3;
    public int field4;
}

And it is right too

class Model1 {
    private int field1;
    private int field2;
    // setter and getter goes here.
}
class Model2 {
    private int field3;
    private int field4;
    // setter and getter goes here.
}

But it is not.

class Model1 { // with setter and getters
    private int field1; 
    private int field2;
    // setter and getter goes here.
}
class Model2 { // with public fields
    public int field3;
    public int field4;
}

Also (more)

Some libraries require the use of setter and getter. JSF used to rely on setter and getter (but not anymore, we could use public fields).

So, there is not a one-solution-fits-all.

More about Java.

The Java Runtime Engine is sneaky and it understands the setter and getter. It optimizes the result code, so if we add a regular setter and getter (without extra logic), then the JRE acts the same than a public field.

Final world.

but, can we use a model class with public fields?. Yes. C works in this fashion (C is not OOP) and some other languages.

struct account {
   int age;
   char *name;
   char *prefix;
};

We usually use setter and getter because they are generated automatically (via refactorization or Lombok) but we use it automatically then, the JIT compiler ends removing it anyways.

https://pursuit.ca/wp-content/uploads/2018/09/iStock-628370976.jpg
JIT removes automatically most setter and getter that were generated, ahem automatically, so why are we adding?

People forget that (we) developers are (still) humans.

https://res.cloudinary.com/practicaldev/image/fetch/s--5iYUkjQO--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/ufp89lszawxw7fbfdisc.jpg

For example and I will repeat myself

object.getField().getOtherField().getOhNoesAnotherField().setFinalField("Some Name"); 

It is not clear, while (using public fields)

object.field.otherField.ohNoesAnotherfield.finalField="Some Name"; 

It's not only short to write but also to understand.

As a note code like this one:

object.getField().getOtherField().someMethod<Class,OtherClass>(arg,moreargs); // is cringe

C# solved it by pleasing every pro encapsulation versus against encapsulation by creating properties. So in C# we used:

object.Field.OtherField.OhNoesAnotherField.FinalField="Some Name"; 

Java doesn't have it and it is a shame.

Example

Let's say we want to show the customer but we need a new column (id), it must be generated automatically and we couldn't generate on the view/template layer.

Id age name prefix
1 10 John Phd
2 20 Annah Dr.
3 30 Paul Junior

We don't need to poison our model class with a new setter and getter.

public class CustomerUI extends Customer {
    private int id;
    public int getId() {
        id=id+1;
        return id;
    }
}

So this class works specifically to solve a visual problem. We could remove the setter and getter of the Customer class while we retain some logic

Discussion

pic
Editor guide
Collapse
_rice_salad profile image
Rhys S

On your point about not wanting to separate the responsibilities of getting and setting values: I will always make data objects immutable and variables final. If I want to change the value of a field, I use a builder and seed it with my original object.

Collapse
jorgecc profile image
Jorge Castro Author

Do you have an example about it?