10
-
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 can identify references which need to be updated and allows us to select the ones we want to update:
- 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.
-
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.
- Scenario is same as example used in Programming 1
- "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.
- 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:
-
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.
- 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 folder created
- Recall earlier we refactored a Customer class to convert an expression into a field, git keeps track of version changes:
- You can revert to a previous version:
-
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); }
- 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