Topics

Important Points

SWE Class Diagrams Intermediate

A class diagram can also show different types of relationships between classes: inheritance, compositions, aggregations, dependencies.

We can use a triangle and a solid line (not to be confused with an arrow, which means navigation) to indicate class inheritance.

Here's an example that combines inheritance with generics:

Composition

1

Concept

A bit unlike what I have learned in CS2030S, here a composition is an association that represents a strong whole-part relationship. It implies that

  1. when the whole is destroyed, parts are destroyed too e.g., the part cannot exist without being attached to a whole.

  2. there cannot be cyclical links.

2

UML Notation

UML uses a solid diamond symbol to denote composition.

Aggregation

1

Concept

Aggregation represents a container-contained relationship. It is a weaker relationship than composition.

2

UML Notation

UML uses a hollow diamond to indicate an aggregation.

Aggregation vs. Composition

The distinction between composition (◆) and aggregation (◇) is rather blurred. Martin Fowler’s famous book UML Distilled advocates omitting the aggregation symbol altogether because using it adds more confusion than clarity.

Dependency

1

Concept

In the context of OOP associations, a dependency is a need for one class to depend on another without having a direct association in the same direction. Reason for the exclusion: If there is an association from class Foo to class Bar (e.g., navigable from Foo to Bar), that means Foo is obviously dependent on Bar and hence there is no point in mentioning dependency specifically. In other words, we are specifically focusing on non-obvious dependencies here. One cause of such dependencies is interactions between objects that do not have a long-term link between them.

One example of dependency lies in the method's parameters. For example, in the code below, Foo has a dependency on Bar but it is not an association because it is only a transient interaction and there is no long term relationship between a Foo object and a Bar object. e.g., the Foo object does not keep the Bar object it receives as a parameter.

2

UML Notation

UML uses a dashed arrow to show dependencies.

For example, in the example above, the dependency between Foo and Bar can be drawn as follows,

Dependency vs. Association
  1. An association and an inheritance already shows dependency, thus no need to show them as a dashed arrow in the UML again.

  2. Use a dependency arrow to indicate a dependency only if that dependency is not already captured by the diagram in another way (for instance, as an association or an inheritance)


A class diagram can also show different types of class-like entities, like enumerations, abstract class, interfaces

We have introduced the concept of enumerations in Lec 04. In UML, the enumerations are represented as follows,

In UML, we can use italics or {abstract} (preferred) keyword to denote abstract classes/methods.

1

Concept

Nothing much to add on. Just recall that a class implementing an interface results in an is-a relationship.

2

UML Notation

An interface is shown similar to a class with an additional keyword <<interface>>. When a class implements an interface, it is shown similar to class inheritance except a dashed line is used instead of a solid line.

For example, the AcademicStaff and the AdminStaff classes implement the SalariedStaff interface.

Java Logging

Logging is the deliberate recording of certain information during a program execution for future reference and it can be useful for troubleshooting problems.

Creating a Logger

To use the default Java logging system, import java.util.logging.*. We obtain a logger instance using a factory method.

Logging Levels (Hierarchy)

Java assigns a level of importance to every log message. This hierarchy determines which messages are important enough to be saved. The standard levels from highest to lowest are:

  • SEVERE (Highest): Critical failures (crashes).

  • WARNING: Potential issues.

  • INFO: Standard operational messages (e.g., "Start", "Stop").

  • CONFIG: Configuration details.

  • FINE / FINER / FINEST (Lowest): Detailed tracing for debugging.

Filtering Mechanism

The Logger acts as a filter (like a volume threshold). We set a specific level for the Logger, and it will only output messages that are equal to or higher than that level.

  • If Level is set to WARNING: It records WARNING and SEVERE. (It ignores INFO and below).

  • If Level is set to INFO: It records INFO, WARNING, and SEVERE.

Java Assertions

Assertions are used to define assumptions about the program state so that the runtime can verify them. If the runtime detects an assertion failure, it typically takes some drastic action such as terminating the execution with an error message.

Implementation

To implement a Java assertion, we can use the keyword assert. For example, this assertion will fail with the message x should be 0 if x is not 0 at this point.

Java assert vs. JUnit assertion

Both check for a given condition but JUnit assertions are more powerful and customized for testing. In addition, JUnit assertions are not disabled by default. Use JUnit assertions in test code and Java assert in functional code.

When to use Java assertion

Exceptions and assertions are two complementary ways of handling errors in software but they serve different purposes. Therefore, both assertions and exceptions should be used in code.

  • The raising of an exception indicates an unusual condition created by the user (e.g. user inputs an unacceptable input) or the environment (e.g., a file needed for the program is missing).

  • An assertion failure indicates the programmer made a mistake in the code (e.g., a null value is returned from a method that is not supposed to return null under any circumstances).

SWE Design Principles

In this part, we will learn three most fundamental design qualities (abstraction, coupling and cohesion) upon which all other design principles are built.

Abstraction

The guiding principle of abstraction is that only details that are relevant to the current perspective or the task at hand need to be considered. As most programs are written to solve complex problems involving large amounts of intricate details, it is impossible to deal with all these details at the same time. That is where abstraction can help.

Awesome explanation above. This is also what we are doing in DDCA!

We have the two abstraction examples,

  1. Data abstraction: abstracting away the lower level data items and thinking in terms of bigger entities

  2. Control abstraction: abstracting away details of the actual control flow to focus on tasks at a higher level. In other words, wrap code into methods.

Coupling

Coupling is a measure of the degree of dependence between components, classes, methods, etc. Low coupling indicates less dependency between components and high coupling should be avoided.

For example, the following image shows that design A have more coupling between the components than design B.

X is coupled to Y if a change to Y can potentially require a change in X. Some examples of coupling: A is coupled to B if,

  • A has access to the internal structure of B (this results in a very high level of coupling)

  • A and B depend on the same global variable

  • A calls B

  • A receives an object of B as a parameter or a return value

  • A inherits from B

  • A and B are required to follow the same data format or communication protocol

Cohesion

Cohesion is a measure of how strongly-related and focused the various responsibilities of a component are. Higher cohesion is better.

Cohesion can be present in many forms. Some examples:

  • Code related to a single concept is kept together, e.g. the Student component handles everything related to students.

  • Code that is invoked close together in time is kept together, e.g. all code related to initializing the system is kept together.

  • Code that manipulates the same data structure is kept together, e.g. the GameArchive component handles everything related to the storage and retrieval of game sessions.

Some other principles

1

Single Responsibility Principle (SRP)

A class should have one, and only one, reason to change.

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

2

Separation of Concerns Principle (SoC)

To achieve better modularity, separate the code into distinct sections, such that each section addresses a separate concern.

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

  • 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

By applying the SoC principle, we have the following benefits

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

  • leads to higher cohesion and lower coupling.

3

Derived classes must be substitutable for their base classes.

LSP implies that a subclass should not be more restrictive than the behavior specified by the superclass. In other words, the subclass cannot break the specifications set by the super class. (How to find that specifications, review CS2030S!)

Classic Questions

1

Assertion failure

A Calculator program crashes with an ‘assertion failure’ message when you try to find the square root of a negative number.


Explanation: An assertion failure indicates a bug in the code. (b) is not acceptable because of the word "terminated". The application should not fail at all for this input. But it could have used an exception to handle the situation internally.

2

Coupling Levels

Discuss the coupling levels of alternative designs x and y.


Ans: Overall coupling levels in x and y seem to be similar (neither has more dependencies than the other). (Note that the number of dependency links is not a definitive measure of the level of coupling. Some links may be stronger than the others.). However, in x, A is highly-coupled to the rest of the system while B, C, D, and E are standalone (do not depend on anything else). In y, no component is as highly-coupled as A of x. However, only D and E are standalone.

3

Statements on coupling

Choose the correct statements.


Easy to understand I think.

4

Some principles

“Only the GUI class should interact with the user. The GUI class should only concern itself with user interactions”. This statement follows from,


Explanation: By making ‘user interaction’ the GUI class’s sole responsibility, we increase its cohesion. This is also in line with the separation of concerns (e.g., we separated the concern of user interaction) and the single responsibility principle (the GUI class has only one responsibility).

Last updated