Part 6

Objects on a list and a list as part of an object

  • Objects that contain list
public class Stack {
    private ArrayList<String> hola;
    
    public Stack(){
        this.hola = new ArrayList<>();
    }
    public boolean isEmpty(){
        return this.hola.isEmpty();
    }
    public void add(String value){
        this.hola.add(value);
        
    }
    public ArrayList<String> values(){
        return this.hola;
    }
    public void addMeal(String meal){
        if(!this.meals.contains(meal)){
            this.meals.add(meal);
        }
    }
    public String take(){
        return this.hola.remove(this.hola.size()-1);
    }
}
  • V.good StringBuilder:
import java.text.MessageFormat;

    public String toString() {
        if (elements.isEmpty()) {
            return MessageFormat.format("The collection {0} is empty.", name);
        }

        final StringBuilder elementsStrings = new StringBuilder();
        final String elementSingularPlural = elements.size() > 1 ? "elements" : "element";
        final String amountOfElements = MessageFormat.format("The collection {0} has {1} {2}:\n", name, elements.size(), elementSingularPlural);
        for (String element : elements) {
            elementsStrings.append(element);
            if (elements.size() > 1) {
                elementsStrings.append("\n");
            }
        }

        return amountOfElements + elementsStrings;
    }
  • V.good usage of stream to iterate list
// public int totalWeight(){
//     int count =0;
//     for(Gift i:list){
//         count += i.getWeight();
//     }
//     return count;
// }
public int totalWeight() {
    return list.stream()
    // <Class name>::<method name>
            .mapToInt(Gift::getWeight)
            .sum();
}
public Person shortest(){
    if (isEmpty()) {
        return null;
    }
    Person shortPeep = new Person("", Integer.MAX_VALUE);
    for(Person i:list){
        if(i.getHeight()<shortPeep.getHeight()){
            shortPeep = i;
        }
    }
    return shortPeep;
}
  • Exercises with ArrayList as part of object and ArrayList methods on objects

Separating the user interface from program logic

  • Creating class UserInterface:
public class UserInterface {
    private Scanner scanner;

    public UserInterface(Scanner scanner) {
        this.scanner = scanner;
    }

    public void start() {

        while (true) {
            System.out.print("Enter a word: ");
            String word = scanner.nextLine();

            if (*stop condition*) {
                break;
            }

        }

        System.out.println("You gave the same word twice!");
    }
}

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    UserInterface userInterface = new UserInterface(scanner);
    userInterface.start();
}
  • We should separate a distinct concept into a class of its own via abstraction, and all the “dirty details” should be encapsulated neatly inside an object like class ‘WordSet’ “encapsulates” an ArrayList.
import java.util.ArrayList;

public class WordSet {
    private ArrayList<String> words

    public WordSet() {
        this.words = new ArrayList<>();
    }

    public void add(String word) {
        this.words.add(word);
    }

    public boolean contains(String word) {
        return this.words.contains(word);
    }
}
public class UserInterface {
    private WordSet wordSet;
    private Scanner scanner;

    public userInterface(WordSet wordSet, Scanner scanner) {
        this.wordSet = wordSet;
        this.scanner = scanner;
    }

    public void start() {

        while (true) {
            System.out.print("Enter a word: ");
            String word = scanner.nextLine();

            if (this.wordSet.contains(word)) {
                break;
            }

            this.wordSet.add(word);
        }

        System.out.println("You gave the same word twice!");
    }
}
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    WordSet set = new WordSet();

    UserInterface userInterface = new UserInterface(set, scanner);
    userInterface.start();
}
  • Changes made inside the class WordSet don’t affect the class UserInterface (as long as names of methods that class user interface uses). This is because the user interface uses WordSet through the methods that it provides (public interfaces)

  • If we want to augment the program to include new functionalities by class Wordset, user interface remains clean because method is implemented in Wordset object.

  • Programming tips: Try to always separate the program into several sub-problems and work on only one sub-problem at a time and do only one thing inside one method.

  • Good UI example:

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class TextUI {

    private final Scanner scanner;
    private final SimpleDictionary simpleDictionary;

    public TextUI(Scanner scanner, SimpleDictionary simpleDictionary) {
        this.scanner = scanner;
        this.simpleDictionary = simpleDictionary;
    }

    public void start() {
        final List<String> validCommands = new ArrayList<>();
        validCommands.add("end");
        validCommands.add("add");
        validCommands.add("search");

        while (true) {
            System.out.print("Command: ");
            String input = scanner.nextLine();
            if (!validCommands.contains(input)) {
                System.out.println("Unknown command");
            }

            if ("end".equalsIgnoreCase(input)) {
                System.out.println("Bye bye!");
                break;
            }

            if ("add".equalsIgnoreCase(input)) {
                System.out.print("Word: ");
                final String word = scanner.nextLine();

                System.out.print("Translation: ");

                final String translation = scanner.nextLine();
                simpleDictionary.add(word, translation);
            }

            if ("search".equalsIgnoreCase(input)) {
                System.out.print("To be translated: ");
                final String wordToTranslate = scanner.nextLine();
                final String translation = simpleDictionary.translate(wordToTranslate);
                if (translation == null) {
                    System.out.print(MessageFormat.format("Word {0} was not found", wordToTranslate));
                } else {
                    System.out.println(translation);
                }
            }

        }
    }
}
import java.util.HashMap;

public class SimpleDictionary {

    private HashMap<String, String> translations;

    public SimpleDictionary() {
        this.translations = new HashMap<>();
    }

    public String translate(String word) {
        return this.translations.get(word);
    }

    public void add(String word, String translation) {
        this.translations.put(word, translation);
    }
}

Introduction to testing

  • Verify that all variables you are using are initialized. If they aren’t, a NullPointerException error will occur.

  • Passing Test Input to Scanner - instead of manually testing program with input, can automate the passing of input like this: Passing a string to the constructor of the Scanner class replaces input read from the keyboard. As such, the content of the string variable input ‘simulates’ user input. A line break in the input is marked with \n. Therefore, each part ending in an newline character in a given string input corresponds to one input given to the nextLine() command.

String input = "one\n" + "two\n"  +
                "three\n" + "four\n" +
                "five\n" + "one\n"  +
                "six\n";

Scanner reader = new Scanner(input);

ArrayList<String> read = new ArrayList<>();

while (true) {
    System.out.println("Enter an input: ");
    String line = reader.nextLine();
    if (read.contains(line)) {
        break;
    }

    read.add(line);
}

System.out.println("Thank you!");

if (read.contains("six")) {
    System.out.println("A value that should not have been added to the group was added to it.");
}
  • Automated testing method is good but limited in large programs. Unit testing is better. Unit testing is the testing of individual components in the source code, such as classes and their provided methods. The writing of tests reveals whether each class and method observes or deviates from the guideline of each method and class having a single, clear responsibility. The more responsibility the method has, the more complex the test. If a large application is written in a single method, writing tests for it becomes very challenging. So if application is broken into clear classes and methods, then writing tests is straightforward.

  • Unit test writing begins by creating a test class, which is created under the Test-Packages folder. When testing the Calculator class, the test class is to be called CalculatorTest. The string Test at the end of the name tells the programming environment that this is a test class. Without the string Test, the tests in the class will not be executed.

  • Tests are methods of the test class where each test tests an individual unit. Creating a test method that confirms that the newly created calculator’s value is intially 0:

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void calculatorInitialValueZero() {
        Calculator calculator = new Calculator();
        assertEquals(0, calculator.getValue());
    }

    @Test
    public void valueMinusTwoWhenTwoSubstracted() {
        Calculator calculator = new Calculator();
        calculator.subtract(2);
        assertEquals(-2, calculator.getValue());
    }
}
  • In the calculatorInitialValueZero method, calculator object is first created. The assertEquals method provided by the JUnit test framework is then used to check the value. The method is imported from the Assert class with the import Static command, and it’s given the expected value as a parameter - 0 in this instance - and the value returned by the calculator. If the values of the assertEquals method values ​​differ, the test will not pass. Each test method should have an “annotation” @ Test. This tells the JUnit test framework that this is an executable test method.

  • Test-driven development: a software development process that’s based on constructing a piece of software in small iterations. The first thing a programmer always does is write an automatically-executable test, which tests a single piece of the computer program.

  • TDD 1) Write a test for a functionality 2) Run the test 3) Write functionality that meets the test’s requirements 4) Run the test 5) Refactor (clean code while maintaining functionality of program, cleaning is improving readability and dividing program into smaller methods and classes)

  • We annotate the ‘initialize’ method with @Before, which guides the program to execute this method before each test (@Test)

  • Unit testing is only a part of software testing. On top of unit testing, a developer also performs integration tests that examine the interoperability of components, such as classes, and interface tests that test the application’s interface through elements provided by the interface, such as buttons.

Strings

  • The core of Dikstra’s message is, that the problem areas of a program must be separated from each other — this is exactly what we have been doing with object-oriented programming and by separating the UI from the program logic. Each problem area has been separated into its own part.

  • When you write a software module, you want to make sure that when changes are requested, those changes can only originate from a single person, or rather, a single tightly coupled group of people representing a single narrowly defined business function. You want to isolate your modules from the complexities of the organization as a whole, and design your systems such that each module is responsible (responds to) the needs of just that one business function.

  • [..in other words..] Gather together the things that change for the same reasons. Separate those things that change for different reasons.