tag:blogger.com,1999:blog-69619875191285314562024-03-05T15:02:05.049+01:00Java Blog für Clean Code Developer<b>Willkommen!</b>
<b>In diesem Blog schreibt ein Clean Code Developer über Java, Java EE und CCD.</b>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comBlogger60125tag:blogger.com,1999:blog-6961987519128531456.post-37636999259839730032013-10-27T10:22:00.000+01:002013-10-27T10:25:37.893+01:00DRY unit tests<div style="text-align: justify;">
<div class="separator" style="clear: both; text-align: center;">
<div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnrk4AV1lN-jIlab2qb5yXAMxluB990KoHWl31bBYOSn7utmmLeNO4lsYNb_u3vzGG_a0yKM8Kxi7cZ3fdJs5yPQBYvvnTA2Ock7xH9rRFkXmhHlb-FjGD318Yz1XJaRSkH1pDP_bV1rl-/s1600/DukeCurve.gif" /></div></div>
Unit tests are written in the same quality as the production source code therefore, they follow the <b>DRY principle</b>. To achieve this goal it is sometimes necessary to enhance unit tests with a new runner for the JUnit tooling. <b>JUnitParams</b> adds a new runner to provide easier and more readable unit tests. By using JUnitParams you have to write less source code for your unit tests through short cuts like the $(…) method. The following example compares JUnitParams with a regular written unit test.</div>
<br />
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Class under test</b></div>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
public class Message {<br />
<br />
private final String message;<br />
<br />
public Message(final String message) {<br />
<br />
if(null == message || message.isEmpty()) {<br />
<br />
throw new IllegalArgumentException("Message is null or empty!");<br />
}<br />
<br />
this.message = message;<br />
}<br />
<br />
public String getMessage() {<br />
<br />
return message;<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b>Regular unit test</b></div>
<br />
public class MessageTest {<br />
<br />
@Test<br />
public void testMessageHello() {<br />
<br />
final Message message = new Message("Hello");<br />
<br />
Assert.assertEquals("Hello", message.getMessage());<br />
}<br />
<br />
@Test<br />
public void testMessageWorld() {<br />
<br />
final Message message = new Message("World");<br />
<br />
Assert.assertEquals("World", message.getMessage());<br />
}<br />
<br />
@Test(expected= IllegalArgumentException.class)<br />
public void testMessageIsNull() {<br />
<br />
final Message message = new Message(null);<br />
}<br />
<br />
@Test(expected= IllegalArgumentException.class)<br />
public void testMessageIsEmpty() {<br />
<br />
final Message message = new Message("");<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b>Unit test with JUnitParams </b></div>
<br />
<span style="background-color: lime;">@RunWith(JUnitParamsRunner.class)</span><br />
public class MessageParameterizedTest {<br />
<br />
<span style="background-color: white;">private static final Object[] getMessageValue() {</span><br />
<span style="background-color: white;"><br /></span>
<span style="background-color: white;"> return $(</span><br />
<span style="background-color: white;"><br /></span>
<span style="background-color: white;"> $("Hello"),</span><br />
<span style="background-color: white;"> $("World")</span><br />
<span style="background-color: white;"> );</span><br />
<span style="background-color: white;"> }</span><br />
<span style="background-color: white;"><br /></span>
<span style="background-color: white;"> private static final Object[] getInvalidMessageValue() {</span><br />
<span style="background-color: white;"> return new String [] [] {{null},{""}};</span><br />
<span style="background-color: white;"> }</span><br />
<br />
@Test<br />
@Parameters(method="getMessageValue")<br />
public void testValidMessages(final String messageParam) {<br />
<br />
final Message message = new Message(messageParam);<br />
<br />
Assert.assertEquals(messageParam, message.getMessage());<br />
}<br />
<br />
@Test(expected=IllegalArgumentException.class)<br />
@Parameters(method="getInvalidMessageValue")<br />
public void testInvalidMessages(final String messageParam) {<br />
<br />
final Message message = new Message(messageParam);<br />
}<br />
}<br />
<br />
<div style="background-color: #cccccc;">
<br />
<b>Der
Rechtshinweis des Java Blog für Clean Code Developer ist bei der
Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu
beachten.</b><br />
<br /></div>
</div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-19515051042131709332013-08-27T15:24:00.003+02:002013-10-27T10:02:44.109+01:00Policy Driven Implementation<div style="text-align: justify;">
Dominant design patterns for domain models are the strategy and the composite pattern. To build rich domain models with both patterns is an honest achievement. My focus in the following explanation is on the strategy pattern which is the base for the implementation of policies. Policies are true friends in case of designing an application programming interface. Policies guarantee the possibility to alter algorithms in the background without the need to touch a previous released interface. It might be true, that this is only one part of the story. To ensure extensibility you need another design pattern which uses overriding in a subclass. As you already assumed, it is the template method or factory method pattern.</div>
<br />
<div style="text-align: justify;">
<div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;">
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiluLSwRGrU7hFfKc15FUL3druYfDVE-EuXOFUrTlQfEW-YJgtzozFdaTGjyF81RVvP3Bf3uB4_39PB-dLnFcjSTUKWH7UX_ePAepA7j0zlZ0CffmQRgSF6szCqnZvRbOsshZHKdiwVz360/s1600/Duke_Blueprint.gif.png" /></div>
</div>
<div style="text-align: justify;">
<b><u>Duke proudly presents the definition for the strategy pattern:</u></b></div>
<div style="text-align: justify;">
<br />
<div style="text-align: justify;">
Define a family of algorithms, encapsulate each one and make them interchangeable. <br />
The strategy pattern lets the algorithm vary independently from clients that use it. <br />
[Gang of Four]</div>
</div>
<br />
<br />
<div style="text-align: justify;">
The power of design patterns is hidden in their combinations. But be aware: A design pattern should be applied only when it’s needed! Well, don’t worry now. Look first to solve your business problem and then look for a combination of patterns to ensure that your software is reusable and extensible.</div>
<br />
<div style="text-align: justify;">
The strategy and template method patterns are a strong combination to write software which meets the criteria of the <b>Open Closed Principle (OCP)</b>. The strategy makes the algorithms changeable and the template method pattern ensures the extensibility without touching an existing implementation. For sure it’s difficult to see such combinations in a deep object oriented class hierarchy, but on the other hand a few principles will help you to build robust object oriented software. Consider first the usage of the template or factory method pattern and then decide how you can vary your algorithms in your system context which will lead to a <b>policy driven implementation</b>.</div>
<div style="text-align: justify;">
<br />
The following example is the implementation of a billing class with two variants. The first variant is the implementation of a 19% tax billing class and the second one is a 7% tax billing class. The implementation uses the template method pattern to determine the tax rate and the strategy pattern to implement the tax rate read strategy.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Billing base class:</b></div>
</div>
<br />
<div style="text-align: justify;">
package org.ccd.policy;<br />
<br />
import java.math.BigDecimal;<br />
<br />
abstract class Billing {<br />
<br />
private final BigDecimal price; <br />
<br />
protected Billing(final BigDecimal price) {<br />
<br />
this.price = price;<br />
}<br />
<br />
/**<br />
* Calculate the price including <br />
* the tax rate.<br />
* <br />
* @return price * tax rate<br />
*/<br />
public BigDecimal calculate() { <br />
return price.multiply(<span style="background-color: lime;">tax()</span>);<br />
}<br />
<br />
/**<br />
* The tax rate method.<br />
* <br />
* @return tax rate<br />
*/<br />
<span style="background-color: lime;">protected abstract BigDecimal tax();</span><br />
<br />
/**<br />
* The TaxPolicy interface.<br />
*/<br />
protected interface TaxPolicy { <br />
<span style="background-color: cyan;"> public BigDecimal rate();</span><br />
}<br />
} </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Billing min tax rate subclass:</b></div>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
package org.ccd.policy;<br />
<br />
import java.math.BigDecimal;<br />
<br />
public class BillingMinTaxRate extends Billing {<br />
<br />
public BillingMinTaxRate(final BigDecimal price) { <br />
super(price);<br />
}<br />
<br />
@Override<br />
<span style="background-color: lime;"> protected BigDecimal tax() </span>{<br />
<br />
return new TaxPolicy() {<br />
<br />
@Override<br />
<span style="background-color: cyan;">public BigDecimal rate() </span>{<br />
<br />
// read tax rate from database,<br />
// it's a fake right here!<br />
final BigDecimal taxRate = new BigDecimal("1.07");<br />
<br />
return taxRate;<br />
}<br />
<br />
}.rate();<br />
}<br />
}</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Billing max tax rate subclass:</b><br />
<div style="background-color: white;">
<b></b></div>
</div>
<b> </b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
package org.ccd.policy;<br />
<br />
import java.math.BigDecimal;<br />
<br />
public class BillingMaxTaxRate extends Billing {<br />
<br />
public BillingMaxTaxRate(final BigDecimal price) { <br />
super(price);<br />
}<br />
<br />
@Override<br />
<span style="background-color: lime;"> protected BigDecimal tax()</span> {<br />
<br />
return new TaxPolicy() {<br />
<br />
@Override<br />
<span style="background-color: cyan;"> public BigDecimal rate()</span> {<br />
<br />
// read tax rate from database,<br />
// it's a fake right here!<br />
final BigDecimal taxRate = new BigDecimal("1.19");<br />
<br />
return taxRate;<br />
}<br />
<br />
}.rate();<br />
}<br />
}</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Billing test class:</b></div>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
package org.ccd.policy;<br />
<br />
import static org.junit.Assert.*;<br />
import org.junit.Test;<br />
import java.math.BigDecimal;<br />
<br />
public class BillingTest {<br />
<br />
@Test<br />
public void testBillingMinTaxrate() {<br />
<br />
final Billing billingMinTaxRate = <br />
new BillingMinTaxRate(new BigDecimal("10.00"));<br />
<br />
assertEquals(new BigDecimal("10.70").doubleValue(), <br />
billingMinTaxRate.calculate().doubleValue(), <br />
new BigDecimal("0.00").doubleValue()); <br />
}<br />
<br />
@Test<br />
public void testBillingMaxTaxrate() {<br />
<br />
final Billing billingMaxTaxRate = <br />
new BillingMaxTaxRate(new BigDecimal("10.00"));<br />
<br />
assertEquals(new BigDecimal("11.90").doubleValue(), <br />
billingMaxTaxRate.calculate().doubleValue(), <br />
new BigDecimal("0.00").doubleValue()); <br />
}<br />
}<br />
<br />
<br />
<div style="background-color: #cccccc;">
<br />
<b>Der
Rechtshinweis des Java Blog für Clean Code Developer ist bei der
Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu
beachten.</b><br />
<br /></div>
</div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-75550001918952925252013-08-24T10:37:00.002+02:002013-10-13T11:24:30.157+02:00Thinking about caching<div style="text-align: justify;">
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlQrsDZMMuPwFtnELVf2GKgtsrBs1kJzMuZPvR6ytSSHLvrvvrXm-KtRpllWmPnZ4O4MfeLBHtNuxGCY1eAHFYx8kUiUmMNRvdueshmGol1JGp7I-3RctF_8Yb2v08S8PJUNutRjwI_ezP/s1600/Thinking.jpg.png" /></div>
I have heard it multiple times: Caching is evil! The risks and benefits of caching are obvious. The main risks using a caching strategy are to work with stale data and to waste memory. On the other hand the benefits are reducing database access and increasing performance by reading pre calculated data. The Java EE entity manager (JPA) uses a first level cache and optional a second level cache. The first level cache is related to the persistence context which means the cache is valid between a single transaction. After the transaction commits or possibly after a rollback the first level cache is gone.<br />
<div style="text-align: justify;">
<br />
<br />
The second level cache (L2-Cache) has a longer lifetime. The second level cache is independent of a particular entity manager instance and the related transactions. Using a second level cache reduces database access to gain better performance. The second level cache should be configured that the count of maintained entities are limited and that the cache is refreshed automatically after an expired timeframe. To reduce the problem of stale data put mainly entities with static data in the second level cache. Static data are system data which could be application profile data, labels of an I18n strategy or fixed GUI related data which alter seldom.</div>
<br />
<div style="background-color: orange;">
<b>The rule of thumb is:</b></div>
</div>
<div style="text-align: justify;">
<br />
Caching is valuable without risk for static data which are not refreshed during program execution. Caching can be evil for frequent altered data even in a transactional context.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Caching to reduce database access is one part of the story, but caching could also lead to better performance within the application itself. Java has several collection classes which supports safer caching to avoid stale data. A good example is the WeakHashMap. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use.</div>
<br />
<div style="text-align: justify;">
Since Java 5 the LinkedHashMap can be used as a LRU Cache (last recently used cache). By overriding the “removeEldestEntry” method to implement a policy for removing stale mappings automatically when new mappings are added. There are even more sophisticated caching solutions (e.g.: Ehcache) available which handle evict and refreshing better as the simple Java API implementations. </div>
<br />
<u><b>The conclusion is:</b></u> Caching could be evil when wrongly considered and valuable when considered with care. <b>Therefore use caching with care!</b>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-40162587944226682842013-05-09T17:00:00.002+02:002013-10-13T11:39:44.089+02:00Tell don't ask<div style="text-align: justify;">
Over the years <b>data transfer objects </b>which are generally full with getter and setter are used without business logic. Technologies like J2EE lead to see them as pattern and common development practice. Originally designed to carry data over layers (serializable object) and to be part of an <b>object assemble strategy</b> they mutated to a development practice which can now be seen as an anti-pattern. Okay, we know the danger of getter to retrieve state data to manage the state outside the instance of a class. Smart guys are used to immutable objects by using finals and only getter without any setter, but indirect state changing by retrieving a reference with a getter is still possible and dangerous.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
As a developer I have never heard: “Remove all getter, we don’t use them”. There is no coding convention for this topic. I still think it is not possible to live without getter, but I know to follow the way of <b>immutable objects</b> is a good way to write stable software. The usage of <b>defensive copies</b> may help to implement real immutable objects. Think about using the clone() method and in case of collections about deep copying objects. Strategies you can trust, even when race conditions occur.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I share my opinion with experienced developers which it is a good design practice to give the domain objects behavior to expose less state. I know that entities needs getter/setter as the entity manager (ORM technology) expects them, but on the other hand it's a rich implementation practice to give entities additionally behavior. The annotation @Transient may help you. Don’t see the domain layer as a stupid data heap by putting all business logic in the service layer. Doing this you will break OOP by starting functional programming.</div>
<br />
<div style="text-align: justify;">
Even in the past, J2EE already propagated to put business logic in CMP/BMP entities. Until today I do not understand why this had not been emphasized and written down in red bold letters that every developer recognized this fact as an important development practice.There is no doubt that using rich domain objects are in line with OO principles like <b>high cohesion</b> and <b>data hiding</b>.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Following this principle you will not end with a layer of <b>anemic domain objects</b> which are difficult to put into a test strategy. Think about isolation and the power to test rich domain objects which do not allow publishing their state. The design of <b>lean service layer</b> accessing rich domain models which holds the key business logic is an honest achievement which is comfortably implementable with POJOs and the rich set of Java EE annotations.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The evil often begins with some of the Java API classes like the iterator. The iterator is a vehicle to loop through the collection of state holder. A common practice is to call a getter to get the reference of a collection instance to call the iterator method. How will you test that? Think about isolation and the benefit of <b>louse coupling</b>. The problem occurs when you have developed such a structure and later you write the test case. Tight coupling often makes it impossible to write test cases. Test cases are important. Without test cases no refactoring will happen.<br />
<br />
The following example includes a simple division functionality which shows the difference between tell don't ask like software and the opposite. Look at the test class which tests both classes and hopefully gives an impression about the benefits of <u>not using</u> anemic objects. <br />
<br />
<div style="background-color: orange;">
<div style="color: #20124d;">
<b>Anemic division class (not tell don't ask conform):</b></div>
</div>
<br />
package org.ccd.tell.dont.ask;<br />
<br />
public class AnemicDivision {<br />
<br />
private int divisor;<br />
private int divident;<br />
<br />
public int getDivisor() {<br />
return divisor;<br />
}<br />
<br />
public void setDivisor(int divisor) {<br />
this.divisor = divisor;<br />
}<br />
<br />
public int getDivident() {<br />
return divident;<br />
}<br />
<br />
public void setDivident(int divident) {<br />
this.divident = divident;<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b>Division class (tell don't ask conform):</b> </div>
<br />
package org.ccd.tell.dont.ask;<br />
<br />
public class Division {<br />
<br />
public int buildQuotient(final int divident, final int divisor) {<br />
<br />
assertDivisor(divisor);<br />
<br />
return divident / divisor;<br />
}<br />
<br />
private void assertDivisor(final int divisor) {<br />
<br />
if(0 == divisor) {<br />
<br />
throw new IllegalArgumentException("Divisor is 0: Leads to divide by zero exception!");<br />
}<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b>Test: </b></div>
<br />
package org.ccd.tell.dont.ask;<br />
<br />
import junit.framework.Assert;<br />
<br />
import org.junit.Before;<br />
import org.junit.Test;<br />
<br />
public class DivisionTest {<br />
<br />
private AnemicDivision anemicDivision;<br />
private Division division;<br />
private int quotient;<br />
<br />
@Before<br />
public void before() {<br />
<br />
anemicDivision = new AnemicDivision();<br />
division = new Division();<br />
}<br />
<br />
@Test<br />
public void testAnemicDivision() {<br />
<br />
anemicDivision.setDivident(100);<br />
anemicDivision.setDivisor(10);<br />
<br />
quotient = anemicDivision.getDivident() / anemicDivision.getDivisor();<br />
<br />
Assert.assertEquals(10, quotient);<br />
}<br />
<br />
@Test(expected=ArithmeticException.class)<br />
public void testAnemicDivisionDivideByZero() {<br />
<br />
anemicDivision.setDivident(100);<br />
anemicDivision.setDivisor(0);<br />
<br />
try {<br />
<br />
quotient = anemicDivision.getDivident() / anemicDivision.getDivisor();<br />
}<br />
catch(ArithmeticException arithmeticException) {<br />
<br />
throw arithmeticException;<br />
} <br />
}<br />
<br />
@Test<br />
public void testDivision() {<br />
<br />
quotient = division.buildQuotient(100, 10);<br />
<br />
Assert.assertEquals(10, quotient);<br />
}<br />
<br />
@Test(expected=IllegalArgumentException.class)<br />
public void testDivisionDivideByZero() {<br />
<br />
quotient = division.buildQuotient(100, 0);<br />
}<br />
}<br />
<br />
<u><b>Note:</b></u> There is a difference in semantics between Data Transfer Objects (DTOs) and Value Objects (VOs) therefore, VO should not be an alias name for DTO. Both objects hold data but a VO is identified by its values (enum kind style, no identity but equality) as against to a DTO which can have an identity (unique identifier as attribute). <br />
<br />
<div style="background-color: #cccccc;">
<br />
<b>Der
Rechtshinweis des Java Blog für Clean Code Developer ist bei der
Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu
beachten.</b><br />
<br /></div>
</div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-66287583911617625782012-11-08T21:34:00.001+01:002012-11-08T21:47:46.255+01:00Abstraction<div style="text-align: justify;">
Introducing <b>Java Generics</b> in Java 5 has been a big deal for Java developers. Avoid failures at runtime is the biggest challenge in software development. The best solution to avoid failures is to detect errors early at compile time. The compile time type check of generic collections is the real benefit of this Java feature. Get rid of the type casts necessary by using legacy collections simplifies the development of software in the generic context. </div>
<div style="text-align: justify;">
<br />
Generics work by erasure which can be a weakness. The genius may use a leak to break the compile time type check by mixing legacy collections with generic collections. On the other hand the inexperienced programmer could make a programming error which breaks a generic collection as well. Anyway, we have the iron-cast guarantee and we know what we do, therefore we are safe. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The reason to build Generics on erasure is the binary compatibility to legacy collections. This reduces the migration effort from earlier Java releases to Java 5 software to zero. The possibilites range from using legacy collections with or without Generics and vice versa. Using Generics seems to be difficult. Programmers must learn some rules to have fun with Generics. One famous rule is the get-put-principle. Maurice Naftalin and Philip Wadler documented this rule in the book "Java Generics and Collections". Do you know that rule? Remember or learn: extends = get, super = put and no wildcard get and put. Isn't it a funny gotcha.</div>
<br />
<div style="text-align: justify;">
Regarding the requirements of clean code, Generics fulfill the criteria for writing self-explanatory source code. Well, that sounds good, but I must admit it is only true when you have learned all these rules which can sometimes mutate to brain burner. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Interesting is the influence of abstraction in case of Generics related to data. Why not abstract over behavior in the same way Generics abstract over data? First it seems we have a big gap. But we are already able to abstract over behavior. We know and use the <b>Strategy Pattern </b>(GoF-Pattern) and the possible implementation by using an anonymous class which can be threat as a<b> function object</b> (Joshua Bloch "Effective Java"). A function object can be used to implement the requirements of behavior abstraction which leads to<b> Lamda expressions</b>. The experienced programmer will say no: function objects are a similar technique but weaker compared to Lamda expressions and less readable. Again, that sounds good. But how about that little rules, we already know from Generics?! Ok, we will see and learn these gotchas. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div style="background-color: orange;">
<b>Spot on to light the following excerpt: </b></div>
</div>
<div style="text-align: justify;">
<br />
<b><i>Generics allowed developers to better abstraction over types, the goal of Lamda is to help developer better abstract over behavior. [Brian Goetz, Oracle’s Java Language Architect]<br /> </i></b></div>
<div style="text-align: justify;">
Wow! But what’s behind that excerpt? The idea is simple to threat a chunk of code like a piece of data. Describing and characterizing a method in the same way in which you would declare and use a variable is a real benefit of Lamda. The possibility for passing a Lamda expression into a method like an ordinary type will fundamentally change the development of Java software.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNq_RlTR8P4BR7MeeJ4FMOF1bKbqcmB4KR5OLNHiTDNJB7OCZocnD5b-Izan9JBmC8raxGc_atX0nFHGLjsy5QoK0EpXZAbw85RTGWi1d-5uiNTjN0zFNT0hyfOkhf7l50-UeaWjEf20SY/s400/Abstraction.png" width="400" /></div>
<br />
I’m looking forward to Java 8 and the features which improve the Java developer toolset. This toolset has additional features like the<b> virtual extension methods </b>which provides to implement default methods for interfaces. Default methods for interfaces mean methods with a body! That's sounds strange but why not? It is simply the support for interface evolution to maintain binary
compatibility with classes already compiled with older interface versions. Older binaries work with the new default interface methods without the requirement of recompilation. Great, no more nightmares with public interfaces.<br />
<br />
Thank you for your attention! See you next time! I promise to publish more source code next time to discuss Java topics which is clearer than plain text.</div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-27373302421782267872012-10-14T21:28:00.000+02:002012-10-14T21:57:36.959+02:00Armed against breaking Liskov<div style="text-align: justify;">
Software is generally based on contracts which are declared by interfaces and abstract base classes. The danger in OOP is to break the <b>Liskov Substitution Principle (LSP)</b> during implementing a contract in a class hierarchy. We remember: Liskov provides the correct way to implement subtyping without "the must" to modify classes during extending the class hierarchy. This is only the short version of the story. We know that Liskov has a deeper sight which describes the expected behaviour of subclasses in relationship to their base classes. Extensions of a class hierarchy are a cornerstore of OOP which is based on inheritance and ploymorphism. Therefore, be careful at design time of a class hierarchy by following Liskov.<br />
<br />
There are several solutions to avoid breaking Liskov. The first solution is to use delegate instead of inheritance. <b>Favour Composition over Inheritance (FCoI)</b> is the proper CCD principle for this case. Another practical way to ensure that Liskov is satisfied is to use <b>Design By Contract (DBC)</b> which is <u>not a CCD</u> principle. In the CCD community we discussed hard whether DBC should be part of the CCD universe. The Result of the discussions was that DBC is not a typical CCD principle these days. The pronounciation lays on “these days”. The range of opinions were from "we need DBC at runtime to ensure the correct implementation of contracts" to "DBC is not necessary since we use <b>Unit-Tests</b> in combination with agile methods<b> (TDD)</b> in the software development process". My personal opinion was and still is that DBC is worth to be a CCD principle. With a DBC framework on board the conditions to fulfill a contract are expressed at runtime (such as during testing) which in my opinion is a honest benefit. </div>
<div style="text-align: justify;">
<br />
Anyway, DBC helps to avoid Liskov violation using preconditions, postconditions and invariants. If you think about conditions you’ll notice that preconditions are requirements to the user of a method, while the postconditions are the requirements to the method themselves.</div>
<div style="text-align: justify;">
<br />
The simple way to avoid breaking Liskov is to use inheritance in combination with design patterns. <b>Template Method</b> is the pattern that provides a useful constraint in subtyping. Errors during subtyping may occur by overriding concrete methods and forgetting calling the parent method. Another interesting subject is that the Template Method Pattern helps to avoid duplication <b>(DRY)</b>. To avoid duplication is important to reduce the danger of messy software. Template Method Pattern is the vehicle during refactoring software to eliminate duplication which is a strong part of the TDD mantra.</div>
<div style="text-align: justify;">
<br />
With the Template Method Pattern we do not override a concrete method. Instead we implement a method as a template in the base class and let the subclass decide whether to implement the method or not. This technique helps us to avoid breaking Liskov. Therefore, the Template Method Pattern can be treated as an aid against breaking Liskov.</div>
<div style="text-align: justify;">
<br />
<div style="background-color: orange;">
<b>Simple class hierarchy implementing the Template Method Pattern: </b></div>
</div>
<br />
abstract class Cooking {<br />
<br />
/**<br />
* No Source code duplication. <br />
* Let the subclasses decide the cooking strategy. <br />
*/<br />
public void prepareMeal() {<br />
<br />
// buy food<br />
// prepare food<br />
<span style="background-color: lime;"> cook();</span><br />
// serve food<br />
}<br />
<br />
<span style="background-color: lime;"> protected abstract void cook();</span><br />
}<br />
<br />
class CookInOven extends Cooking {<br />
<br />
@Override<br />
protected void <span style="background-color: lime;">cook() </span>{/* in oven */} <br />
}<br />
<br />
class CookOnGrill extends Cooking {<br />
<br />
@Override<br />
protected void <span style="background-color: lime;">cook()</span> {/* on grill */} <br />
}<br />
<br />
class CookingBean {<br />
<br />
public void prepareMeal(final Cooking cooking) {<br />
<br />
cooking.prepareMeal();<br />
}<br />
}<br />
<br />
<div style="background-color: #cccccc;">
<br />
<b>Der
Rechtshinweis des Java Blog für Clean Code Developer ist bei der
Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu
beachten.
</b><br />
<br /></div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-76540542566443363162012-10-07T18:27:00.001+02:002012-10-14T15:00:48.208+02:00Builder Factory<div style="text-align: justify;">
A common problem is to initialize Java Collections within the builder pattern. The combination of the builder and the factory pattern solves this problem. Remember that the builder separates the construction of an object from its representation. The builder can therefore create objects which are variable initialized. The builder gives greater control over the construction process and isolates the construction code from the representation. This is easy in case of simple attribute types, but not for collections.</div>
<br />
<div style="text-align: justify;">
Collections have to be created first to store objects during the initialization process. Java inner classes support the isolation requirements of the creation process which is implemented in the builder. Isolation is one of the important principles of object oriented programming and often mentioned as a key principle of clean code. Why? The answer is simple: Isolation gives you the possibility to test your code.</div>
<br />
<div style="text-align: justify;">
The general rule is that only isolated loosely coupled objects are ready for testing. This rule is even more important whenever you start not with <b>TDD</b> which means you write your test code later. This is not the usual case but possible when the development team is separated from the test team. Take care and implement loosely coupled objects which are chained via <b>Dependency Injection (DI) </b>which is a principle integrated in the Java EE Standard as well as frameworks like Spring.</div>
<br />
<div style="background-color: orange;">
<b> Builder Factory Implementation:</b></div>
<br />
import java.util.ArrayList;<br />
import java.util.List;<br />
<br />
public class BuilderFactoryBean {<br />
<br />
private final ItemBean itemBean;<br />
<br />
public BuilderFactoryBean(final Instance builderFactory) {<br />
<br />
itemBean = builderFactory.itemBean;<br />
}<br />
<br />
static class Instance {<br />
<br />
private final ItemBean itemBean = new ItemBean();<br />
<br />
public Instance cid(final String cid) { <br />
<br />
itemBean.setCid(cid);<br />
<br />
return(this);<br />
}<br />
<br />
public Instance item(final String listItem) { <br />
<br />
<span style="background-color: lime;"> initializeItemList();</span><br />
<br />
addItem(listItem);<br />
<br />
return(this);<br />
}<br />
<br />
<span style="background-color: white;"><span style="background-color: lime;"> private void initializeItemList()</span> {</span><br />
<span style="background-color: white;"> </span><br />
<span style="background-color: white;"> if(null == itemBean.getListItems()) {</span><br />
<span style="background-color: white;"> </span><br />
<span style="background-color: white;"> final BuilderFactory<String> factory = new BuilderFactory<String>() {</span><br />
<span style="background-color: white;"> </span><br />
<span style="background-color: white;"> @Override</span><br />
<span style="background-color: white;"> public List<String> create() {</span><br />
<span style="background-color: white;"> </span><br />
<span style="background-color: white;"> return new ArrayList<String>();</span><br />
<span style="background-color: white;"> } </span><br />
<span style="background-color: white;"> };</span><br />
<span style="background-color: white;"> </span><br />
<span style="background-color: white;"> itemBean.setListItems(factory.create());</span><br />
<span style="background-color: white;"> }</span><br />
<span style="background-color: white;"> }</span><br />
<br />
private void addItem(final String listItem) {<br />
<br />
itemBean.getListItems().add(listItem);<br />
}<br />
<br />
public BuilderFactoryBean build() {<br />
<br />
return(new BuilderFactoryBean(this));<br />
}<br />
<br />
private interface BuilderFactory<T> {<br />
<br />
public List<T> create(); <br />
}<br />
}<br />
<br />
public ItemBean getItemBean() {<br />
<br />
return itemBean;<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b> Item Bean:</b></div>
<br />
import java.util.List;<br />
<br />
public class ItemBean {<br />
<br />
private String cid;<br />
private List<String> listItems;<br />
<br />
public String getCid() {<br />
return cid;<br />
}<br />
<br />
public void setCid(String cid) {<br />
this.cid = cid;<br />
}<br />
<br />
public List<String> getListItems() {<br />
return listItems;<br />
}<br />
<br />
public void setListItems(List<String> listItems) {<br />
this.listItems = listItems;<br />
}<br />
<br />
@Override<br />
public String toString() {<br />
<br />
final StringBuilder strBuilder = new StringBuilder();<br />
strBuilder.append("cid=");<br />
strBuilder.append(cid);<br />
<br />
for(String listItem : listItems) { <br />
<br />
appendItem(strBuilder, listItem);<br />
}<br />
<br />
return strBuilder.toString();<br />
}<br />
<br />
private void appendItem(final StringBuilder strBuilder, final String listItem) {<br />
<br />
strBuilder.append(",");<br />
strBuilder.append("item=");<br />
strBuilder.append(listItem);<br />
}<br />
<br />
@Override<br />
public int hashCode() {<br />
final int prime = 31;<br />
int result = 1;<br />
result = prime * result + ((cid == null) ? 0 : cid.hashCode());<br />
result = prime * result<br />
+ ((listItems == null) ? 0 : listItems.hashCode());<br />
return result;<br />
}<br />
<br />
@Override<br />
public boolean equals(Object obj) {<br />
if (this == obj)<br />
return true;<br />
if (obj == null)<br />
return false;<br />
if (getClass() != obj.getClass())<br />
return false;<br />
ItemBean other = (ItemBean) obj;<br />
if (cid == null) {<br />
if (other.cid != null)<br />
return false;<br />
} else if (!cid.equals(other.cid))<br />
return false;<br />
if (listItems == null) {<br />
if (other.listItems != null)<br />
return false;<br />
} else if (!listItems.equals(other.listItems))<br />
return false;<br />
return true;<br />
}<br />
}<br />
<br />
<div style="background-color: orange;">
<b> Builder Factory Test:</b></div>
<br />
import org.junit.Assert;<br />
import org.junit.Test;<br />
<br />
public class BuilderFactoryBeanTest {<br />
<br />
@Test<br />
public void testBuilderFactory() {<br />
<br />
final BuilderFactoryBean firstBuilderFactory = new BuilderFactoryBean.Instance().cid("cid-1").<br />
item("item-1").item("item-2").build();<br />
<br />
final BuilderFactoryBean secondBuilderFactory = new BuilderFactoryBean.Instance().cid("cid-1").<br />
item("item-1").item("item-2").build();<br />
<br />
Assert.assertEquals(firstBuilderFactory.getItemBean(), secondBuilderFactory.getItemBean());<br />
}<br />
}<br />
<br />
<div style="text-align: justify;">
<br />
<div style="background-color: #cccccc;">
<br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.
</b><br />
<br /></div>
</div>
Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-9546352940699919462011-12-28T19:45:00.002+01:002011-12-29T18:37:12.727+01:00Principle of Least Astonishment<div style="text-align: justify;">A fundamental rule of software development is building small assemblies with high cohesion. Software assemblies do one job and they have a clear name describing this job. This means, if you are not able to give assemblies a clear name, you have to split the assemblies to even smaller ones. Breaking this rule will violate the <b>Principle of Least Astonishment</b> and annoy the users of the software assemblies. In bad cases, wrong usage and bugs follow simple code smells which could be solved by clear naming or the separation of the assemblies.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The following method is an example of a code smell violating the Principle of Least Astonishment. This software shows that it is possible to break a source code base with a few lines of smelly code.</div><br />
<div style="background-color: orange;"><b>Principle of Least Astonishment violation:</b></div><br />
public void addInstance(String className) {<br />
<br />
try {<br />
<br />
Class clazz = Class.forName(className);<br />
objectSet.add(clazz.newInstance());<br />
}<br />
catch (RuntimeException ex) {<br />
<br />
throw ex;<br />
}<br />
catch (Exception ex) {<br />
<br />
throw new RuntimeException(ex);<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>What is wrong with this code?</b></div><br />
<ol style="text-align: justify;"><li>There are no asserts in the code to check pre and post conditions. A stable implementation would check the method argument and type of the created instance with “instanceof” (be careful you would on the other hands side lose the flexibility of the implementation and violate the <b>Liskov Substitution Principle</b>). <br />
<br />
</li>
<li>The compile time type checking is lost. Therefore, errors may come up late at runtime, which hit the code hard and open the door for a security violation.<br />
<br />
</li>
<li>The more stable implementation of the code would lead to code bloat.<br />
<br />
</li>
<li>The method name is bad. There is more than adding an instance. The better name is “createAndAddInstance”. Think about the name. The possible better solution is to split the method instead of changing the name.<br />
<br />
</li>
<li>The implementation assumes that instances which the access modifiers "public" or "package" are created and added to the set.</li>
</ol><div style="text-align: justify;">An alternative implementation covers the strong points and shows the code bloat to remember “new” with all its benefits could be more appropriate solving the main problems of the implementation.</div><br />
<div style="background-color: orange;"><b>Alternative implementation including an Unit-Test:</b></div><br />
import java.util.HashSet;<br />
import java.util.Set;<br />
import org.ccd.reflection.test.DomainObjectException.ErrorCode;<br />
import org.junit.Assert;<br />
import org.junit.Test;<br />
<br />
public class InstantiationByReflectionTest {<br />
<br />
private Set<DomainObject> objectSet = new HashSet<DomainObject>();<br />
<br />
@Test<br />
public void testInstantiation() {<br />
<br />
createAndAddDomainObject("org.ccd.reflection.test.DomainObject");<br />
createAndAddDomainObject("org.ccd.reflection.test.DomainObject");<br />
<br />
Assert.assertTrue(objectSet.size() == 2 ? true : false); <br />
}<br />
<br />
@Test<br />
public void testNullPointerArgument() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject(null);<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.NullPointerArgument);<br />
}<br />
}<br />
<br />
@Test<br />
public void testEmptyArgument() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject(" ");<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.EmptyArgument);<br />
}<br />
}<br />
<br />
@Test<br />
public void testIllegalType() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject("java.lang.String");<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.IllegalType);<br />
}<br />
}<br />
<br />
@Test<br />
public void testClassNotFound() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject("org.ccd.reflection.test.MissingDomainObject");<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.ClassNotFound);<br />
}<br />
}<br />
<br />
@Test<br />
public void testInstantiationFailure() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject("java.io.Serializable");<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.InstantiationFailed);<br />
}<br />
}<br />
<br />
@SuppressWarnings("unused")<br />
private class MyDomainObject {}<br />
<br />
@Test<br />
public void testInstantiationPrivate() {<br />
<br />
try {<br />
<br />
createAndAddDomainObject("org.ccd.reflection.test.MyDomainObject");<br />
<br />
}<br />
catch(DomainObjectException ex) {<br />
<br />
Assert.assertTrue(ex.errorCode() == ErrorCode.ClassNotFound);<br />
} <br />
}<br />
<br />
private void createAndAddDomainObject(final String className) {<br />
<br />
assertNotNull(className);<br />
assertNotEmpty(className);<br />
<br />
Class<?> cl = null;<br />
try {<br />
<br />
cl = Class.forName(className);<br />
} <br />
catch (ClassNotFoundException ex) {<br />
<br />
throw new DomainObjectException(ErrorCode.ClassNotFound);<br />
}<br />
<br />
Object domainObject = null;<br />
try {<br />
<br />
domainObject = cl.newInstance();<br />
} <br />
catch (InstantiationException ex) {<br />
<br />
throw new DomainObjectException(ErrorCode.InstantiationFailed);<br />
} <br />
catch (IllegalAccessException ex) {<br />
<br />
throw new DomainObjectException(ErrorCode.IllegalAccess);<br />
}<br />
<br />
assertDomainObjectType(domainObject);<br />
<br />
objectSet.add((DomainObject) domainObject);<br />
}<br />
<br />
private void assertNotNull(final String className) {<br />
<br />
if(null == className) {<br />
<br />
throw new DomainObjectException(ErrorCode.NullPointerArgument);<br />
}<br />
}<br />
<br />
private void assertNotEmpty(final String className) {<br />
<br />
if(className.trim().isEmpty()) {<br />
<br />
throw new DomainObjectException(ErrorCode.EmptyArgument);<br />
}<br />
}<br />
<br />
private void assertDomainObjectType(final Object domainObject) {<br />
<br />
if(!(domainObject instanceof DomainObject)) {<br />
<br />
throw new DomainObjectException(ErrorCode.IllegalType);<br />
}<br />
}<br />
}<br />
<br />
class DomainObject {}<br />
<br />
class DomainObjectException extends RuntimeException {<br />
<br />
public enum ErrorCode {<br />
<br />
ClassNotFound("Class not found!"),<br />
InstantiationFailed("Instantiation failed!"),<br />
IllegalAccess("Access not denied!"),<br />
NullPointerArgument("Parameter cannot be null!"),<br />
EmptyArgument("Parameter cannot be empty!"),<br />
IllegalType("Illegal type detected");<br />
<br />
private final String errorMessage;<br />
<br />
private ErrorCode(final String errorMessage) {<br />
<br />
this.errorMessage = errorMessage;<br />
}<br />
<br />
String errorMessage() {<br />
<br />
return errorMessage;<br />
}<br />
}<br />
<br />
private final ErrorCode errorCode;<br />
<br />
public DomainObjectException(final ErrorCode errorCode) { <br />
<br />
this.errorCode = errorCode;<br />
}<br />
<br />
ErrorCode errorCode() {<br />
<br />
return errorCode;<br />
}<br />
}<br />
<br />
<div style="background-color: #cccccc; text-align: justify;"><br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.</b><br />
<b> </b> </div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-67071626581213251322011-07-08T19:14:00.072+02:002011-08-06T20:51:48.718+02:00Java 7 Spike<div style="text-align: justify;"><div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDOoKuyRG6ZFyx7T-c7ouvJXsDMM_4thAWO7IOkgQxMyTLEEA-8ro5UrwfJJ_uTvzqOLq4z-6l0XdQ4TT1IW2PoBNmNb0cNq0Qaswlgws3aRVuctUeGtxRb43vShg4dl9x3ZJfktF-iS_Q/s1600/Duke.wave.shadow.gif.png" /></div></div></div>Oracle released the JDK developer preview for <b>Java SE 7</b>. This release is feature complete, stable and ready for testing. These were enough reasons to write a short Java SE 7 <b>Spike</b>. The first step writing the Spike was to install the JDK. After the installation of the JDK, I installed Eclipse Indigo for Java Developers and a patch to enable the Java 7 compiler compliance level in Eclipse. The Spike involves small tests to gain experience and knowledge about Java 7 language enhancements which are hosted in the <b>Coin Project</b>.</div><br />
<div style="background-color: orange;"><b>The Spike covers the following Java 7 features:</b></div><br />
<ul><li>strings in switch statements</li>
<li>underscores in numeric literals</li>
<li>catching multiple exceptions</li>
<li>rethrow exceptions with improved type checking</li>
<li>type inference for generic instance creation (Diamond)</li>
<li>try-with-resources statement</li>
</ul><br />
<div style="background-color: orange;"><b>Java 7 Spike:</b></div><br />
import static org.junit.Assert.assertEquals;<br />
import java.io.IOException;<br />
import java.nio.ByteBuffer;<br />
import java.nio.channels.AsynchronousFileChannel;<br />
import java.nio.channels.CompletionHandler;<br />
import java.nio.file.Path;<br />
import java.nio.file.Paths;<br />
import java.text.ParseException;<br />
import java.util.LinkedList;<br />
import java.util.List;<br />
import org.junit.Test;<br />
<br />
public class <b>Java7Spike</b> {<br />
<br />
@Test<br />
public void <b>testStringsInSwitch()</b> {<br />
<br />
assertEquals("right direction", decideDirection("right"));<br />
assertEquals("left direction", decideDirection("left"));<br />
assertEquals("no direction", decideDirection("no"));<br />
}<br />
<br />
private String decideDirection(final String decision) {<br />
<br />
switch(<b>decision</b>) {<br />
<br />
case <b>"right"</b>:<br />
<br />
return "right direction";<br />
<br />
case <b>"left"</b>:<br />
<br />
return "left direction";<br />
<br />
default:<br />
<br />
return "no direction"; <br />
}<br />
}<br />
<br />
@Test<br />
public void <b>testUnderscoreInNumericLiterals()</b> {<br />
<br />
int numericLiteral = <b>12_30</b>;<br />
numericLiteral++;<br />
<br />
assertEquals(1231, numericLiteral);<br />
}<br />
<br />
@Test<br />
public void <b>testMultiCatch()</b> {<br />
<br />
try {<br />
<br />
final String fileText = readFile("src/org/ccd/java/features/testNio.txt");<br />
parseFile(fileText); <br />
} <br />
catch (<b>IOException | ParseException ex</b>) {<br />
<br />
throw new IllegalStateException("Error during file handling occured!", ex);<br />
} <br />
}<br />
<br />
private String readFile(final String filename) throws IOException {return null;}<br />
private void parseFile(final String content) throws ParseException {}<br />
<br />
@Test(expected=FirstException.class)<br />
public void<b> testRethrowExceptionWithTypeChecking() </b><br />
<b>throws</b> <b>FirstException, SecondException </b>{ <br />
try {<br />
<br />
simulateFailure();<br />
}<br />
catch (Exception ex) {<br />
<br />
throw ex;<br />
} <br />
}<br />
<br />
private void simulateFailure() throws FirstException {<br />
<br />
throw new FirstException();<br />
}<br />
<br />
private class FirstException extends Exception {<br />
<br />
private static final long serialVersionUID = -1872200400984813924L; <br />
}<br />
<br />
private class SecondException extends Exception {<br />
<br />
private static final long serialVersionUID = 7453507163657978623L; <br />
}<br />
<br />
@Test<br />
public void <b>testDiamond()</b> {<br />
<br />
final List<String> list = new LinkedList<b><></b>();<br />
list.add("anEntry");<br />
<br />
assertEquals(1, list.size());<br />
}<br />
<br />
@Test<br />
public void <b>testTryWithResources()</b> throws IOException {<br />
<br />
final Path file = Paths.get("src/org/ccd/java/features/testNio.txt");<br />
<br />
try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(file)) {<br />
<br />
final ByteBuffer buffer = ByteBuffer.allocate(100_000);<br />
<br />
channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {<br />
<br />
public void completed(Integer result, ByteBuffer attachment) {<br />
<br />
assertEquals(19, result.intValue());<br />
}<br />
<br />
public void failed(Throwable exception, ByteBuffer attachment) {<br />
<br />
exception.printStackTrace();<br />
} <br />
});<br />
}<br />
}<br />
}<br />
<br />
<div style="text-align: justify;">I am looking forward to <b>Java SE 8</b> which may include further language enhancements, summarized in the Coin Project and additional features like the integration of <b>Closures (JSR 335 / Lambda Project)</b>.<br />
<br />
<div style="background-color: #cccccc;"><b><br />
Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-22565867713868815462011-07-04T23:19:00.004+02:002011-07-07T17:18:59.827+02:00Catch the web service bug<div style="text-align: justify;">Agile developer catch bugs with tests. Unit tests improve the internal quality of a software system and acceptance tests the external quality visible to the users. The testing effort reduces the defect rate and increases maintainability. Smart testing is a shield against the trap of slow and high cost development that could occur by poor written code which is not covered by tests. The fast feedback of the tests gives developers confidence and more time for other tasks in the software development cycle.</div><div style="text-align: justify;"><br />
But take care! Test driven development (TDD) including high test coverage is not the guarantee to pass all possible problems. There is the danger to walk in a bad direction forgetting the big picture during refactoring. This is also true working in the flow zone, forgetting the picture which the software model defines. Some refactoring steps following KISS and pragmatic programming could possibly drive the ball also in a new direction, which could be in the future a problem while extending the software due to a change request.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Agile developers are careful and aware of such traps. TDD should start at the major components. The model of a software solution is a typical candidate for TDD. This is also true for web services which are simply backend services with a remote interface accessible via the internet. This sounds simple, but is it simple? There is a network between the backend service and the user of the web service. Networks may be slow, sometimes the connection is missing and the world wide web is like the deep universe. A good reason to limit the access to web services and secure them as strong as possible. Securing web services and fulfill all the non-functional requirements needs time and can’t be covered with a simple unit test. Special testing is necessary beneath the unit and acceptance tests. Agile developers know that they have to have a test pyramid in the pocket!</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The lowest levels of the pyramid are unit and acceptance tests. From the developer perspective the aim to catch bugs should be to find them as early as possible. Perfectly with unit tests not using additional acceptance test frameworks. Java SE 6 gives web service developers an endpoint publisher that offers the possibility to test end to end in simple unit tests. This statement fits for SOAP- and REST-based web services which are based on JAX-WS. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Here comes a little story about the competition of SOAP- and REST-style web services:</b></div></div><div style="text-align: justify;"><br />
The opinion that REST-style web services are the vehicle which solves a lot of problems easily and pretty lightweight flows around in the community. I’ve heard that often. Few days ago, I read a statement in a Java EE blog and wrote a few lines:<br />
<br />
<i>JAX-WS is a mature technology for building SOAP- and REST-based web services. The REST part of JAX-WS is not comfortable enough for web service developer, therefore JAX-RS was borne which is easier to use even with more functionality. But this is not the point! </i><br />
<br />
<i><b>The point is that JAX-WS (SOAP part) and JAX-RS are both legal depending on use cases. </b>The real benefit of SOAP-based web services is the WSDL which defines the web service interface. The top-down approach (WSDL2Java) is still a best practice to solve interoperability problems. WSDL interfaces are clear and (quasi) normed by using the WS-I basic profile as a guide for web service development. </i><br />
<br />
<i>The WSDL is the important artifact from which the generation of Java artifacts is easy by tooling. The power of WSDL is important in Enterprise Service Bus (ESB) environments and Java Business Integration (JBI) components, which are described by WSDL. My opinion is that REST is not the big player in ESB environments. I think you need a definition (for SOAP services the WSDL) to handle parameter types and encodings, protocol transformations, routings and mediation. Therefore, I see REST rather as the lightweight and maybe agile gate to portal backend systems to build single processes (no orchestration necessary).</i><br />
<i><br />
I fully agree that JAX-RS makes coding funny and I like to write RESTful web services rather to write a complex WSDL file (although Eclipse support is available). The lightweight approach of REST is nice, but has a danger. The input line of the browser (you need no scripting to call the service) is the direct gateway to the database for a not properly secured REST service. To secure the REST service is still effort, including configuration and programming tasks.</i> <br />
<br />
<b><i>The response came very fast: Which use cases?! </i></b><br />
<br />
<i>The simplest use case is the usage of an existing session facade. The publication of the facade as web service is easy by adding an annotation (@WebService). More fine granular by using annotations on the facade methods (@WebMethod). This use case is not really a strong point, but doing the same with REST is more coding and design effort for the simple goal to use the legacy software as web service.</i><br />
<i><br />
A stronger point is that with JAX-WS the developer defines domain specific interfaces (domain specific verbs) which are self-describing by the WSDL. The REST interface instead is generic (set of defined verbs) leaving the domain specific subjects in the addressed resources. This is essential! JAX-WS (SOAP-part) is therefore primarily activity-oriented and REST is strongly resource-oriented. The architecture of REST is based on the generic interface for mainly CRUD operations. You see it also in the Response and Fault codes. SOAP faults are domain specific. REST faults are Http specific error codes. To use the response for the domain specific fault message in association with Http state 200 (OK) is a poor decision. This leads to a possible use case were domain specific faults has to be traced and processed (JAX-WS - SOAP fault has a benefit!).</i><br />
<i><br />
Developer can build complex interactions with REST. Even multipart messages are possible with JAX-RS. The argument that REST is inherently stateless and SOAP has the possibility to define stateful services can be moderated by defining a stateless architecture not holding any state on the server side. On the other side, I see some bricks to lay out use cases.</i><br />
<br />
<i>When we come to ESBs the strong points occur. SOAP services can be bound to different protocols. ESBs work internally with JMS to cover the requirements of the pipeline architecture. Without the possibility to bound SOAP to different protocols this architecture which is a key point for SOA wouldn’t as it is today. The SOAP extensibility model is the key, defining SOAP nodes processing the header of the SOAP envelope for routing and mediation issues. We do not have such functions for REST. REST is easy but tightly coupled with the Http protocol. I do not actually see the orchestration and workflow requirements covered by REST where human tasks are involved and the reply is minutes, sometimes hours or days away from the primary request.</i><br />
<i><br />
<b>I told you that short story that you have a feeling for </b></i><i><b> the effort of writing the SOA- and REST-parts which are published </b></i><i><b>in the following unit test with the endpoint publisher. </b></i>You see in the unit tests how easy it is to publish a SOAP-based web service from an existing model and the additional effort to use the existing model in a REST-based web service. I admit that the RESTful web service is a Java EE 5 one. Java EE 6 and JAX-RS makes the implementation of RESTful web services simpler.<br />
<br />
The backend model of the web service is a quiz application with two simple operations. I show only the unit tests of the web service backend model and the web service test parts. The implementation of the backend service is not interesting in this context. My goal is to show the endpoint publisher in action and not the implementation of simplified web services.<br />
<br />
<div style="background-color: orange;"><b>Web Service backend model test:</b></div><br />
@RunWith(value=Parameterized.class)<br />
public class QuizServiceTest {<br />
<br />
private String[][] quizData;<br />
private final String question;<br />
private final String answer;<br />
<br />
private Quiz quizService;<br />
<br />
public QuizServiceTest(final String[] questions,<br />
final String[] answers) {<br />
<br />
this.question = questions[0];<br />
this.answer = answers[0];<br />
<br />
initializeQuizData(questions, answers);<br />
}<br />
<br />
private void initializeQuizData(final String[] questions, <br />
final String[] answers) {<br />
<br />
quizData = new String[1][2];<br />
quizData[0][0] = questions[0];<br />
quizData[0][1] = answers[0];<br />
}<br />
<br />
@Parameters<br />
public static Collection<String[][]> getTestParameters() {<br />
<br />
return Arrays.asList(new String[][][] {<br />
<br />
{{"firstQuestion"},{"firstAnswer"}},<br />
{{"secondQuestion"},{"secondAnswer"}}});<br />
}<br />
<br />
@Before<br />
public void setUp() {<br />
<br />
quizService = new QuizServiceController();<br />
quizService.initialize(quizData); <br />
}<br />
<br />
@Test<br />
public void testQuizCorrectAnswer() {<br />
<br />
assertEquals(true, quizService.answer(question, answer));<br />
}<br />
<br />
@Test<br />
public void testQuizWrongAnswer() {<br />
<br />
assertEquals(false, quizService.answer(question, "anAnswer"));<br />
}<br />
}<br />
<br />
<b><div style="background-color: orange;">SOAP-Part unit test using the endpoint publisher:</div></b><br />
@RunWith(value = Parameterized.class)<br />
public class QuizSOAPServiceEndToEndTest {<br />
<br />
private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";<br />
private static final String WSDL_URL = "http://127.0.0.1:8080/quiz?wsdl";<br />
private static final String QNAME_NAMESPACE_URL = "http://quiz.service.web.java.ccd.org/";<br />
private static final String QNAME_SERVICE_NAME = "QuizServiceService";<br />
<br />
private String[][] quizData;<br />
private final String question;<br />
private final String answer;<br />
<br />
private QuizServiceSOAP quizService;<br />
<br />
public QuizSOAPServiceEndToEndTest(final String[] questions,<br />
final String[] answers) {<br />
<br />
this.question = questions[0];<br />
this.answer = answers[0];<br />
<br />
initializeQuizData(questions, answers);<br />
}<br />
<br />
private void initializeQuizData(final String[] questions, <br />
final String[] answers) {<br />
<br />
quizData = new String[1][2];<br />
quizData[0][0] = questions[0];<br />
quizData[0][1] = answers[0];<br />
}<br />
<br />
@Parameters<br />
public static Collection<String[][]> getTestParameters() {<br />
<br />
return Arrays.asList(new String[][][] {<br />
<br />
{{"firstQuestion"},{"firstAnswer"}},<br />
{{"secondQuestion"},{"secondAnswer"}}});<br />
}<br />
<br />
@Before<br />
public void setUp() throws MalformedURLException {<br />
<br />
<b>publishQuizService(); </b> <br />
<b> lookupQuizService(); </b><br />
<b> </b> initializeQuizService(); <br />
}<br />
<br />
private void <b>publishQuizService()</b> {<br />
<br />
<span style="background-color: lime;"> <b>Endpoint.publish(PUBLISH_URL, new QuizService());</b></span><br />
}<br />
<br />
private void<b> lookupQuizService()</b> throws MalformedURLException {<br />
<br />
final URL url = new URL(WSDL_URL);<br />
final QName qname = new QName(QNAME_NAMESPACE_URL,QNAME_SERVICE_NAME);<br />
<b style="background-color: lime;">final Service service = Service.create(url,qname);</b><br />
<b><span style="background-color: white;"> </span><span style="background-color: lime;">quizService = service.getPort(QuizServiceSOAP.class); </span></b><br />
}<br />
<br />
private void initializeQuizService() {<br />
<br />
quizService.initialize(quizData);<br />
}<br />
<br />
@Test<br />
public void testQuizCorrectAnswer() {<br />
<br />
assertEquals(true, quizService.answer(question, answer));<br />
}<br />
<br />
@Test<br />
public void testQuizWrongAnswer() {<br />
<br />
assertEquals(false, quizService.answer(question, "anAnswer"));<br />
}<br />
}<br />
<br />
<b>The publishing of the SOAP-based web service in the unit test is simple and fetching the service after publishing it is possible in a leightweight manner. Publishing the web service first and using later the browser or an external web service test tool based on the retrieval of the WSDL is also an opportunity.</b><br />
<br />
<div style="background-color: orange;"><b div="background-color: orange;">REST-part unit test using the endpoint publisher:</b></div></div><br />
@RunWith(value = Parameterized.class)public class QuizRESTServiceEndToEndTest {<br />
<br />
private static final String XPATH_QUIZ_URI_PART = "quiz";<br />
private static final String XPATH_QUIZ_RESPONSE = "/quiz:response";<br />
private static final String GET_REQUEST = "GET";<br />
private static final String POST_REQUEST = "POST";<br />
<br />
private static final String QUIZ_PORT = "quizPort";<br />
private static final String QUIZ_SERVICE = "quizService";<br />
private static final String QUIZ_URI = "urn:quiz";<br />
<br />
private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";<br />
<br />
private final String XML_START = "<quiz:request xmlns:quiz ='urn:quiz'>";<br />
private final String XML_END = "</quiz:request>";<br />
<br />
private static final String FALSE_RESPONSE = "false";<br />
private static final String TRUE_RESPONSE = "true";<br />
private static final String QUESTION_DELIMITER = ":";<br />
private static final String QUESTION_ANSWER_DELIMITER = ";";<br />
<br />
private String[][] quizData;<br />
private final String question;<br />
private final String answer;<br />
<br />
private Dispatch<Source> dispatch;<br />
private String uri;<br />
<br />
public QuizRESTServiceEndToEndTest(final String[] questions,<br />
final String[] answers) {<br />
<br />
this.question = questions[0];<br />
this.answer = answers[0];<br />
<br />
initializeQuizData(questions, answers);<br />
}<br />
<br />
private void initializeQuizData(final String[] questions, <br />
final String[] answers) {<br />
<br />
quizData = new String[1][2];<br />
quizData[0][0] = questions[0];<br />
quizData[0][1] = answers[0];<br />
}<br />
<br />
@Parameters<br />
public static Collection<String[][]> getTestParameters() {<br />
<br />
return Arrays.asList(new String[][][] {<br />
<br />
{{"firstQuestion"},{"firstAnswer"}},<br />
{{"secondQuestion"},{"secondAnswer"}}});<br />
}<br />
<br />
@Before<br />
public void setUp() throws URISyntaxException {<br />
<br />
<b> publishQuizService(); <br />
lookupQuizService(); </b> <br />
initializeQuizService(); <br />
}<br />
<br />
private void <b>publishQuizService()</b> {<br />
<br />
<b style="background-color: lime;">Endpoint.publish(PUBLISH_URL, new QuizServiceRest());</b><br />
}<br />
<br />
private void <b>lookupQuizService()</b> throws URISyntaxException {<br />
<br />
uri = new URI(QUIZ_URI).toString();<br />
<br />
final QName serviceName = new QName(QUIZ_SERVICE, uri);<br />
final QName port = new QName(QUIZ_PORT, uri);<br />
final String endpoint = PUBLISH_URL; <br />
<br />
final Service service = Service.create(serviceName);<br />
service.addPort(port,HTTPBinding.HTTP_BINDING, endpoint);<br />
<br />
<b style="background-color: lime;">dispatch = service.createDispatch(port,Source.class,Service.Mode.PAYLOAD);</b> <br />
}<br />
<br />
private void initializeQuizService() {<br />
<br />
invokePost(dispatch, buildPayload(convertQuizDataToString(quizData)));<br />
}<br />
<br />
private String invokePost(final Dispatch<Source> dispatch, <br />
final String payload) {<br />
<br />
final Map<String, Object> requestContext = dispatch.getRequestContext();<br />
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, POST_REQUEST); <br />
<br />
final StreamSource source = makeStreamSource(payload.toString());<br />
final Source result = dispatch.invoke(source);<br />
<br />
return parseResponse(result);<br />
}<br />
<br />
private StreamSource makeStreamSource(final String payload) {<br />
<br />
final ByteArrayInputStream stream = new ByteArrayInputStream(payload.getBytes());<br />
return new StreamSource(stream);<br />
}<br />
<br />
private String buildPayload(final String payload) {<br />
<br />
return XML_START + payload + XML_END;<br />
}<br />
<br />
private String convertQuizDataToString(final String[][] payload) {<br />
<br />
final StringBuilder builder = new StringBuilder();<br />
<br />
for(int i = 0; i < payload.length; i++) {<br />
<br />
builder.append(payload[i][0]);<br />
builder.append(QUESTION_DELIMITER);<br />
builder.append(payload[i][1]);<br />
builder.append(QUESTION_ANSWER_DELIMITER);<br />
}<br />
<br />
return builder.toString();<br />
}<br />
<br />
private String parseResponse(final Source response) {<br />
<br />
final DOMResult domResult = new DOMResult();<br />
String result = null;<br />
try {<br />
<br />
final Transformer transformer = TransformerFactory.newInstance().newTransformer();<br />
transformer.transform(response, domResult);<br />
<br />
final XPathFactory xpFactory = XPathFactory.newInstance();<br />
final XPath xp = xpFactory.newXPath();<br />
<br />
xp.setNamespaceContext(new NSResolver(XPATH_QUIZ_URI_PART, QUIZ_URI));<br />
<br />
result = xp.evaluate(XPATH_QUIZ_RESPONSE, domResult.getNode()); <br />
} <br />
catch (TransformerConfigurationException ex) {<br />
<br />
throw new IllegalStateException(ex);<br />
} <br />
catch (TransformerFactoryConfigurationError ex) {<br />
<br />
throw new IllegalStateException(ex);<br />
} <br />
catch (TransformerException ex) {<br />
<br />
throw new IllegalStateException(ex);<br />
} <br />
catch (XPathExpressionException ex) {<br />
<br />
throw new IllegalStateException(ex);<br />
}<br />
<br />
return result;<br />
}<br />
<br />
@Test<br />
public void testQuizCorrectAnswer() {<br />
<br />
assertEquals(TRUE_RESPONSE,<br />
invokeGet(dispatch,question + QUESTION_DELIMITER + answer));<br />
}<br />
<br />
@Test<br />
public void testQuizWrongAnswer() {<br />
<br />
assertEquals(FALSE_RESPONSE,<br />
invokeGet(dispatch,question + QUESTION_DELIMITER + "anAnswer"));<br />
}<br />
<br />
private String invokeGet(final Dispatch<Source> dispatch, <br />
final String queryString) {<br />
<br />
final Map<String, Object> requestContext = dispatch.getRequestContext();<br />
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, GET_REQUEST);<br />
requestContext.put(MessageContext.QUERY_STRING, queryString);<br />
<br />
final Source result = dispatch.invoke(null);<br />
<br />
return parseResponse(result);<br />
}<br />
}<br />
<br />
<div style="text-align: justify;"><b div="background-color: orange;">The RESTful web service unit test uses the endpoint publisher and the dispatch interface. The server side RESTful web services implements the provider interface. It is obviously more coding effort in the given scenario to publish the quiz backend model as RESTful web service in the unit test and the service implementation. The SOAP-part is in line with the quiz backend model, simply using the JAX-WS annotations. For the RESTful part it's (slightly) a little bit to much coding necessary. Praise for Java EE 6 which gives us JAX-RS, to reduce the coding effort of RESTful web services noticeable.</b></div><div style="text-align: justify;"><b div="background-color: orange;"><br />
</b></div><div style="background-color: #cccccc; text-align: justify;"><b><br />
Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-52363408826441309892011-06-17T12:20:00.015+02:002011-08-06T20:53:47.912+02:00Let it flow...<div style="text-align: justify;">The Object Modeling Technique (OMT) introduced by James Rumbaugh defines for the analysis phase (OOA) three modeling types. Starting at the object level, defining an object model, the dynamic modeling is following for the core parts of the business logic mainly to visualize state changes. Object models and dynamic models covered through state diagrams are present in the UML today. The functional modeling, also known as data flow models, are not part of the UML. The reason might be the functional background of data flows, not recognized as an important modeling technique in the object-oriented world.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">For my computer science degree I designed with OMT and developed a multimedia system in the programming language C which is a portable solution for Microsoft Windows NT and IBM OS/2. I designed in the object-oriented style and used the functional programming language C to implement the model, that was possible with some tricks and effort. I remember one important point during modeling the multimedia system. I had some problems with the object model relationships and sometimes problems to define the dynamic model in detail. The data flow modeling was easier for me, more natural and rather fluent. </div><div style="text-align: justify;"><br />
</div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="308" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSgXO27r8yoP7RZbFy1m7lhjV9nXUPnmlWba-_CTvyCblCzn-K0Oc-maCJFUliCScKCuINSEoitFJGRdLZMELEQ77Ed0B7INcy-KcJsWvJthuks8dj9lliornFyncOwR77ujioOQxFYc-9/s400/data-flow.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Data Flow Diagram</td></tr>
</tbody></table><div style="text-align: justify;">At that time I thought the problem is my weak experience in modeling complex object-oriented systems. Later in my career I learned object-oriented modeling starting with the domain layer (DDD). I learned to visualize the software model in class and component diagrams using stereotypes and the power of UML diagrams. This power is in some cases weak when it touches the implementation and the differences in the model and the implementation occur. Continuous rework for the improvement of the models are necessary to avoid this weakness. Modeling on an appropriate level with appropriate details is not simple and some lessons are necessary to learn modeling on the right level. My advice is to try the Java EE architect certification as an opportunity to find this level.</div><div style="text-align: justify;"><br />
During last weeks, I followed the discussion about <b>Event Based Components (EBC)</b> which is a technique to develop software flow-oriented. Ralf Westphal defined principles and patterns for the flow-oriented programming. I read some of his blog articles and listened to the discussions in the clean code developer forum. It seems to me that the .NET community is very eager in the field of flow-oriented programming. Later, I found a website from a developer whose aim is to bring EBC to the Java platform. Impressed of the flow-oriented modeling which again seems natural and fluent, usable on different levels and in line with the implementation, I was inspired to find a <b>Flow Pattern</b>. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Therefore it was obvious to start Eclipse to do some TDD practice to dive into the flow.<b> </b>I admit that my knowledge about flow-oriented programming is weak and I rather think in object-oriented structures. This is what I have learned over the years developing software on the Java platform. Usually it is a worse starting position developing software in a field with weak domain knowledge, but it’s still a training session. Starting with this background programming flows leading to a solution for a simple pattern for flow-oriented programming. Do not expect the "killer pattern" for a wide range of scenarios and problem solutions covered by this flow pattern.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The pattern is following the principle of the “input-process-output” model. It is usable in isolation or in chained scenarios. The use case for the pattern is a simple calculator board adding and subtracting values encapsulated in assemblies. The following discusses the flow pattern that is first documented with UML.</div><div style="text-align: justify;"><br />
</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglTP3lsQtX7rjzcxFe51MDfJfzS5I38rFy9g9VziZQIhPI57YUeyZD565eZWWNZiMiwZFEL8bikdAIHN1fyNboGogX4NRIWn0lU0uDrwMBYsPk1SpU0F76hxCk0asdKaTUNlEc13z8-nNl/s1600/FlowPattern.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Flow Pattern</td></tr>
</tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: justify;"><br />
The<b> “FlowChain”</b> is an inspiration of the <b>ChainOfResponsibility</b> pattern. The ChainOfResponsibility pattern loosely couples assemblies and let the assemblies decide whether to process a value or not. The core of the pattern is based on <b>Template Method</b> by overriding the flow method to chain assemblies. The benefit of this pattern is to chain in the overriden flow method which is well encapsulated. The drawback is that you can only test assemblies covering their relationships, chained in the flow method. It’s not possible to test assemblies in isolation, that might be a drawback for some use cases.<br />
<br />
The interface of the flow pattern is simple by calling the process method of an assembly instance which extends the flow pattern. The process method expects an input pin and returns an output pin as the result of the processing task. It’s not possible to build the chaining outside of the assemblies which is flexible but on the other side a slightly more complex programming interface.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The <b>“FlowWire”</b> is weaker coupled than the “FlowChain” with the possibility for outside wiring and testing of assemblies in isolation. In the flow pattern implementation the "FlowWire" extends the “FlowChain” with a wire method for outside wiring. The pattern consists of small super classes as the base for boards or assemblies of the boards using pins to fulfill the requirements of the “input-process-output” model.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The flow assemblies are based on classes whose instances are chained. It’s also possible to see flows as a chain of method calls which is finally more fine grained. The flow pattern covers simple use cases, not supporting complex routings and broadcasts.</div><br />
<div style="text-align: justify;"></div><div style="text-align: justify;"><div style="background-color: orange;"><b>FlowChain (stronger coupling)</b></div></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">public abstract class FlowChain<T> {<br />
<br />
public FlowPin<T> process(final FlowPin<T> inPin) {<br />
<br />
return flow(inPin);<br />
}<br />
<br />
protected abstract FlowPin<T> flow(final FlowPin<T> inPin);<br />
}<br />
<br />
<div style="background-color: orange;"><b> FlowWire (weaker coupling)</b></div><br />
public abstract class FlowWire<T> extends FlowChain<T> {<br />
<br />
public FlowPin<T> wire(final FlowWire<T> assembly, <br />
final FlowPin<T> inPin) {<br />
<br />
return assembly.process(inPin);<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>FlowPin</b></div></div><br />
public class FlowPin<T> {<br />
<br />
private final T value;<br />
<br />
public FlowPin() {<br />
<br />
this(null);<br />
}<br />
<br />
public FlowPin(final T value) {<br />
<br />
this.value = value;<br />
}<br />
<br />
public T value() {<br />
<br />
return value;<br />
}<br />
<br />
@Override<br />
public int hashCode() {<br />
<br />
final int prime = 31;<br />
int result = 1;<br />
result = prime * result + ((value == null) ? 0 : value.hashCode());<br />
<br />
return result;<br />
}<br />
<br />
@Override<br />
public boolean equals(Object obj) {<br />
<br />
if (this == obj)<br />
return true;<br />
if (obj == null)<br />
return false;<br />
if (getClass() != obj.getClass())<br />
return false; <br />
final FlowPin<T> other = (FlowPin<T>) obj;<br />
if (value == null) {<br />
if (other.value != null)<br />
return false;<br />
} else if (!value.equals(other.value))<br />
return false;<br />
<br />
return true;<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>FlowChainTest</b></div><br />
import static org.junit.Assert.assertEquals;<br />
import org.ccd.flow.pattern.api.FlowChain;<br />
import org.ccd.flow.pattern.api.FlowPin;<br />
import org.junit.Test;<br />
<br />
public class FlowChainTest {<br />
<br />
@Test<br />
public void testSubtractAssembly() {<br />
<br />
final FlowChain<Integer> subtractAssembly = new SubtractAssembly(); <br />
assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));<br />
}<br />
<br />
@Test<br />
public void testAddAssembly() {<br />
<br />
final FlowChain<Integer> addAssembly = new AddAssembly(); <br />
assertEquals(new FlowPin<Integer>(17), addAssembly.process(new FlowPin<Integer>(12)));<br />
}<br />
<br />
@Test<br />
public void testCalculatorBoard() {<br />
<br />
final FlowChain<Integer> calculatorBoard = new CalculatorBoard(); <br />
assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));<br />
}<br />
<br />
private class CalculatorBoard extends FlowChain<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
final AddAssembly add = new AddAssembly();<br />
<br />
return add.process(inPin);<br />
}<br />
}<br />
<br />
private class AddAssembly extends FlowChain<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
final Integer value = addValue(inPin.value());<br />
final SubtractAssembly subtract = new SubtractAssembly(); <br />
<br />
return subtract.process(new FlowPin<Integer>(value));<br />
}<br />
<br />
private Integer addValue(final Integer value) {<br />
<br />
return value + 10;<br />
}<br />
}<br />
<br />
private class SubtractAssembly extends FlowChain<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
Integer value = substractValue(inPin.value()); <br />
<br />
return new FlowPin<Integer>(value);<br />
}<br />
<br />
private Integer substractValue(final Integer value) {<br />
<br />
return value - 5;<br />
}<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>FlowWireTest</b></div><br />
import static org.junit.Assert.assertEquals;<br />
import org.ccd.flow.pattern.api.FlowPin;<br />
import org.ccd.flow.pattern.api.FlowWire;<br />
import org.junit.Test;<br />
<br />
public class FlowWireTest {<br />
<br />
@Test<br />
public void testSubtractAssembly() {<br />
<br />
final FlowWire<Integer> subtractAssembly = new SubtractAssembly(); <br />
assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));<br />
}<br />
<br />
@Test<br />
public void testAddAssembly() {<br />
<br />
final FlowWire<Integer> addAssembly = new AddAssembly(); <br />
assertEquals(new FlowPin<Integer>(22), addAssembly.process(new FlowPin<Integer>(12)));<br />
}<br />
<br />
@Test<br />
public void testCalculatorBoard() {<br />
<br />
final FlowWire<Integer> calculatorBoard = new CalculatorBoard(); <br />
assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));<br />
}<br />
<br />
private class CalculatorBoard extends FlowWire<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
final FlowWire<Integer> add = new AddAssembly(); <br />
final FlowPin<Integer> outPin = wire(add,inPin);<br />
<br />
final FlowWire<Integer> subtract = new SubtractAssembly(); <br />
return add.wire(subtract,outPin);<br />
}<br />
}<br />
<br />
private class AddAssembly extends FlowWire<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
final Integer value = addValue(inPin.value());<br />
<br />
return new FlowPin<Integer>(value);<br />
}<br />
<br />
private Integer addValue(final Integer value) {<br />
<br />
return value + 10;<br />
}<br />
}<br />
<br />
private class SubtractAssembly extends FlowWire<Integer> {<br />
<br />
@Override<br />
protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {<br />
<br />
final Integer value = substractValue(inPin.value()); <br />
<br />
return new FlowPin<Integer>(value);<br />
}<br />
<br />
private Integer substractValue(final Integer value) {<br />
<br />
return value - 5;<br />
}<br />
}<br />
}<br />
<br />
<div style="text-align: justify;">The flow pattern is developed in an object-oriented manner to support the programming of flows. Their is usually a border between object-oriented and flow-oriented programming. But listen: "This border is in your mind". You see it's possible to mix different styles to find a solution. The Flow Pattern is object-oriented and the implemented flows in the test cases are a mixture of the flow-oriented and object-oriented style.Using the power of both styles is appropriate to define a solution that fullfils the criteria of "input-process-output", encapsulation, isolation, loose coupling (coupling happens only on the board) and high cohesion to develop small assemblies and wire them to build a bigger assembly named as board. Wire boards and build a bigger solution that I call the flow-oriented application.You see, let it flow...to cross the border.</div><br />
<div style="background-color: #cccccc; text-align: justify;"><b><br />
Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-39512851607160284002011-06-13T17:48:00.029+02:002011-08-06T20:48:13.686+02:00Code of Conduct<div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: justify;">There is no doubt that Robert C. Martin is an experienced programmer who gives the community of programmers a framework to write clean code. Robert published a lot of articles about principles to improve software quality which flows in his book “Clean Code: A Handbook of Agile Software Craftsmanship”. This book is rather technique centric, viewing code smells, guidelines how to refactor smells, explaining best practices for writing clean code and testing. The mission of the book is to sharpen programmer skills and learn how to reflect the daily written code using the boy scout rule for continuous improvements.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">In Germany, Ralf Westphal and Stefan Lieser founded the clean code developer community launching a website which summarizes the principles and practices of clean code. In technical discussion the community understands the principles of clean code knowing that there must be more than technical terms. Proposals were made to missing principles, new ideas came up and the constructive critique boosted the understanding of clean code principles. Discussions about object-oriented principles, testing, agile models, programming language specifics followed along with the discussions in the clean code community. Code katas, to show how to write clean code, were used to view the benefits of clean code for discussion in the community. There is still some critique regarding code bloat, doubts about some principles in detail and how to handle the development lifecycle (e.g. TDD "yes or no" or how and when to do the design for modeling the software).</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAQeFwxb0i5XyFg4ITDW9jLOWt5soJ0JOSH2dsvAFNtAvIcFuzE9tFhSsKGMkvGU1D1kyku5XIlrjgtzsZL2SWUwQGebYmyczxHsqsR4srST0kba7FMD_i48R1X7f9SCLFZyMguCWlrS1X/s200/thecleancoder.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Clean Coder</td></tr>
</tbody></table>One fact was completely missing: The attitude of a professional programmer. First, I was doubtful about the new book of Robert which defines a code of conduct for professional programmers describing the attitude of a professional in detail. Personally, I don’t like books which advice behavior, how to handle schedules, conflicts and pressure. On the other hand, I like Robert’s style of writing books, explaining daily work scenarios and true experience of his craftsmanship career. The introduction of the book includes the excerpt: "I’ am a programmer, too. I’ve been a programmer for 42 years (don’t panic); and in that time - let me tell you – I’ve seen it all…". Robert has definitively a strong voice which is based on his experience leading to his brilliant reputation. But no one is born which such a voice, it’s still hard work to get every day a little bit better following the principles of clean code.<br />
<br />
His message is to work clean (QA should find nothing), to have a work ethic, to know your field and domain, to learn continuously, to practice frequently, to collaborate with colleagues and to mentor junior developers as well as identification with your company and customers to mention the primarily subjects. Diving deeper into the subjects explains when to say “yes” or “no”, how to say it and what it really means. Some subjects are already known in the agile community like the “definition of done” (Scrum) and the commitment (the contract for agreement) to deliver features. </div></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">The book is easy to read and full of real life experience of a programmer with brilliant reputation. I have also experience working in different teams using different models and programming languages (mainly C/C++ and Java) for software development. I found a lot of experience written in this book which I have also made in my career. Leading to the simple principle to work clean, to avoid high pressure, conflicts and overtime. In Robert's words the best technical programmer could be fired when he has no professional attitude and his work is like a mess. We all know that pressure, conflicts and overtime could lead to bad code quality. But all this reasons are not acceptable as an excuse for bad code quality. Therefore, avoid pressure by clear communication and commitments which are reachable. Work clean to avoid overtime, and if overtime is necessary, the overtime should happen only in a defined time frame. Resolve conflicts like a professional and when this is not possible resolve it with a sense of humor (laugh may help). It’s also natural to have a wall in front of you, sitting on the keyboard and nothing happens. You may solve this situation by asking for a pair partner or simply accept the situation trying to resolve by going to the window to get some fresh air.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Interesting for me is Robert's opinion according the Flow Zone. For an experienced programmer the Zone is usually like a vacuum, working in a closed room with high speed producing large pieces of source code. I compare the Zone with a mountain bike trail having the power to cycle without hurts forgetting all sorrows in a perfect manner. It’s a nice feeling which could on the other hand be exhausting, if you cycle too far away from your home, it could be a problem to get back. Robert has the same opinion. Avoid the Zone although you write more code with higher speed. But the danger is to lose the big picture not getting the way back home. His advice is to enter the Zone only during practicing a training kata.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">I could write even more about the book: <b>“The Clean Coder – A Code of Conduct for Professional Programmers"</b> but I might leave it up to you to participate in a valuable publication which completes the craftsmanship series with useful hints to work clean in the software development lifecycle with the appropriate attitude and responsibility to get things done.</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-57600755107186602562011-06-11T17:22:00.005+02:002011-06-14T15:32:00.847+02:00Vertical separation<div style="text-align: justify;">Die <b>vertikale Trennung</b> schreibt die Definition von Variablen und Methoden nahe bei ihrer Verwendung vor. Verbunden mit der vertikalen Trennung ist die Kohäsion von Methoden. Eine kohäsive Methode ist klein und erfüllt genau eine Aufgabe. Das Band zwischen der vertikalen Trennung und kohäsiven Methoden wird durch die Lesefolge gebunden. Ausgehend von einer öffentlichen Methode werden die privaten Methoden, welche in der öffentlichen Methode verwendet werden, unterhalb der öffentlichen Methode im Quellcode geschrieben. Der vertikale Abstand zwischen den kohäsiven Methoden soll gering sein.</div><br />
<div style="text-align: justify;">Die vertikale Trennung wird oft mit dem Lesen einer Tageszeitung gleichgesetzt. In einer Tageszeitung werden bewusst hervorgehobene Überschriften (Headlines) genutzt, denen ein Artikel folgt. Das <b>Single Level of Abstraction (SLA)</b> Prinzip gibt vor, dass in Klassen und Methoden möglichst nur ein Abstraktionsniveau verwendet werden soll. Teil dieses Prinzips ist, dass vorzugsweise mit lokalen Variablen in Methoden anstelle von Attributen einer Klasse gearbeiten wird. Die Nutzung lokaler Variablen vermeidet Seiteneffekte und beugt Problemen, die durch Nebenläufigkeit entstehen können vor. Quellcode, der dem SLA Prinzip folgt, ist gut zu lesen und zu verstehen. Der Leser erkennt durch die saubere Anordnung des Quellcodes sehr viel leichter essentielle Abschnitte ohne sich dabei in Details zu verfangen. Die vertikale Trennung und Bildung kleiner kohäsiver Methoden ist eine essentielle Praktik bei der Refaktorisierung durch Anwendung der <b>Pfadfinderregel</b>.</div><br />
<div style="text-align: justify;">Die vertikale Trennung erhöht aber nicht nur die Lesbarkeit von Quellcode, sondern ist partiell nötig, um Kompilierprobleme zu Gunsten eines klaren Methodendesigns zu beseitigen.</div><div style="text-align: justify;"><br />
</div><div style="background-color: orange;"><b>Beispiel „Wildcard Capture“:</b></div><br />
public static void reverse(<b>List<?></b> list);<br />
public static <b><T></b> void reverse(<b>List<T></b> list);<br />
<br />
<div style="text-align: justify;">Die Methodensignatur der “reverse” Methode kann auf Basis eines Wildcards oder mit einem Typparameter beschrieben werden. Die Wildcard Methodensignatur ist kürzer und lesbarer, bringt aber die Problematik mit sich, dass die übergebene Liste per Definition nicht mit neuen Elementen erweitert werden darf. Ein Kompilierfehler zeigt dieses Problem an. Das Problem ist durch eine zusätzliche Methode, die der vertikalen Trennung genügt, lösbar.</div><div style="text-align: justify;"></div><br />
<div style="background-color: orange; text-align: justify;"><b>Quellcode einer einfachen ListHelper-Klasse:</b></div><div style="text-align: justify;"><b> </b> </div><div style="text-align: justify;">public class ListHelper {<br />
<br />
public static void reverse(final List<?> list) {<br />
<br />
assertNotNull(list); <br />
assertNotEmpty(list);<br />
<br />
reverseOperation(list);<br />
}<br />
<br />
private static void assertNotNull(final List<?> list) {<br />
<br />
if(null == list) {<br />
<br />
throw new NullPointerException("List argument is null!");<br />
}<br />
}<br />
<br />
private static void assertNotEmpty(final List<?> list) {<br />
<br />
if(list.isEmpty()) {<br />
<br />
throw new IllegalArgumentException("List contains no elements!");<br />
}<br />
}<br />
<br />
private static <T> void reverseOperation(final List<T> list) {<br />
<br />
final List<T> tmpList = new ArrayList<T>(list);<br />
final int maxPositions = list.size() - 1;<br />
int readPosition = 0;<br />
<br />
for(int writePosition = 0; writePosition <= maxPositions; writePosition++) {<br />
<br />
readPosition = maxPositions - writePosition;<br />
list.set(writePosition, tmpList.get((readPosition)));<br />
}<br />
}<br />
} </div><br />
<b>In der Methode "reverseOperation" hat der Typparameter den Wildcard erfasst (captured).</b><br />
<br />
<div style="background-color: orange;"><b>Testfälle der ListHelper-Klasse:</b></div><br />
@RunWith(value=Parameterized.class)<br />
public class TestListHelper {<br />
<br />
private final List<String> original;<br />
private final List<String> reversed;<br />
<br />
public TestListHelper(final String[] original,<br />
final String[] reversed) {<br />
<br />
this.original = Arrays.asList(original);<br />
this.reversed = Arrays.asList(reversed);<br />
}<br />
<br />
@Parameters<br />
public static Collection<String[][]> getTestParameters() {<br />
<br />
return Arrays.asList(new String[][][]{<br />
<br />
{{"one", "two", "three"},{"three", "two", "one"}},<br />
{{"1", "2", "3"},{"3", "2", "1"}}});<br />
}<br />
<br />
@Test<br />
public void testReverse() {<br />
<br />
ListHelper.reverse(original); <br />
assertEquals(reversed, original);<br />
}<br />
}<br />
<br />
public class TestListHelperExceptions {<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testReverseWithNullPointerException() {<br />
<br />
ListHelper.reverse(null);<br />
}<br />
<br />
@Test(expected=IllegalArgumentException.class)<br />
public void testReverseWithIllegalArgumentException() {<br />
<br />
ListHelper.reverse(new ArrayList<String>());<br />
} <br />
}<br />
<br />
<div style="background-color: #cccccc; text-align: justify;"><b><br />
Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-59920616178019254502011-05-27T13:23:00.004+02:002011-06-09T10:11:35.912+02:00Shy coding<div style="text-align: justify;">Schüchterner Quellcode minimiert die Abhängigkeiten zwischen Objekten. Die Reduzierung der Abhängigkeiten führt zu loser Kopplung, die wiederum isolierte testbare Softwarebausteine hervorbringt. Testbare Softwarebausteine mit ihren spezifischen Eigenschaften (gekapselt, kohäsiv, Mock freundlich, etc.), fördern die Evolvierbarkeit einer Software-Applikation.</div><br />
<div style="text-align: justify;">Das<b> „Law of Demeter (LoD)"</b> definiert Regeln für schüchternen Quellcode. Das klassische Law of Demeter basiert auf einer Veröffentlichung von Karl Lieberherr und Ian Holland. Das Law of Demeter wird mit dem <b>"Principle of Least Knowledge"</b> gleichgesetzt. Dieses Prinzip sagt aus, dass Objekte nur mit eng befreundeten Objekten sprechen sollen. Die Interaktionen zwischen Objekten sollen durch Einhaltung des Prinzips reduziert werden, sodass lose gekoppelte Software-Architekturen entstehen. Wird das Prinzip häufig verletzt, entstehen zerbrechliche Systeme, die schwer zu warten und zu verstehen sind.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Anfällig für die Verletzung des Prinzips sind <b>Facaden</b>, die eine einfachere Sicht auf ein komplexes Teilsystem bieten und <b>Aggregatsobjekte</b> (Kompositionen, Kontextobjekte, etc.), die eine Menge an Funktionalitäten bündeln. Vorsicht ist auch bei <b>Mediatoren</b> geboten, die komplexere Interaktionen zwischen Objekten vereinfachen sollen.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Entwicklungsumgebungen wie Eclipse unterstützen die Suche von Objekten durch ausgefeilte Kontextfunktionen. Dennoch ist es ratsam Interaktionen zwischen Objekten zu minimieren, sodass eine Suche nach Objekten nicht nötig ist. Die <b>transitive Navigation</b> führt dabei besonders zu unübersichtlichen Objektvernetzungen, die nur schwer aufgelöst werden können. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Beispiel für eine transitive Navigation: </b></div></div><br />
<b>long count = order<span style="background-color: red;">.getProduct().getItems().countItems();</span></b><br />
<br />
<u><b>Besser ist:</b></u><br />
<br />
<b>long count = order<span style="background-color: lime;">.countItems();</span></b><br />
<br />
<div style="background-color: orange;"><b>Beispiel für einen Verstoß gegen das Law of Demeter: </b></div><br />
<b>Constructor(final <span style="background-color: red;">Context context</span>) {</b><br />
<b><br />
</b><br />
<b> this.view = <span style="background-color: red;">context.getFrame().getView();</span></b><br />
<b>}</b><br />
<br />
<u><b>Korrekt ist:</b></u><br />
<br />
<b>Constructor(final <span style="background-color: lime;">View view</span>) {</b><br />
<b><br />
</b><br />
<b> this.view = <span style="background-color: lime;">view;</span></b><br />
<b>}</b><br />
<br />
<div style="background-color: orange;"><b>Merkregeln, rufe nur Methoden auf von...</b></div><ol><li>dir selbst (normalerweise private Methoden),</li>
<li>Objektreferenzen, die als Methodenargument übergeben wurden,</li>
<li>jedem selbst erzeugten Objekt (in Methoden der Klasse erzeugten Objekten),</li>
<li>in Assoziation stehenden Klassen (Attribute der Klasse) </li>
</ol>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-80799304021569458632011-05-15T13:21:00.013+02:002011-08-06T20:55:47.509+02:00Testing for Clean Agility<div style="text-align: justify;">Softwareentwicklungsmethoden nutzen Tests zur Verifikation und Sicherstellung der Qualität einer Softwareanwendung. Im klassischen <b>Wasserfallmodell</b> erfolgt der Testzyklus nach der Implementierungsphase in Form einer Verifikation der Implementierung. Wurde die Verifikation erfolgreich abgeschlossen, erfolgt das Deployment der Applikation in die Kundenumgebung. In der Kundenumgebung finden im Rahmen der Abnahmeprozesse die Akzeptanztests statt.<br />
<br />
Das Wasserfallmodell funktioniert sofern der Kunde zuvor eine Detailspezifikation abgenommen hat und keine größeren Änderungen im Projektverlauf fordert. Häufig ist sich der Kunde im Vorfeld eines Projektes aber unschlüssig und kennt noch nicht alle seine Anforderungen im Detail. Das Wasserfallmodell blockiert das Lernen des Kunden und das Finden der geeigneten Anforderungen im Projektverlauf. <br />
<br />
Im Wasserfallmodell sind die einzelnen Phasen strikt voneinander getrennt, sodass das Design und die Akzeptanztests weit auseinander liegen. Die Distanz zwischen der Implementierungsphase und der Verifikation der Software durch Testzyklen im Anschluss an die Implementierung ist darüber hinaus zu weitläufig.</div><br />
<div style="text-align: justify;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2MFYjQX9T40pIWRYkPmRjWu39sR4Aoj_4RALx1E5NfaS2bG-jPmc6ID21GYNb9oJ8tc8A851kyJOtOUzTK8nMi2Z2HhRvTM4oe3fx4gZoeIMhbO6dIpIwM1A-JYhha0SMuz_jj5xPvXto/s320/ccd-waterfall.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Wasserfallmodell </td></tr>
</tbody></table><br />
<div style="text-align: justify;">Agile Methoden wie <b>Scrum</b> nutzen überschaubare Planungs- und Implementierungsphasen von max. 30 Tagen. Tests stehen bei den agilen Methoden im Vordergrund. Agile Softwareentwicklung nutzt kurze Entwicklungszyklen und zeitnahes Feedback. Das zeitnahe Feedback wird durch automatisierte Testläufe und die kontinuierliche Integration erreicht.<br />
<br />
Automatisierte Testläufe, die zyklisch ausgeführt werden, sind eine wesentliche Voraussetzung für Refaktorisierungen zur Verbesserung der Softwarequalität. Die kontinuierliche Verbesserung der Softwarequalität ist fest in den agilen Methoden verankert. In Scrum ist normalerweise kein eigener Raum für Refaktorisierungen vorhanden. Refaktorisierungen und die Fehlerbehebung werden im Sprint bearbeitet. Nur in Ausnahmefällen werden größere Refaktorisierungen in ein technisches Backlog übernommen und in die Sprint-Planung miteinbezogen.<br />
<br />
Laut der <b>„Definition of Done“</b> erstellen Scrum Teams lieferbare User Stories in hoher Qualität. Fehlende Qualitätssicherung und mangelnde Refaktorisierungen würden sich in Sprints in der Folge negativ auf die Velocity eines Scrum Teams auswirken. Bei <b>CCD</b> sind Refaktorisierungen und automatisierte Tests ebenfalls tief verwurzelt. Die <b>Pfadfinderregel</b> propagiert geradezu Refaktorisierungen und damit die Beseitigung von <b>„Code Smells“</b>.</div></div><br />
<div style="text-align: justify;">Im agilen Lager gibt es dennoch unterschiedliche Meinungen wann und wie Testzyklen durchgeführt werden sollen. Es ist kein Streitpunkt mehr, dass automatisierte Tests zur Erzielung hoher Softwarequalität notwendig sind. Das schnelle Feedback und die mit den automatisierten Tests einhergehenden Vorteile sind ebenfalls unstrittig und anerkannt. Testbare Softwarebausteine sind in der Regel durch die lose Kopplung isoliert von ihrer Umgebung, weisen eine hohe Kohäsion <b>(SRP = Single Responsibility Principle) </b>auf und sind gut gekapselt <b>(IHP = Information Hiding Principle)</b>. Die Nutzung von Testattrappen (Mocks) erlaubt es unabhängig von den externen Abhängigkeiten einer komplexen Softwarearchitektur testbare Softwarebausteine zu erstellen.Dies sind alles Vorteile, welche Tests attraktiv machen.</div><br />
<div style="text-align: justify;">Rückt der <b>Test First</b> und <b>Test Driven Design/Development (TDD)</b> Ansatz die Unit-Tests zunächst in den Mittelpunkt verfolgen andere Ansätze, wie beispielsweise <b>DDT (Design Driven Testing)</b>, das Ziel, ausgehend von einem stabilen Design, testbaren Quellcode zu erstellen. Bei diesem Ansatz sind Implementierungsregeln einzuhalten, damit testbare Softwarebausteine entstehen können.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Beispiele für DDT Implementierungsregeln:</b></div></div><ol><li>Verwende die Vererbung mit Bedacht<b> (FCoI = Favour Composition over Inheritance)</b></li>
<li>Beachte das <b>Single Level of Abstraction (SLA)</b> Prinzip</li>
<li>Vermeide Singletons, statische Initialisierungen, statische Methoden und Variablen</li>
<li> Nutze die lose Kopplung</li>
<li>Trenne Oberflächen- und Geschäftslogik</li>
<li>Entferne Initialisierungen aus dem Konstruktor</li>
</ol><div style="text-align: justify;">Testbare Softwarebausteine werden bei DDT aus den Anwendungsfällen der Analysephase abgeleitet und entsprechen den Akzeptanztests. DDT geht Top-Down von der Benutzersicht aus. TDD hingegen ist eher als Bottom-Up Methode anzusehen, bei dem der Verwender eines Softwarebausteins eine zentrale Rolle spielt. Der Verwender ist allerdings nicht unbedingt der Benutzer des Softwarebausteins, sondern möglicherweise ein anderer Softwarebaustein.<br />
<br />
Bei TDD entstehen sehr viele kleine Unit-Tests. Die Gegner von TDD argumentieren deshalb gerne, dass TDD zu viele Testfälle hervorbringt, die nicht den gleichen Stellenwert haben wie die Kerntestfälle der Benutzersicht. Dennoch sind Unit-Tests für die Qualität von Software wesentlich, weil Unit-Tests grundsätzlich den Quellcode verbessern. Die Begründung für diese Aussage ist einfach: Ein Unit-Test ist ein Verwender des Quellcodes, sodass der Quellcode (insbesondere die Schnittstellen) im Zuge der einhergehenden Refaktorisierungen stetig besser wird. Übermäßiger Fleiß beim Schreiben der Unit-Tests kann allerdings zu viel des Guten sein. Eine hohe Testabdeckung durch Unit-Tests ist nämlich nicht immer ein Indikator für Softwarequalität. <b>Code Coverage </b>Tools, welche die Testabdeckung messen nutzen den Quellcode einer Applikation zur Analyse der Testabdeckung nicht aber alle möglichen Permutationen, die semantisches Wissen beinhalten.</div><br />
<div style="text-align: justify;">Frei nach dem Motto<b> „Design muss sein!“</b><b> </b>gehen deshalb neuere Ansätze zur Entwicklung von Software davon aus, dass die Akzeptanztests an erster Stelle stehen und die Unit-Tests ausschließlich ihren Platz in der Implementierungsphase haben. Demzufolge steht an erster Stelle ein Design in unterschiedlich starker Ausprägung. Ein Design drückt sich durch die Modellierung von Softwarebausteinen und das Nachdenken über Anforderungen, Schnittstellen und Randbedingungen aus. In der Summe gute Voraussetzungen für Softwarequalität ohne einen großen Graben wie beim Wasserfallmodell überwinden zu müssen.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">In TDD spricht man heute von einer inneren und äußeren Feedback Loop. Die äußere Feedback Loop wird durch einen Akzeptanztest (End-To-End Test) gebildet und die innere durch eine Reihe von Unit-Tests.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="276" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhujpMis8FaECFaYoVGz52Sz-XkX7ijB9fqknAWWXS_zRWWljQv70zxhvvdX7jWtrZcPBAa9nJ6ixKf02gFJNlEezDywPjEG_jI5b2TvKNzVo2x9mXNGFzZfN2NPpFDuOSp7wVee-ZD7omt/s640/ccd-tdd.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">TDD (innere/äußere Feedback Loops)</td></tr>
</tbody></table><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Bei Scrum spricht man von <b>"Acceptance Test Driven Development (ATDD)”</b>. Bei diesem Ansatz werden die funktionalen Anforderungen einer <b>User Story</b> als automatisierte Tests vor der Implementierung der Funktionalität geschrieben. Fehlgeschlagene Tests bringt man bei der inkrementellen Umsetzung einer User Story zum Laufen. Diese Tests werden für gewöhnlich vom Product Owner durchgeführt, der ein starkes Interesse an dem Wert einer User Story aus Sicht des Geschäftsvorteils hat. Scrum schreibt einem Team nicht vor, wie es im Sprint zu arbeiten hat. Unit-Tests, Refaktorisierungen und CCD-Prinzipien sind dennoch essentiell für das Erreichen eines Sprint-Ziels und der „Definition of Done“ lieferbare User Stories zu erstellen.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqjJj60RDAslpG2K5vw4K0xK9ZMRlngUwKesCZ7ft573yF4DWhf0CZjQAefxaCnnBZubySPaAYqijWaqr9kkwU5HsMs01Qb0JuIL_O861ZUTcdJ127Mg3Z9ItDQ7mqqLMice0sbe6K-Mvz/s640/ccd-atdd.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">ATDD-Kreislauf</td></tr>
</tbody></table><br />
<div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: justify;">Die bisher diskutierten Methoden nutzen <b>Feedbacks und Testlevels</b>. Die Einhaltung der Testlevels und häufiges zeitnahes Feedback haben sich in der Praxis bewährt und sind fest mit dem agilen Gedanken verbunden.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><b><div style="background-color: orange;">Testlevels beantworten folgende Fragen:</div></b><ol><li><b>Akzeptanztests:</b> Funktioniert eine Kette von Softwarebausteinen und das Gesamtsystem?</li>
<li><b>Integrationstests:</b> Funktioniert der entwickelte Quellcode im Kontext externer Schnittstellen und Systeme, die wir nicht ändern können?</li>
<li><b>Unit-Tests:</b> Erledigen die Objekte den Job richtig und sind die Objektschnittstellen einfach zu verwenden?</li>
</ol>Im Java-Umfeld sind für jeden Testlevel einfach zu nutzende Werkzeuge und Frameworks zur automatisierten Testdurchführung vorhanden. Für Akzeptanztests können Fitnesse, Selenium und JSFUnit verwendet werden. Integrationstests lassen sich mit Buildsystemen (<b>Continuous Integration</b>) wie Maven, Ant und Continuous Integration Servern wie Hudson, CruiseControl, Apache Gump sowie Apache Continuum durchführen. Unit-Tests sind die Domäne von JUnit, dem populären Framework für automatisierte Entwicklertests.<br />
<br />
Eine „Best Practice“ ist es, ausgehend von den Anforderungen, Akzeptanztests zu definieren/auszuführen, in der Implementierungsphase Unit-Tests konform dem TDD Mantra einzusetzen und zyklisch automatisierte Integrationstests beim „Nightly Build“ durch ein Continuous Integration (CI) System durchführen zu lassen. Durch diese "Best Practice" können Applikationen sehr viel schneller und in höherer Qualität entwickelt und regelmäßig mit geringer Reibung ausgeliefert werden. Wie wichtig automatisierte Tests und die kontinuierliche Integration für die agile Bewegung sind, verdeutlicht die Definition der <b>„Balanced Agility“ </b>von Mike Beedle. Diese Definition setzt minimale Voraussetzungen für den agilen Entwicklungsprozess voraus.<br />
<br />
<div style="background-color: orange;"><b>Definition für "Balanced Agility":</b></div></div><ol><li>Scrum als Vorgehensmodell</li>
<li>Grundlegende Engineering Practices</li>
<ul><li> Kontinuierliche Integration</li>
<li>Automatisierte Testdurchführung auf unterschiedlichen Testlevels</li>
<li>Release Management</li>
</ul></ol><div style="text-align: justify;">Die Definition von Mike Beedle zu adaptieren und um die CCD-Prinzipien zu erweitern ergibt das Synonym <b>„Clean Agility"</b>.</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-79817133803297738322011-04-29T20:19:00.058+02:002011-08-06T20:57:20.559+02:00Camel integration trail<div style="text-align: justify;"><b>Apache Camel</b> ist ein Routing und Mediation Engine. Camel unterstützt eine Vielzahl von Komponenten zur Lösung von Integrationsproblemen. Camel nutzt <b>Domain Specific Languages (DSLs)</b> für die Definition des Nachrichtenflusses und die Transformation von Nachrichten (Typ- und Formattransformationen). Die Architektur von Camel ist modular und erweiterbar. Camel kann als eigenständige, leichtgewichtige Laufzeitumgebung oder in ESB-Umgebungen betrieben werden. Apache Service Mix (ESB und JBI-Container) und die JBoss Enterprise SOA Platform 5.1(beinhaltet ein Camel Gateway als Technical Preview) können als Laufzeitumgebung für Camel dienen. Für andere ESB- und Laufzeitumgebungen wurden spezielle Integrationsszenarion, wie beispielsweise das Deployment des Camel Frameworks, als WAR definiert. Camel ist nicht als eigenständiger Integrationsserver oder ESB designed. Die Einbettung von Camel in zahlreiche Plattformen (Web, Spring, Java EE, JBI Container, OSGi, etc.) hingegen ist ein primäres Ziel des Camel-Teams.<br />
<br />
</div><div style="text-align: justify;"><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSK0kZJRkRn-GG5lTyjNWVQ-tpl8HkBf8lWs1kdEXcHAtAcpqsob5MgN9w5-Bu0mgf8RqQFVstGTWGDuS-WY7ub0z50r8o9LyfzaAOb2AbAkExkFjX07As5oNB2wumVQNue_7TQNXWxERA/s200/eip-book.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Documentation of 65 EIPs</td></tr>
</tbody></table>Apache Camel basiert auf den von Gregor Hohpe und Bobby Woolf definierten <b>Enterprise Integration Patterns (EIPs)</b>. Die beiden Autoren haben in ihrem Fachbuch: "Enterprise Integration Patterns" die wesentlichen immer wiederkehrenden Integrationsmuster publiziert. Das Buch ist als Lektüre für Integrationsspezialisten empfehlenswert. Die EIPs sind nicht nur für die Lösung von Integrationsproblemen nützlich, sondern definieren ein einheitliches Vokabular für die Kommunikation und Notation von Integrationslösungen.</div><div style="text-align: justify;"><br />
Apache Camel ist sehr einfach zu nutzen. Das Paradigma<b> Convention over Configuration</b> ist komfortabel für die Integrationsentwickler umgesetzt. Integrationsentwickler können sich sehr gezielt auf die Lösung eines fachlichen Integrationsproblems konzentrieren. Komplexe Konfigurationen und der Aufbau der Integrationsinfrastruktur zur Problemlösung sind nicht mehr durch den Entwickler vorzunehmen.Camel erlaubt die Lösung eines Integrationsproblems im kleinen Rahmen mit der Möglichkeit die gefundene Lösung in der Folge in eine komplexe ESB-Umgebung zu deployen. </div><div style="text-align: justify;"><br />
</div><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: justify;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="158" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinCvnfOmszaliMVFtoHEQSEXjr3gf39Uw4miKaw-U86MfQgw3CobqoiVKElCoAE9_nIn5KMbPfCIMdQOCFgN0SIg4fMwhZ7qaSopYrJJVPMM78UvnDFC-VmbJ1YK8P0qF1pllgo5GfsMn2/s640/camel-audit-route.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Best Practice: Develop single route and deploy in ESB environment</td></tr>
</tbody></table><br />
</div><div class="separator" style="clear: both; text-align: center;"></div><div style="text-align: justify;">Camel beinhaltet ein umfangreiches Test-Kit (inklusive Mocks für Endpoints) und unterstützt dadurch die <b>TDD</b> Strategie. Ein Endpoint wird über eine URI definiert und ist mit der URI-Notation parametrierbar. Zahlreiche Komponenten für gängige Protokolle und Schnittstellen für die Verwendung der gängigen Technologien sind bereits in Camel integriert. Typkonverter, eine einfache Beanschnittstelle, ein ausgefeiltes Fehlerhandling, DSLs (Java, Spring, Scala), Akka-Integration, Transaktionen, ein definiertes Threading-Modell und eine Expression Language sind weitere Highlights von Camel. Rasche Produktivität und zeitnahes Feedback ist bei der Anwendung von Apache Camel deshalb nicht nur ein Versprechen, sondern durch die vielfältigen Funktionalitäten bei gleichzeitiger Leichtgewichtigkeit des Frameworks garantiert.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Szenario Camel Route mit Auditfunktionalität:</b></div><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSNxU4hg3710Sv4jIo8ybKBmNgKPZrNMucYk-Wb33i5dCBwo2r5YOIT8ioXSKLT81x4FFfKfNxzS-h7NFr0K6cH3u1Oo0EeTnZ0K1L5c76hn4whMgmaAGbwgPzBJEihm7jk2XkGfjgsb6Y/s640/camel-audit-route.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Camel route with audit functionality</td></tr>
</tbody></table><br />
In dem Szenario werden aus einer Dateiquelle XML- und CSV-Dateien mit Auftragsdaten gelesen, prozessiert und über ein <b>Wire Tap</b> im Rahmen der Auditierung an den <b>Service Manager 2.0</b> zur Visualisierung gesendet.<br />
<br />
<div style="background-color: orange;"><b>Szenario Camel Route Quellcode (Java DSL):</b></div><br />
import javax.jms.ConnectionFactory;<br />
import org.apache.activemq.ActiveMQConnectionFactory;<br />
import org.apache.camel.CamelContext;<br />
import org.apache.camel.Exchange;<br />
import org.apache.camel.Processor;<br />
import org.apache.camel.builder.RouteBuilder;<br />
import org.apache.camel.component.jms.JmsComponent;<br />
import org.apache.camel.impl.DefaultCamelContext;<br />
import com.service.manager.gateway.api.application.camel.audit.OrderAuditService;<br />
import com.service.manager.gateway.api.application.camel.processor.OrderProcessorService;<br />
<br />
public class CamelRouteXmlAndCsvOrders {<br />
<br />
public static void main(String args[]) throws Exception {<br />
<br />
<b>final CamelContext context = new DefaultCamelContext();</b><br />
final OrderProcessorService processorService = OrderProcessorService.newInstance();<br />
<br />
final ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");<br />
context.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));<br />
<br />
<b>context.addRoutes</b>(new RouteBuilder() {<br />
<br />
@Override<br />
public void configure() {<br />
<br />
<b> from("file:data?noop=true").to("jms:incomingChannel");</b><br />
<br />
<b>from("jms:incomingChannel").<br />
wireTap("jms:serviceManagerAuditChannel").</b><br />
choice().<br />
when(header("CamelFileName").endsWith(".xml")).<br />
<b> to("jms:xmlDataChannel").</b><br />
when(header("CamelFileName").regex("^.*(csv|csl)$")). <br />
<b> to("jms:csvDataChannel").</b><br />
otherwise().<br />
to("jms:badDataChannel");<br />
<br />
<b> from("jms:xmlDataChannel").process</b>(new Processor() {<br />
<br />
public void process(Exchange exchange) throws Exception {<br />
<br />
processorService.processXmlData(exchange.getIn().getBody());<br />
} <br />
}); <br />
<br />
<b>from("jms:csvDataChannel").process</b>(new Processor() {<br />
<br />
public void process(Exchange exchange) throws Exception {<br />
<br />
processorService.processCsvData(exchange.getIn().getBody());<br />
}<br />
<br />
}); <br />
<br />
<b>from("jms:serviceManagerAuditChannel").</b></div><div style="text-align: justify;"><b> bean(OrderAuditService.class, "auditOrderMessage");</b><br />
}<br />
});<br />
<br />
context.start();<br />
Thread.sleep(2000);<br />
context.stop();<br />
}<br />
}</div><div style="text-align: justify;"><br />
Der Aufruf der Methode<b> bean(OrderAuditService.class, "auditOrderMessage") </b>delegiert die Verarbeitung der Audit-Message zu dem Audit-Service. Das<b> Service Activator Pattern</b> ist dabei die Grundlage für den Service-Aufruf. Der Service Activator dient als Mediator zwischen einem Requester und einem Service. Das Service Activator Pattern wird in Java EE Applikationen häufig in Message Driven Beans (MDBs) genutzt. Die Anwendung des Patterns in nicht Messaging-Szenarien ist allerdings ebenso möglich.<br />
<br />
<b><div style="background-color: orange;">Szenario Camel Route Audit Service:</div></b><br />
import org.apache.log4j.Logger;<br />
import com.service.manager.api.gateway.annotation.ServiceManagerGateway;<br />
import com.service.manager.api.gateway.context.ServiceManagerGatewayContext;<br />
import com.service.manager.gateway.api.application.camel.helper.DataConverterHelper;<br />
<br />
@ServiceManagerGateway(servicePlanId="SERVICEPLAN:12f9d7f03a413639340c8172e32354246e1", <br />
serviceNodeId="SERVICE:12f9d8074be1363e84b43082e32351810c22")<br />
public class OrderAuditService {<br />
<br />
static {<br />
<br />
ServiceManagerGatewayContext.newContext(OrderAuditService.class);<br />
}<br />
<br />
private static Logger log = Logger.getLogger(OrderAuditService.class);<br />
<br />
public void auditOrderMessage(final String body) {<br />
<br />
final DataConverterHelper dataConverter = DataConverterHelper.newInstance();<br />
<br />
if(dataConverter.isXml(body)) {<br />
<br />
log.info(dataConverter.convertXmlMessage(body));<br />
}<br />
else {<br />
<br />
log.info(dataConverter.convertCsvMessage(body));<br />
}<br />
}<br />
}<br />
<br />
Der Camel Route Audit Service sendet die Audit-Messages über das Service Manager 2.0 Gateway zu der Servicemanagementanwendung zur Visualisierung. Der Service Manager 2.0 eignet sich perfekt für die Anzeige von Audit- und Alarmmeldungen in SOA-Infrastrukturen, die mittels Camel und/oder ESBs betrieben werden.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSYDIKpWwI4cdpIMqMYJTQAFXlfat17mMyRTxZypW0qZCe8n8togWCIRQxj_UF46_u960vb7zC14qQbnQ_h5odZXCVBy2_vMOPjsEG3S39VUfceutuj7mXT8EZIHI46CLWgnbnZct4MkP9/s640/service-manager-audit.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Visualisation of audit-messages in Service Manager 2.0 </td></tr>
</tbody></table></div><div style="text-align: justify;"><br />
Der Service Manager 2.0 erlaubt es, Camel Routing-Schemata abzubilden und über einen eindeutigen Identifier zu verknüpfen, sodass jeder einzelne Schritt im Routing-Prozess tracebar ist. Spezielle Camel Event Notifier zur Übermittlung von Camel Routing und Transformationsdaten können registriert und mit dem Service Manager 2.0 verbunden werden. Alarmfälle, die durch Fehler verursacht werden, sind dabei zeitnah visualisierbar und unterstützen schnelle Reaktionszeiten des Support-Teams zur Einhaltung der Service Level Agreements (SLAs) während der Betriebszeit einer service-orientierten Applikation.</div><br />
<div style="background-color: #cccccc; text-align: justify;"><br />
</div><div style="background-color: #cccccc; text-align: justify;"><b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.</b></div><div style="background-color: #cccccc; text-align: justify;"><br />
</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-23900845133518463582011-04-06T11:44:00.028+02:002011-08-06T20:58:11.508+02:00Pack the bag<div style="text-align: justify;">Ein Bag wird als Multiset Datentyp bezeichnet. Der Multiset Datentyp ist keine Collection Klasse des Java SE Frameworks. Die <b>Collection Klassen von Google</b> hingegen beinhalten eine Multiset Klasse, die in Java-Anwendungen einsetzbar ist. Die in dem Blogbeitrag implementierte Bag-Klasse ist während einer Code Kata entstanden. Die Code Kata hat nur den Speicheralgorithmus des Bags beschrieben, ohne einen detaillierten Hinweis auf die Schnittstelle und Implementierung des Bags zu geben.</div><br />
<u><b>Textauszug der Kata-Beschreibung:</b></u><br />
<br />
<div style="text-align: justify;">Eine Bag-Klasse speichert zu einem Objekt die Anzahl der Vorkommnisse. Ein Bag mit den Einträgen {"firstObj", "firstObj", "secondObj", "thirdObj"} liefert für einen getCount(„firstObj“) Aufruf den Wert zwei. Der Aufruf von uniqueEntrySet() hingegen liefert {"firstObj", "secondObj", "thirdObj"}.</div><br />
<u><b>Hinweis:</b></u><br />
<br />
<div style="text-align: justify;">Ein Bag eignet sich zur Speicherplatzreduzierung, weil nicht jedes einzelne Objekt verwaltet wird. Ein ähnliches Verhalten ist von dem <b>Flyweight Pattern</b> bekannt. Das Flyweight Pattern ist wie der Bag ebenfalls sparsam im Umgang mit dem verfügbaren Speicherplatz.</div><br />
<div style="background-color: orange;"><b>UML-Diagramm:</b></div><br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvADyYq_urso-jn8US3pxEJZlochvMUY_ifZfWZHH8nweAhUS_AiyHodjYZUqHK_PlzJX2xesEbfi11XJrPiWFphnCj4Hq9PUaNMg6pEX6TaG7lEn-jtpkrsFlV1BqPPK4UofIo4z3bjdi/s1600/blog.jpg" /></a></div><br />
<div style="background-color: orange;"><b>Bag-Schnittstelle:</b></div><br />
import java.util.Collection;<br />
import java.util.Map;<br />
<br />
public interface Bag<T> {<br />
<br />
public boolean put(final T element);<br />
public boolean put(final T element, final int occurrences);<br />
public int remove(final T element);<br />
public int remove(final T element, final int occurrences);<br />
public int count(final T element);<br />
public Collection<Map.Entry<T, Integer>> uniqueSet();<br />
}<br />
<br />
<div style="background-color: orange;"><b>Bag-Implementierung:</b></div><br />
import java.util.Collection;<br />
import java.util.Collections;<br />
import java.util.Map;<br />
import java.util.concurrent.ConcurrentHashMap;<br />
<br />
public class HashBag<T> implements Bag<T> {<br />
<br />
private final Map<T, Integer> bagMap = new ConcurrentHashMap<T, Integer>();<br />
<br />
@Override<br />
public boolean put(final T element) {<br />
<br />
assertElementParameterNotNull(element);<br />
<br />
final int elementCount = count(element);<br />
if (0 == elementCount) {<br />
<br />
bagMap.put(element, 1); <br />
} <br />
else {<br />
<br />
bagMap.put(element, elementCount + 1);<br />
}<br />
<br />
return true;<br />
}<br />
<br />
@Override<br />
public boolean put(final T element, final int occurrences) {<br />
<br />
assertElementParameterNotNull(element);<br />
assertOccurrencesNotUnderOrOverflowed(occurrences);<br />
<br />
final int elementCount = count(element);<br />
if (0 == elementCount) {<br />
<br />
bagMap.put(element, occurrences); <br />
} <br />
else {<br />
<br />
bagMap.put(element, elementCount + occurrences);<br />
}<br />
<br />
return true;<br />
}<br />
<br />
@Override<br />
public int remove(final T element) {<br />
<br />
assertElementParameterNotNull(element);<br />
assertElementAvailableInBag(element);<br />
<br />
return bagMap.remove(element);<br />
}<br />
<br />
@Override<br />
public int remove(final T element, final int occurrences) {<br />
<br />
assertElementParameterNotNull(element);<br />
assertElementAvailableInBag(element);<br />
assertOccurrencesNotUnderOrOverflowed(occurrences);<br />
assertOccurrencesNotGreaterThanElementCount(occurrences, element);<br />
<br />
final int elementCount = count(element);<br />
if (occurrences == elementCount) {<br />
<br />
return remove(element);<br />
}<br />
<br />
return bagMap.put(element, elementCount - occurrences);<br />
}<br />
<br />
@Override<br />
public Collection<Map.Entry<T, Integer>> uniqueSet() {<br />
<br />
return Collections.unmodifiableCollection(bagMap.entrySet());<br />
}<br />
<br />
@Override<br />
public int count(final T element) {<br />
<br />
assertElementParameterNotNull(element);<br />
<br />
final Integer elementCount = bagMap.get(element);<br />
if (null == elementCount) {<br />
<br />
return 0;<br />
}<br />
<br />
return elementCount;<br />
}<br />
<br />
private void assertElementParameterNotNull(final T element) {<br />
<br />
if (null == element) {<br />
<br />
throw new NullPointerException("Element parameter must not be null!");<br />
}<br />
}<br />
<br />
private void assertElementAvailableInBag(final T element) {<br />
<br />
final int elementCount = count(element);<br />
if (0 == elementCount) {<br />
<br />
final String errorMessage = String.format(<br />
"Element (%s) not available in bag!", element);<br />
<br />
throw new IllegalArgumentException(errorMessage);<br />
}<br />
}<br />
<br />
private void assertOccurrencesNotGreaterThanElementCount(final int occurrences, final T element) {<br />
<br />
final int elementCount = count(element);<br />
if (occurrences > elementCount) {<br />
<br />
final String errorMessage = String.format(<br />
"Bag occurrences (%d) to remove greater than element count (%d) in bag!",<br />
occurrences, elementCount);<br />
<br />
throw new IllegalArgumentException(errorMessage);<br />
}<br />
}<br />
<br />
private void assertOccurrencesNotUnderOrOverflowed(final int occurrences) {<br />
<br />
if(occurrences < 0) {<br />
<br />
throw new IllegalArgumentException(String.format("Underflow (occurrences (%d) < 0)!", occurrences));<br />
} <br />
else if(occurrences > Integer.MAX_VALUE) {<br />
<br />
throw new IllegalArgumentException(String.format("Overflow (occurrences (%d) > max. int)! ", occurrences));<br />
}<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>Bag-Testfälle:</b></div><br />
import static org.junit.Assert.*;<br />
import java.util.Collection;<br />
import java.util.Map;<br />
import org.junit.Before;<br />
import org.junit.Test;<br />
<br />
public class BagTest {<br />
<br />
private Bag<String> bag;<br />
<br />
private String firstBagElement;<br />
private String secondBagElement;<br />
<br />
@Before<br />
public void setUp() {<br />
<br />
bag = new HashBag<String>();<br />
<br />
firstBagElement = "firstBagElement";<br />
secondBagElement = "secondBagElement";<br />
}<br />
<br />
@Test<br />
public void testPutSingleElement() {<br />
<br />
assertEquals(true, bag.put(firstBagElement));<br />
assertEquals(1, bag.count(firstBagElement));<br />
}<br />
<br />
@Test<br />
public void testPutMultipleElements() {<br />
<br />
assertEquals(true, bag.put(firstBagElement, 20));<br />
assertEquals(20, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testPutSingleNullElement() {<br />
<br />
assertEquals(true, bag.put(null));<br />
}<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testPutMultipleNullElements() {<br />
<br />
assertEquals(true, bag.put(null, 2));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testPutMultipleElementsWithUnderflow() {<br />
<br />
bag.put(firstBagElement, 20);<br />
bag.put(firstBagElement, -20);<br />
<br />
assertEquals(20, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testPutMultipleElementsWithOverflow() {<br />
<br />
bag.put(firstBagElement, 20);<br />
bag.put(firstBagElement, Integer.MAX_VALUE + 1);<br />
<br />
assertEquals(20, bag.count(firstBagElement));<br />
}<br />
<br />
@Test<br />
public void testPutSingleAndMultipleElements() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement, 20);<br />
bag.put(firstBagElement);<br />
<br />
assertEquals(22, bag.count(firstBagElement));<br />
}<br />
<br />
@Test<br />
public void testPutDifferentElements() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(secondBagElement);<br />
<br />
assertDifferentBagElements(bag.uniqueSet(), 2, 1, 1);<br />
}<br />
<br />
@Test<br />
public void testPutMultipleDifferentElements() {<br />
<br />
bag.put(firstBagElement, 20);<br />
bag.put(secondBagElement, 10);<br />
<br />
assertDifferentBagElements(bag.uniqueSet(), 2, 20, 10);<br />
}<br />
<br />
@Test<br />
public void testPutMultipleAndSingleDifferentElements() {<br />
<br />
bag.put(firstBagElement, 20);<br />
bag.put(firstBagElement);<br />
bag.put(secondBagElement, 10);<br />
bag.put(secondBagElement, 10);<br />
<br />
assertDifferentBagElements(bag.uniqueSet(), 2, 21, 20);<br />
}<br />
<br />
@Test<br />
public void testRemoveSingleElement() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement);<br />
<br />
assertEquals(2, bag.remove(firstBagElement));<br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testRemoveSingleNullElement() {<br />
<br />
assertEquals(0, bag.remove(null));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testRemoveWhileElementIsNotAvailable() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement);<br />
bag.remove(secondBagElement);<br />
<br />
assertEquals(0, bag.count(secondBagElement));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testRemoveMoreOccurencesThanAvailable() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement);<br />
bag.remove(firstBagElement,3);<br />
<br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test<br />
public void testRemoveMultipleSingleElements() {<br />
<br />
bag.put(firstBagElement, 30);<br />
<br />
assertEquals(30, bag.remove(firstBagElement, 30)); <br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testRemoveMultipleNullElements() {<br />
<br />
assertEquals(0, bag.remove(null, 2));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testRemoveMultipleSingleElementsWithUnderflow() {<br />
<br />
bag.put(firstBagElement, 30);<br />
bag.remove(firstBagElement, -1);<br />
<br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testRemoveMultipleSingleElementsWithOverflow() {<br />
<br />
bag.put(firstBagElement, 30);<br />
bag.remove(firstBagElement, Integer.MAX_VALUE + 1);<br />
<br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test<br />
public void testRemoveDifferentSingleElements() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(secondBagElement);<br />
<br />
assertEquals(1, bag.remove(firstBagElement));<br />
assertEquals(1, bag.remove(secondBagElement)); <br />
assertDifferentBagElements(bag.uniqueSet(), 0, 1, 1);<br />
}<br />
<br />
@Test<br />
public void testRemoveDifferentMultipleElements() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement);<br />
bag.put(secondBagElement);<br />
bag.put(secondBagElement);<br />
<br />
assertEquals(2, bag.remove(firstBagElement,1));<br />
assertEquals(2, bag.remove(secondBagElement,1));<br />
<br />
assertDifferentBagElements(bag.uniqueSet(), 2, 1, 1);<br />
}<br />
<br />
@Test<br />
public void testRemoveMultipleSingleElementsWithRest() {<br />
<br />
bag.put(firstBagElement, 30);<br />
bag.remove(firstBagElement, 10);<br />
<br />
assertEquals(20, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected = IllegalArgumentException.class)<br />
public void testRemoveWithOverflow() {<br />
<br />
bag.put(firstBagElement, 10);<br />
bag.remove(firstBagElement, 20);<br />
<br />
assertEquals(0, bag.count(firstBagElement));<br />
}<br />
<br />
@Test(expected=NullPointerException.class)<br />
public void testNullElementCount() {<br />
<br />
assertEquals(0, bag.count(null));<br />
}<br />
<br />
@Test<br />
public void testUniqueElementSet() {<br />
<br />
bag.put(firstBagElement);<br />
bag.put(firstBagElement);<br />
bag.put(secondBagElement);<br />
<br />
assertDifferentBagElements(bag.uniqueSet(), 2, 2, 1);<br />
}<br />
<br />
private void assertDifferentBagElements(final Collection<Map.Entry<String, Integer>> ElementSet, <br />
int ElementSetElementCount, int firstExpectedElementCount, <br />
int secondExpectedElementCount) {<br />
<br />
assertEquals(ElementSetElementCount, ElementSet.size());<br />
<br />
for (Map.Entry<String, Integer> element : ElementSet) {<br />
<br />
if (element.getKey().equals("firstBagElement")) {<br />
<br />
assertEquals(firstExpectedElementCount, element.getValue().intValue());<br />
<br />
} <br />
else if (element.getKey().equals("secondBagElement")) {<br />
<br />
assertEquals(secondExpectedElementCount, element.getValue().intValue());<br />
}<br />
}<br />
}<br />
}<br />
<br />
<div style="text-align: justify;">Der aufmerksame Leser wird sofort erkennen, dass in den Testfällen <b>Magic Numbers</b> verwendet werden. Die Lesbarkeit des Quellcodes wird durch Magic Numbers verschlechtert. Ein reiner Zahlenwert hat keine semantische Aussagekraft. Änderungen an den Zahlenwerten sind überall im Quellcode nachzuziehen. Diese Änderungen sind aufwendig und fehleranfällig. Im produktiven Quellcode sind Magic Numbers deshalb nicht tolerierbar. Aufgrund der Einfachheit des Testfalls im Rahmen der Code Kata aber pragmatisch. Den Verstoß gegen das Gebot: "Schreibe die Testfälle mit der gleichen Sorgfalt wie den produktiven Quellcode" nehme ich in diesem einen Fall in Kauf.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: #cccccc;"><br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-47153701392068477342011-03-11T15:12:00.057+01:002011-03-13T17:18:54.576+01:00Defensive programming<div style="text-align: justify;">Die Programmiersprache Java zählt zu den sicheren Sprachen. Zeigerarithmetik, korrupte Speicherstellen und Pufferüberläufe wie in C/C++ sind in Java nicht möglich. Bei der Weiterentwicklung von Java ist die Typsicherheit hinzugekommen und Meta-Daten (Annotation: @Override), mit denen potentielle Fehler früh zur Kompilierzeit aufgedeckt werden können.</div><br />
<div style="text-align: justify;"><b>Java Idiome</b>, wie beispielsweise unveränderliche Objekte (Immutables), fördern die Kapselung und Datensicherheit. Ein Risiko, das bei Java Programmen nicht unterschätzt werden darf, ist die Referenzierung von Objekten. Objektreferenzen ziehen sich teilweise vom Modell bis in die Benutzerschnittstelle hinein. Sind unveränderliche Objekte sicher im Kontext der Nebenläufigkeit, trifft für Referenzen auf veränderliche Objekte das Gegenteil zu. Die Folge können Laufzeitausnahmen (ConcurrentModificationException) bis hin zu zerstört aussehenden Oberfläche sein, die eine Anwendung fehlerhaft erscheinen lassen.</div><br />
<div style="text-align: justify;">Defensive Kopien sind bei hoher Nebenläufigkeit sinnvoll, in denen keine threadsichere Collection oder ein anderes Mittel zur Synchronisation eingesetzt werden kann. Weitere Anwendungszwecke defensiver Kopien sind die Sicherheit und Robustheit zu erhöhen, sodass Objektdaten nicht bewusst oder unbewusst verändert werden können.</div><br />
<div style="text-align: justify;">Es ist nicht angebracht, grundsätzlich defensiv zu programmieren. Intensive defensive Programmierung beeinflusst das Laufzeitverhalten negativ, weil mehr Objekte erzeugt werden. Grundsätzlich verhindert eine defensive Programmierung lange Spaghetti-Referenzen, die sich in einer Anwendung verknäulen und den Aufbau eines Fehlerbildes erschweren. Spaghetti-Referenzen haben die Eigenheit, im Fehlerfall nur sporadisch je nach Programmzustand Fehler zu erzeugen. Diese Fehler sind deshalb schwer nachzuvollziehen und können bei hoher Nebenläufigkeit nur mit sehr viel Aufwand entdeckt werden. Spaghetti-Referenzen werden partiell lange gehalten bzw. manchmal sogar niemals freigegeben. Im Extremfall führt das zu einem OutOfMemoryError. Die Programmierung auf kleinstmöglichen Level (<b>SLA</b>) und <b>defensive Kopien </b>verhindern Fehler, die ein Programm zerbrechen lassen.</div><br />
<div style="text-align: justify;">Die Nutzung von „final“ bei der Übergabe von Referenzparameter an Methoden hilft nur bedingt als Markierung für hohe Aufmerksamkeit, weil nach einem entsprechenden Methoden-Aufruf eine Setter-Methode aufgerufen werden kann, die den Objektzustand auf den die Referenz verweist ändert. Defensive Kopien nach der Übergabe eines Parameters und bei Rückgabewerten sind ein Schutz gegen das ungewollte ändern des Objektzustandes über eine Referenz.</div><br />
<b><div style="background-color: orange;">Einfaches Beispiel für eine defensive Programmierung:</div></b><br />
import java.util.Date;<br />
<br />
public class DefensiveCopyStrategy {<br />
<br />
private final Date date;<br />
<br />
public DefensiveCopyStrategy(final Date date) {<br />
<br />
this.date = new Date(date.getTime());<br />
}<br />
<br />
public Date getDate() {<br />
<br />
return new Date(date.getTime());<br />
}<br />
}<br />
<br />
<div style="text-align: justify;">Eine gute Praxis ist es, konform zu <b>Tell, don't ask</b>, gänzlich auf Getter-Methoden zu verzichten. Dies zeichnet Objekte aus, die ein definiertes Verhalten implementieren. Objekte, mit ausschließlich Getter-/Setter-Methoden hingegen, werden als Datenobjekte bezeichnet, die kein eigenes Verhalten implementieren.<br />
<br />
Mutationen, die sich aus Verhaltensobjekten und Datenobjekten zusammensetzen, stehen der Erweiterbarkeit im Wege, weil die Getter-/Setter-Aufrufe dazu verleiten, Funktionalitäten in einer Applikation zu implementieren. Die Objektkapselung wird dadurch gebrochen. Die Kohäsion und die gewünschte Eigenschaft der losen Kopplung verschlechtern sich beim massiven Gebrauch von Getter-/Setter-Methoden in Objekten, die nicht als reine Datenobjekte fungieren.</div><br />
<div style="text-align: justify;"><div style="background-color: #cccccc;"><br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-18806929926986568442011-03-04T11:34:00.004+01:002011-03-04T11:49:10.924+01:00OCM Certification message received<div style="text-align: justify;">Der Aufwand das Assignment (Part 2) und das Essay (Part 3) der Java EE 5 Enterprise Zertifizierung in Kauf zu nehmen hat sich gelohnt. Angespornt von dem Ergebnis des Wissenstests, bin ich mit sehr viel Motivation an die Bearbeitung des Assignments gegangen. Das Assignment umfasste eine B2C Applikation, was mir sehr entgegenkam. Die B2C Anwendung war verbal, mit einem Domain-Modell und vier Use Cases beschrieben.</div><div style="text-align: justify;"><br />
Nach der Anforderungsanalyse, für die ich mir zwei Tage Zeit genommen hatte, habe ich die funktionalen und nicht funktionalen Anforderungen verstanden. Einen weiteren Tag verbrachte ich mit Nachdenken, wie die Menge der Anforderungen technisch zu lösen wären. In in dieser Phase notierte ich meine Gedanken, um diese in der Folge schrittweise verfeinern zu können.</div><div style="text-align: justify;"><br />
Mit dem Entwurf des Klassendiagramms hat die Arbeit an dem Entwurf begonnen. In einer Tages- und Nachtsitzung brachte ich 75% der Lösung zu Papier, sodass ich im Anschluss mit dem Komponentendiagramm beginnen konnte. Beide Diagramme waren Dreh- und Angelpunkt für das Deployment und die Sequenzdiagramme. Als Werkzeug für die UML Diagramme verwendete ich den UML Modeler 8.1 von Visual Paradigm in der Vollversion. Das Werkzeug ist sehr gut anzuwenden und bereitete keine Probleme.</div><div style="text-align: justify;"><br />
Die Diagramme wurden anschließend in mehreren Sitzungen verfeinert. Parallel zu dieser Arbeit formulierte ich meine Annahmen und wesentlichen Entwurfsentscheidungen. Die drei größten Risiken der Lösung wurden in einem extra Kapitel beschrieben.</div><div style="text-align: justify;"><br />
Assignment und Essay wurden von einem Gutachter bewertet. Ich rate deshalb, das Assignment mit hoher Sorgfalt zu erstellen und die UML Notation strikt einzuhalten. Der Entwurf soll von Klarheit geprägt sein, weder zu wenig noch zu viele Informationen sind passend. Es handelt sich nicht um eine Fleißarbeit, noch um die reine Anwendung der Java EE 5 APIs. Die Geschäftslogik soll transparent werden und die Anforderungen des Assignments bestmöglich erfüllen.</div><div style="text-align: justify;"><br />
Es handelt sich um eine Zertifizierung im Java EE 5 Umfeld, deshalb ist es ratsam, die Java EE 5 APIs zu nutzen. Leicht rutscht man in die neuen Java EE 6 APIs ab, die mit der Java EE 5 Technologie nicht lauffähig sind! <br />
<br />
Man bedenke, das Design soll von einem Java Entwickler verstanden und umgesetzt werden können. Wesentlich ist, dass keine Informationen verloren gehen. Achte deshalb auf die Multiplizität (Leserichtung einhalten), nutze die Aggregation/Komposition, die Abstraktion und Vererbungsbeziehungen. Achte auf die Kernpunkte, die schwierige Entwurfsentscheidungen beinhalten und entwerfe mit viel Bedacht. Sei zu jeder Zeit sorgfältig und entwerfe die Software wie in einem realen Projekt.</div><div style="text-align: justify;"><br />
Setze ein Zeitlimit, damit die Gefahr sich in dem Assigment zu verfangen gebannt ist. Mein Zeitlimit nach der Anforderungsanalyse waren maximal 80 Arbeitsstunden, was normalerweise ausreichend ist.</div><br />
<div style="text-align: justify;"><div style="background-color: orange;"><b>Literatur und UML Werkzeug:</b></div></div><div style="text-align: justify;"><br />
<ul><li>Sun Certified Architect for Java EE Study Guide (Second Edition) von Mark Cade und Humphrey Sheil (Kapitel 9)</li>
</ul><ul><li>UML 2 Glasklar von Chris Rupp, Stefan Queins, Barbara Zengler</li>
</ul><ul><li>Fast Track UML 2.0 von Kendall Scott</li>
</ul><ul><li>SCEA Forum der JavaRanch mit Referenzen zu weiteren Informationsquellen</li>
</ul><ul><li>Visual Paradigm for UML 8.1 Modeler Edition</li>
</ul><br />
Für die Vorbereitung auf das Essay, welches in einem Prometric Testcenter zu absolvieren ist, habe ich meine Notizen und den Entwurf des Assignments verwendet. Die Fragen in dem Essay haben sich auf den Entwurf bezogen und waren deshalb recht einfach zu beantworten. Ich habe das Assignment direkt nach dem Essay abgesendet und eine Bestätigung vom Oracle Zertifizierungsteam erhalten.<br />
<br />
Ein paar Tage später ist eine weitere E-Mail eingegangen, dass das Assignment und das Essay an die Gutachter zur Bewertung versendet wurde. Die Bewertung dauerte zwei Wochen, sodass ich mit der Performanz des Bewertungsprozesses sehr zufrieden bin. Die Frage, ob sich der Aufwand für die Zertifizierung in drei Schritten gelohnt hat, beantworte ich unumstritten mit „Ja". Man lernt sehr viel bei dieser Zertifizierung und es macht großen Spaß eine Projektaufgabe zu lösen, die sehr nahe an reellen Anforderungen ist.</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-55897623364741583652011-03-01T17:19:00.014+01:002011-03-13T15:52:47.166+01:00DRY Coding<div style="text-align: justify;"><b>DRY (Don’t repeat yourself) </b>ist ein Grundprinzip von Clean Code. DRY ist in allen Entwicklungsphasen anwendbar. Im Design bei der Komposition von Komponenten, bei der Datenmodellierung stellvertretend durch die Normalisierung und bei der Implementierung durch die Wiederverwendung von Quellcode.</div><div style="text-align: justify;"><br />
Nichts ist einfacher als mit „Copy & Paste“ gegen DRY zu verstoßen. Der Sumpf der dabei entsteht wird immer größer und führt häufig zu schlechter Evolvierbarkeit und Inkonsistenzen einhergehend mit höherer Fehleranfälligkeit. Ein Verstoß gegen DRY hat eine höhere Abhängigkeit im Quellcode zur Folge, eine geringere Isolation von Komponenten und schlechtere Testbarkeit. Zusätzliche Arbeit durch höhere Komplexität sind die Folgen von DRY. Änderungen in einem System sollen deshalb nur einmal durchgeführt werden. Die Gefahr ein Quellcode-Duplikat zu vergessen und damit verbunden eine Änderung während der Weiterentwicklung zu übersehen ist stets gegeben.<br />
<br />
DRY einzuhalten ist ein fester Bestandteil des <b>TDD Mantras</b> und wird regelmäßig als integrierter Schritt beim Schreiben von Testfällen ausgeführt. Patterns wie <b>Template Method</b> helfen Duplikate beim Refactoring zu eliminieren. <b>Abstraktionen und generische Strukturen</b> sind mächtige Mittel, um das DRY Prinzip einhalten zu können.</div><br />
<div style="text-align: justify;">Die Wiederverwendung von Quellcode durch Refactoring ist nicht auf Makrokomponenten beschränkt. Grundsätzlich findet ein Refactoring zunächst auf der Mikroebene statt, mit dem Ziel die innere Struktur eines Softwarebausteins zu verbessern. Selbst einzelne Quellcodezeilen und die Bündelung kleiner Methoden können durch das DRY Prinzip die Komplexität im Quellcode verringern und dadurch die Wartbarkeit verbessern. Es ist deshalb eine gute Praxis die gängigen <b>Java Idiome</b> und APIs zu nutzen.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Java Syntax verhindert DRY auf dem Abstraktionslevel einer Methode:</b></div></div><div style="background-color: white;"><br />
</div><div style="background-color: white;"><b>public double calculateProduct(double factor) {<br />
</b></div><div style="background-color: white;"><b> <span style="background-color: red;"> product = <span style="background-color: red;">product</span> * factor;</span><br />
<br />
return product; </b></div><div style="background-color: white;"><b><div>}</div><br />
</b></div><div style="background-color: white;"><b>public double calculateProduct(double factor) {<br />
</b></div><div style="background-color: white;"><b> <span style="background-color: lime;">product *= factor;</span><br />
<br />
return product;</b></div><div style="background-color: white;"><b><div>}</div></b><br />
</div><b><div style="background-color: orange;">Java Idiom (Static Factory Method) verhindert DRY bei der Definition einer Datenstruktur:</div></b><br />
<div style="background-color: white;"><b><div>private Map<Double, List<Double>> products = new HashMap<<span style="background-color: red;">Double, List<Double></span>>();</div></b><br />
</div><b>private Map<Double, List<Double>> products = <span style="background-color: lime;">Products.newInstance();</span></b><br />
<br />
<div style="text-align: justify;">In <b>Java 7</b> sind "Static Factory Methods" als Problemlösung für das DRY Prinzip überflüssig. Die neue Java Diamant Syntax (<b>Diamond</b>) ist für diesen Fall die beste Lösung.</div><br />
<div style="background-color: white;"><b><b>private Map<Double, List<Double>> products = new HashMap<span style="background-color: lime;"><></span>();</b></b></div><br />
<div style="text-align: justify;">Interessant ist, dass in der Methode "getProduct" das Attribut "product" verwendet wird. In diesem Fall wird gegen das <b>Single Level of Abstraction (SLA)</b> Prinzip verstoßen. Die weitläufige Verwendung von Attributen in den Methoden einer Klasse, führt wie DRY zu geringerer Isolation und darüber hinaus zu schlechterer Lesbarkeit des Quellcodes.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Im Java-Umfeld birgt der Verstoß gegen SLA die Gefahr in sich <b>Memory Leaks</b> zu erzeugen. Dieses Phänomen tritt durch Objektreferenzen auf, die über einen langen Zeitraum hinweg als Attribute in der Instanz einer Klasse gehalten werden. Der Garbage Collector gibt den Speicher, der durch Instanzen belegt wird, nicht frei sofern noch Referenzen auf die Instanzen verweisen. Zirkulare Referenzen können dabei eine Referenzinsel bilden, die es dem Garbage Collector besonders schwer machen Speicher freizugeben. Die Zuweisung von "null" nach der Benutzung von Referenzen hilft zwar dem Garbage Collector bei der Freigabe des Speichers, verschlechtert auf der anderen Seite aber die Lesbarkeit des Quellcodes.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Es ist deshalb eine gute Praxis SLA anzuwenden und dabei den natürlichen Mechanismus der Speicherfreigabe zu nutzen. Dieser Mechanismus tritt in Kraft, sobald eine lokale Variable, die eine Referenz auf eine Instanz hält, "Out of Scope" ist (Methode wurde beendet). SLA und damit verbunden eine Referenzvariable im kleinstmöglichen "Scope" zu verwenden fördert im Java-Umfeld deshalb nicht nur die Lesbarkeit sondern schont Resourcen.</div><br />
<div style="text-align: justify;"><div style="background-color: #cccccc;"><br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-65926028469173856642011-02-10T20:15:00.002+01:002011-02-13T22:35:48.359+01:00Loose Coupling<div style="text-align: justify;">Die lose Kopplung von Softwarebausteinen ist ein Grundprinzip der Softwareentwicklung. Moderne Software-Architekturen, die serviceorientiert entworfen worden sind, nutzen lose miteinander gekoppelte Komponenten zur Flexibilisierung ihres Zusammenspiels. Die lose Kopplung ist bei Makrokomponenten und kleineren Entwurfseinheiten fest verankert. Entwurfsmuster nutzen das Konzept lose gekoppelter Softwarebausteine in vielfältiger Weise. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Ein <b>Observer</b> ist ein Muster, welches im Zusammenhang mit loser Kopplung häufig zuerst genannt wird. Die klassische Eigenschaft des Observer-Musters ist die Übermittlung von Zustandsänderung eines Objektes an beliebig viele andere Objekte. Diese Nachrichtenform nennt man Broadcasting. Broadcastings haben einen eingeschränkten Anwendungsfall in dem eins-zu-n Beziehungen relevant sind.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Eine <b>Facade</b> entkoppelt Client- von Serverkomponenten, ein <b>Mediator</b> zentralisiert die Verknüpfungen von Objekten und führt dadurch zu lose gekoppelten Objekten und das <b>Command Muster</b> verpackt Requests in Ereignisobjekte, um dem Prinzip der losen Kopplung gerecht zu werden. Die Musterbeispiele sind vielfältig, bei denen die lose Kopplung eine Rolle spielt.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Eng verbunden mit der losen Kopplung sind die <b>Kohäsion</b> (<b>Single Responsibility Principle</b>) und das Trennen der Verantwortlichkeiten (<b>Separation of Concerns</b>) bei Softwarebausteinen. Grundsätzlich wird durch die lose Kopplung und hohe Kohäsion die <b>Evolvierbarkeit</b> einer Software-Anwendung verbessert. Die hohe Kohäsion von Softwarebausteinen und deren lose gekoppelten Assoziationen schaffen <b>isolierte Einheiten</b>. Die einfachere Testbarkeit der isolierten Einheiten und weniger Seiteneffekte bei den in Assoziation stehenden Objekten sind weitere Merkmale loser gekoppelter Softwarebausteine.<br />
<br />
Ein Entwurfsmuster, das die Kopplung zwischen Softwarebausteinen reduziert und eine Menge von Klassen als Einheit auftreten lässt, ist das <b>Chain of Responsibility</b> Pattern. Ereignisse, die in einer Klasse produziert werden, können bei diesem Muster an andere Klassen gesendet werden. Das Muster bietet deshalb sehr gute Voraussetzungen für die Weiterentwicklung ohne konkrete Logik in den Methoden einer Klasse anpassen zu müssen.<br />
<br />
Die nachfolgende Implementierung von <b>FizzBuzz</b> nutzt das Muster, um auf Basis einer übergebenen Zahl die FizzBuzz konforme Ausgabe zu produzieren.<br />
<br />
<div style="background-color: orange;"><b>FizzBuzz Schnittstelle </b></div></div><br />
interface FizzBuzz {<br />
<br />
public String answer(int number);<br />
}<br />
<br />
<div style="background-color: orange;"><b>FizzBuzz Implementierung</b></div><br />
public class FizzBuzzQuiz implements FizzBuzz {<br />
<br />
private final FizzBuzzRule answerRuleChain = createAnswerRuleChain();<br />
<br />
private FizzBuzzRule createAnswerRuleChain() {<br />
<br />
final FizzBuzzRule fizzBuzzAnswerRule = new FizzBuzzAnswerRule();<br />
final FizzBuzzRule buzzAnswerRule = new BuzzAnswerRule();<br />
final FizzBuzzRule fizzAnswerRule = new FizzAnswerRule();<br />
final FizzBuzzRule numberAnswerRule = new NumberAnswerRule(); <br />
<br />
fizzBuzzAnswerRule.setNext(buzzAnswerRule);<br />
buzzAnswerRule.setNext(fizzAnswerRule);<br />
fizzAnswerRule.setNext(numberAnswerRule);<br />
<br />
return fizzBuzzAnswerRule;<br />
}<br />
<br />
@Override<br />
public String answer(int number) {<br />
<br />
if(number < 0 || <br />
number > Integer.MAX_VALUE) {<br />
<br />
throw new IllegalArgumentException("FizzBuzz number out of range: " + number);<br />
}<br />
<br />
return answerRuleChain.executeRule(number); <br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>FizzBuzz Basisklasse der Regelimplementierungen</b></div><br />
abstract class FizzBuzzRule {<br />
<br />
private FizzBuzzRule next;<br />
<br />
FizzBuzzRule setNext(final FizzBuzzRule next) {<br />
<br />
this.next = next;<br />
<br />
return getNext();<br />
}<br />
<br />
private FizzBuzzRule getNext() {<br />
<br />
return next;<br />
}<br />
<br />
String executeRule(int partNumber) {<br />
<br />
if(isNextRuleAvailable()) {<br />
<br />
if(executeRulePart(partNumber)) {<br />
<br />
return ruleResult(partNumber);<br />
}<br />
<br />
return getNext().executeRule(partNumber);<br />
}<br />
<br />
return ruleResult(partNumber);<br />
}<br />
<br />
private boolean isNextRuleAvailable() {<br />
<br />
return null != getNext();<br />
}<br />
<br />
boolean executeRulePart(int partNumber) {<br />
<br />
return partNumber >= replacementNumber() ?<br />
partNumber % replacementNumber() == 0 : false;<br />
}<br />
<br />
String ruleResult(int partNumber) {<br />
<br />
return toString();<br />
}<br />
<br />
abstract int replacementNumber();<br />
}<br />
<br />
<div style="background-color: orange;"><b>FizzBuzz Regelimplementierungen</b></div><br />
class FizzAnswerRule extends FizzBuzzRule {<br />
<br />
@Override<br />
int replacementNumber() {<br />
<br />
return 3;<br />
}<br />
<br />
@Override<br />
public String toString() {<br />
<br />
return "fizz";<br />
}<br />
}<br />
<br />
class BuzzAnswerRule extends FizzBuzzRule {<br />
<br />
@Override<br />
int replacementNumber() {<br />
<br />
return 5;<br />
}<br />
<br />
@Override<br />
public String toString() {<br />
<br />
return "buzz";<br />
}<br />
}<br />
<br />
class FizzBuzzAnswerRule extends FizzBuzzRule {<br />
<br />
@Override<br />
int replacementNumber() {<br />
<br />
return 15;<br />
}<br />
<br />
@Override<br />
public String toString() {<br />
<br />
return "fizzbuzz";<br />
}<br />
}<br />
<br />
class NumberAnswerRule extends FizzBuzzRule {<br />
<br />
@Override<br />
int replacementNumber() {<br />
<br />
return 0;<br />
}<br />
<br />
@Override<br />
String ruleResult(int partNumber) {<br />
<br />
return String.valueOf(partNumber);<br />
}<br />
}<br />
<br />
<div style="background-color: orange;"><b>FizzBuzz Testklasse</b></div><br />
import static org.junit.Assert.assertEquals;<br />
import org.junit.Before;<br />
import org.junit.Test;<br />
<br />
public class FizzBuzzQuizTest {<br />
<br />
private FizzBuzz fizzBuzzQuiz;<br />
<br />
@Before<br />
public void setUp() {<br />
<br />
fizzBuzzQuiz = new FizzBuzzQuiz();<br />
}<br />
<br />
@Test(expected=IllegalArgumentException.class)<br />
public void fizzBuzzMinValueTest() {<br />
<br />
fizzBuzzQuiz.answer(-1);<br />
}<br />
<br />
@Test(expected=IllegalArgumentException.class)<br />
public void fizzBuzzMaxValueTest() {<br />
<br />
fizzBuzzQuiz.answer(Integer.MAX_VALUE + 1);<br />
}<br />
<br />
@Test<br />
public void fizzBuzzValueTest() {<br />
<br />
assertEquals("0", fizzBuzzQuiz.answer(0)); <br />
assertEquals("1", fizzBuzzQuiz.answer(1));<br />
assertEquals("2", fizzBuzzQuiz.answer(2));<br />
assertEquals("fizz", fizzBuzzQuiz.answer(3));<br />
assertEquals("4", fizzBuzzQuiz.answer(4));<br />
assertEquals("buzz", fizzBuzzQuiz.answer(5)); <br />
assertEquals("fizz", fizzBuzzQuiz.answer(6));<br />
assertEquals("7", fizzBuzzQuiz.answer(7));<br />
assertEquals("8", fizzBuzzQuiz.answer(8));<br />
assertEquals("fizz", fizzBuzzQuiz.answer(9));<br />
assertEquals("buzz", fizzBuzzQuiz.answer(10));<br />
assertEquals("fizzbuzz", fizzBuzzQuiz.answer(15));<br />
assertEquals("16", fizzBuzzQuiz.answer(16));<br />
assertEquals("17", fizzBuzzQuiz.answer(17));<br />
assertEquals("fizz", fizzBuzzQuiz.answer(18));<br />
assertEquals("19", fizzBuzzQuiz.answer(19));<br />
assertEquals("buzz", fizzBuzzQuiz.answer(20));<br />
assertEquals("fizzbuzz", fizzBuzzQuiz.answer(30));<br />
assertEquals("31", fizzBuzzQuiz.answer(31)); <br />
}<br />
}<br />
<div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: #cccccc;"><br />
<b>Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
</b><br />
</div></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-65394613464957895182011-01-21T19:51:00.569+01:002011-01-24T22:58:48.732+01:00OCM Certification, Java EE 5 Enterprise Architect<div style="text-align: justify;">Das <b>Java EE 5 Enterprise Architect</b> Zertifikat von Oracle ist die Fortführung der <b>Sun Certified Enterprise Architect for Java EE 5 (SCEA 5)</b> Zertifizierung. Der Zertifizierungspfad setzt sich aus drei Blöcken zusammen.<br />
<br />
Der erste Block besteht aus einem <b>Wissenstest</b> (Multiple-Choice und Drag-and-Drop Fragen), der 64 Fragen beinhaltet und der in einem Prometric Testcenter absolviert wird.</div><br />
<div style="text-align: justify;">Der zweite Testblock ist ein <b>Assignment,</b> eine praktische Aufgabe, bei der eine Java EE Applikation zu entwerfen ist. Die zu entwerfende Java EE Applikation ist mit UML-Diagrammen zu beschreiben. Die Basis für die praktische Aufgabe sind ein verbal beschriebenes virtuelles Geschäftsszenario, ein graphisches Domainmodell und ein Anwendungsfalldiagramm (Use-Case). Neben der Beschreibung des entworfenen Systems mit einem Klassendiagramm, Komponentendiagramm, Deploymentdiagramm und Sequenzdiagrammen sind zusätzliche Kommentare über die Anforderungen, Risiken und Risikomilderung zu verfassen. Der zweite Testblock darf erst nach dem Absolvieren des Wissenstests bearbeitet werden.<br />
<br />
Der dritte Testblock beinhaltet ein <b>Essay</b> mit acht Fragen zu dem zuvor entworfenen System, das wie der Wissenstest in einem Prometric Testcenter zu absolvieren ist. Entwurfsentscheidungen, Alternativen zu dem gewählten Entwurf und die Abdeckung von nichtfunktionalen Anforderungen sind mögliche Fragestellungen. Es gilt, den Entwurf des Assignments bestmöglich zu verteidigen.<br />
<br />
Der Wissenstest ist sehr weitläufig, sodass es von Vorteil ist, entsprechende Projekterfahrung zu haben. Bei der Vorbereitung für den Wissenstest war ich regelmäßig über die breite Streuung der Fragen überrascht. Besonders die Erfahrungen der Projekte aus der Vergangenheit haben geholfen, die Fragestellungen fernab von Java EE 5 verstehen zu können. Beispiele: JSPs mit Delegierung zu Geschäftsobjekten (Business Objects), Servlets die Diagramme erzeugen, die JDBC-ODBC-Bridge, proprietäre MVC-Frameworks, EJB Persistenztechniken auf Basis von JDBC und ResultSets, CORBA Programmierung mit der Java IDL und Applet-Sicherheit (Sandbox-Restriktionen).<br />
<br />
<div style="background-color: orange;"><b>Liste (ohne den Anspruch der Vollständigkeit) für den Wissenstest:</b></div><br />
<ul><li>OO-Prinzipien (Kapselung, Vererbung, Komposition, Kopplung, Separation of Concerns)<br />
<br />
</li>
<li>Architekturen (Client-Server-Systeme, Mehrschichtsysteme, SOA, Thin-/Thick-Clients, nichtfunktionale Anforderungen, EJB- und Web-zentierte Architekturen, Cluster, Load Balancing, Replikation) <br />
<br />
</li>
<li>Integration und Messaging (Java EE WebServices (SOAP und REST), JCA, JMS (Queue und Topic)<br />
<br />
</li>
<li>Geschäftsschicht Technologien (Session Beans, MDBs, Entity Beans, ORM, Unterschiede der vorherigen EJB-Standards zu EJB 3.0, Programmier- und Deploymentmodelle) <br />
<br />
</li>
<li>Webschicht Technologien<b> </b>(JSP, Servlets, Servletfilter, JSTL, EL, JSF, AJAX)<br />
<br />
</li>
<li>Design Patterns (GoF und J2EE Patterns, Patternszenarios in unterschiedlichen Layern einer Architektur, Anwendungsfälle und Merkmale einzelner Patterns)<br />
<br />
</li>
<li> Sicherheit (Applet- und JNLP-Restriktionen der JRE Sandbox, Byte Code Verification, Web-, EJB- und WebServices Sicherheit, SSO, potentielle Sicherheitsrisiken (Attacken wie XSS, SQL-Injection, Denial of Service, Spoofing, Tampering, Phising, Man in the Middle und deren Vermeidung (Verschlüsselung, Filter, Sicherheitszertifikate, Signaturen) <br />
<br />
</li>
<li> APIs und deren Verwendung (JAX-WS, JAXB, JAXP, JAXR, SAAJ, JAX-RPC, WS-Security, JAAS, JCE, JSSE, Java IDL, JMS, JCA, JNDI, JDBC)<br />
<br />
</li>
<li>Protokolle (RMI, IIOP, JRMN, SOAP, XML over HTTP, SSL)<br />
</li>
</ul><br />
Die Fragestellungen des Wissenstests kombinieren Architekturansätze, nichtfunktionale Anforderungen, Programmiermodelle, Design Patterns und APIs miteinander, sodass auf Basis eines gestellten Szenarios jeweils die richtige Antwort aus Sicht des Architekten zu finden ist. <br />
<br />
<div style="background-color: orange;"><b>Exemplarische Fragestellungen in vereinfachter Form:</b></div><br />
(1) Welche nichtfunktionalen Anforderungen einer Java EE Applikation werden erhöht, wenn statt eines Applikationsservers ein Cluster von Applikationsservern mit "Load Balancing" Funktionen eingesetzt wird? Lösung: Die Skalierbarkeit, Ausfallsicherheit und Verlässlichkeit der Anwendung werden erhöht.<br />
<br />
(2) Eine Web-Applikation in der die komplette Geschäftslogik als JSP-Scriptlets programmiert wurde, hat eine schlechte Performanz. Warum ist die Performanz schlecht und wie könnte man die Performanz verbessern? Lösung: Die aus den JSP-Seiten generierten Servlets werden zur Laufzeit bei jedem Request "gelockt". Die Geschäftslogik sollte deshalb von JSP-Scriptlets zu Business Objects refactored werden.<br />
<br />
(3) Wie kann die Gefahr einer "SQL-Injection" minimiert werden? Lösung: Durch einen Filter und Prepared Statements.<br />
<br />
(4) Wie verwaltet man den Konversationsstatus eines Clients? Lösung: Mit einer HTTP Session oder einem Stateful Session Bean.<br />
<br />
(5) Welches Pattern ist nützlich, um die Konstruktions- und Implementierungsdetails eines Objektes zu verbergen? Lösung: Abstract Factory.<br />
<br />
(6) Welche Nachteile entstehen durch enge Bindung? Lösung: Die Wiederverwendung von Klassen ist schwierig und Änderungen in einer Klasse haben Änderungen in abhängigen Klassen zur Folge.<br />
<br />
(7) Welches Design Pattern nutzen ORM-Frameworks? Lösung: Domain Store.<br />
<br />
(8) Nenne zwei Design Patterns, die konform zu Separation of Concerns sind? Lösung: Service To Worker (trennt die Geschäftslogik von der Anzeigelogik) und Data Access Object (trennt die Geschäftslogik von der Speicherlogik).<br />
<br />
(9) Welches Entwurfsprinzip besteht zwischen Session und Entity Beans? Lösung: Separation of Concerns.<br />
<br />
(10) Welche Java EE Komponente hilft bei der Migration zu einer SOA? Lösung: Stateless Session Bean (kann als Web Service publiziert werden: @WebService).<br />
<br />
(11) Welche Techniken kennzeichnen Polymorphie? Lösung: Erweitern einer Klasse und Überschreiben einer existierenden Methode. Implementieren einer Schnittstelle mit unterschiedlichen Klassen.<br />
<br />
(12) Nenne zwei Aktionen, die ein nicht signiertes Applet ausführen kann? Lösung: Neue Threads in der JVM erzeugen und überdurchschnittlich viel Prozessorleistung nutzen.<br />
<br />
<div style="background-color: orange;"><b>Literatur, Mock-Tests und Online-Quellen für die Vorbereitung </b></div></div><br />
<div style="text-align: justify;"><ul><li>Sun Certified Architect for Java EE Study Guide (Second Edition) von Mark Cade und Humphrey Sheil (das Buch ist einfach Klasse!)<br />
<br />
</li>
<li>Java EE 5 Tutorial<br />
<br />
</li>
<li>Mock-Tests bei Whizlabs (SCEA Exam Simulator CX 310-52)<b><br />
<br />
</b></li>
<li>ePractice bei Oracle<b> </b>(<span style="font-size: small;">Java Enterprise Edition 5 Enterprise Architect Certified Master Exam)<br />
<br />
</span></li>
<li><span style="font-size: small;"> SCEA Forum der JavaRanch mit Referenzen zu weiteren Informationsquellen </span><br />
<br />
</li>
</ul></div><div style="text-align: justify;">Die Aufzählung der Informationsquellen ist nur ein Richtungsweiser für die Vorbereitung auf die Zertifizierung. Je nach Erfahrung sind weitere Quellen zu Java EE Technologien heranzuziehen.<br />
<br />
Ein interessanter Aspekt der Zertifizierung ist, zu einem gegebenen Geschäftsmodell die richtige Architekturentscheidung zu treffen. Nicht immer ist der EJB-zentrierte Ansatz mit EJB 3.0, JPA und JSF die richtige Lösung. Der Web-zentrierte Ansatz hat bei leichtgewichtigen Architekturen, die wenige bis keine Transaktionen und keine anderen EJB Containerdienste nutzen wollen, durchaus Vorteile.<br />
<br />
Interessant sind die vielfältigen Integrationsszenarien, sodass Java Web Services im heterogenen Umfeld ein gutes Mittel für die Integration von Komponenten und Applikationen sind. In reinen Java-Umgebungen aber auch JMS die Nase vorne haben kann. JCA als klassische EAI-Technik ist im Legacy Umfeld mit JMS (MDBs) für Notifizierungen kombinierbar und nutzt dabei die Dienste des EJB Containers, wodurch weitere Integrationsmöglichkeiten entstehen.<br />
<br />
Die Vielfältigkeit der Fragestellungen schafft Raum für neue Blickwinkel, Eindrücke und Ideen. In der Summe ein schöner Lohn für den Arbeitsaufwand auf dem Zertifizierungspfad zum Java EE 5 Architekten.</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-79480495485616173852011-01-12T22:52:00.013+01:002011-01-13T17:54:18.597+01:00Code Kata<div style="text-align: justify;">Tai Chi Sportler führen regelmäßig Übungen zur Perfektion ihrer Bewegungsabläufe durch. Der Bewegungsablauf ist bei den Tai Chi Übungen stilisiert und fest vorgegeben. Diese Art von Übungsform wird als Kata bezeichnet. Das häufige Wiederholen der Bewegungsabläufe bei einer Kata ist unerlässlich für den persönlichen Fortschritt eines Schülers und dient neben der Entspannung zur Vorbereitung auf einen Formwettkampf. Nur die regelmäßige Übung macht den Schüler zu einem Tai Chi Meister.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Programmierer nutzen das Konzept der Kata zur Verbesserung des Programmierstils. Die Programmierung beherrscht man nur durch ständiges Training. Eine regelmäßige <b>Code Kata</b> ist deshalb empfehlenswert und macht als öffentliches <b>Code Dojo</b> sehr viel Spass. Im Rahmen einer Diskussion des <b>Clean Code Developer Forums</b> über stinkenden Quellcode ist spontan ein Code Dojo entstanden.</div><br />
<div style="text-align: justify;">Die Aufgabenstellung des Code Dojo war eine Code Kata mit dem Ziel einen einfachen Algorithmus, der eine arabische Zahl in eine römische Zahl konvertiert, zu refaktorisieren Der Ursprungsalgorithmus in der Java Notation umfasst folgende Implementierung:</div><pre>public class RomanNumerals {
static int[] nums = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
static String[] rum = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX",
"V", "IV", "I" };
public static string toRoman(int number) {
String value = "";
for (int i = 0; i < nums.length && number != 0; i++) {
while (number >= nums[i]) {
number -= nums[i];
value += rum[i];
}
}
return value;
}
} </pre><div style="text-align: justify;">Die ersten Refactorings haben den Algorithmus auf die Leserlichkeit hin verbessert. Kleinere Methoden wurden konform den CCD-Prinzipien geschrieben, um den Lesefluss zu erhöhen. Das <b>Single Level of Abstraction (SLA)</b> Prinzip wurde dabei partiell verletzt. Relativ schnell erkannte man, dass die beiden Arrays zu fehleranfällig sind. Die Array-Elemente müssen in einer definierten Ordnung vorliegen und die Arrays könnten unabhängig voneinander weitere Elemente aufnehmen.</div><br />
<div style="text-align: justify;">Erweiterte Implementierungen wie Value Objects, die die konvertierte römische Zahl speichern, ein selbständiges Mapping-Objekt und andere Implementierungen sind diskutiert worden. Damit das Refactoring stattfinden konnte und damit die <b>Pfadfinderregel</b> überhaupt angewendet werden kann, sind zunächst eine Reihe von Unit-Tests geschrieben worden.</div><br />
<div style="text-align: justify;">Die Teilnehmer der Code Kata waren nach den ersten Implementierungen noch nicht mit den Ergebnissen des Refactorings zufrieden. Der einfache Konvertierungsalgorithmus wurde mit dem Fortschritt des Refactorings durch eindeutige Bezeichner zwar lesbarer, auf der anderen Seite blähte sich der Quellcode auf. Teilweise hat sich der Quellcode sogar verfünfacht. Die Balance zwischen der Lesbarkeit und Einfachheit der Implementierung war noch nicht gegeben. Ein Teilnehmer hat die beiden Arrays durch ein Mapping-Objekt ersetzt, sodass die Zuordnung einer arabischen Ziffer zu einer römischen Ziffer gewährleistet ist. Das Mapping-Objekt wurde dabei wiederum in einem Array verwaltet. Das Array fungierte dabei als ein Dictionary. Eine naheligende Idee war deshalb basierend auf dem Dictionary Gedanken eine geradlinige Java API-Lösung zu implementieren.</div><br />
<div style="text-align: justify;"><div style="background-color: orange;"><b>API-Lösung:</b></div></div><div style="text-align: justify;"><pre>import java.util.LinkedHashMap;
import java.util.Map;
public class RomanNumber {
private static Map<String, Integer> mappings = new LinkedHashMap<String, Integer>();
static {
mappings.put("M", 1000);
mappings.put("CM", 900);
mappings.put("D", 500);
mappings.put("CD", 400);
mappings.put("C", 100);
mappings.put("XC", 90);
mappings.put("L", 50);
mappings.put("XL", 40);
mappings.put("X", 10);
mappings.put("IX", 9);
mappings.put("V", 5);
mappings.put("IV", 4);
mappings.put("I", 1);
}
public static String arabicToRoman(int arabicNumber) {
int arabicNumberRemainder = arabicNumber;
final StringBuilder romanNumber = new StringBuilder();
for (Map.Entry<String, Integer> mapping : mappings.entrySet()) {
while(arabicNumberRemainder >= mapping.getValue()) {
arabicNumberRemainder -= mapping.getValue();
romanNumber.append(mapping.getKey());
}
}
return romanNumber.toString();
}
}</pre></div><div style="text-align: justify;">Eine LinkedHashMap bietet sich als Dictionary für das Mapping der Ziffern an. Die LinkedHashMap hält die Einfügeordnung aufrecht und garantiert damit, dass der Konvertierungsalgorithmus funktioniert. Die API-Lösung nutzt hauptsächlich das Mapping der Ziffern und ist deshalb hinsichtlich der Erweiterbarkeit sicherer als die Array-Variante. Grundsätzlich sind Lösungen, die auf einer Datenstruktur basieren dehnfähiger als Lösungen, die ausgefeilte Algorithmen verwenden. Das ist sicherlich bei dem hier vorliegenden einfachen Softwarebaustein kein Argument, kann sich aber bei umfangreicheren Softwarebausteinen durchaus auszahlen.</div><br />
<div style="text-align: justify;">Die innere Struktur der Lösung konnte mit einer Abstraktion verbessert werden. Die Abstraktion erlaubt es darüber hinaus auf Basis der Implementierung weitere Konvertierungsalgorithmen zu implementieren.</div><br />
<div style="text-align: justify;"><div style="background-color: orange;"><b>Abstraktion der API-Variante:</b></div></div><pre>import java.util.Map;
abstract class NumberConverter {
abstract Map<String, Number> convertMappings();
abstract String convert(final Map<String, Number> mappings, final Number number);
String convertNumber(final Number number) {
return convert(convertMappings(), number);
}
}</pre><div style="text-align: justify;">Die Abstraktion wendet das <b>Template Method Pattern</b> an. Die überschriebenen Methoden in einer Ableitung der Abstraktion bestimmen, welche Konvertierungsdaten und welcher Konvertierungsalgorithmus für die Konvertierung verwendet wird. Durch diese Implementierung wird innere Flexibilität erreicht, sodass in der Folge unterschiedliche Ausprägungen von Konvertierungen stattfinden können.</div><br />
<div style="text-align: justify;"><b><div style="background-color: orange;">Implementierung der API-Variante:</div></b></div><pre>import java.util.LinkedHashMap;
import java.util.Map;
public final class RomanNumber extends NumberConverter {
public static String arabicToRoman(int number) {
return new RomanNumber().convertNumber(number);
}
private RomanNumber() {/* prevents instantiation */}
@Override
Map<span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Number</span><span class="o">></span><string, number=""> convertMappings() {
final Map</string,><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Number</span><span class="o">></span><string, number=""><string, number=""> mappings = new LinkedHashMap<string, number=""></string,></string,></string,><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Number</span><span class="o">></span><string, number=""><string, number=""><string, number="">();
mappings.put("M", 1000);
mappings.put("CM", 900);
mappings.put("D", 500);
mappings.put("CD", 400);
mappings.put("C", 100);
mappings.put("XC", 90);
mappings.put("L", 50);
mappings.put("XL", 40);
mappings.put("X", 10);
mappings.put("IX", 9);
mappings.put("V", 5);
mappings.put("IV", 4);
mappings.put("I", 1);
return mappings;
}
@Override
String convert(final Map</string,></string,></string,><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Number</span><span class="o">></span><string, number=""><string, number=""><string, number=""><string, number=""> mappings, final Number number) {
int numberRemainder = number.intValue();
final StringBuilder convertedNumber = new StringBuilder();
for (Map.Entry<string, number=""></string,></string,></string,></string,></string,><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Number</span><span class="o">></span><string, number=""><string, number=""><string, number=""><string, number=""><string, number=""> mapping : mappings.entrySet()) {
while(numberRemainder >= mapping.getValue().intValue()) {
numberRemainder -= mapping.getValue().intValue();
convertedNumber.append(mapping.getKey());
}
}
return convertedNumber.toString();
}
} </string,></string,></string,></string,></string,></pre><div style="text-align: justify;"><b><div style="background-color: orange;">Unit-Test der API-Variante:</div></b></div><pre>import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
public class RomanNumberTest {
private Map<String, Integer><string, integer=""> mappings = new HashMap<String, Integer><string, integer="">();
@Before
public void setUp() {
mappings.put("", 0);
mappings.put("I", 1);
mappings.put("II", 2);
mappings.put("III", 3);
mappings.put("IV", 4);
mappings.put("V", 5);
mappings.put("VI", 6);
mappings.put("VIII", 8);
mappings.put("IX", 9);
mappings.put("X", 10);
mappings.put("XI", 11);
mappings.put("XL", 40);
mappings.put("L", 50);
mappings.put("LXXX", 80);
mappings.put("XC", 90);
mappings.put("C", 100);
mappings.put("D", 500);
mappings.put("CMXCIX", 999);
mappings.put("M", 1000);
mappings.put("MI", 1001);
mappings.put("MDCCCLXXVIII", 1878);
mappings.put("MCMLIII", 1953);
mappings.put("MCMLXXXIV", 1984);
mappings.put("MMM", 3000);
mappings.put("MMMMMM", 6000);
mappings.put("MMMMMMMMMCMXCIX", 9999);
mappings.put("MMMMMMMMMM", 10000);
mappings.put("MMMMMMMMMMI", 10001);
}
@Test
public void testArabicToRomanNumberConvertion() {
for(Map.Entry<string, integer=""></string,></string,></string,><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Integer</span><span class="o">></span><string, integer=""><string, integer=""><string, integer=""> mapping : mappings.entrySet()) {
assertEquals(mapping.getKey(),
RomanNumber.arabicToRoman(mapping.getValue()));
}
}
}</string,></string,></string,></pre><div style="text-align: justify;">Die Implementierung der Klasse "RomanNumber" ist durch Vererbung nicht erweiterbar und kann aufgrund des privaten Konstruktors nicht instanziiert werden. Der einzige Zugang zu der Klasse ist die Methode "arabicToRoman". Die Verriegelung ist notwendig, damit die Klasse nicht durch Instanziierung oder Vererbung gebrochen wird.<br />
<br />
Eine Performanzbetrachtung wurde bei der Implementierung der API-Variante nicht durchgeführt. Eine deutliche Optimierung könnte durch das einmalige Vorhalten der Konvertierungsdaten erreicht werden. Keine klar definierten, nichtfunktionalen Anforderungen ergeben unterschiedliche Lösungen. Bei der Code Kata ist der Verwender des Softwarebausteins und dessen Anforderungen nicht benannt worden, deshalb sind viele kreative und rein funktional korrekte Implementierungen entstanden.<br />
<br />
<b><u>Tipp:</u></b> Bei Softwarebausteinen, die produktiv eingesetzt werden, sind der Verwender des Softwarebausteins und dessen Anforderungen die treibenden Kräfte. Ein Softwarebaustein sollte deshalb immer aus Sicht des Verwenders implementiert werden.<br />
<br />
</div><div style="text-align: justify;">Die Code Kata hat verdeutlicht, dass viele Wege zum Ziel führen und je nach Implementierung die eine oder andere Lösung die Nase vorn hat. Eine Kata in einem Code Dojo ist unabhängig von der Qualität der gefundenen Lösungen ein gutes Mittel, um sich mit anderen Entwicklern auszutauschen und dabei seinen eigenen Programmierstil zu verbessern. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><b><div style="background-color: #cccccc;"><br />
Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.<br />
<br />
</div></b></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-65879596996321150532011-01-09T17:16:00.014+01:002011-08-06T20:59:43.898+02:00Software complexity by example<div style="text-align: justify;">Die Entwicklung von Software wird hauptsächlich von fachlichen Anforderungen getrieben. Der fachliche Nutzen einer Software ist dabei wesentlich für die Benutzerakzeptanz eines Softwareproduktes. Die Benutzerfreundlichkeit im Kontext der nicht funktionalen Anforderungen erhöht die Komplexität einer Software. Das Datenvolumen beeinflusst stark die Antwortzeiten einer Applikation. und ist deshalb eng mit der Benutzerakzeptanz verbunden. Die Stabilität einer Applikation und die Verlässlichkeit spielen eine wichtige Rolle für die Benutzerakzeptanz und den Erfolg eines Softwareproduktes.<br />
<br />
Eine fachliche Funktionalität ist bei schlechter Antwortzeit und/oder Stabilität nahezu wertlos und ein Ärgernis für die Mitarbeiter der Fachabteilung. Das Spannungsfeld zwischen der Softwareentwicklung und der Fachabteilung kann dabei zum Schmelztiegel werden. Wie die Komplexität eines einfachen Dialogbeispiels bereits den Tiegel aufheizt, versucht dieser Artikel zu erläutern.</div><br />
<div style="background-color: orange;"><b>Beschreibung des Dialogbeispiels: </b></div><br />
<div style="text-align: justify;">Das Beispiel beinhaltet einen Dialog zum Anzeigen und Quittieren von Alarmmeldungen. </div><br />
<u><b>Folgende fachliche Funktionalitäten bietet der Dialog:</b></u><br />
<br />
<ul><li>Laden einer Alarmmenge</li>
<li>Anzeigen des Ladezustandes mit einem Fortschrittsbalken</li>
<li>Anzeigen der Alarme in einem Listenfeld (Listbox) mit einem Renderer</li>
<li>Filtermöglichkeit zur Reduzierung der Alarmmenge</li>
<li>Speicherung des Filters zur schnellen Anzeige einer Alarmmenge</li>
<li>Anzeige und Quittiermöglichkeit für einen selektierten Alarm </li>
</ul><br />
<div style="text-align: justify;"><u><b>Beschreibung weiterer Anforderungen:</b></u><br />
<br />
Der Dialog soll jederzeit geschlossen werden können. Der aktuelle Ladezustand spielt dabei keine Rolle. Die Benutzeroberfläche des Dialoges darf nicht einfrieren. Die Interaktionsmöglichkeiten des Benutzers sollen reibungslos zu jedem beliebigen Zeitpunkt durchführbar sein. Der Dialog darf die Benutzeroberfläche der zugrundeliegenden Applikation nicht beeinträchtigen. Im Hintergrund eingehende Alarmmeldungen sollen verarbeitet und angezeigt werden. Der Dialog soll für beliebige Services zur Anzeige der Alarmmeldungen aufschaltbar sein.<br />
<br />
<b><u>Tipp:</u></b> Bei der Programmierung von Benutzeroberflächen sollte stets nach dem Kernprinzip<b> Separation of Concerns </b>implementiert werden. Es lohnt sich deshalb das <b>Command Pattern</b> einzusetzen, weil die Aktionen in einer Benutzeroberfläche über Menüs, die Werkzeugleiste (Toolbar) und Hot-Keys ausgelöst werden können. Die Anwendung des Command Patterns ist eine wesentliche Voraussetzung für die Einhaltung des <b>DRY-Prinzips</b>. Die Möglichkeiten von Java Generics in Kombination mit einer Ellipse, erlauben es, parametrisierbare Kommandos anzuwenden, sodass einer lose gekoppelten und erweiterbaren Implementierung nichts mehr im Wege steht. Die Abhängigkeiten zwischen den aktionsauslösenden Elementen der Benutzeroberfläche sind mit einem <b>Mediator</b> und/oder <b>Visitor</b> synchronisierbar. Der Status eines Menüeintrages ist auf diese Art und Weise mit dem Status einer Schaltfläche der Werkzeugleiste abgleichbar.</div><div style="text-align: justify;"><br />
</div><div style="background-color: orange; text-align: justify;"><div><b>Erste Hürde - Anzeigen des Ladezustandes</b></div></div><br />
<div style="text-align: justify;">Der Fortschrittsbalken zeigt beim Ladevorgang für eine beliebige Menge von Alarmmeldungen den Ladezustand an. Das sieht nach einem mathematischen Problem aus. Die variable Menge der Alarmmeldungen stellt hierbei die erste Hürde dar. Das Problem ist mit einem einfachen Dreisatz nicht zu lösen. Der Dreisatz gibt allerdings die Richtung der Problemlösung vor. Mehr soll an dieser Stelle nicht verraten werden. Eine kleine Rechenaufgabe vor dem Weiterlesen beugt der Langeweile vor.</div><div style="text-align: justify;"><br />
</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="425" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1OI2zX9Yn730XrMfFZ4RYOM6axxkF5__7nUvc6doo-HO2KzKMnbis_x66saup7h-WcsJDvU1Qi5QIRbr3B5HKx-zZlndfYxhnobaws5r0Z1YdRrbXJJEvVhYPjPmUzrqlUjxCtAayjfxB/s640/alert-dialog-load.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Ladezustand anzeigen</td><td class="tr-caption" style="text-align: center;"><br />
</td></tr>
</tbody></table><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><div style="background-color: orange;"><b>Zweite Hürde - Alarmmenge laden</b></div></div><br />
<div style="text-align: justify;">In dem Dialog wird über ein <b>Business Delegate</b> auf die <b>Session Facade</b> der Serverapplikation zum Laden der Alarmmenge zugegriffen. Der Aufruf zwischen dem Business Delegate und der Session Facade erfolgt synchron. Die Benutzeroberfläche blockiert deshalb während dem Laden der Alarmmenge.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Eine brauchbare Lösung für das Problem ist, die Alarmmenge in kleineren Teilmengen in einem Hintergrundthread zu laden. Durch diese Variante kann der Ladevorgang unterbrochen werden und die Benutzeroberfläche friert nicht ein. In Java Swing Applikationen bietet sich der in Java SE 6 integrierte <b>Swing Worker</b> zur Problemlösung an. Der Swing Worker startet einen Hintergrundthread, der unterbrechbar ist und bietet darüber hinaus die Möglichkeit an, die Daten nach der Ladeoperation auszulesen, um anschließend die Benutzeroberfläche zu initialisieren.</div><div style="text-align: justify;"><br />
</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="425" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikl1qfqFfMx1F1MVP9EMO5CzxObXnm6cotJJTHKx9sNb1Lw3k6zKAzFeYTAkqn-WwVEeaNKoZeRt6fKCUxN5LrJwbOVGi3Joy_w8wdwqRo-g2WXZedn76Z_F_TopnE7b9OrA5Q4Rm0IJJA/s640/alert-dialog-view.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Alarmmenge anzeigen</td></tr>
</tbody></table><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><b><u>Anmerkung:</u></b> Die in dem Dialog angezeigten Alarme sind mit einem Unit-Test erzeugt worden und haben keinen fachlichen Anspruch.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Die Anzeige der Icons in dem Listenfeld ist keine Standardfunktionalität. Für die Icon-Darstellung ist ein spezieller Renderer zu implementieren. Das Listenfeld wird initialisiert, nachdem der Hintergrundthread des Swing Workers seine Arbeit erledigt hat. Die Anwendung des Swing Workers in GUI-Applikationen ist ratsam. Dem <b>Event Dispatch Thread (EDT) </b>des Swing GUI-Systems sollte man nicht in die Quere kommen, weil sich dabei Refresh-Probleme ergeben können. Refresh-Probleme sind neben dem Einfrieren der Benutzeroberflächen ein weiteres Problem, das bei der GUI-Programmierung zu vermeiden ist.</div><div style="text-align: justify;"><br />
</div><div style="background-color: orange; text-align: justify;"><b>Dritte Hürde - Alarmmenge filtern</b></div><br />
<div style="text-align: justify;">Das Listenfeld verwaltet die Alarmmenge in einem eigenen Datenmodell. Die Alarmmeldungen werden als eine Collection von <b>Data Transfer Objects (DTOs)</b> über das Business Delegate geladen und im Datenmodell des Listenfeldes verwaltet. Zur Filterung der Modelldaten reicht das einfache Modell des Listenfeldes nicht aus. Das Modell des Listenfeldes ist deshalb um die Filterfunktionalität erweitert worden. Dem Filtermodell liegt eine <b>Model View Controller (MVC) </b>Struktur zugrunde, sodass das Filtermodell in der Folge erweiterbar ist.</div><div style="text-align: justify;"><br />
</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOyikFOJfgsSYB-Huox4nucC0QWP225k-TfItwbeX0w3uoICkTXEQOVWyfaIyx2IPQ9PBwjqCtXWPnA13R1iQHuVAEkiTlSiUEjMH1C9FocaMNcKmbF0tChyphenhyphenuYL8rLba-wRS1gvAqEeRV4/s640/alert-dialog-filter.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Alarmmenge filtern</td></tr>
</tbody></table><div style="text-align: justify;">Der Alarmfilter reagiert bei der der Eingabe eines Suchstrings in dem Filtereingabefeld. Gibt man ein einfaches "q" ein, werden alle Alarme angezeigt die bereits quittiert worden sind. Der Alarmfilter ist speicherbar, sodass dieser ohne Texteingabe ausgelöst werden kann.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Das einfache Dialogbeispiel veranschaulicht, warum die Entwicklung von Softwareprodukten komplex ist. Die Balance zwischen den fachlichen Anforderungen eines Softwareproduktes und den nicht funktionalen Anforderungen ist häufig schwierig. Manchmal ist es eine Gratwanderung große Datenmengen zu verarbeiten und dabei die Benutzerakzeptanz aufrecht zu erhalten. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Der Dialog ist nur ein sehr kleiner Teil einer komplexen Java EE 6 Anwendung, die Daten im Hintergrund verarbeitet, selbst bei hoher Last auf Benutzerinteraktionen reagiert und keine Refresh-Probleme aufweist. Das solche Applikationen und deren Benutzeroberflächen nicht mit einem "Single Thread Model" umsetzbar sind, dürfte klar sein. Parallele Threads in Benutzeroberflächen zu synchronisieren, ohne dem EDT in die Quere zu kommen, keine Deadlocks zu verursachen und die Performanz auszubremsen, sind Herausforderungen, die es bei der Programmierung von Benutzeroberflächen zu meistern gilt. Wie man Synchronisationsprobleme in Benutzeroberflächen löst ist nicht Gegenstand des Artikels, aber eine der wesentlichen Herausforderungen bei der Programmierung von interaktiven Benutzeroberflächen.<br />
<br />
<b>Wenn ein einzelner Dialog einer Anwendung offensichtlich bereits eine nicht zu unterschätzende Komplexität besitzt, lässt sich der Schluss ziehen, dass die Entwicklung von Softwareprodukten eine komplexe Angelegenheit ist.</b></div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.comtag:blogger.com,1999:blog-6961987519128531456.post-82069213104176516392011-01-03T11:44:00.009+01:002011-01-06T12:43:50.070+01:00Autonomic computing grip for SOA<div style="text-align: justify;">Serviceorientierte Anwendungen setzen sich aus Services zusammen, die eine hohe Kohäsion aufweisen. Best Practices, wie beispielsweise die Kapselung und generische Programmiermethoden, erlauben es, Services als allgemeine, wiederverwendbare Komponenten auszulegen. SOA-Architekturen sind lose gekoppelt und orchestrierbar. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Die Evolution von monolithischen Systemen zu serviceorientierten Architekturen basiert auf der Erfahrung bei der Entwicklung und dem Betrieb von komplexen Softwaresystemen, bei der sich lose gekoppelte Systeme durchgesetzt haben. Die lose Koppelung wurde durch die Möglichkeiten der Messaging-Systeme (MOM) populär. Messaging-Systeme und klassische Hub-and-Spoke sowie Message Broker Architekturen zeichnen sich durch eine hohe Verfügbarkeit, Skalierbarkeit, asynchrone Kommunikation und sehr hohe Flexibilität durch Routingtabellen und standardisierte Datentransformationen aus.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Der Java EE Standard unterstützt optimal die Anbindung an Messaging-Systeme mit Message Driven Beans und der JMS-API. Die Anwendung von Messaging-Techniken ist dabei nicht nur auf die Applikationsentwicklung begrenzt, sondern wird häufig in Integrationsszenarien eingesetzt. Die Integration von komplexen Softwaresystemen bringt neue Herausforderungen mit sich, sodass die Vorteile von Messaging Systemen nicht ausreichend sind um Integrationsszenarien lösen zu können. Im Bereich der Integration von Softwaresystemen und dem Enterprise Application Integration (EAI) sind deshalb auch die Komponenten in ihrer Struktur flexibel ausgelegt. Die Flexibilität wird dort durch klar definierte Schnittstellen mit der Möglichkeit zur Adaptivität, der Unabhängigkeit von Protokollen (Protokolltransformationen), der Komposition und Orchestrierung (BPMN / BPEL) erreicht. </div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Beim Technologieschritt vom Message Broker zum Enterprise Service Bus (ESB) haben die Hersteller von SOA-Entwicklungswerkzeugen große Fortschritte erzielt. Integrationsszenarien sind dabei unabhängig von ihrer Komplexität, ohne eine Zeile Programmcode schreiben zu müssen, lösbar. Graphische Werkzeuge und die Normierung der Prozessdarstellungen (BPMN 2) erlaubt es, komplexe Geschäftsanforderungen mit Zeitabhängigkeiten, Parallelität und Verzweigungen zu modellieren. Ein in der Designphase erstelltes Modell bildet dabei die Basis für die automatisierte Generierung der Serviceartefakte und dem Deployment in der ESB Umgebung.</div><br />
<div style="text-align: justify;">Die neue Generation von Werkzeugen verschiebt den Schwerpunkt von der Erstellung einer Applikation in Richtung Wartung und Erfordernissen des Betriebes. Die Verlagerung des Schwerpunktes wird erst durch die Reduzierung des Programmieraufwandes möglich, sodass mehr Zeit für die strategische Inbetriebnahme eines Softwareproduktes vorhanden ist. Die Hersteller von Softwareprodukten haben diesen Trend erkannt und mit der Common Event Infrastructure (CEI) einen standardisierten Layer definiert.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Der Nachrichtenfluss im CEI-Layer basiert auf dem Common Base Event (CBE) Format. Ein Nachrichtenfluss in einer CEI-Umgebung besteht aus Informationsmeldungen und Meldungen, die durch Fehlerszenarien produziert werden. Diese Meldungen fungieren als Ereignisse, die zeitnah bearbeitet und analysiert werden müssen, um den reibungslosen Betrieb einer Unternehmensanwendung sicherzustellen.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">In der Eclipse Test & Performance Tools Platform (Eclipse TPTP) ist der Hyades-Framework integriert, der das Common Base Event Format nutzt. Strukturierte Detailinformationen eines Services, mit nach Kategorien angelegten Detailinformationen, eignen sich zur gezielten Analyse von Ereignismeldungen. Anders als bei herkömmlichen Trace-Frameworks wird ein standardisiertes und bereits in vielen Werkzeugen integriertes Format zum Austausch der Ereignismeldungen verwendet.</div><br />
<div style="text-align: justify;">Eine CEI-Infrastruktur beinhaltet sowohl Ereigniserzeuger, die Services einer serviceorientierten Architektur und Infrastrukturkomponenten (Applikationsserver, Message Broker, ESB) als auch Ereignisempfänger, die Werkzeuge zum Auswerten der Ereigniskette. Zur Auswertung sind Analysemöglichkeiten mit Filtern und Navigationsmöglichkeiten in der Ereigniskette nötig, um Fehlerszenarien detailiert auswerten und bewerten zu können. Ohne Analysemöglichkeiten sind Fehlerbilder nur sehr schwer aufzubauen und Wiederholungsfehler kaum zu vermeiden.</div><br />
<div style="text-align: justify;">Die CEI-Umgebung ist Teil des Autonomic Computings einem Paradigma das neben der Analyse von Nachrichtenflüssen weitergehende Funktionalitäten wie Selbstkonfiguration, Selbstmanagement und automatisierte Optimierungen beinhaltet.</div><br />
<div style="text-align: justify;">Der Service Manager 2.0 ist eine Komposition von Applikationen, die zur Auswertung der Ereigniskette in einer CEI-Umgebung eingesetzt werden kann. Der Service Manager 2.0 stellt deshalb eine interessante Möglichkeit dar fehlerhaftes Verhalten zeitnah analysieren und auswerten zu können. Die Trennung des Monitorings der Services und damit verbunden schnellere Reaktionszeiten im Fehlerfall, sowie das Analysewerkzeug für die nachträgliche Analyse einer Fehlerkette, erlaubt gezielt Maßnahmen zum richtigen Zeitpunkt zu treffen.</div><br />
<div style="background-color: white; color: black;"><b><a href="http://soa-servicemanager.blogspot.com/" target="_blank">Interessiert am Service Manager 2.0?!</a></b><br />
<br />
<div style="background-color: orange;"><b>Informationen zum Common Base Event Format</b></div></div><div style="background-color: white; color: black; text-align: justify;"><br />
Das Common Base Event Format definiert die Struktur von Ereignissen, die von verschiedenen Applikationen erzeugt werden, in einem einheitlichen, gemeinsamen Datenformat. Dieses Format wird von verteilten Applikationen zum Logging, Management und Problemerkennung verwendet, um eine standardisierte Auswertung der darin enthaltenen Informationen zu ermöglichen. Ausgetauscht werden sowohl technische als auch fachliche Informationen. Mit der Zusammenführung von technischen und fachlichen Daten lässt sich eine Vielzahl von Funktionen automatisieren.</div>Jörg Rückert (SCJP, SCBCD, SCDJWS, SCEA)http://www.blogger.com/profile/05942192082903537882noreply@blogger.com