Our Blog

AlgoTrader

Refactoring katas

As part of our series of blog posts about ‘Refactoring’ katas, we now publish a second kata: AlgoTrader.

This kata was written in java and the refactoring operations described here were done using Intelij IDEA.

The code can be found here (look for the “AlgoTrader” folder)

Second kata: AlgoTrader

 

Background

In an algo trade system (a bot that trades in the stock markets), there is a service called StockMarketAPI and it’s responsibility is to abstract the web requests to the stock market API. It looks like this:

 

public class StocksMarketApi implements IStocksMarketApi {

  public void sell(Stock stock) throws ClientProtocolException, IOException {
     Session session = null;
     Transaction tx = null;
     try {
        session = HibernateSessionGenerator.openSession();
        tx = session.getTransaction();
        tx.begin();
        session.delete(stock);
        tx.commit();
        session.flush();
     } finally {
        if (session != null) {
           session.clear();
           session.close();
        }
     }
     sendSellCommand(stock);
  }

  private void sendSellCommand(Stock stock) throws ClientProtocolException, IOException {
     CloseableHttpClient client = HttpClientBuilder.create().build();
     HttpGet get = new HttpGet("http://www.some.com/api/sell?id=" + stock.getId());
     client.execute(get);
  }
}

 

As you can probably see, this class is doing something that’s not under its responsibility – it’s deleting a stock from the database, using hibernate (see lines above highlighted in yellow). This kind of code should reside under a class called StocksRepository.

 

The class that uses the method above is Trader (see the highlighted line in yellow) and it looks like this:

public class Trader {


  private IStocksRepoistory _stocksRepository;
  private IStocksMarketApi _stocksMarketApi;

  public Trader(IStocksRepoistory stocksRepository, IStocksMarketApi stocksMarketApi) {
     _stocksRepository = stocksRepository;
     _stocksMarketApi = stocksMarketApi;
  }

  public void sellStocks() throws ClientProtocolException, IOException {  
     List<Stock> stocks = _stocksRepository.getAllStocks();
     for (Stock stock : stocks) {
        if(stock.shouldBeSold()){
           _stocksMarketApi.sell(stock);
        }
     }
  }
}

 

The logic that deletes a stock from the DB should be extracted from StocksMarketAPI and should be placed in StocksRepository under a method called delete. Then Trader should call both StocksRepository.delete() and StocksMarketAPI.sendSellCommand()

We want Trader to look like this

    _stocksRepository.delete(stock);
    _stocksMarketApi.sendSellCommand(stock);

Our solution

As we always say, you can start doing it manually, cut and paste and typing yourself to death, but then it will be tedious and very risky. Your task is to use the IDE refactoring capabilities as much as possible. You will find out that you need to calculate your path very carefully, otherwise you might find yourself doing a lot of manual work.

Step 1.

This step is kind of a preparation for Step 2 and it’s very manual. First we remove the method sell from IStocksMarketAPI. Next, since sell is no longer a method of IStocksMarketAPI, but only a method of StocksMarketAPI, we should change Trader to cast _stocksMarketApi in order to invoke the method sell 

Now Trader looks like this:

   ((StocksMarketApi) _stocksMarketApi).sell(stock);  

Step 2.

Now we would like to inline the method sell of StocksMarketAPI directly into Trader where it is being called (Inline method is an operation in which instead of letting code A invoke a method B, we let the IDE to automatically “cut and paste” the content of method B directly into code A and then delete method B).

To do that we put the cursor on the method sell (doesn’t matter where in the code) and we press CTRL+ALT+N. In the dialog that will popup choose “Inline all invocations and remove the method”. Next, ignore the “problems detected” dialog and press continue.

 

This is the result:

           Session session = null;
           Transaction tx = null;
           try {
               session = HibernateSessionGenerator.openSession();
               tx = session.getTransaction();
               tx.begin();
               session.saveOrUpdate(stock);
               tx.commit();
               session.flush();
           } finally {
               if (session != null) {
                  session.clear();
                  session.close();
               }
           }
           ((StocksMarketApi)_stocksMarketApi).sendSellCommand(stock);        

The highlighted lines are those that were imported (inlined) from StocksMarketAPI.sell(). The last line though is not compiling since StocksMarketAPI.sendSellCommand() is not visible to Trader – it’s private. To fix that, stand on that line with the cursor and press ALT+ENTER. Then select “Make Method… Public”

 

Step 3.

Now we would like to extract  the highlighted code above (the one that’s deleting a stock from the db) into a new method called delete. This refactoring operation is called Extract Method. To do that we select the lines that we would like to extract (lines 16-30) and we press CTRL+ALT+M, then we give a name to this method – delete.

Now Trader should look like this:

public void sellStocks() throws ClientProtocolException, IOException {
   List<Stock> stocks = _stocksRepository.getAllStocks();
   for (Stock stock : stocks) {
       if (stock.shouldBeSold()) {
          delete(stock);
          ((StocksMarketApi) _stocksMarketApi).sendSellCommand(stock);
       }
   }
}

private void delete(Stock stock) {
   Session session = null;
   Transaction tx = null;
   try {
       session = HibernateSessionGenerator.openSession();
       tx = session.getTransaction();
       tx.begin();
       session.saveOrUpdate(stock);
       tx.commit();
       session.flush();
   } finally {
       if (session != null) {
          session.clear();
          session.close();
       }
   }
}

 

Step 4.

No doubt, step 3 was in the right direction, but we are not there yet – any operation on the DB should reside in a repository class, the same goes with our new method delete, it should be moved to StocksRepository. 

This refactoring operation is called Move Method. To do that we put the cursor on the method name (anywhere in the code) and we press F6. In the dialog that pops up we choose _stocksRepository as the object to which this method will be moved.

Now Trader looks like this:

_stocksRepository.delete(stock);
((StocksMarketApi) _stocksMarketApi).sendSellCommand(stock);

 

Step 5.

Awesome, we are almost there.

One last thing: on line 17 we are casting _stocksMarketApi to its concrete class instead of working with its interface. To fix that we are manually removing the casting, but then we are staying with a compilation error since sendSellCommand is not declared in IStocksMarketAPI even though it is implemented in the concrete class StocksMarketAPI.  To fix that we put the cursor on the problematic line, we press CTRL+ENTER and we choose Create method sendSellCommand.

That’s it. Now Trader looks like this:

_stocksRepository.delete(stock);
_stocksMarketApi.sendSellCommand(stock);

 

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 4 other subscribers

Comments