I’ve been using Google Web Toolkit (GWT) off and on for the last 4 years (nonstop for the last 2), and was going over my experiences with a friend recently. GWT transpiles
Java code into Javascript, allowing you to effectively write Java for
the browser. There are some definite pros and cons to this approach,
here are some of my own thoughts on the subject.
[EDIT This blog post is just about my own personal experience. Recently Vaadin put out a very nice set of infographics about GWT which
covers a more statistically significant view of the strengths and
weaknesses of GWT since it surveyed many GWT developers.]
In
this post we’ll review the advantages. The advantage of GWT falls
into two main categories: the fact that you’re using Java, and the capabilities of the framework itself. Let’s start
with the first of these.
Because the front-end code is written in Java, you can leverage the ecosystem’s very mature IDEs such as Eclipse and Netbeans.
You can use the same IDE to develop the front end and the back end,
providing a seamless end-to-end development experience. The same is true
for debugging - the debugger steps from front end to back end and you
never have to leave your IDE. Other big advantages of using a mature IDE
include refactoring support, functionality to find usages, and other
code navigation.
You
can leverage language features not available in Javascript directly,
like a built in module system (Java’s packages). For large projects, it
is easier to use modules to manage one million lines of Java than one
million lines of Javascript. Support for unit testing in Java has been
first class for many years with JUnit and TestNG. Javascript does have
unit testing capability (say, with Jasmine), but I don’t have the impression that unit testing in Javascript is as widespread or easy as it is with Java.
Finally
for the “you’re using Java” aspect: you can leverage Java developers.
For a small software shop that doesn’t have the resources or needs for a
pure front end development team, it makes a lot of sense to make use of
the skills your developers already have. I have seen several workplaces where
this was the case, and it has generally worked out pretty well, making efficient use of existing development resources.
The
other advantage of GWT lies in the capabilities of the framework
itself. GWT as a framework doesn’t do anything that you can’t do with
the right combination of other Javascript libraries. But as an AJAX
framework, GWT pulls together a cohesive set of functionality for
building a RIA and gives it to you out of the box. For example: AJAX
handling (actually multiple communication techniques: GWT-RPC,
RequestFactory, and direct HTTP requests), history and place management, templating system with UI binder, form binding, and internationalization.
In
conclusion, GWT is a mature and proven product, it is still being
actively maintained and used, and it provides a lot of advantages to the
software shop where it’s a good fit (say, a small team of Java
developers who don’t have front end developers). As for whether or not
GWT is always a good choice: As usual, it depends. In the next segment
we’ll look at some of the disadvantages and see when you might not want
to use it.
Sunday, March 17, 2013
Sunday, March 10, 2013
Setting up HTTPS with CA certificates on Tomcat
There’s
no doubt that every website needs to consider security. And serving
critical portions of your site over HTTPS instead of HTTP is an
important first step to securing your site.
There are any number of tutorials for setting up SSL on Tomcat. Many of us have generated our own keys and self-signed certificates at some point for side projects and personal development, that part is easy. But when you are ready to take your site public and a browser finds a self-signed certificate and it’s not from a certificate authority (CA), the browser will display all kinds of scary warnings about your site being unknown and potentially dangerous (which objectively it is at that point).
To resolve this, you need to get a certificate from an actual CA. They will verify your identity and send you a certificate that you can use with your site, and your site will become part of the CA’s - and hopefully the web’s - circle of trust.
Some CA’s that provide inexpensive certificates include Thawte, Digicert, and GeoTrust. To get a certificate from a CA, you generate a private key and use it to create a Certificate Signing Request (CSR). Send the CSR to the CA, and they will send you back a certificate that is registered with them that you can use on your site.
Ok here are the steps to setting up HTTPS with CA certificates on Tomcat. If nothing else, take note of step 7 - it’s the hardest step to figure out if you don’t have these directions.
And there you have it! Enjoy serving your site with HTTPS, and don’t neglect all the other aspects of security that need to be addresses for your public-facing site.
There are any number of tutorials for setting up SSL on Tomcat. Many of us have generated our own keys and self-signed certificates at some point for side projects and personal development, that part is easy. But when you are ready to take your site public and a browser finds a self-signed certificate and it’s not from a certificate authority (CA), the browser will display all kinds of scary warnings about your site being unknown and potentially dangerous (which objectively it is at that point).
To resolve this, you need to get a certificate from an actual CA. They will verify your identity and send you a certificate that you can use with your site, and your site will become part of the CA’s - and hopefully the web’s - circle of trust.
Some CA’s that provide inexpensive certificates include Thawte, Digicert, and GeoTrust. To get a certificate from a CA, you generate a private key and use it to create a Certificate Signing Request (CSR). Send the CSR to the CA, and they will send you back a certificate that is registered with them that you can use on your site.
Ok here are the steps to setting up HTTPS with CA certificates on Tomcat. If nothing else, take note of step 7 - it’s the hardest step to figure out if you don’t have these directions.
- You’ll need the password for your java keystore, or you’ll need to create a new java keystore. If you don’t know it you can always create a new keystore and point tomcat to that. A keystore is just a single file with keys and certificates inside it. Don’t be afraid to experiment, if you create a key in your keystore that you don’t want, you can just delete that key. And since a keystore is just a single file, you can always back it up, restore it, start over, etc. If you forget information about your keystore (such as its password), and tomcat was already configured with it, you can just review the keystore information in tomcat’s server.xml file to recover that information.
- list the contents of my keystore: keytool -storepass password -list -keystore keystore.jks
- you can always delete a key if you mess something up: keytool -storepass password -delete -alias tomcat -keystore keystore.jks
- Read up on the documentation to make sure you understand what goes where. You can start with the Tomcat documentation.
- Use openssl to generate a certificate signing request (CSR). This creates a .csr and a .key, the .csr is what you send to your CA and they will return a .crt that corresponds to your private key.
- Keep the .key file, and send the .csr to your CA. The CA accepts the CSR and sends back possibly multiple .crt files (a root certificate, possibly some intermediates, and the one for your site).
- Create a new keystore if you don’t have one already (see step 1). Creating a new keystore is easy with these handy notes. You can create a keystore with a single dummy key and delete that key later if you want.
- You can import all of the certs from the CA into your keystore using the java keytool. The command looks something like this: keytool -storepass password -import -trustcacerts -alias intermediate_cert_name -file intermediate_file.crt -keystore keystore.jks
- Here is the tricky part: You also need to import the private key (the .key file created at the same time as the .csr) into the keystore. This is the tricky part because the Java Keytool does not natively import the .key file, and you need to use two steps.
- Use openssl to convert the key along with the .crt for your site (the .crt for your site that you got from theCA) into a .p12 file (technically another keystore) that the java keytool can import. The command looks something like this: openssl pkcs12 -export -in www_mycoolsite_com.crt -inkey mygeneratedkey.key -out www_mycoolsite_com.p12 -name "www_mycoolsite_com_private_key"
- Import the .p12 file as a keystore into your keystore. The command looks something like this: keytool -storepass password -importkeystore -srckeystore www_give3_org.p12 -srcstoretype PKCS12 -destkeystore keystore.jks
- Configure tomcat’s server.xml to use the https connector, you will need to modify the port, keystore file location, and keystore password. There are plenty examples of how this is done online.
- Finally, configure the app to serve the site on HTTPS. This is done in web.xml
- If the certificate is only good for “www.yourdomain.com” rather than “*.yourdomain.com”, you can test the certificate locally by editing your /etc/hosts file so the site corresponding to the site certificate actually goes to the local machine. Add a line like “127.0.0.1 www.yourdomain.com”
And there you have it! Enjoy serving your site with HTTPS, and don’t neglect all the other aspects of security that need to be addresses for your public-facing site.
Sunday, March 3, 2013
Strong Type Weak Type
I was playing around with handlebars the other day, and I got to a point where the json object going into my handlebars template had a date (number) that I wanted to convert and format into a date string.
I quickly ran through a few thoughts: Can I execute javascript inside the handlebars template? Nope. Can I use a JSP Scriptlet? No this is on the browser. Do I need to define a new string field on my domain object on the server just so the json comes through in a way that I can display what I want in the UI? Ugh, besides requiring a change to my domain model and recompiling, that is mixing up my server side code with my display which is not what I want.
Waaaait a second...
Javascript objects are dynamically typed! I can just format the date and add the string as a new property to the object in javascript before passing the object to my handlebars template, and reference that new property from inside the template. Problem: solved.
Ok, this sounds like a trivial exercise for anybody who has ever coded in a dynamically typed language. It even sounds trivial to me. But even after having read some javascript books (including Javascript the Good Parts... a few times), and writing and practicing javascript on the side, years of programming in Java have oriented my brain to a certain way of thinking. That way of thinking at a fundamental level includes the idea of redefining a class and recompiling if I want to add an attribute to an object.
The new ways of dynamic typing DO come. But they do not come first. That is the experience of a strongly typed programmer moving into a weakly typed language.
I quickly ran through a few thoughts: Can I execute javascript inside the handlebars template? Nope. Can I use a JSP Scriptlet? No this is on the browser. Do I need to define a new string field on my domain object on the server just so the json comes through in a way that I can display what I want in the UI? Ugh, besides requiring a change to my domain model and recompiling, that is mixing up my server side code with my display which is not what I want.
Waaaait a second...
Javascript objects are dynamically typed! I can just format the date and add the string as a new property to the object in javascript before passing the object to my handlebars template, and reference that new property from inside the template. Problem: solved.
Ok, this sounds like a trivial exercise for anybody who has ever coded in a dynamically typed language. It even sounds trivial to me. But even after having read some javascript books (including Javascript the Good Parts... a few times), and writing and practicing javascript on the side, years of programming in Java have oriented my brain to a certain way of thinking. That way of thinking at a fundamental level includes the idea of redefining a class and recompiling if I want to add an attribute to an object.
The new ways of dynamic typing DO come. But they do not come first. That is the experience of a strongly typed programmer moving into a weakly typed language.
Sunday, February 24, 2013
Dependency Injection Part V: When to use (or not)
Previously we looked at Dependency Injection in detail, looking at the advantages and disadvantages in detail, and examining ways to overcome some of the disadvantages. I’d like to summarize by exploring when to use it and when not to use it.
In general it would be a good idea to use DI when the advantages outweigh the disadvantages, or the disadvantages can be mitigated. A careful review of the advantages and disadvantages we’ve already discussed might point you in the right direction. We don’t have hard and fast rules to say “use it here” and “don’t use it there”, we really need to investigate the pros and cons and see how the apply to what we’re trying to build. The decision is an architectural one and generally needs to be made early in the design of an application.
As a rule of thumb however, using a DI container could be a good choice at the very least if you are starting a project and want a standard technique to compose subsystems and orchestrate the large scale construction. This is another advantage that I didn’t mention before, but many of the large scale structures in an application are singletons, and DI containers facilitate a solution to the Singleton anti-pattern. Rather than controlling singleton instantiation yourself (which is easy to get wrong, common solutions include double-checked locking or enum singletons) the container controls the instantiation of a singleton.
As another rule of thumb, there are times when DI might not be a good choice. If you are working with a large legacy system that does not use DI at all, it could be prohibitively expensive to bolt on. DI is high-level and is best introduced at design time. Java lang objects that really are implementation details, such as Lists and Maps should probably not be injected. And objects created late in the scope's lifecycle should be not injected (or the scope of the object should be rethought). Finally, if your project is a library or a small application, the overhead of using a framework (indeed, any framework, not just a DI framework) tips the scale in favor of not using it. For software in general it’s good to keep things as simple as possible for as long as possible.
In general it would be a good idea to use DI when the advantages outweigh the disadvantages, or the disadvantages can be mitigated. A careful review of the advantages and disadvantages we’ve already discussed might point you in the right direction. We don’t have hard and fast rules to say “use it here” and “don’t use it there”, we really need to investigate the pros and cons and see how the apply to what we’re trying to build. The decision is an architectural one and generally needs to be made early in the design of an application.
As a rule of thumb however, using a DI container could be a good choice at the very least if you are starting a project and want a standard technique to compose subsystems and orchestrate the large scale construction. This is another advantage that I didn’t mention before, but many of the large scale structures in an application are singletons, and DI containers facilitate a solution to the Singleton anti-pattern. Rather than controlling singleton instantiation yourself (which is easy to get wrong, common solutions include double-checked locking or enum singletons) the container controls the instantiation of a singleton.
As another rule of thumb, there are times when DI might not be a good choice. If you are working with a large legacy system that does not use DI at all, it could be prohibitively expensive to bolt on. DI is high-level and is best introduced at design time. Java lang objects that really are implementation details, such as Lists and Maps should probably not be injected. And objects created late in the scope's lifecycle should be not injected (or the scope of the object should be rethought). Finally, if your project is a library or a small application, the overhead of using a framework (indeed, any framework, not just a DI framework) tips the scale in favor of not using it. For software in general it’s good to keep things as simple as possible for as long as possible.
Sunday, February 17, 2013
Getting Started with Selenium
Selenium is a neat little tool for integration testing with browser automation. If you’re not familiar with it already, check it out. The Selenium IDE can record and playback actions on your browser, with the caveat that it’s normal to have to go back and manually tweak some of the steps that it recorded. Here are some notes from when I first started out with it, and some issues that I worked through. Hopefully reading this will save you some headaches.
The native file format for tests cases in Selenium IDE is html, but you can convert Selenium IDE files into unit tests in the language of your choice. The big advantage of this is running automated tests as a part of your build process. For Java, navigate to file -> export test case as -> java / junit 4 / webdriver. Selenium unit tests need to run on a machine that has graphics capabilities so it can open a browser, but it is possible to configure a headless machine to run selenium. Some gotchas related to unit testing with selenium test cases:
One thing that you generally have to tweak in any tests generated by Selenium IDE is the timing of the steps. When you record, you have natural pauses in your activity but the test case runs each step one after the other as quickly as possible. I had a test case that hit an update button, verified the response, and logged out at the end of the case. However the javascript that would have received the update response stopped running on logout, and verification subsequently failed. You need careful use of andWait / waitFor / pause commands to make sure your app functions correctly under test. Also, if you use a pause or a clickAndWait as the last command of a case (say, to log out at the end of a test case), the case will not actually pause or wait because the time is added to the beginning of the next step of a test case, not the end of the current command. You can get around this by putting an echo as the last command of the case.
Sometimes the selectors that Selenium IDE generates are either too brittle or not working at all. One thing to try is to select the element you want to click in firebug and view the xpath from that, and use that in the selenium step. Another (preferred) option is to be more intelligent about how you select it, say by selecting element by text content or by css class if you know that they will be unique on the page.
The native file format for tests cases in Selenium IDE is html, but you can convert Selenium IDE files into unit tests in the language of your choice. The big advantage of this is running automated tests as a part of your build process. For Java, navigate to file -> export test case as -> java / junit 4 / webdriver. Selenium unit tests need to run on a machine that has graphics capabilities so it can open a browser, but it is possible to configure a headless machine to run selenium. Some gotchas related to unit testing with selenium test cases:
- Tests tend to be brittle. If you start building a complete test suite with code, look into the Page Object pattern, this will make your test more resilient.
- If a unit test fails an assertion, the rest of the test will fail to run, which can hurt if each test does setup and teardown for itself and subsequent tests expect a “clean” state before they start running. It’s best to not depend on teardown steps in your test.
- If you’re testing with webdriver and firefox, then firefox must be shutdown before running selenium tests because selenium currently can’t attach to a running instance and a new instance of firefox can’t be started if it’s already running.
- Selenium must run in sequence per machine, they can’t be run in parallel tests because of the aforementioned firefox issue. Parallelized selenium tests can be achieved with selenium grid.
One thing that you generally have to tweak in any tests generated by Selenium IDE is the timing of the steps. When you record, you have natural pauses in your activity but the test case runs each step one after the other as quickly as possible. I had a test case that hit an update button, verified the response, and logged out at the end of the case. However the javascript that would have received the update response stopped running on logout, and verification subsequently failed. You need careful use of andWait / waitFor / pause commands to make sure your app functions correctly under test. Also, if you use a pause or a clickAndWait as the last command of a case (say, to log out at the end of a test case), the case will not actually pause or wait because the time is added to the beginning of the next step of a test case, not the end of the current command. You can get around this by putting an echo as the last command of the case.
Sometimes the selectors that Selenium IDE generates are either too brittle or not working at all. One thing to try is to select the element you want to click in firebug and view the xpath from that, and use that in the selenium step. Another (preferred) option is to be more intelligent about how you select it, say by selecting element by text content or by css class if you know that they will be unique on the page.
Sunday, February 10, 2013
Dependency Injection Part IV: Responses to Arguments Against Dependency Injection
In the previous post, we looked at arguments against using dependency injection frameworks. I posted the arguments because I thought they were all valid points, so my response won't just be "no its not a problem." rather I want to show how these problems can be mitigated so that DI can made worth your while.
About complexity: yes example code online using DI is usually more complex than the straight-up counterparts. But examples are just examples, when we are demonstrating code online the code has to be small enough to fit on a page (or small enough for a newcomer to wrap their head around easily). With a small example or even small projects, the overhead of DI can be a significant part of the overall code leading to the first impression that it’s very heavy or very imposing on your code. In reality, for large real-world projects, DI amounts for a very small amount of our overall code, so the overhead is relatively small. In other words, DI is too complex for small examples but shines on large object graphs with varying scopes. We just don’t usually see examples like that when we discuss DI online. So the problem of overhead and complexity is mitigated by saving DI for larger projects.
As for breaking encapsulation, yes you can break encapsulation if you inject concrete classes which are your implementation details. I would respond that if we inject a concrete class then we are indeed advertising our implementation, but if we inject an interface we are advertising much less (given that we’re using the Interface Segregation Principle). At that point we are advertising not so much the implementation as we are the dependency on a subsystem. To “do it right” we should inject interfaces for each element of your object tree and use a DI container. The top level class depends on a minimal interface, the implementation of that class itself depends only on minimal interfaces, and so on. Additionally, encapsulation can be managed with visibility control provided by our language.
Sometimes some members should be final but we might think we can’t inject final attributes. In fact we can inject final attributes... In this case, we just cannot use setter injection. Instead we need to not assign the member when it’s declared and use constructor injection.
It’s been said that even though DI eases changing configuration at runtime, we'd rarely change configuration anyway. But I'd like to point out that we change configuration every time we use a mock or stub object in a test. If you do not use unit tests, then the original argument holds water. But if you do not use unit tests, I would say we need to have a different conversation about why unit testing is helpful in the first place. If you see the benefits of unit tests, then you will see the benefits of DI for making your code more testable.
Using a DI container ties us to a framework. Each container offers its own annotations that threaten to invade our code and make it very difficult to ever disentangle from that framework. however, if we only code with the javax inject annotations, the annotations in our code can be just standard JEE. We can code it so that the only framework specific code is in the wiring, and if that is always in one place it should not be a big deal to swap one DI container implementation for another.
Finally, some DI containers use XML for their configuration. XML is not quite in fashion like it used to be. But fortunately newer DI containers (newer versions of Spring and Guice for sure) allow us to write our configuration in code rather than XML. This gets around the problem of using strings to identify and wire dependencies, and lets us leverage our IDE for things like refactoring and renaming classes, navigating with "find usages", and so on.
Hopefully this shows you how to work around some shortcomings of DI so that you feel better about adding it to your software engineering arsenal.
About complexity: yes example code online using DI is usually more complex than the straight-up counterparts. But examples are just examples, when we are demonstrating code online the code has to be small enough to fit on a page (or small enough for a newcomer to wrap their head around easily). With a small example or even small projects, the overhead of DI can be a significant part of the overall code leading to the first impression that it’s very heavy or very imposing on your code. In reality, for large real-world projects, DI amounts for a very small amount of our overall code, so the overhead is relatively small. In other words, DI is too complex for small examples but shines on large object graphs with varying scopes. We just don’t usually see examples like that when we discuss DI online. So the problem of overhead and complexity is mitigated by saving DI for larger projects.
As for breaking encapsulation, yes you can break encapsulation if you inject concrete classes which are your implementation details. I would respond that if we inject a concrete class then we are indeed advertising our implementation, but if we inject an interface we are advertising much less (given that we’re using the Interface Segregation Principle). At that point we are advertising not so much the implementation as we are the dependency on a subsystem. To “do it right” we should inject interfaces for each element of your object tree and use a DI container. The top level class depends on a minimal interface, the implementation of that class itself depends only on minimal interfaces, and so on. Additionally, encapsulation can be managed with visibility control provided by our language.
Sometimes some members should be final but we might think we can’t inject final attributes. In fact we can inject final attributes... In this case, we just cannot use setter injection. Instead we need to not assign the member when it’s declared and use constructor injection.
It’s been said that even though DI eases changing configuration at runtime, we'd rarely change configuration anyway. But I'd like to point out that we change configuration every time we use a mock or stub object in a test. If you do not use unit tests, then the original argument holds water. But if you do not use unit tests, I would say we need to have a different conversation about why unit testing is helpful in the first place. If you see the benefits of unit tests, then you will see the benefits of DI for making your code more testable.
Using a DI container ties us to a framework. Each container offers its own annotations that threaten to invade our code and make it very difficult to ever disentangle from that framework. however, if we only code with the javax inject annotations, the annotations in our code can be just standard JEE. We can code it so that the only framework specific code is in the wiring, and if that is always in one place it should not be a big deal to swap one DI container implementation for another.
Finally, some DI containers use XML for their configuration. XML is not quite in fashion like it used to be. But fortunately newer DI containers (newer versions of Spring and Guice for sure) allow us to write our configuration in code rather than XML. This gets around the problem of using strings to identify and wire dependencies, and lets us leverage our IDE for things like refactoring and renaming classes, navigating with "find usages", and so on.
Hopefully this shows you how to work around some shortcomings of DI so that you feel better about adding it to your software engineering arsenal.
Sunday, February 3, 2013
Dependency Injection Part III: Arguments Against Dependency Injection
When it comes to DI (as with many things in software) people tend to have one of two very strong opinions: love it or hate it.
Full disclosure: I love it, but I’ve done my best to examine the topic from both sides. Since I am in the “love it” camp, when I started researching Dependency Injection I knew that confirmation bias would probably lead me to arguments for it and little else. I was surprised at how easy it was to find heated arguments lengthy conversations going back and forth about the merits and drawbacks, there is a lot of good discussion online. So I feel safe in saying the advantages are not as obvious and clear-cut as the pro-DI camp would like to believe.
I thought some good arguments against DI were
Let’s look at each of these in turn.
It’s true that DI brings complexity, there are plenty of things that are more complex with DI than without. You need to teach the concept to new developers. You need to bring in yet another framework to your code and learn how to use it. Some parts of your application may require more code as constructor argument lengths and/or number of setter methods increase. When the framework is wiring things behind the scenes, it may be harder to see what was instantiated when and why.
There is definitely the possibility of breaking encapsulation. The members of a class constitute its implementation, and listing its members in a constructor list is just advertising through the API what the implementation is and by extension how this class works. If you use an empty constructor instead, the user of a class does not know (indeed can not know) what is going on underneath the class, which in general is what we want.
When designing a class, sometimes it is appropriate to declare some members as final. But DI puts some constraints on this part of your class design. Once a final attribute is assigned it cannot be reassigned, and it cannot be assigned after the constructor returns, so you cannot use setter injection for final members. If you feel like DI is forcing you to not use final members so you can use setter injection, you will not be happy with that.
DI advertises that it allows you to configure you object graph at runtime rather than compile time. However, in many real world applications we rarely need to change the dependency configuration. There are some cases (plugins, for example) where implementations need to be swapped at runtime, but those are not that common. If you do need to change a dependency configuration, generally you just change it and recompile your code.
The definition of DI that I’m using for this blog series is using the design pattern along with a DI container to structure an entire application. With that definition, yes there is another framework you need to bring in to your application and learn how to use. Besides increasing the overhead of learning your application, using more libraries and frameworks increases your chances of dependency hell.
Finally, some DI containers use XML files to wire applications together. Using XML files to describe your application has some disadvantages such as being brittle in the face of refactoring tools, and moving what would otherwise be code into places away from your code. Generally things that change together should stay together.
Full disclosure: I love it, but I’ve done my best to examine the topic from both sides. Since I am in the “love it” camp, when I started researching Dependency Injection I knew that confirmation bias would probably lead me to arguments for it and little else. I was surprised at how easy it was to find heated arguments lengthy conversations going back and forth about the merits and drawbacks, there is a lot of good discussion online. So I feel safe in saying the advantages are not as obvious and clear-cut as the pro-DI camp would like to believe.
I thought some good arguments against DI were
- Introduces complexity
- A dependency is an implementation detail, specifying it externally breaks encapsulation
- Some members should be final but you can’t inject final attributes.
- Rarely change dependency configuration anyway
- Ties you to a framework
- Uses XML to configure your application
Let’s look at each of these in turn.
It’s true that DI brings complexity, there are plenty of things that are more complex with DI than without. You need to teach the concept to new developers. You need to bring in yet another framework to your code and learn how to use it. Some parts of your application may require more code as constructor argument lengths and/or number of setter methods increase. When the framework is wiring things behind the scenes, it may be harder to see what was instantiated when and why.
There is definitely the possibility of breaking encapsulation. The members of a class constitute its implementation, and listing its members in a constructor list is just advertising through the API what the implementation is and by extension how this class works. If you use an empty constructor instead, the user of a class does not know (indeed can not know) what is going on underneath the class, which in general is what we want.
When designing a class, sometimes it is appropriate to declare some members as final. But DI puts some constraints on this part of your class design. Once a final attribute is assigned it cannot be reassigned, and it cannot be assigned after the constructor returns, so you cannot use setter injection for final members. If you feel like DI is forcing you to not use final members so you can use setter injection, you will not be happy with that.
DI advertises that it allows you to configure you object graph at runtime rather than compile time. However, in many real world applications we rarely need to change the dependency configuration. There are some cases (plugins, for example) where implementations need to be swapped at runtime, but those are not that common. If you do need to change a dependency configuration, generally you just change it and recompile your code.
The definition of DI that I’m using for this blog series is using the design pattern along with a DI container to structure an entire application. With that definition, yes there is another framework you need to bring in to your application and learn how to use. Besides increasing the overhead of learning your application, using more libraries and frameworks increases your chances of dependency hell.
Finally, some DI containers use XML files to wire applications together. Using XML files to describe your application has some disadvantages such as being brittle in the face of refactoring tools, and moving what would otherwise be code into places away from your code. Generally things that change together should stay together.
In conclusion: these are just some arguments that I found compelling, other people might find other arguments compelling or not find these compelling at all. In the next part of this series, we will respond to these arguments and look at how to determine when to use DI and when not to use it.
Subscribe to:
Posts (Atom)