Mar 12, 2009
Be careful of large log files using TeamCity
Dec 19, 2008
Risk of inheritance
Imagine situation when you want to add some behavior to class and decided to override its public methods in the child class. The following hidden issues are waiting for you:
* You don't know how this method is used and how it will be used in future in code of the parent class. Your changes may cause unpredictable behavior.
* You want to control some access points of the parent class, but in future some more points can be added and your code will fail in unpredictable time interval.
* Overridden methods will be removed in future, deprecated or change their access level.
* Some other issues depending of concrete scenario.
To avoid most of these issues use composition instead of inheritance. Create new class and declare needed behavior by implementing the same set of interfaces such a basic class. May be you need only some of them, so you can localize your needs and have easier implementation saving time for support. Then implement some methods and delegate all other to the instance of basic class.
Composition has its own disadvantages when basic class is too large or creation of the instances is too complicated, then you need to duplicate a lot of code or logic. IDE or other automation tools can help you with generation of such kind of code.
But what to do if basic class doesn't implement any interfaces and declared abstract? In this case you have to protect yourself from as many issues as possible. Create unit tests to control basic class methods and validate if all of them are known by you. These tests will notify you if basic class was changed so you need to look at new implementation and may be make changes in code or tests. To access information about declared methods use reflection API or some open source libraries that simplify access to this information. But think before if you really need inheritance in this case (if class was not designed for inheritance) and try to avoid it if possible. Develop with pleasure!
Crisis in action
public interface I {
int b = 5;
void a();
}
The question was if this declaration is correct. A received the following answer: "No. You need to change it to be correct." Here is what he wrote:
public interface I {
int b = 5;
private void a();
}
The explanation was great: "Instead this method will have visibility only in the package". The next question was about difference between abstract class and interface. The answer makes me smile: "The advantage of abstract class is that I can declare method in it and don't think about it anymore". Imagine how hard is to think about method in all other cases. :) "And why do you ask all these stupid questions? All this is so simple and doesn't matter" - he continued. After that phrase interview was finished.
So, do you want your code to be developed by such guys? If no be aware of crisis. And develop with pleasure...
Sep 13, 2008
Generics commons collections
Everybody of you who have large experience with Java thanks Apache for their commons project. This project includes a lot of libraries to extend or simplify work with JDK. One of these libraries is commons-collections.
It was very helpful when you needed to work with specific kinds of collections like multi map, lazy collections, bidirectional map, etc. Also it had a great number of iterators, comparators and utilities to avoid writing unnecessary code in your application. It allowed us to transform one collection to another, filter collection elements and many other things very easy. It introduced completely new types of collections like buffer (collection that allows objects to be removed in some well-defined order), bag (collection that counts the number of times an object appears in the collection), bidirectional map (map that allows bidirectional lookup between key and values) and created a great number of custom implementations of existing collections types.
But why I always use past sentences? Because from the beginning of Java 5 usage this library is not more so helpful. One of the main features introduced in Java 5 was generics. And of course the most useful place for generics to be applied is collections framework. I hoped that it takes not more than year to refactor existing library and add support of generics by commons-collections developers. But it didn't happen. :( But don't worry! There are some solutions of this issue. First one is to use generics version of commons-collections developed in parallel with main Apache version. At this moment they have functionality synchronized with version 3.1. Another option is to use google-collections instead. This project was started as free time development of one developer in Google for internal usage and now it contains a lot of features in comparison to commons-collections. It is driven by community, so only really needed things are included. To start work with this library or to get your own opinion take a look at the video presentation. Develop with pleasure!
Jul 20, 2008
Acceptance Test Driven Development training in Kiev
Jul 19, 2008
Advanced EasyMock techniques
Imagine that you have following document processing code:
public class DocumentService {
private DocumentProcessor processor;
public Document getDocument(long id) {
Document doc = //code to retrive document
processor.process(doc);
return doc;
}
public void setProcessor(DocumentProcessor processor) {
this.processor = processor;
}
}
public interface DocumentProcessor {
void process(Document document);
}
public class Document {
private String text;
private long wordsCount;
public Document(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public long getWordsCount() {
return wordsCount;
}
public void setWordsCount(long wordsCount) {
this.wordsCount = wordsCount;
}
}
In your tests you want to check if changes made by processor really change returned document. To check this you want to set 'wordsCount' variable value to 5. You may create your own processor to do this:
public class DocumentServiceTest {
private DocumentService documentService = new DocumentService();
private DocumentProcessor processor = new DocumentProcessor() {
public void process(Document document) {
document.setWordsCount(5);
}
};
@Test
public void testGetDocument() {
documentService.setProcessor(processor);
Document document = documentService.getDocument(3);
Assert.assertEquals(5, document.getWordsCount());
}
}
But also you can use EasyMock (in my example I use Unitils to easy manage mocks):
@RunWith(UnitilsJUnit4TestClassRunner.class)
public class DocumentServiceTest {
private DocumentService documentService = new DocumentService();
@RegularMock
private DocumentProcessor processor;
@Test
public void testGetDocument() {
expectDocumentProcessed();
documentService.setProcessor(processor);
Document document = documentService.getDocument(3);
Assert.assertEquals(5, document.getWordsCount());
}
private void expectDocumentProcessed() {
processor.process(isA(Document.class));
expectLastCall().andAnswer(new IAnswer<Object>() {
public Object answer() throws Throwable {
((Document) getCurrentArguments()[0]).setWordsCount(5);
return null;
}
});
replay(processor);
}
}
Why this is more powerful solution? Because you can change behavior of the processor in runtime and reuse the same mock in different tests with different behavior. Some times your dependencies are not so simple as document processor in my example and it is not so easy to extend or implements their methods (for example interface has 10 methods and you need only one of them). There is one more side effect of this usage - you have access to the parameters passed to the methods. You can save the parameter instance to use it in asserts or for other dependencies behavior mocking.
In 2.4 version of EasyMock 'capture' option was introduced to simplify work with method parameters. Document class from previous example doesn't have custom 'equals' method, so you can't use 'eq' method of EasyMock to check parameter of 'process' method. You can implement your own argument matcher (see IArgumentMatcher in the EasyMock guide) or use Unitils reflection comparison 'refEq', but another good option for you is to use new 'capture' option:
Capture<Document> capture = new Capture<Document>();
processor.process(and(isA(Document.class), capture(capture)));
replay(processor);
Document captured = capture.getValue();
These two techniques must be in your arsenal to use full power of EasyMock library and make your tests better. Develop with pleasure!