University of London / MSc Computer Science: Software design and programming(前半)

University of London / MSc Computer Science: Software design and programming(前半)

May 13, 2024

ロンドン大学で MSc Computer Science: Software design and programming モジュールを履修中。

講義内容に関して記録した個人的なスタディノートです。

全 12 週のうち 1〜5 週目の内容を記録します。(1 週目開始:2024 年 4 月 8 日 / 5 週目終了:2024 年 5 月 12 日)

モジュール概要 #

Aims

The main aim of the module is to provide students with the necessary skills to design software in an object-oriented way according to high-quality standards. This ranges from learning advanced concepts, designing object-oriented software using a proven methodology (such as the Unified Process), applying software design patterns and anti-patterns, the SOLID methodology and code refactoring for maintainability.

Weeks

講義は Week 10 まで。Week 11, 12 は最終課題を作成する期間。

  • Week 1: The object model: a revision
  • Week 2: Java Generics and Agile software development
  • Week 3: Dependency injection (DI) and the Java Reflection API
  • Week 4: The functional paradigm
  • Week 5: SOLID
  • Week 6: Design Patterns I
  • Week 7: Design Patterns II
  • Week 8: Graphical user interfaces
  • Week 9: Modules and versioning
  • Week 10: Concurrency

参考文書 #

Module reading list

Acronym Title
CoreJava Core Java, Volume I: Fundamentals, 12th Edition by Cay Horstmann Oracle Press December 2021
Impatient Core Java for the Impatient, 3rd Edition by Cay Horstmann, Addison-Wesley Professional, 2022
TDS Think Data Structures: Algorithms and Information Retrieval in Java by Allen B. Downey, Green Tea Press, 2016 (pdf)
DIDP Dive Into Design Patterns by Alexander Shvets, notes from the website which are free to access.
TCR Java: The Complete Reference, 12th Edition by Herbert Schildt, November 2021, McGraw-Hill (alternative main text)
GoF Design Patterns: Elements of Reusable Object-Oriented Software is a software engineering book describing software design patterns. The book’s authors are Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, affectionately known as The Gang of Four.
HFDP Head First Design Patterns, 2nd Edition by Eric Freeman, Elisabeth Robson, 2020
Refactor Refactoring: Improving the Design of Existing Code by Martin Fowler, Addison-Wesley Professional, November 2018

Week 1: The object model - a revision #

レクチャーのキーコンセプト

  • modern software development
  • revision of OOP concepts and their implementation within the Java ecosystem.

レクチャーリスト

  • Getting started with this module
    • Lecture 1: Software development in industry
    • Lecture 2: Java as a programming language
  • The object model
    • Lecture 3: Inheritance
    • Lecture 4: Polymorphism
    • Lecture 5: Encapsulation
    • Lecture 6: (screencast) Worked examples
  • Labs
    • Practical: Objects and Classes
    • Practical: Mutability
    • Practical: Encapsulation
    • Practical: Inheritance
    • Practical: Polymorphism

Lecture 1: Software development in industry #

なぜこの講義では Java を題材に扱っているのか。静的型付け言語であり、産業界でも広く使われていてコミュニティが大きいから。Java を学んだ後で必要ならば他の言語にスイッチすれば良いだけ。

Lecture 2: Java as a programming language #

A brief review of Java

// Constructors With Parameters:
// This allows us to use the java.util.Random class in our program.
import java.util.Random;

// Javadocs - Class Headers:
// All public classes should be commented on in the following format:
/**
 * Represents a canine animal
 * @author Thomas People
 * @author Chris Person
 * @version 1.0 Oct. 2 2022
 */
public class Hello {
    // Static versus Non-Static:
    // static variable is shared between all instances
    private static final String[] GREETINGS = {"Hello!", "Hi!", "W'sup!"};
    // Each instance has its own distinct non-static variable.
    private String greeting;

    // Constructors With Parameters:
    // Constructors initialize an object of a class. In this case, the constructor initializes the greeting instance variable
    private Hello() {
        Random rand = new Random();
        int greetingsIndex = rand.nextInt(GREETINGS.length);
        greeting = GREETINGS[greetingsIndex];
    }

    // Constructors With Parameters:
    // To call this constructor, provide an argument in the call to new, as in Hello h = new Hello(1);
    public Hello(int anIndex) {
        int greetingsIndex = anIndex % GREETINGS.length;
        greeting = GREETINGS[greetingsIndex];
    }

    public String getGreeting() {return greeting;}

    // Javadocs - Method Headers:
    // Method headers should be commented as follows:
    /**
     * Calculates the speed an object was travelling given the distance and time
     * @param time      How long the object is moving for
     * @param distance  How far the object moved
     * @throws ArithmeticException If the time is zero.
     * @return a double representing the speed at which the object was travelling.
     */
    public void javaDocExample() {}

    public static void main(String[] args) {
        Hello h = new Hello();
        System.out.println(h.getGreeting());
    }
}

Lecture 3: Inheritance #

(既知の内容が多かったため省略)

Lecture 4: Polymorphism #

(既知の内容が多かったため省略)

Lecture 5: Encapsulation #

(既知の内容が多かったため省略)

Lecture 6: (screencast) Worked examples #

(既知の内容が多かったため省略)

Appendix: Records come to Java #

Records are intended to be simple data carriers, a version of tuples that fits into Java’s established type system logically and consistently. This will help many applications make domain classes more transparent and smaller. It will also help teams eliminate many hand-coded implementations of the underlying pattern and reduce or remove the need for libraries like Lombok.

An Example Without Records:

public final class FXOrderClassic {
    private final int units;
    private final CurrencyPair pair;
    private final Side side;
    private final double price;
    private final LocalDateTime sentAt;
    private final int ttl;

    public FXOrderClassic(int units,
               CurrencyPair pair,
               Side side,
               double price,
               LocalDateTime sentAt,
               int ttl) {
        this.units = units;
        this.pair = pair; // CurrencyPair is a simple enum
        this.side = side; // Side is a simple enum
        this.price = price;
        this.sentAt = sentAt;
        this.ttl = ttl;
    }

    public int units() {
        return units;
    }

    public CurrencyPair pair() {
        return pair;
    }

    public Side side() {
        return side;
    }

    public double price() { return price; }

    public LocalDateTime sentAt() {
        return sentAt;
    }

    public int ttl() {
        return ttl;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass())
            return false;

        FXOrderClassic that = (FXOrderClassic) o;

        if (units != that.units) return false;
        if (Double.compare(that.price, price) != 0)
            return false;
        if (ttl != that.ttl) return false;
        if (pair != that.pair) return false;
        if (side != that.side) return false;
        return sentAt != null ?
            sentAt.equals(that.sentAt) : that.sentAt == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = units;
        result = 31 * result +
                   (pair != null ? pair.hashCode() : 0);
        result = 31 * result +
                   (side != null ? side.hashCode() : 0);
        temp = Double.doubleToLongBits(price);
        result = 31 * result +
                   (int) (temp ^ (temp >>> 32));
        result = 31 * result +
                   (sentAt != null ? sentAt.hashCode() : 0);
        result = 31 * result + ttl;
        return result;
    }

    @Override
    public String toString() {
        return "FXOrderClassic{" +
                "units=" + units +
                ", pair=" + pair +
                ", side=" + side +
                ", price=" + price +
                ", sentAt=" + sentAt +
                ", ttl=" + ttl +
                '}';
    }
}

The new concept is a record class (usually just called a record). This is an immutable (in the usual “shallow” Java sense) transparent carrier for a fixed set of values known as the record components. Each component gives rise to a final field that holds the provided value and an accessor method to retrieve the value. The field name and the accessor name match the name of the component.

public record FXOrder(
    int units,
    CurrencyPair pair,
    Side side,
    double price,
    LocalDateTime sentAt,
    int ttl
) {}

Compact Constructors

A compact constructor declaration intends that only validation and/or normalization code need be given in the body of the canonical constructor; the compiler supplies the remaining initialization code.

public record FXOrder(
    int units,
    CurrencyPair pair,
    Side side,
    double price,
    LocalDateTime sentAt,
    int ttl
) {
    public FXOrder {
        if (units < 1) {
            throw new IllegalArgumentException(
                "FXOrder units must be positive");
        }
        if (ttl < 0) {
            throw new IllegalArgumentException(
                "FXOrder TTL must be positive, or 0 for market orders");
        }
        if (price <= 0.0) {
            throw new IllegalArgumentException(
                "FXOrder price must be positive");
        }
    }
}

Alternative Constructors

It is also possible to use some static factory methods within the body of the record. You might include a static factory like this to declare a quick way to create orders with default parameters:

public record FXOrder(
    int units,
    CurrencyPair pair,
    Side side,
    double price,
    LocalDateTime sentAt,
    int ttl
) {
    public static FXOrder of(
        CurrencyPair pair,
        Side side,
        double price
    ) {
        return new FXOrder(
            1,
            pair,
            side,
            price,
            LocalDateTime.now(), 1000
        );
    }
}

Further material #

Week 2: Java Generics, and Agile software development #

レクチャーのキーコンセプト

  • Agile software development
  • testing and debugging
  • the use of parameterised types (Java Generics)
  • unit testing
  • code coverage
  • TDD.

レクチャーリスト

  • Java Generics
    • Lecture 1: Overview
    • Lecture 2: Java Generics
    • Lecture 3: Java Collections (review)
    • Lecture 4: Java Collections - Essential Algorithms
    • Lecture 5: (screencast) Examples of the Java Collections Framework
    • Lecture 6: (screencast): How to use Exercism to practice your Java
  • Agile software development
    • Lecture 7: Overview of te agile process
    • Lecture 8: An introduction to Extreme Programming
    • Lecture 9: Test-Driven and Behavioural-Driven Development
  • Labs
    • Practical: Unit testing recommendations
    • Practical: Recursion
    • Practical: Java Generics and Type Erasure

Lecture 1: Overview #

(内容省略)

Lecture 2: Java Generics #

// No Generics
private void printList(List list) {
    for (Iterator i = list.iterator(); i.hasNext();) {
        System.out.println(i.next());
    }
}

// Generics
private void printListofString(List<?> list) {
    for (Iterator<?> i = list.iterator(); i.hasNext();) {
        System.out.println(i.next());
    }
}

Lecture 3: Java Collections (review) #

(内容省略)

Lecture 4: Java Collections - Essential Algorithms #

(内容省略)

Lecture 5: (screencast) Examples of the Java Collections Framework #

(内容省略)

Lecture 6: (screencast): How to use Exercism to practice your Java #

講師おすすめの初心者向けプログラミング練習サイト。

  • Exercism | Learn, practice and get world-class mentoring in over 50 languages. 100% free.
  • https://exercism.org/

Lecture 7: Overview of te agile process #

eXtreme Programming

eXtreme Programming works on the principle of Simplicity, Communication, Feedback and Courage. The application is designed such that it is adaptive to changing requirement and is simple. Lot of client interaction and communication in team are key factors which help in development of adaptive application.

Scrum

In Scrum, a list of all the deliverables for the project, named backlog, is identified and is continuously updated. Project is divided into small units named sprint, lasting 2-4 weeks generally. Deliverable, which is visible and usable increment, is decided for each sprint. Active communication in the team and follow-up using the backlog help and ease the development.

Agile Modeling

Agile Modeling is used for modeling and documentation of software systems. It involves the software values, principle and practices which lead to software development in more flexible manner.

Adaptive Software Development (ASD)

Adaptive Software development works on the principle of continuous adaptation. It uses repeated series of speculate, collaborate and learn cycles to develop the application.

Dynamic Systems Development Methods

DSDM is based on Rapid Application Development (RAD) and can be used for projects which have tight schedule and budget. DSDM works on pareto principle-80/20 rule i.e. 80% of the business benefit comes from 20% of the system requirements. Therefore DSDM starts implementing this first 20% of system requirements to meet 80% of the business needs. And then it progresses to cover rest of requirements.

Feature Driven Development (FDD)

The main purpose of FDD is to deliver tangible, working software repeatedly in a timely manner. The development process involves 5 activities: Develop Overall Model, Build Features List, Plan by feature, Design by feature, Build by feature. Milestones are identified and progress is tracked on basis of the achievement of milestone.

Lean software development

Lean software development is based on lean manufacturing principle. The principles followed are:

  1. Eliminate waste i.e. anything that does not add value to customer.
  2. Amplify learning by doing and testing things rather than documenting them and then implementing.
  3. Decide as late as possible thus keeping all the possible the options open as long as possible in the life cycle.
  4. Deliver as fast as possible. It works on basis of the fact that sooner the end product is delivered, sooner is the feedback received and sooner it is implemented in next iteration.
  5. Empower the team by keeping people motivated.
  6. Build integrity in by keeping the code simple and avoiding repetitions.
  7. See the whole so the problems can dealt at application level, product level, instead of dealing with them at minute levels.

Agile Unified Process (AUP)

AUP is simplified version of Ration Unified Process (RUP). It distinguishes Development Release iterations from Production Release iterations. It works on building up a model, implementing, testing and deploying it and then followed by configuration management. The focus is on the covering the high-value activities rather than covering every possible thing.

Advantage and Disadvantage of Agile Methodology #

Advantages:

  • No Detail requirement needed: You don’t need to have the entire requirements finalised to start the development work. Build and Test can start as soon as initial high level requirements are available.
  • Early benefit to the user/business: Following Agile methodology gives an early view to the user about how the final product might look and behave. This helps them into finalizing the user requirements. It might also happen that the prototype delivers some of the requirements which use may want to use and evaluate till the final product is delivered with all the functionalities.
  • Face to face communication: Agile method give more emphasis on having the face to face communication between the user/customer and project team to make sure there is no room left for any kind of confusion in understanding requirement and inputs.
  • Less time to market: Using Agile method, final product is delivered to the customer in least possible time.
  • Less cost to customer: It saves cost for both customer and supplier as resources are used for less time.
  • High Quality: Since customer is involved in all the stages of software development, means the quality of final quality if high resulting in highly satisfied customer.

Disadvantages:

  • Smaller Planning Horizon: Since Agile projects have smaller planning horizon meaning that project is started without detailed planning, there is always a chance that initial project effort and cost estimation may not be correct. This might result in multiple changes to estimation in subsequent estimation.
  • Lesser design and documentation: Since build and test starts early, there is always a chance that proper designing and documentation may take a back seat.
  • Need clear customer vision: As customer input is required in all iterations, it is very necessary that customer should have clear vision of end product. Project can easily lose its direction if client have only vague idea of the product they want.
  • Necessity of experienced and senior resources: Since Agile method is more about less planning and more decision making, it is absolutely necessary to have experienced and senior resources in the team or experienced resources mentoring new resources.

Lecture 8: An introduction to Extreme Programming #

(内容省略)

Lecture 9: Test-Driven and Behavioural-Driven Development #

(内容省略)

Further material #

Week 3: Dependency injection (DI) and the Java reflection API #

レクチャーのキーコンセプト

  • dependency injection
  • design patterns
  • spring framework.

レクチャーリスト

  • Dependency injection
    • Lecture 1: Overview
    • Lecture 2: Dependency injection
    • Lecture 3: (screencast): Dependency injection example
    • Lecture 4: (screencast): The Spring framework
  • Java Reflection API
    • Lecture 5: Overview of Java reflection
    • Lecture 6: (screencast): Java reflection API
  • Labs
    • Practical: Reflection API
    • Practical: Dependency Injection

Lecture 1: Overview #

(内容省略)

Lecture 2: Dependency injection #

(内容省略)

Lecture 3: (screencast): Dependency injection example #

Benefits of using DI

  • Helps in Unit testing.
  • Boiler plate code is reduced, as initializing of dependencies is done by the injector component.
  • Extending the application becomes easier.
  • Helps to enable loose coupling, which is important in application programming.

Disadvantages of DI

  • It’s a bit complex to learn, and if overused can lead to management issues and other problems.
  • Many compile time errors are pushed to run-time.
  • Dependency injection frameworks are implemented with reflection or dynamic programming. This can hinder use of IDE automation, such as “find references”, “show call hierarchy” and safe refactoring.

Tools

Lecture 4: (screencast): The Spring framework #

Lecture 5: Overview of Java reflection #

(内容省略)

Lecture 6: (screencast): Java reflection API #

(内容省略)

Further material #

Week 4: The functional paradigm #

レクチャーのキーコンセプト

  • Java Lambdas and Streams APIs
  • the functional programming approach to software development
  • immutable data structures and the resulting implications to program design and execution.

レクチャーリスト

  • Functional programming paradigm and Java
    • Lecture 1: Getting started with functional programming
    • Lecture 2: Functions
  • Java Reflection API
    • Lecture 3: Default methods in interfaces — Single Abstract Methods
    • Lecture 4: (screencast): Java Lambdas
    • Lecture 5: (screencast): Java Streams
  • Labs
    • Practical: Lambda expressions in Java
    • Practical: Java Streams

Lecture 1: Getting started with functional programming #

(内容省略)

Lecture 2: Functions #

(内容省略)

Lecture 3: Default methods in interfaces — Single Abstract Methods #

(内容省略)

Further material #

Week 5: SOLID #

レクチャーのキーコンセプト

  • various object-oriented design principles, focusing on the SOLID and GRASP techniques.

レクチャーリスト

  • Overview
    • Lecture 1: Introduction to the SOLID principles and GRASP
    • Lecture 2: SOLID
  • Realisation in code
    • Lecture 3 (screencast): The SOLID principles
    • Lecture 4 (screencast): Project Lombok
  • Labs
    • Practical: SOLID Principles in practice

Lecture 1: Introduction to the SOLID principles and GRASP #

Design principles guide towards sound design and good coding practices. There are two main approaches, SOLID and GRASP. They are complimentary. They give us a set of guidelines on how to avoid design pitfalls.

  • Over time, software changes are due to changing requirements, maintenance, etc.
  • If there are software design problems, we use various “design principles” to determine the issues.
  • We can then use “design patterns” to address these problems.

GRASP

The GRASP (general responsibility assignment software patterns) design principle is a set of guidelines used in object-oriented software design.

  • Information expert: The principle suggests that an object responsible for a task or information should have the most information and knowledge required to perform that task.
  • Creator: This principle states that the class that creates another object should be responsible for tis initialisation, ensuring the proper setting of its attributes and handling of any exceptions.
  • Low coupling: This principle suggests that classes and objects should have minimal dependencies on each other, reducing the impact of changes in one class on other classes.
  • High cohesion: This principle recommends that classes should be designed to have a single, well-defined responsibility that encapsulates related functionality.
  • High Controller: This principle suggests that a single, centralised object should coordinate and control interactions between different objects.
  • Amongst other features…

SOLID

We will focus on SOLID because this is more general object-oriented design principles in the next lecture…

Lecture 2: SOLID #

  • Single-responsibility principle
  • Open-closed principle
  • Liskov substitution principle
  • Interface-segregation principle
  • Dependency-inversion principle

Single-responsibility principle (SRP)

A class should have only one reason to change.

  • Cohesion: how good a reason the elements of a module have to be in the same module.
  • Cohesion and SRP: the forces that cause the module to change.

Open-closed principle (OCP)

Software entities should be open for extension, but closed for modification.

  • Open for extension: the behaviour of a module can be extended with new behaviours to satisfy the changing requirements
  • Closed for modification: extending the module must not result in changes to the source, or even the binary code of the module

Liskov substitution principle (LSP)

Subtype must be substitutable for their base types.

  • Functions that refer to base classes must be able to use objects of both existing, and future, derived classes without knowing it.
  • Inheritance must be used in a way that any property proved about supertype objects also holds for the subtype objects.

Interface-segregation principle (ISP)

Clients should not be forced to depend upon methods that they do not use:

  • Many client-specific interfaces are better that one general-purpose interface
  • No “fat” interfaces (“fat” means general purpose interface)
  • No non-cohesive interfaces

Dependency-inversion principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

  • Modules with detailed implementations are not depended upon, but depend themselves upon abstractions.

Lecture 3 (screencast): The SOLID principles #

(内容省略)

Lecture 4 (screencast): Project Lombok #

Project Lombok is a Java library that helps developers reduce boilerplate code in their Java applications. It provides a set of annotations that can be added to Java classes to generate getter, setter, constructor, and other methods at compile time. Here are some examples of how Lombok can be used in a Java project:

Getter and Setter annotations: Instead of writing getter and setter methods for each field in a Java class, Lombok provides @Getter and @Setter annotations that generate these methods automatically.

import lombok.Getter;
import lombok.Setter;

public class Person {
   @Getter @Setter private String name;
   @Getter @Setter private int age;
}

This code generates the following getter and setter methods at compile time:

public String getName() {
  return name;
}

public void setName(String name) {
  this.name = name;
}

public int getAge() {
  return age;
}

public void setAge(int age) {
  this.age = age;
}

Further material #