The purpose of this workshop, is to give you hands-on experience with the Java language and insight on how to write Clean Code. Through a mix of short videos, small exercises and bits of theory, you’ll experience the Java language, solving problems in a Test-Driven(-Development, TDD) manner, using plain Java.

And a homework assignment which will be reviewed, and feedback provided, by bol.com engineers.

1. Pre-requisites

  • Know the Java language, eg. you’ve followed a basic Java course

    The labs may contain references to more info, and you might learn a thing or two about the Java language, but this workshop won’t learn you Java if you haven’t got any previous knowledge
  • Have Java 8+ JDK installed and available on your PATH

    $ java -version
    java version "1.8.0_121"
    Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
    $ javac -version
    javac 1.8.0_121

2. How does this workshop work?

The workshop organization is as following:

  • First a bit of theory explaining the concepts, principles, practices and conventions. Then a lab to bring the theory to practise

  • Each lab is self-contained, but continues on the knowledge you gained in previous labs

  • Labs contain additional practical information which you need to be able to do the lab exercises

  • Labs often contain references to resources. These resources provide you with more in-depth information on how things work

  • Each lab explains where you can find the files for the lab. All lab files are structured inside the labs directory

3. Introduction

Welcome to the Code Dojo for Students workshop (ʘ‿ʘ)╯

So what is this "Dojo" thing?
Dojo (道場 dōjō) is a Japanese term which literally means "place of the way". Initially, dōjōs were adjunct to temples.
— https://en.wikipedia.org/wiki/Dojo

During a Code Dojo commonly koans and katas are practised. Say what?

Koans

"a paradoxical anecdote or riddle without a solution, used in Zen Buddhism to demonstrate the inadequacy of logical reasoning and provoke enlightenment." — https://en.wikipedia.org/wiki/K%C5%8Dan

These are exercises that are complete, but miss a snippet of code to make it run successful. It’s up to you to provide the snippet and make it succeed. These exercises are very good to learn specific rules and syntax of a programming language.

Katas

"an individual training exercise in karate and other martial arts" — https://en.wikipedia.org/wiki/Kata

These are exercises where there is only a problem description, the exercise is to implement a solution. You can (and should) implement and re-implement katas many times. This will give you the practise of solving different type of problems, and every time you solve it again you will become quicker and better at it. These exercises are also perfect to learn new programming languages, or new features of programming languages. eg. by implementing a kata in Java 6, Java 7, Java 8, Scala, Ruby, Groovy, etc. You’ll quickly learn the specifics of each of the languages.

This Code Dojo you’ll practise a kata in the Java programming language. During this kata you’ll learn and apply some techniques to get to Clean Code.

Clean Code?
cleancoders logo

Let me introduce you to Robert C. Martin, aka Uncle Bob, the writer of the books Clean Code and The Clean Coder, and the creator of the Clean Coders video series. In his books and videos he shows you principles that help you write clean code. Some may seem extreme, some may seem obvious, but overall you’ll benefit from having seen his vision on clean code.

4. Get the code

This project is available at Github: https://github.com/bolcom/student-dojo

  • If you have git installed, and your ssh key configured in your Github account, you can clone it right away:

    git clone git@github.com:bolcom/student-dojo.git
  • If you have git installed, but have no Github account nor ssh key configured, you can clone over https:

    git clone https://github.com/bolcom/student-dojo.git
  • Else, you can find out how to install and use git at https://git-scm.com/doc

  • Or download the repository as zip from: https://github.com/bolcom/student-dojo/archive/master.zip

5. Testing

For a quick introduction into testing, check this video at: What kind of testing? from lynda.com

5.1. Test-Driven Development

Test-Driven Development, or TDD for short.

A quick introduction from lynda.com: What is TDD?

5.1.1. Three laws of TDD

  1. Write no production code, except to pass a failing test

  2. Write only enough of a test to demonstrate a failure

  3. Write only enough production code to pass the test

5.1.2. Red-Green-Refactor

The Red/Green/Refactor cycle is typically executed once for every complete unit test, or once every dozen or so cycles of the three laws. The rules of this cycle are simple.

  1. Create a unit tests that fails

  2. Write production code that makes that test pass

  3. Clean up the mess you just made

The philosophy is based on the idea that our limited minds are not capable of pursuing the two simultaneous goals of all software systems:

  1. Correct behavior

  2. Correct structure

So the Red/Green/Refactor cycle tells us to first focus on making the software work correctly; and then, and only then, to focus on giving that working software a long-term survivable structure.

Executing the Red/Green/Refactor cycle takes on the order of a minute or so. This is the granularity of refactoring. Refactoring is not something you do at the end of the project; it’s something you do on a minute-by-minute basis. There is no task on the project plan that says: Refactor. There is no time reserved at the end of the project, or the iteration, or the day, for refactoring. Refactoring is a continuous in-process activity, not something that is done later (and therefore optionally).

6. Legacy Code

Code that does its thing, but is old and/or isn’t up to par with the latest code guidelines and principles, is commonly marked as Legacy Code. The same goes for code that isn’t covered by tests.

When starting refactoring legacy code, we ask our selves some of the following questions:

  • Where to start?

  • If I start here will it cause problems later on?

  • I see 2-3 options, which one should I choose?

First and foremost, find the smallest piece of code that you can start refactoring. For that piece, create unit tests which tests the boundaries and special cases. Then, start refactoring! Take very small steps, create additional tests when needed, and keep those tests green.

To help refactoring towards clean code and object oriented concepts, there are the 9 Object Calisthenics rules:

  1. One level of indentation per method

  2. Don’t use the else keyword

  3. Wrap all primitives and Strings

  4. First class collections (wrap all collections)

  5. One dot per line

  6. Don’t abbreviate

  7. Keep all entities small

  8. No classes with more than two instance variables

  9. No getters/setters/properties

More info around these rules can be found at: http://williamdurand.fr/2013/06/03/object-calisthenics/

These rules are good practise for all code, not only legacy code.

These 9 Object Calisthenics rules, are "rules". In this lab the idea is that you apply these rules to the max, but this doesn’t mean you can apply it everywhere. There are situations where it simply doesn’t make sense.

Next, some tips to help you apply the 9 rules.

6.1. How to calculate the number of code dependencies

Given code sample
1
2
3
places[currentPlayer] = places[currentPlayer] + diceResult;
if (places[currentPlayer] > 11)
    places[currentPlayer] = places[currentPlayer] - 12;
  • Line 3 is the deepest nested code. It has 2 dependencies, namely: places and currentPlayer

  • If we include line 2, we still find 2 dependencies, namely: places and currentPlayer

  • If we include line 1, we find 3 dependencies, namely: places, currentPlayer, and diceResult

That’s how you calculate the number of dependencies in a piece of code.

6.2. How to reduce the number of code dependencies

6.2.1. Extract local variable

This code depends on items and i
if (items.get(i).getQuality() > 0) {
If we declare item above, the if statement has only one dependency: item
Item item = items.get(i);

if (item.getQuality() > 0) {

This is called the "extract local variable" refactoring.

6.2.2. Invert if-statement

The code below has 2 dependencies: quality and name
if (quality > 0) {
    if (!name.equals("Sulfuras, Hand of Ragnaros")) {
        quality = quality - 1;
    }
}
By simply inverting the if statements, we’re able to group the instructions about quality
if (!name.equals("Sulfuras, Hand of Ragnaros")) {
    if (quality > 0) {
        quality = quality - 1;
    }
}

This is called the "invert if-statement" refactoring.

6.3. Wrap integer with domain concept

public class Discounter {
    private int discount;

    public int applyTo(int initialPrice) {
        return initialPrice - discount;
    }

    // ...
}
Becomes
public class Discounter {
    private Money discount;

    public Money applyTo(Money initialPrice) {
        return initialPrice.minus(discount);
    }

    // ...
}

public class Money {
    private int discountInCents;

    // ...
}

If the variable of your primitive type has behaviors, you MUST encapsulate it. And this is especially true for Domain Driven Design (DDD). DDD describes Value Objects like Money, or Hour for instance.

6.4. How to get rid of else-statements

The else keyword is well-known as the if/else construct is built into nearly all programming languages. Do you remember the last time you saw a nested conditional? Did you enjoy reading it? I don’t think so, and that is exactly why it should be avoided. As it is so easy to add a new branch to the existing code than refactoring it to a better solution, you often end up with a really bad code.

Here some ways to remove the else keyword from your code.

6.4.1. Early Return

The condition can be either optimistic, meaning you have conditions for your error cases and the rest of your method follows the default scenario, or you can adopt a defensive approach (somehow related to Defensive Programming), meaning you put the default scenario into a condition, and if it is not satisfied, then you return an error status. This is better as it prevents potential issues you didn’t think about.

public boolean isValidEmail(String email) {
    if (validFormat(email)) {
        // do logic
    } else {
        return false;
    }
}
Becomes
public boolean isValidEmail(String email) {
    if (!validFormat(email)) {
        return false;
    }
    // do logic
}

As an alternative, you can introduce a variable in order to make your return statement parameterizable. This is not always possible though.

Returning from a method as soon as possible, keeps the code readable, focused on the actual logic, and minimally indented.

6.4.2. Polymorphism

Polymorphism is an alternative to if/else/switch-statements. For instance, it is possible to use the Strategy Pattern or inheritance to replace every clause in the control statement.

There are variations to how the "right" strategy is obtained, eg.:

  • Caller provides the strategy (as a method argument)

  • Strategy is memorised as member variable

  • Strategy is obtained from a map (or any other lookup mechanism)

6.4.3. Null Object, Optional, or Empty list

A Null Object is an object with no referenced value or with defined neutral (null) behavior.

The Java Optional , Collections#emptyList() , Collections#emptyMap() , Collections#emptySet() , Collections#emptyListIterator() , and Collections#emptyEnumeration() , are all implementations of the Null Object provided by the Java language.

Using Null Object dramatically reduces the need for null checking.

Example without Optional
SoundCard soundcard = ...;
if(soundcard != null){
  System.out.println(soundcard);
}
Example with Optional
Optional<Soundcard> soundcard = ...;
soundcard.ifPresent(System.out::println);

6.5. Lab 1: Refactoring legacy code

In labs/01-legacy-code you can find the first lab.

Project structure
.
├── src
│   ├── main
│   │   └── java
│   │       └── org
│   │           └── joyofcoding
│   │               └── objectcalisthenics
│   │                   ├── GildedRose.java
│   │                   └── Item.java
│   └── test
│       └── java
│           └── org
│               └── joyofcoding
│                   └── objectcalisthenics
│                       ├── assertions
│                       │   └── ItemsAssert.java
│                       └── GildedRoseTest.java
├── 01-legacy-code.gradle
└── README.adoc

The goal of this lab is to let you try out heuristics that allows to guide refactoring efforts towards clean code and object oriented concepts.

We’ll refactor the code in 4 steps:

  1. Introduction

  2. Move code to existing objects

  3. Encapsulate primitives and collections

  4. Make the business domain emerge

6.5.1. Introduction

Hi and welcome to team Gilded Rose. As you know, we are a small inn with a prime location in a prominent city run by a friendly innkeeper named Allison. We also buy and sell only the finest goods. Unfortunately, our goods are constantly degrading in quality as they approach their sell by date. We have a system in place that updates our inventory for us. It was developed by a no-nonsense type named Leeroy, who has moved on to new adventures. Your task is to add the new feature to our system so that we can begin selling a new category of items.

First an introduction to our system:

  • All items have a SellIn value which denotes the number of days we have to sell the item

  • All items have a Quality value which denotes how valuable the item is

  • At the end of each day our system lowers both values for every item

However, as usual, the original developers have left and there’s no reliable document about the business rules. You’ll have to go with the code…​

Luckily there are basic unit-tests. The tests make sure the application works as it should, and therefor should functionally not be changed during refactoring. Of course, when you rename/move methods, you’ll have to update the test code accordingly, but functionally it should not change.

6.5.2. Exercise 1: Move code to existing objects

The first step is to make sure all the code is in the right object or abstraction. In terms of OO that implies that we encapsulate data so that refining the rules around a particular piece of data is easy (i.e. changing the least amount of code). For instance, code in class A that speaks about data in B should be in B. Move it there.

Heuristic

  • Start by identifying the lines that have the least amount of dependencies. That is the most indented piece of code. It is likely to work with only one object/variable.

    • Now extend the zone as far as possible until you hit another dependency

      • If you have only one dependency, use rule 5 or 9

      • If you have two or more dependencies, try to reduce the number

Object Calisthenics

  • Rule 5: One dot per line

  • Rule 9: No getters/setters/properties

6.5.3. Exercise 2: Encapsulate primitives and collections

Now that all logic is in the appropriate object or abstraction, we’ll have to create new objects that can receive data and related behavior. In this phase we’ll encapsulate primitives (rule 3), but we could just as well encapsulate collections in so-called first class collections (rule 4).

Heuristic

  • Now that the code is in the appropriate object/abstraction, use rules 3 and 4 to create new objects and abstractions

Object Calisthenics

  • Rule 3: Wrap all primitives and Strings

  • Rule 4: First class collections (wrap all collections)

6.5.4. Exercise 3: Make the business domain emerge

Make the business domain as visible as possible in the code.

Heuristic

  • Continue refactoring until all the other rules (1, 2, 6, 7, 8) are satisfied

Object Calisthenics

  • Rule 1: One level of indentation per method

  • Rule 2: Don’t use the ELSE keyword

  • Rule 6: Don’t abbreviate

  • Rule 7: Keep all entities small

  • Rule 8: No classes with more than two instance variables

6.5.5. Conclusion

The 9 Object Calisthenics rules:

  1. One level of indentation per method

  2. Don’t use the ELSE keyword

  3. Wrap all primitives and Strings

  4. First class collections

  5. One dot per line

  6. Don’t abbreviate

  7. Keep all entities small

  8. No classes with more than two instance variables

  9. No getters/setters/properties

These rules help you create better, cleaner, software. Does that mean you should always apply all of them? In short, no. Like everything in software, it depends on the situation, and the opinions, of those creating and reviewing the code. But keeping these rules in the back of your mind, evaluating if you should apply them to the code you’re working on, that’s the goal of these rules.

Especially rule 7, 8, and 9, are commonly seen as extreme. It’s good to use them to the letter in learning situations, so you experience them to the best.

6.5.6. Resources

7. Homework

The purpose of this homework assignment is to give you an idea of a real assignment, in a simplified context/architecture. Which will be reviewed, and feedback provided, by bol.com engineers.

Requirements for this homework assignment:

  • Work in a team of 2 - 4 people

  • In a single zip-file, deliver:

    • All code for your project

    • All executable, distributable versions of your project
      (think jars and configuration)

    • README containing all (design) decisions, with reasonings

  • Deliver the zip-file to your teacher, he/she will do the communication with bol.com for the review and feedback

Good luck! ᕙ(░ಥ╭͜ʖ╮ಥ░)━☆゚.*・。゚ ᕕ( ᐛ )ᕗ

7.1. Lab 2: Project Price Alert

Using BigData analyses we’ve discovered certain behavioural patterns in customers visiting our site. In this case we could point out daily visits to the same products by the same customer without actually buying anything. Then, when there was a price drop or the availability of the product changed, the purchase was done.

We could distinguish two types of customers:

  • The first type of customers are mostly moms and dads, with newborn children, usually visit in the morning between 09:00-11:00h and 13:00-15:00h. They are looking mostly for diapers and baby wipes. Statistical analyses shows that this is usually the time that babies take a nap

  • The second group consist of students mostly between the age of 18 and 26 years. Their interest mainly go out to consoles and (computer)games. Since the release of the Nintendo Switch this behaviour has quadrupled. Interestingly enough they are active at the same time periods the young parents are, even though that is the same time as college is…​

After doing marketing research and crunching more BigData, our Business Analysts have come to the conclusion that if we offer a price-drop notification service:

  • Customers no longer need to refresh the same page every day. They can simply wait for the notification in their email and then instantly buy the product

  • Young parents will be able to get some sleep while their toddlers sleep, so they’ll be happier and eventually we estimate that they will buy more products at our store

  • Students will either be able to attend college, sleep more or do both (inconclusive due to insufficient data for analyses)

  • We will be able to analyse what customers are actually willing to pay for a product and make sharper offers

Your team has been assigned to create a technical solution for this opportunity.

In labs/02-homework you can find the homework lab.

Project structure
.
├── stubs
│   ├── customerservice
│   │   ├── src
│   │   │   ├── dist
│   │   │   │   └── customerservice.yaml
│   │   │   └── main
│   │   │       └── java
│   │   │           └── com
│   │   │               └── bol
│   │   │                   └── customerservice
│   │   │                       ├── api
│   │   │                       │   └── Customer.java
│   │   │                       ├── health
│   │   │                       │   └── V1ApiHealthCheck.java
│   │   │                       ├── jdbi
│   │   │                       │   └── CustomerDao.java
│   │   │                       ├── resources
│   │   │                       │   └── CustomerServiceResource.java
│   │   │                       ├── CustomerServiceApplication.java
│   │   │                       └── CustomerServiceConfiguration.java
│   │   └── customerservice.gradle
│   ├── emailservice
│   │   ├── src
│   │   │   ├── dist
│   │   │   │   └── emailservice.yaml
│   │   │   └── main
│   │   │       └── java
│   │   │           └── com
│   │   │               └── bol
│   │   │                   └── emailservice
│   │   │                       ├── api
│   │   │                       │   └── Email.java
│   │   │                       ├── health
│   │   │                       │   └── V1ApiHealthCheck.java
│   │   │                       ├── resources
│   │   │                       │   └── EmailServiceResource.java
│   │   │                       ├── EmailServiceApplication.java
│   │   │                       └── EmailServiceConfiguration.java
│   │   └── emailservice.gradle
│   └── productservice
│       ├── src
│       │   ├── dist
│       │   │   └── productservice.yaml
│       │   └── main
│       │       └── java
│       │           └── com
│       │               └── bol
│       │                   └── productservice
│       │                       ├── api
│       │                       │   └── Product.java
│       │                       ├── health
│       │                       │   └── V1ApiHealthCheck.java
│       │                       ├── jdbi
│       │                       │   └── ProductDao.java
│       │                       ├── resources
│       │                       │   └── ProductServiceResource.java
│       │                       ├── ProductServiceApplication.java
│       │                       └── ProductServiceConfiguration.java
│       └── productservice.gradle
├── 02-homework.gradle
├── price-notification-service.xsd
└── README.adoc

7.1.1. Acceptance criteria

  • Customers are able to register for a price drop

  • Customers are identified by their email address

  • When the price drops below a given threshold, an email is send out to the customer

    • The email will contain the new price and a link to the product page. Links to the productpage are formatted like this: https://www.bol.com/nl/p/{title}/{ean}/

    • The email is only sent to the customer once, even if the price raises and drops below the threshold again

  • Customers are able to create notifications for different products, but only for one price per product. If a notification is created for a product that is already registered to that customer, the existing notification will be updated

  • Customers will also be able to remove notifications, for when they are no longer interested in the product

7.1.2. Non-functional requirements

  • At bol.com we have a microservice architecture. So the component your team will build should fit in that landscape. Also take into account that some services may act up by throwing server errors at you, responding slow or may not be available at all. Write your code defensively

  • Most of the services at bol.com are written in Java, we also have service written in atleast golang and scala, and possibly more. But for this assignment you’re limited to build your service using Java 8

  • There are three backend services you will need to talk to (The source code for the stubs is provided):

    • The 'Product Service' which will have an API call to get the price of a certain product. A product is identified by an EAN (European Article Number)

    • The 'Customer Service' which will have an API call to get customer information (name, email) A customer is identified by an internal customerId

    • The 'Email Service' which will have an API call to send an email to the customer

  • To decrease the footprint of our infrastructure, we are experimenting with running java applications stand-alone. Therefor the result of your project should be a stand-alone java application, which is runnable via single command (eg. java -jar server.jar, or ./my-service)

  • Your service should be configurable using a configuration file (eg. .properties or .yaml).
    The following things should be configurable:

    • The port your service API is available on

    • The host and port for each service your service is dependent on

  • Changes must be persistent - so if you restart your service any previously registered notifications will still be available.

    • Your service needs to store its data in a file called 'service.db'.

    • Data should be read and written using Java’s 'java.io.RandomAccessFile'.

    • Any sql/jdbc solutions are out of scope for this project

  • The service must be able to handle concurrent API calls, even for the same customer and the same product, without causing any data inconsistencies. Data inconsistencies that pop up during testing are unacceptable and will fail the project

  • The project must have a README file, containing:

    • Team composition

    • Instructions on how to build, and run, your service

    • All (design) decisions you take, with reasonings

    • Clear documentation on how the 'service.db' file is structured.

  • Test that your service will never return a 500+ ServerError

  • The frontend is built by another team. Your focus is on writing a backend REST service. Your service API contract is already defined, as given in API Design

7.1.3. API Design

Price notification Service

The model for your API is described in the price-notification-service.xsd file.

The frontend will do the following calls to your service:

Method

GET

Path

/v1/customer/{customerId}/notifications

Accept

application/xml

Status 200

Returns a NotificationList object containing a list of active Notifications that haven’t been fired

Functionality

Retrieves all the notifications for the customer with the given customerId. The functionality will always return with a list, even if there are no notifications configured for the customer

Method

PUT

Path

/v1/customer/{customerId}/notifications/{ean}

Body

A Notification object containing the ean, and the price

Content-Type

application/xml

Status 201

Operation successful, returns nothing

Functionality

Create or update notifications for products (eans). If there is already a notification available for the given ean, then that notification will be updated

Method

DELETE

Path

/v1/customer/{customerId}/notifications/{ean}

Body

None

Content-Type

application/xml

Status 204

Notification successfully deleted

Status 404

No notification can be found for the customer with this ean

Functionality

Deletes the notification for the customer with the given ean

Customer service

Method

GET

Path

/v1/customer/

Content-Type

application/xml

Status 200

Returns a CustomerList object containing a list of Customer objects

Functionality

Retrieves all customers. The functionality will always return with a list, even if there are no customers

Method

GET

Path

/v1/customer/{customerNumber}

Content-Type

application/xml

Status 200

Returns a Customer object

Status 404

Customer does not exist

Functionality

Returns the specific customer

Email service

Method

PUT

Path

/v1/email

Body

An Email object containing the receiver' emailaddress, subject, and the body content

Content-Type

application/xml

Status 202

Operation successful, returns nothing

Functionality

Sents given email to receiver' emailaddress

Product service

Method

GET

Path

/v1/product/

Content-Type

application/xml

Status 200

Returns a ProductList object containing a list of Product objects

Functionality

Retrieves all products. The functionality will always return with a list, even if there are no products

Method

GET

Path

/v1/product/{ean}

Content-Type

application/xml

Status 200

Returns a Product object

Status 404

Product does not exist

Functionality

Returns the specific product

8. Appendix

8.1. Build tools

gradle logo

This workshop uses Gradle as its build tool.

Another common build tool in the Java world, is Maven.

8.2. IDEs

  • IntelliJ
    Very popular all-round IDE. There’s a (free) community edition, but as student you can get the ultimate edition for free.

  • Eclipse

  • Netbeans

8.3. Meetups

meetup logo

Meetup.com has a big amount of free IT related meetups, knowledge sharing sessions; commonly in the evening between 18:00 and 21:00, ~two presentations, free food and drinks included, all over the netherlands (but most are around Amsterdam).
Perfect to learn about real IT projects, usage, latest hypes, technologies, and to learn other people in the business.

Just sign-up, choose your IT topics of interest, and go to the suggested meetups.

Some known high-quality meetups, possibly with specific topics:

8.4. Conferences

Conferences are knowledge sharing and networking events, ranging from a day, to multiple days, to a week. Some are free, some are expensive, most have student discounts.

Here a short list of IT conferences, there are a lot more on any topic of your choice: