Category Archives: java

Studying Builder Pattern with Inheritance

Just to pay some tech debts.

Builder Pattern alone is easy to understand, but not so much if inheritance is concerned.

(Part I) understand the problem

First, we need to understand how inner class works.

Given A <— B (B is subclass of A), and A.Builder is inner class of A, and B.Builder is inner class of B. If we have A.Builder <— B.Builder as well, what is the consequence of following calls?

  1. new B.Builder(): A and B are not created, but A.Builder() is called and then B.Builder() is called. new B.Builder().getClass()returns B$Builder;
  2. new B.Builder().Bmethod(): call B’s Bmethod (who returns B$Builder); getClass() returns B$Builder;
  3. new B.Builder().Bmethod().Amethod(): call A’s Amethod (who returns A$Builder); but getClass() still returns B$Builder();
  4. new B.Builder().Bmethod().Amethod().build(): call B’s build() (who returns new B(B$Builder)) because Amethod return “this”, by no means compiler knows who is “this”; Once B(B$Builder) is called, first B’s super(B$Builder) is called, and then B’s rest is called. So finally, B is created and returned. However, this only happens in runtime. In compile time, the build() that is involved is A’s build() . So casting has to happen so that compiler can pass.

Compile time follows the definition/ signature strictly, but in runtime, jvm finds the nearest method to call. Method getclass() only returns the runtime class, and down cast (from parent to child) is only possible if in runtime parent type is actually a child object. Compiler does not know what is “this” it is referring, so runtime cast must be done.

Therefore, as Eamonn McManus mentioned in the post,

new B.Builder().Bmethod().Amethod().build() will be compiled, but the sequence requirement is not a pleasant requirement, and a down cast must be there in order to do sth. like

B b = (B) new B.Builder().Bmethod().Amethod().build();

(Part II) explore to solve the problem

In summary, the problem happens because in parent class A$Builder we have a method Amethod who returns this, but in compile time, compiler correctly thinks “this” is A$Builder, but what we hope is that “this” is B$Builder. So we need a trick to cheat compiler, here we go,
Continue reading

Advertisements

Get to know the Java Best Practice

Although it should be learnt the other way around, I find it interesting to get to know some bits of enterprise best practice before I can appreciate.
It is true that I can only know the real benefit after I pay the price, but it won’t stop me from knowing those rules of thumb beforehand.

Reference:

Maven Configuration

  • Create a project pom.xml  as <packaging> pom, which is defining all the project dependencies for modules, specified in <modules/>
  • Specify all version numbers in parent’s pom file’s properties
  • Specify <dependencyManagement>, and inside <build> specify <pluginManagment>, if sub modules exist. It is used for child pom to transitively use parent pom configurations. Outside <*Management>, Sections <dependencies> or <plugins> must exist for maven to download actual jars.
  • Fix java compiler version by maven-compiler-plugin as 1.6
  • Enforce maven version between 2.2.1 to 3.1.1 by  maven-enforcer-plugin
  • Replace common-logging and log4j by slf4j, and don’t use logging utility either in the code. Q: Why is that. The reason is here.
  • Ensure no SLF4j 1.5 or 1.6 Q: Why? Since they do not work together?
  • Don’t allow spring framework 2.x and 3.0. Q: What is the difference from 3.1?
  • Add SLF4j 1.7 for Logging, and prefer logback over log4j, and enable root exception logging.  Q: Why prefer? Reason is here
  • Ensure source encoding across developing environments <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>, as well as <project.reporting.outputEncoding>UTF-8 for reporting plugins like surefire, failsafe
  • Solving maven dependency conflicts by explicitly defining in <dependencyManagement> after analyzing dependency conflicts using IntelliJ diagrams marked in red.
  • Make developer environment loaded from ./config directory instead of module’s resource folder resource folder is supposed to load more static configurations like logger configs. Since developer environment is changing more likely between individual developers, like test host url, password, or temporary data folder, these configs should be put inside config directory and make it ignored by git. Therefore, static resource is loaded by class().getClassLoader().getResourceAsStream(), while for config directory, make maven look for project.home property (by -Dproject.home=…) and put ${project.home}/config into class path before calling with getResourceAsStream(). The additional classpath can be added by surefire’s additional classpath element.
  • Pay attention to transitive conflicts if during runtime you encounter error like no such method. It is a highly chance that one lib is using a common transitive dependency with different version. Finding the bugs by looking
    mvn dependency:tree -Dverbose //with keywords google which jar has classes related to the error "no such method"

Test-Driven Design

  • Class naming: ClassNameUnderTest+Tests, i.e. EventDaoTests
  • Method naming: UnitOfWork_StateUnderTest_ExpectedBehavior; reference
  • Naming practice for Junit
  • Using Junit for unit test; examples (testNG across class dependencies is too messy for unit test)
  • @BeforeClass/@AfterClass static method for creating/releasing expensive objects, like DB connection objects.
  • @Before/@After for creating/releasing common test objects, usually private fields for test class. To ensure no dependency over test methods (each test method is running on a separate test class instance, and releasing by assigning null to objects and Garbage collector will collect them. It is not needed maybe.)
  • Declare private static final SLF4J Logger at the beginning of test class
  • Separate integration test with unit test. Referred organization of IT with original blog and useful practice. I prefer the use of systest, and in maven I need to configure surefire (rather than failsafe) with profiles (profile flags -P must be enabled when mvn verify. Details can be found in the comments of codehaus)

Design Patterns and Packaging Concepts

Code Reference

      • Integration Test Profiles
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/systest/**</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>itest</id>
<activation>
<property>
<name>itest</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>surefire-it</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/systest/**</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>