Tuesday, July 11, 2006

Avoid your getters and setters, Part 1

In the Java world, getters and setters are one of those things everyone says to hate but everyone actually uses.
I think this is mainly because:

  1. They are procedural, heritage of C-style (or even older) programming.

  2. They are a practice embedded by JavaBeans conventions.

  3. Many ORM and Web frameworks heavily use getters and setters.


In a series of blog posts called Avoid your getters and setters, I'll try to show you some ideas for avoiding getters and setters, by simply applying some fundamental Object Oriented and Domain Driven Design principles.

Any feedback will be highly appreciated.

Let's go!

Avoid your getters and setters: Programming to an Interface

From the GOF book:

Don't declare variables to be instances of particular concrete classes. Instead, commit only to an interface
defined by an abstract class.

This is probably the main Object Oriented principle: we could write words and words about it, but I prefer to remind you some good books like the one cited above, or like this one.

Programming to an interface is the first step toward good OO code.
Moreover, and here we come to our point, if you program to an interface, and think in terms of interfaces, you can better choose the getters and setters you need, while removing or hiding the unwanted ones.

First, thinking in terms of interfaces is thinking in terms of contracts.
Say you have a Customer entity in your domain model.
How does it interact with other entities?
What are its behaviours?
What its pre / post conditions and invariants?
That is: what is the contract defined by the Customer interface?

You may have an issue(Order ) method, and you may discover you need to know, for business reasons, the customer code.
So, here is our Customer interface:


public interface Customer {

public String getCustomerCode();

public void issue(Order newOrder);
}


Think always in terms of business contracts: all methods in your interfaces must have a business meaning.
Obviously, also getters and setters can have business meaning, which is the case of getCustomerCode().
All getters and setters without this meaning must be removed from the interface.

Here it comes your preferred ORM tool or Web Framework: it requires a lot of setters and getters for writing and reading properties, and you really don't want to do all that dirty job by hand!

What to do?

Too bad, we cannot actually avoid all those getters and setters, but we can hide them from your code simply declaring them in your concrete classes, and leaving them out from interfaces: your code, depending on interfaces, will never see all those unwanted methods!.

So you have a Customer concrete class:


public class CustomerImpl implements Customer{

...

public String getCustomerCode() { ... }

public void issue(Order newOrder) { ... }

// Setters and getters not in the interface:

public void setSurname(String surname) { ... }

public String getSurname() { ... }
}


What really hurts is the setSurname() method: in your business, once defined a customer's surname should never change!
But, hey: your code is written in terms of the Customer interface, so it will not be able to call unwanted methods!


public class CustomerService {

public void register(Customer c) { ... }
}


public class CustomerDAO {

public void save(Customer c) { ... }
}


Moreover, under the hood they actually are CustomerImpl objects, so your Web or ORM frameworks will be able to call all getters and setters thanks to Java reflection!



That's all.
And it's awful simple.
I hope to have explained well how you can hide getters and setters by simply programming to interfaces, while still using common frameworks.

In my next Avoid your getters and setters post we'll talk about the expert pattern ... in the meantime, let me know what do you think!

11 comments:

Roni & Cindy said...

I think you hit the nail with the argument that using getters and setters is the cause for sometimes missing more important business methods opportunities.

What I struggle to understand is why to go the trouble to define ENTITIES as interfaces and then create an implementation. What is the concrete reason not making Customer (which seems like a strong candidate) a class (abstract if you will)?

Anonymous said...

Hi Roni,

I think having an interface for an entity class A has the benefit that you can test easily another class B which collaborates with A (providing a mock for A), resulting in a "stricter" unit-test.

I would like to know what you guys think about this topic also :-)

Roni & Cindy said...

Good point..this is probably due to the fact that I'm recently new to TDD and my testing are a little naive.

Sergio Bossa said...

Hi Roni,

thanks for your answer.

> What I struggle to understand is why to
> go
> the trouble to define ENTITIES as
> interfaces and then create an
> implementation. What is the concrete
> reason not making Customer (which seems
> like a strong candidate) a class
> (abstract if you will)

This is an hot debate, because I read (and know) of a lot of people who prefer to not go to the trouble of defining interfaces for domain objects, advocating simplicity reasons.

I strongly disagree for a main reason: domain objects have business methods and business contracts, and interfaces are IMHO the best and proper (yet far from perfection) way to express them in Java.

Other reasons are those explained by me in my blog post, or those suggested by Alessio in his comment; however, the main reason is IMHO the one cited above.

What do you think?

Cheers,

Sergio B.

Anonymous said...

If you're using Hibernate for OR Mapping, then you could use field level mapping, and avoid having to expose getters and setters altogether.

Vivek Prahlad

Ricky Clarkson said...

Maybe it'd be a good idea to use DAO here, i.e., keep your 'unsafe' objects separate from your 'safe' ones. The safe ones would be guaranteed valid all the time, and probably really simple to implement.

Not only that but they could be immutable, which is often quite useful.

Even better would be if Hibernate et al knew about the builder pattern, then instead of having half-initialised objects around, you could have fully initialised objects, builders, that cannot accidentally be used as if they were the end product, thanks to type safety.

Alex Wei said...

Hi Sergio,
Thanks for your comment on my related post Is using domain entities in presentation layer encouraged?.
I understand your resolution, which I also adopt when I do pass domain objects to the web layer.
However, during the binding phase, the object is constructed but not in a valid state, while entity objects as described in DDD should always have valid state after construction.
To me, this is not a perfect solution.
I can think of a few solutions, none of them perfect:
1) Provide a validate() method, similar to Spring's afterPropertiesSet() method to check the data integrity. The drawback is it relies on the client's invocation of this method.
2) Use a builder pattern, as Ricky Clarkson suggested: bind the input to a builder instead of the domain object. However, it would be tricky in the update scenario. Preferrably the domain object should not be aware of the existence of builders.
3) Use mutable value objects as command objects. The domain object should always return a copy of the mutable value objects in defense of client's unintentional modification. The domain object also needs to provide an update method to accept the value objects updated in the web layer.

My two Australian cents. :-)

Kind Regards,
alex

Peter Lawrey said...

I would say 90%+ of getters and setters fail the DRY and YAGNI principles.

http://en.wikipedia.org/wiki/Don't_repeat_yourself
http://en.wikipedia.org/wiki/You_Ain't_Gonna_Need_It

Try using just plain fields and you won't look back.

praseodym said...

This is an excellent advice. However, I encountered a serious problem when trying to apply this in combination with JPA/Hibernate.

Look at this example:

public interface Player {
   // some methods, not important
}

public interface Match {
    Collection<Player> getPlayers();
}

@Entity
public MatchEntity implements Match {

    //this mapping won't work!
    @OneToMany
    private Collection<Player> players;

    public MatchEntity() { ... }

    @Override
    public Collection<Player> getPlayers() {
       return players;
    }
}

The field in the implementing class needs to have the same collection type ( <Player> ) as the collection type in the method in the interface, there's no way around that. At the same time, you cannot use the Player supertype for the JPA entity, it requires that the type of the annotated collection field to be an entity, too.

If anyone knows about a workaround, please let me know! I hope I am missing something, I really like this approach you explained in your post.

Sergio Bossa said...

Hi praseodym,

glad you've found the article useful, after so much time too ;)

Anyways, if I understand your problem correctly, you should specify the target entity on the oneToMany annotation to be the actual Player implementation (say PlayerImpl), as follows:

@OneToMany(targetEntity = PlayerImpl.class)

Let me know.
Cheers,

Sergio B.

praseodym said...

Thanks a lot, Sergio! You saved my day. :-)