• 10.0 Refactoring

    Refactoring is the process of improving code without changing functionality, e.g. renaming and restructuring code. Refactoring has several benefits such as better software design due to, clearer, cleaner, and faster code; which in turn makes it more maintainable and easier to debug.

    Refactoring Support in NetBeans

    📹 Introduce Refactoring

    Let’s refactor an "Account" class by changing the name of a "balance" field to "accountBalance":

    NetBeans Screenshot

    NetBeans can identify references which need to be updated and allows us to select the ones we want to update:

    NetBeans Screenshot

    Let’s now refactor a "Customer" class by creating a field from an expression, e.g. the Customer class has "firstName" and "lastName" fields but commonly a name combination is required. This has been implemented as "String" creation when a "getName()" method is invoked. We can refactor to create a "Name" field with the "String" value when a "Customer" object is created.

    NetBeans Screenshot

    public class Customer {
      private String firstName;
      private String lastName;
        
      private String name;
        
      /** 
       * Constructor for objects of class Customer
       * 
       * @param firstName  the first name
       * @param lastName   the last name
       */ 
      public Customer(String firstName, String lastName)
      { 
        name = firstName + " " + lastName;
        this.firstName = firstName;
        this.lastName = lastName;
      } 
    
  • 10.1 Application Testing

    The purpose of application testing to find bugs. Tests should be planned in advance of coding and should attempt to test each path through the code. Tests should include boundary and extreme values and there are different types of test we can run:

    🔗 Types of software Testing

    JUnit

    JUnit testing framework facilitates both manual and automated testing. With manual testing we can execute the test manually without any tool support, whereas automated testing is carried out with tool support. Manual testing is considered to be more time consuming and less reliable than automated testing. JUnit allows us to test right down to the smallest piece of testable software e.g. a method.

    JUnit support in NetBeans

    Netbeans supports JUnit testing and allows us to create, update, and run tests.

    NetBeans Screenshot

    NetBeans Screenshot

    Scenario is same as example used in Programming 1
    Class Diagram

    Class Diagram

    "setUp()" method used to define fixture state, i.e. create object instances. Annotated with @Before to indicate runs before any test:

    @Before
    public void setUp() {
      customer1 = new Customer("Fernando", "Alonso");
      transact1 = new Transaction(200, "CREDIT", "ref1", new java.util.Date());
      transact2 = new Transaction(100, "DEBIT", "ref2",  new java.util.Date());
      transact3 = new Transaction(300, "CREDIT", "ref3", new java.util.Date());
      account1 = new Account(customer1, "12345");
      account1.addTransaction(transact1);
      account1.addTransaction(transact2);
      account1.addTransaction(transact3);        
    } 
    /**
     * Test of getAccountNumber method, of class Account.
     */
    @Test
    public void testGetAccountNumber() {
      System.out.println("getAccountNumber");
      String expResult = "12345";
      String result = account1.getAccountNumber();
      assertEquals(expResult, result);
    }
    /**
      * Test of addTransaction method, of class Account.
     */
    @Test
    public void testAddTransaction() {
      System.out.println("addTransaction");
      Transaction newTransaction = new Transaction(200.0, "CREDIT", "test", new java.util.Date());
      account1.addTransaction(newTransaction);
      assertEquals(4, account1.getNumberOfTransactions());
      assertEquals(600.00, account1.getBalance(), 0.1);
      assertEquals("test", account1.getTransactions()[3].getReference());
    }
    

    JUnit can run test classes and NetBeans displays testing window with results of each test.

    NetBeans Screenshot

    Above the "testAddTransaction()" & "testGetBalance()" methods fail as the balance value is not being calculated properly. Correcting the updateBalance() method and re-running tests provides success:

    NetBeans Screenshot
  • 10.2 Software Configuration Management (SCM)

    SCM involves tracking and controlling changes to software, including initial development, updates and releases. SCM includes revision control (version control) and ensures processes and standards adhered to. SCM facilitate team working and tracks changes and personnel, consequently, if something goes wrong it allows us to determine what was changed and by whom.

    Version Control

    Version control (aka revision control and source control) facilitates the management of, and changes to, software components, documentation and collections of information. Revisions are identified by number or code. Each new revision must be documented so that bugs can be tracked down and software reverted to a previous version and we must ensure team members/other software are using correct version. Version Control Software can be used to documents versions and changes.

    Major changes create to components result in new versions. Version control can be used during development to

    Resolve bugs
    branches
    explore alternative solutions
    trunk

    Different teams can be working on (different) bug resolution and on new feature additions. Good developers have always maintained different versions when resolving bugs and adding new features. However, this requires a great deal of discipline as it is often error prone and communication between team members can be difficult.

    Version Control Software can be used to provide (partial) automation, e.g. GIT, CVS, Subversion, Mercurial, ClearCase.

    GIT Screenshot

    GIT Screenshot

    Initially designed & developed in 2005 for Linux kernel development by Linus Torvalds.

    Git Support in NetBeans

    NetBeans allows you to create a Git repository and track versions and changes:

    GIT Screenshot

    .git folder created

    GIT Screenshot

    GIT Screenshot

    Recall earlier we refactored a Customer class to convert an expression into a field, git keeps track of version changes:

    GIT Screenshot

    You can revert to a previous version:

    GIT Screenshot
  • 10.3 Concurrency

    Java supports software which aims to carry out more than one task ‘simultaneously’ (Concurrency), e.g. a word processor should be able to handle user interaction while formatting text, saving documents etc. Concurrent programming develops software which can create different processes/threads which run at the same time (mainly threads). Single processor systems use time slicing to share processing time between threads and processes, whereas multiple processor systems where are becoming common can use threading (sending different operation to different processors). Java has the following packages which support concurrency: java.util.concurrent.

    Processes

    A process is a self-contained unit of execution which has its own memory and allocated run-time resources. A program/application may be executed in a single process or involve cooperating processes. IPC (Inter Process Communication) is facilitated through sockets and pipes. Most Java Virtual Machine implementations run as single process.

    Thread

    A thread is a lightweight process that uses less resources than a process; a process has at least one thread. Share process’s resources including memory & open files which ensures efficient communication between threads but problems can arise with this sharing. Java applications have at least one thread know as the main thread, however “system” threads such as memory management are also used. Thread classes are instantiated each time application wants to initiate an asynchronous task - a task which works away on its own and isn’t dependent on other task’s output. Alternatively, abstract thread management is achieved by passing an application task to an executor - an object which executes a task but isn’t concerned with how the task is initiated.

    "Runnable" interface defines a single method run which can contain the code to be executed in a thread; a "Runnable" object is passed to a "Thread" constructor and a start() method is invoked.

    public class HelloRunnable implements Runnable {
      public void run() { 
        System.out.println("Hello from a thread!"); 
      } 
      public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start(); 
      } 
    } 
    

    Alternatively, a "Thread" class can implement Runnable (note run() method does nothing until it is implemented) and we can create a subclass and implement the run() method:

    public class HelloThread extends Thread {
      public void run() {
        System.out.println("Hello from a thread!"); 
      }
    
      public static void main(String args[]) {
        (new HelloThread()).start(); 
      }
    }
    

    The "sleep()" method used to suspend execution for a specified period (milliseconds or nanoseconds). This allows processor time to be allocated to other threads/processes and can be used to slow a process down for synchronising with other threads.

    The "interrupt()" method used to send a signal to a thread that it should stop what it is doing and take alternate action, often an interrupted thread will terminate.

    if (Thread.interrupted()) {
       throw new InterruptedException();
    }
    

    The "join()" method tells a thread to wait for another one to complete; "t.join();". Join will make the current thread suspend execution until thread "t" completes.

    Running two threads

    A simple example of running two thread would be running the main thread (thread one) that every Java application has. The main thread then creates a new thread (thread two) from a "Runnable" object which is send to the message loop which holds a list of messages to be dispatched, and waits for it to finish. If MessageLoop thread exceeds a specified time, main thread interrupts it.

    For example here the MessageLoop thread prints out a series of messages, if interrupted before it completes, prints a message and exits:

    public static void main(String args[])
           throws InterruptedException {
      // Delay, in milliseconds before
      // we interrupt MessageLoop 
      // thread (default one hour).
      long patience = 1000 * 60 * 60;
    threadMessage("Starting MessageLoop thread");
    long startTime = System.currentTimeMillis();
    Thread t = new Thread(new MessageLoop());
    t.start();
    threadMessage(
       "Waiting for MessageLoop thread to finish");
    // loop until MessageLoop thread exits
    while (t.isAlive()) {
      threadMessage("Still waiting...");
      // Wait maximum of 1 second for MessageLoop thread
      // to finish.
      t.join(1000);
      if (((System.currentTimeMillis() - startTime)
             > patience) && t.isAlive()) {
        threadMessage("Tired of waiting!");
        t.interrupt();
        // Shouldn't be long now -- wait indefinitely
        t.join();
      } 
    } 
    threadMessage("Finally!");
    private static class MessageLoop implements Runnable {
      public void run() {
        String importantInfo[] = 
          {"Mares eat oats", "Does eat oats", "Little lambs eat ivy",
           "A kid will eat ivy too"}; 
        try {
          for (int i = 0; i < importantInfo.length; i++) {
            // Pause for 4 seconds
            Thread.sleep(4000);
            // Print a message
            threadMessage(importantInfo[i]);
          } 
        } catch (InterruptedException e) {
          threadMessage("I wasn't done!");
        } 
      } 
    } 
    // Display a message, preceded by
    // the name of the current thread
    static void
      threadMessage(String message) {
      String threadName =
        Thread.currentThread().getName();
      System.out.format("%s: %s%n",
                        threadName, message);
    } 
    

    Code Running Screenshot

    Synchronization

    Threads communicate mainly through sharing access to fields and objects which makes them extremely efficient. When threading there are two kinds of errors possible: thread interference and memory consistency errors. Synchronization can introduce thread contention which occurs when two or more threads try to access same resource simultaneously. This causes the Java runtime to execute one or more threads more slowly, or even suspend their execution. Starvation and livelock are forms of thread contention