I'm an Agile and Lean Strategist specialised in coaching and managing the transformation of IT departments, from startups to enterprise-scale organisations, to highly efficient, productive and energised environments. I also have experience as senior development manager and architecture governance in large enterprises, especially in the finance sector. Marco is a DZone MVB and is not an employee of DZone and has posted 26 posts at DZone. You can read more from them at their website. View Full User Profile

Reflection or the "setter" antipattern?

03.25.2011
| 5136 views |
  • submit to reddit

Recently I came across an interesting dilemma. For some time up to now I've been including setter methods in my service interfaces to allow unit tests to setup mocks. The code though had a bad smell; all too often I kept asking the following question: why did my interfaces need to expose setters for internal details (e.g. the components used to achieve a certain functionality)? If I was a user of this API, would I like to see a bunch of setter methods which have nothing to do with the business functionality? All the time the answer was no.

An API should expose the minimum possible, both in terms of actual classes/operations and in terms of state. A business service is just that:  a collection of operations and state to perform some business functionality: exposing to clients setter methods which allow to setup the internal components is not necessary nor optimal; it just clutters the API with unnecessary signatures. This is what I call the setter antipattern.

So since I'm using Spring test support classes also for my unit testing, I came across the ReflectionTestUtil class, which in few words allows one to do so:

Service service = new ServiceImpl();

Dao dao = new ServiceDao();

ReflectionTestUtil.setField(service, "serviceDao", dao); //serviceDao should actually be a constant

With this class I can avoid exposing unnecessary setter methods in my interface (Service in this case) while still preserving encapsulation. However the price to pay is compile time check. Let's say that a refactoring renames the Service attribute "serviceDao" into something else, say "clientServiceDao" or whatever...Now my test is broken because the ReflectionTestUtil statement will fail.

So which one to chose? The setter antipattern or reflection? I think the answer depends on how much value one gives in keeping a clean API, free from boilerplate signatures. I realised that to me a clean API represents the greatest value, and if someone refactored my code by renaming variables used by the ReflectionTestUtil class, the tests would fail and the fix would be, although annoying, quite easy to make, especially if the field name was captured in a String constant.

Happy technology to everyone.

Marco

References
Published at DZone with permission of Marco Tedone, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Richard Midwinter replied on Sat, 2011/03/26 - 3:03am

Use dependency injection, e.g. Guice, for this, surely?

Xavier Dury replied on Sat, 2011/03/26 - 3:28am

how much times do we have to read this article? it has already been posted in the article section not so long ago: http://java.dzone.com/articles/reflection-or-setter

Sam Lewis replied on Sat, 2011/03/26 - 11:55am

Sensible

ServiceImpl service = new ServiceImpl();
Dao dao = new ServiceDao();
service.setServiceDao(dao);

Dumb

Service service = new ServiceImpl();
Dao dao = new ServiceDao();
((ServiceImpl)service).setServiceDao(dao);

Dumber

Service service = new ServiceImpl();
Dao dao = new ServiceDao();
ReflectionTestUtil.setField(service, "serviceDao", dao);

Esko Luontola replied on Sat, 2011/03/26 - 1:31pm

Just use a constructor argument.

Alessandro Santini replied on Sat, 2011/03/26 - 2:26pm in response to: Sam Lewis

+1. It's all said here.

Bruce Fancher replied on Sat, 2011/03/26 - 7:10pm in response to: Sam Lewis

You took the words out of mouth, although I might change "Dumber" to "Dumbest" or maybe even "Completely *$&%ing retarded."

Hrabur Trendafilov replied on Tue, 2011/03/29 - 3:49pm

+1 for Sam's reply

 Work against interfaces. Put your setter in the implementation. Test the implementation

Reflection is something awful when you try to refactor your code using the refactoring tools of Eclipse or any other IDE. So with reflection it may appear you have to manually fix your tests if you refactor your code. Even more "Open call hierarchy" or "Find usages" won't work

Chung Fang replied on Fri, 2011/08/12 - 5:27am

Hello

So what is Cert Sites really all about? The following report includes some fascinating information about Cert Sites--info you can use, not just the old stuff they used to tell you.

 

Truthfully, the only difference between you and Cert Sites experts is time. pass4sure 000-107 If you'll invest a little more time in reading, you'll be that much nearer to expert status when it comes to Cert Sites.

I like your post its quite informative and i love to visit you again as you have done a wonderful job. i love to bookmark this site and would send it to other friends to read it and visit it to get upto date and quite interesting information pass4sure 1z0-536, i like the way you are working,keep it up, i will be here again as i get some time from pass4sure 642-188 because they make me little busy, but i would love to stuck here again image pass4sure 646-204. Thanks for sharing nice information with us.

Those who only know one or two facts about Cert Sites can be confused by misleading information. The best way to help those who are misled is to gently correct them with the truths you're learning here.

James Kear replied on Tue, 2011/09/06 - 2:59pm

In the end a lot of your application's performance depends on the overall quality of your code. hire a programmers

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.