This site is from a past semester! The current version will be here when the new semester starts.

Week 8 [Mon, Mar 6th] - Topics

Last week, you learned intermediate-level class diagram notation. But the quiz only focused on interpreting such diagrams. This week, we cover the aspect of drawing intermediate-level class diagrams to match code. Here are what you can do:
1. Do Part 1 of this week's quiz (which covers the same area).
2. Watch the worked examples in the following videos to learn the process of drawing intermediate-level class diagrams to match code.

Video Drawing class/object diagrams (intermediate) - Action, Task, History

Video Drawing class/object diagrams (intermediate) - Person, Inbox, Message

Video Drawing class/object diagrams (intermediate) - Person, Project, Task

Detailed Table of Contents



Guidance for the item(s) below:

This week, we learn a few design principles that you can try to apply in your project.

These principles build on top of the design fundamentals you learned earlier (i.e., abstraction, coupling, cohesion).

[W8.1] Design Principles

W8.1a

Principles → Separation of concerns principle

Video

Can explain separation of concerns principle

Separation of concerns principle (SoC): To achieve better modularity, separate the code into distinct sections, such that each section addresses a separate concern. -- Proposed by Edsger W. Dijkstra

A concern in this context is a set of information that affects the code of a computer program.

Examples for concerns:

  • A specific feature, such as the code related to the add employee feature
  • A specific aspect, such as the code related to persistence or security
  • A specific entity, such as the code related to the Employee entity

Applying reduces functional overlaps among code sections and also limits the ripple effect when changes are introduced to a specific part of the system.

If the code related to persistence is separated from the code related to security, a change to how the data are persisted will not need changes to how the security is implemented.

This principle can be applied at the class level, as well as at higher levels.

The n-tier architecture utilizes this principle. Each layer in the architecture has a well-defined functionality that has no functional overlap with each other.

This principle should lead to higher cohesion and lower coupling.

Exercises



W8.1b

Principles → Single responsibility principle

Video

Can explain single responsibility principle

Single responsibility principle (SRP): A class should have one, and only one, reason to change. -- Robert C. Martin

If a class has only one responsibility, it needs to change only when there is a change to that responsibility.

Consider a TextUi class that does parsing of the user commands as well as interacting with the user. That class needs to change when the formatting of the UI changes as well as when the syntax of the user command changes. Hence, such a class does not follow the SRP.

Gather together the things that change for the same reasons. Separate those things that change for different reasons. ―- Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin

Resources



W8.1c : OPTIONAL

Principles → Liskov substitution principle


W8.1d : OPTIONAL

Principles → Open-closed principle


W8.1e : OPTIONAL

Principles → Law of Demeter


W8.1f : OPTIONAL

Principles → Interface segregation principle


W8.1g : OPTIONAL

Principles → Dependency inversion principle


W8.1h : OPTIONAL

Principles → SOLID principles


W8.1i : OPTIONAL

Principles → YAGNI principle


W8.1j : OPTIONAL

Principles → DRY principle


W8.1k : OPTIONAL

Principles → Brooks' law



[W8.2] Architecture : OPTIONAL

Video


Architecture: Overview

Video

W8.2a : OPTIONAL

Design → Architecture → Introduction → What


W8.2b : OPTIONAL

Design → Architecture → Architecture Diagrams → Reading


W8.2c : OPTIONAL

Design → Architecture → Architecture Diagrams → Drawing


W8.2d : OPTIONAL

Design → Introduction → Multi-level design



Architecture: Styles

Video

W8.2e : OPTIONAL

Design → Architecture → Styles → What


W8.2f : OPTIONAL

Design → Architecture → Styles → n-Tier Style → What


W8.2g : OPTIONAL

Design → Architecture → Styles → Client-Server Style → What


W8.2h : OPTIONAL

Design → Architecture → Styles → Event-Driven Style → What


W8.2i : OPTIONAL

Design → Architecture → Styles → Transaction Processing Style → What


W8.2j : OPTIONAL

Design → Architecture → Styles → Service-Oriented Style → What


W8.2k : OPTIONAL

Design → Architecture → Styles → Using styles


W8.2l : OPTIONAL

Design → Architecture → Styles → More styles



[W8.3] Design: High-Level View : OPTIONAL

Video

W8.3a : OPTIONAL

Design → Design Approaches → Top-down and bottom-up design


W8.3b : OPTIONAL

Design Approaches → Agile Design → Agile design



Guidance for the item(s) below:

As you add more and more Java classes to your project, keeping all those classes in the same directory becomes untenable. The solution is covered in the next topic.

[W8.4] Java: Packages

W8.4a

C++ to Java → Miscellaneous Topics → Packages

Video

Can use Java packages

You can organize your types (i.e., classes, interfaces, enumerations, etc.) into packages for easier management (among other benefits).

To create a package, you put a package statement at the very top of every source file in that package. The package statement must be the first line in the source file and there can be no more than one package statement in each source file. Furthermore, the package of a type should match the folder path of the source file. Similarly, the compiler will put the .class files in a folder structure that matches the package names.

The Formatter class below (in <source folder>/seedu/tojava/util/Formatter.java file) is in the package seedu.tojava.util. When it is compiled, the Formatter.class file will be in the location <compiler output folder>/seedu/tojava/util:

package seedu.tojava.util;

public class Formatter {
    public static final String PREFIX = ">>";

    public static String format(String s){
        return PREFIX + s;
    }
}

Package names are written in all lower case (not camelCase), using the dot as a separator. Packages in the Java language itself begin with java. or javax. Companies use their reversed Internet domain name to begin their package names.

For example, com.foobar.doohickey.util can be the name of a package created by a company with a domain name foobar.com

To use a public from outside its package, you must do one of the following:

  1. Use the to refer to the member
  2. Import the package or the specific package member

The Main class below has two import statements:

  • import seedu.tojava.util.StringParser: imports the class StringParser in the seedu.tojava.util package
  • import seedu.tojava.frontend.*: imports all the classes in the seedu.tojava.frontend package
package seedu.tojava;

import seedu.tojava.util.StringParser;
import seedu.tojava.frontend.*;

public class Main {

    public static void main(String[] args) {

        // Using the fully qualified name to access the Processor class
        String status = seedu.tojava.logic.Processor.getStatus();

        // Using the StringParser previously imported
        StringParser sp = new StringParser();

        // Using classes from the tojava.frontend package
        Ui ui = new Ui();
        Message m = new Message();

    }
}

Note how the class can still use the Processor without importing it first, by using its fully qualified name seedu.tojava.logic.Processor

Importing a package does not import its sub-packages, as packages do not behave as hierarchies despite appearances.

import seedu.tojava.frontend.* does not import the classes in the sub-package seedu.tojava.frontend.widget.

If you do not use a package statement, your type doesn't have a package -- a practice not recommended (except for small code examples) as it is not possible for a type in a package to import a type that is not in a package.

Optionally, a static import can be used to import static members of a type so that the imported members can be used without specifying the type name.

The class below uses static imports to import the constant PREFIX and the method format() from the seedu.tojava.util.Formatter class.

import static seedu.tojava.util.Formatter.PREFIX;
import static seedu.tojava.util.Formatter.format;

public class Main {

    public static void main(String[] args) {

        String formatted = format("Hello");
        boolean isFormatted = formatted.startsWith(PREFIX);
        System.out.println(formatted);
    }
}

Formatter class


Note how the class can use PREFIX and format() (instead of Formatter.PREFIX and Formatter.format()).

When using the commandline to compile/run Java, you should take the package into account.

If the seedu.tojava.Main class is defined in the file Main.java,

  • when compiling from the <source folder>, the command is:
    javac seedu/tojava/Main.java
  • when running it from the <compiler output folder>, the command is:
    java seedu.tojava.Main

Resources




Guidance for the item(s) below:

As the size of your Java code base grows, every class being able to access every member of every other class can be problematic. Hence, there should be a way to control the access to our Java classes and their members. The solution is given in the topic below.

[W8.5] Java: Access Modifiers

W8.5a

C++ to Java → Miscellaneous Topics → Access modifiers

Can explain access modifiers

Access level modifiers determine whether other classes can use a particular field or invoke a particular method.

There are two levels of access control:

  1. At the class level:

    • public: the class is visible to all classes everywhere
    • no modifier (the default, also known as package-private): it is visible only within its own package

  2. At the member level:

    • public or no modifier (package-private): same meaning as when used with top-level classes
    • private: the member can only be accessed in its own class
    • protected: the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package

The following table shows the access to members permitted by each modifier.

Modifier
public
protected
no modifier
private

Access levels affect you in two ways:

  1. When you use classes that come from another source, such as the classes in the Java platform, access levels determine which members of those classes your own classes can use.
  2. When you write a class, you need to decide what access level every member variable and every method in your class should have.


Guidance for the item(s) below:

As you know, adding comments to the code is a good practice. Let's learn about a specific type of comments that you can use in Java code that can do even more than just explain the code.

[W8.6] Java: JavaDocs

W8.6a

Implementation → Documentation → Tools → JavaDoc → What

Video

Can explain JavaDoc

JavaDoc is a tool for generating API documentation in HTML format from comments in the source code. In addition, modern IDEs use JavaDoc comments to generate explanatory tooltips.

An example method header comment in JavaDoc format (adapted from Oracle's Java documentation)

/**
 * Returns an Image object that can then be painted on the screen.
 * The url argument must specify an absolute {@link URL}. The name
 * argument is a specifier that is relative to the url argument.
 * <p>
 * This method always returns immediately, whether or not the
 * image exists. When this applet attempts to draw the image on
 * the screen, the data will be loaded. The graphics primitives
 * that draw the image will incrementally paint on the screen.
 *
 * @param url an absolute URL giving the base location of the image
 * @param name the location of the image, relative to the url argument
 * @return the image at the specified URL
 * @see Image
 */
public Image getImage(URL url, String name) {
    try {
        return getImage(new URL(url, name));
    } catch (MalformedURLException e) {
        return null;
    }
}

Generated HTML documentation:

Tooltip generated by Intellij IDE:


W8.6b

Implementation → Documentation → Tools → JavaDoc → How

Can write JavaDoc comments

In the absence of more extensive guidelines (e.g., given in a coding standard adopted by your project), you can follow the two examples below in your code.

A minimal JavaDoc comment example for methods:

/**
 * Returns lateral location of the specified position.
 * If the position is unset, NaN is returned.
 *
 * @param x X coordinate of position.
 * @param y Y coordinate of position.
 * @param zone Zone of position.
 * @return Lateral location.
 * @throws IllegalArgumentException If zone is <= 0.
 */
public double computeLocation(double x, double y, int zone)
    throws IllegalArgumentException {
    // ...
}

A minimal JavaDoc comment example for classes:

package ...

import ...

/**
 * Represents a location in a 2D space. A <code>Point</code> object corresponds to
 * a coordinate represented by two integers e.g., <code>3,6</code>
 */
public class Point {
    // ...
}

Resources



Guidance for the item(s) below:

Let's learn about a few more Git techniques, starting with branching. Although these techniques are not really needed for the iP, we force you to use them in the iP so that you have more time to practice them before they are really needed in the tP.

[W8.7] RCS: Branching

W8.7a

Project Management → Revision Control → Branching

Video

Can explain branching

Branching is the process of evolving multiple versions of the software in parallel. For example, one team member can create a new branch and add an experimental feature to it while the rest of the team keeps working on another branch. Branches can be given names e.g. master, release, dev.

A branch can be merged into another branch. Merging usually results in a new commit that represents the changes done in the branch being merged.

Branching and merging

Merge conflicts happen when you try to merge two branches that had changed the same part of the code and the RCS cannot decide which changes to keep. In those cases, you have to ‘resolve’ the conflicts manually.

Exercises



W8.7b

Tools → Git and GitHub → branch: Doing multiple parallel changes

Video

Project Management → Revision Control → Branching


Can use Git branching

Git supports branching, which allows you to do multiple parallel changes to the content of a repository.

A Git branch is simply a named label pointing to a commit. The HEAD label indicates which branch you are on. Git creates a branch named master by default. When you add a commit, it goes into the branch you are currently on, and the branch label (together with the HEAD label) moves to the new commit.

Given below is an illustration of how branch labels move as branches evolve.

  1. There is only one branch (i.e., master) and there is only one commit on it.
  2. A new commit has been added. The master and the HEAD labels have moved to the new commit.
  3. A new branch fix1 has been added. The repo has switched to the new branch too (hence, the HEAD label is attached to the fix1 branch).
  4. A new commit (c) has been added. The current branch label fix1 moves to the new commit, together with the HEAD label.
  5. The repo has switched back to the master branch.
  1. A new commit (d) has been added. The master label has moved to that commit.
  2. The repo has switched back to the fix1 branch and added a new commit (e) to it.
  3. The repo has switched to the master branch and the fix1 branch has been merged into the master branch, creating a merge commit f. The repo is currently on the master branch.

Follow the steps below to learn how to work with branches. You can use any repo you have on your computer (e.g. a clone of the samplerepo-things) for this.

0. Observe that you are normally in the branch called master.


git status

on branch master

1. Start a branch named feature1 and switch to the new branch.

Click on the Branch button on the main menu. In the next dialog, enter the branch name and click Create Branch.

Note how the feature1 is indicated as the current branch.


You can use the branch command to create a new branch and the checkout command to switch to a specific branch.

git branch feature1
git checkout feature1

One-step shortcut to create a branch and switch to it at the same time:

git checkout –b feature1

2. Create some commits in the new branch. Just commit as per normal. Commits you add while on a certain branch will become part of that branch.
Note how the master label and the HEAD label moves to the new commit (The HEAD label of the local repo is represented as in SourceTree).

3. Switch to the master branch. Note how the changes you did in the feature1 branch are no longer in the working directory.

Double-click the master branch.


git checkout master

4. Add a commit to the master branch. Let’s imagine it’s a bug fix.
To keep things simple for the time being, this commit should not involve the same content that you changed in the feature1 branch. To be on the safe side, this commit can change an entirely different file.

5. Switch back to the feature1 branch (similar to step 3).

6. Merge the master branch to the feature1 branch, giving an end-result like the following. Also note how Git has created a merge commit.

Right-click on the master branch and choose merge master into the current branch. Click OK in the next dialog.


git merge master

The objective of that merge was to sync the feature1 branch with the master branch. Observe how the changes you did in the master branch (i.e. the imaginary bug fix) is now available even when you are in the feature1 branch.

Instead of merging master to feature1, an alternative is to rebase the feature1 branch. However, rebasing is an advanced feature that requires modifying past commits. If you modify past commits that have been pushed to a remote repository, you'll have to force-push the modified commit to the remote repo in order to update the commits in it.

7. Add another commit to the feature1 branch.

8. Switch to the master branch and add one more commit.

9. Merge feature1 to the master branch, giving and end-result like this:

Right-click on the feature1 branch and choose Merge....


git merge feature1

10. Create a new branch called add-countries, switch to it, and add some commits to it (similar to steps 1-2 above). You should have something like this now:

Avoid this common rookie mistake!

Always remember to switch back to the master branch before creating a new branch. If not, your new branch will be created on top of the current branch.

11. Go back to the master branch and merge the add-countries branch onto the master branch (similar to steps 8-9 above). While you might expect to see something like the following,

... you are likely to see something like this instead:

That is because Git does a fast forward merge if possible. Seeing that the master branch has not changed since you started the add-countries branch, Git has decided it is simpler to just put the commits of the add-countries branch in front of the master branch, without going into the trouble of creating an extra merge commit.

It is possible to force Git to create a merge commit even if fast forwarding is possible.

Tick the box shown below when you merge a branch:


Use the --no-ff switch (short for no fast forward):

git merge --no-ff add-countries

Pushing a branch to a remote repo

Here's how to push a branch to a remote repo:

Here's how to push a branch named add-intro to your own fork of a repo named samplerepo-pr-practice:


Normally: git push {remote repository} {branch}. Examples:

  • git push origin master pushes the master branch to the repo named origin (i.e., the repo you cloned from)
  • git push upstream-repo add-intro pushes the add-intro branch to the repo named upstream-repo

If pushing a branch you created locally to the remote for the first time, add the -u flag to get the local branch to track the new upstream branch:
e.g., git push -u origin add-intro

See git-scm.com/docs/git-push for details of the push command.



W8.7c

Tools → Git and GitHub → Dealing with merge conflicts

Video

Can use Git to resolve merge conflicts

Merge conflicts happen when you try to combine two incompatible versions (e.g., merging a branch to another but each branch changed the same part of the code in a different way).

Here are the steps to simulate a merge conflict and use it to learn how to resolve merge conflicts.

0. Create an empty repo or clone an existing repo, to be used for this activity.

1. Start a branch named fix1 in the repo. Create a commit that adds a line with some text to one of the files.

2. Switch back to master branch. Create a commit with a conflicting change i.e. it adds a line with some different text in the exact location the previous line was added.

3. Try to merge the fix1 branch onto the master branch. Git will pause mid-way during the merge and report a merge conflict. If you open the conflicted file, you will see something like this:

COLORS
------
blue
<<<<<< HEAD
black
=======
green
>>>>>> fix1
red
white

4. Observe how the conflicted part is marked between a line starting with <<<<<< and a line starting with >>>>>>, separated by another line starting with =======.

Highlighted below is the conflicting part that is coming from the master branch:

blue
<<<<<< HEAD
black
=======
green
>>>>>> fix1
red

This is the conflicting part that is coming from the fix1 branch:

blue
<<<<<< HEAD
black
=======
green
>>>>>> fix1
red

5. Resolve the conflict by editing the file. Let us assume you want to keep both lines in the merged version. You can modify the file to be like this:

COLORS
------
blue
black
green
red
white

6. Stage the changes, and commit.



Guidance for the item(s) below:

Given below are some very basic tools and techniques that are often used in planning, scheduling, and tracking projects.

[W8.8] Project Mgt: Scheduling and Tracking

Video

W8.8a

Project Management → Project Planning → Milestones

Can explain milestones

A milestone is the end of a stage which indicates significant progress. You should take into account dependencies and priorities when deciding on the features to be delivered at a certain milestone.

Each intermediate product release is a milestone.

In some projects, it is not practical to have a very detailed plan for the whole project due to the uncertainty and unavailability of required information. In such cases, you can use a high-level plan for the whole project and a detailed plan for the next few milestones.

Milestones for the Minesweeper project, iteration 1

Day Milestones
Day 1 Architecture skeleton completed
Day 3 ‘new game’ feature implemented
Day 4 ‘new game’ feature tested

W8.8b

Project Management → Project Planning → Buffers

Can explain buffers

A buffer is time set aside to absorb any unforeseen delays. It is very important to include buffers in a software project schedule because effort/time estimations for software development are notoriously hard. However, do not inflate task estimates to create hidden buffers; have explicit buffers instead. Reason: With explicit buffers, it is easier to detect incorrect effort estimates which can serve as feedback to improve future effort estimates.


W8.8c

Project Management → Project Planning → Issue trackers

Can explain issue trackers

Keeping track of project tasks (who is doing what, which tasks are ongoing, which tasks are done etc.) is an essential part of project management. In small projects, it may be possible to keep track of tasks using simple tools such as online spreadsheets or general-purpose/light-weight task tracking tools such as Trello. Bigger projects need more sophisticated task tracking tools.

Issue trackers (sometimes called bug trackers) are commonly used to track task assignment and progress. Most online project management software such as GitHub, SourceForge, and BitBucket come with an integrated issue tracker.

A screenshot from the Jira Issue tracker software (Jira is part of the BitBucket project management tool suite):


W8.8d

Project Management → Project Planning → Work breakdown structure

Can explain work breakdown structures

A Work Breakdown Structure (WBS) depicts information about tasks and their details in terms of subtasks. When managing projects, it is useful to divide the total work into smaller, well-defined units. Relatively complex tasks can be further split into subtasks. In complex projects, a WBS can also include prerequisite tasks and effort estimates for each task.

The high level tasks for a single iteration of a small project could look like the following:

Task ID Task Estimated Effort Prerequisite Task
A Analysis 1 man day -
B Design 2 man day A
C Implementation 4.5 man day B
D Testing 1 man day C
E Planning for next version 1 man day D

The effort is traditionally measured in man hour/day/month i.e. work that can be done by one person in one hour/day/month. The Task ID is a label for easy reference to a task. Simple labeling is suitable for a small project, while a more informative labeling system can be adopted for bigger projects.

An example WBS for a game development project.

Task ID Task Estimated Effort Prerequisite Task
A High level design 1 man day -
B Detail design
  1. User Interface
  2. Game Logic
  3. Persistency Support
2 man day
  • 0.5 man day
  • 1 man day
  • 0.5 man day
A
C Implementation
  1. User Interface
  2. Game Logic
  3. Persistency Support
4.5 man day
  • 1.5 man day
  • 2 man day
  • 1 man day
  • B.1
  • B.2
  • B.3
D System Testing 1 man day C
E Planning for next version 1 man day D

All tasks should be well-defined. In particular, it should be clear as to when the task will be considered done.

Some examples of ill-defined tasks and their better-defined counterparts:

Bad Better
more coding implement component X
do research on UI testing find a suitable tool for testing the UI

Exercises



W8.8e : OPTIONAL

Project Management → Project Planning → Gantt charts


W8.8f : OPTIONAL

Project Management → Project Planning → PERT charts



Last week, you learned intermediate-level class diagram notation. But the quiz only focused on interpreting such diagrams. This week, we cover the aspect of drawing intermediate-level class diagrams to match code. Here are what you can do:
1. Do Part 1 of this week's quiz (which covers the same area).
2. Watch the worked examples in the following videos to learn the process of drawing intermediate-level class diagrams to match code.

Video Drawing class/object diagrams (intermediate) - Action, Task, History

Video Drawing class/object diagrams (intermediate) - Person, Inbox, Message

Video Drawing class/object diagrams (intermediate) - Person, Project, Task