2017 S2
Question 1
Study the partial UML class diagram in Figure Q1.
a) Draw the COMPLETE class diagram by adding the following CLASSES (with their necessary
attributes and/or methods) into the partial diagram in Figure Q1:
extends ClassA , and contains a protected integer instance variable time and one
ClassB
method with implementation, which is the greet(s: String, m: int) method.
ClassC extends ClassA, and implements InterfaceX and InterfaceY . It provides implementation
for the greet(s1: String, s2: String) method. It does not contain any other method.
ClassD extends ClassB , and provides implementation for all the necessary methods.
ClassE extends ClassC , and it contains a private integer instance variable age and provides
implementation for all necessary methods.
Answer
2017 S2 1
b) Write the Java code for all the CLASSES in the complete class diagram and their methods. Note
that the greet methods in all the classes simply print out the values of their parameters, except
that greet() print out "Hello" as default greeting message.
Answer
interface InterfaceX{
public void greet();
public void greet(int m, String s);
}
interface InterfaceY{
public void greet(String s1, String s2, String s3);
}
abstract class ClassA{
public void greet(){
System.out.println("Hello");
}
public abstract void greet(String s, int m);
public abstract void greet(String s1, String s2);
}
abstract class ClassB extends ClassA{
protected int time;
public void greet(String s, int m){
System.out.println("s: " + s + ", m :" + m);
}
}
2017 S2 2
abstract class ClassC extends ClassA implements InterfaceX, InterfaceY{
public void greet(String s1, String s2){
System.out.println("s1: " + s + ", s2 :" + s2);
}
}
class ClassD extends ClassB{
public void greet(String s1, String s2){
System.out.println("s1: " + s + ", s2 :" + s2);
}
}
class ClassE extends ClassC{
private int age;
public void greet(String s, int m){
System.out.println("s: " + s + ", m :" + m);
}
public void greet(String s1, String s2, String s3){
System.out.println("s1: " + s + ", s2 :" + s2 + ", s3: " + s3);
}
}
c) Write the Java mutator and accessor methods for instance variable age in ClassE .
Answer
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
Question 2
Consider the following Java classes:
import java.util.ArrayList;
import java.util.List;
class A {
public A(){System.out.println("Good");}
public void print(Object o)
{ System.out.println("A"); }
}
class B {
public void print(String s)
{ System.out.println("B"); }
public void sms() {System.out.println("Phone");}
}
class C1 extends A {
public C1(){System.out.println("Great");}
public void print(String s)
{ System.out.println("C1"); }
}
class C2 extends A {
public void print(Object o)
2017 S2 3
{ System.out.println("C2"); }
}
class D1 extends B {
public void print(Object o)
{ System.out.println("D1"); }
public void sms() {System.out.println("3G Phone"); }
}
class D2 extends B {
public void sms() {System.out.println("4G Phone"); }
}
public class Test {
public static void main(String[] args) {
A a1 = new A(); a1.print("Line1"); //line1
A a2 = new C1(); a2.print("Line2"); //line2
C1 c1 = new C1(); c1.print("Line3"); //line3
C2 c2 = new C2(); c2.print("Line4"); //line4
B b = new D1(); b.print("Line5"); //line5
D1 d1 = new D1(); d1.print("Line6"); //line6
B d2 = new D2(); d2.print("Line7"); //line7
if ("Line1" instanceof Object) print ("String instof Object"); //line8
if (a1 instanceof C1) print ("a1 instof C"); //line9
if (a2 instanceof C1) print ("a2 instof C"); //line10
if (d1 instanceof B) print ("d instof B"); //line11
List<B> phoneShop = new ArrayList<>() ;
phoneShop.add(d1);
phoneShop.add(d2);
phoneShop.add(b);
sendSMS(phoneShop); //line12
}
public static void print(String str){
System.out.println(str);
}
public static void sendSMS(List<B> bs){
for (B b: bs) b.sms();
}
}
a) For each line, from line1 to line7 indicated in the code comments, state the output and briefly
explain the reason.
Answer
Line Output Explanation
An object of class A is created. The constructor of A is called, and the
1 Good A method print(Object o) of class A is then called since String is an Object
type.
Upcasting from C1 to A. The constructor of C1 is called. The method
print(String s) of class C is called. a2 is of type A but of object reference
C1. First, when a2 was constructed, the superclass (A) constructor is
called, followed by C1, resulting in the messages from the constructor.
Good Great C1
2 During compilation, the compiler only looks at the declaration type of object
A
a2, which is class A. Since method overloading occurs, static binding is
used. Object a2 is binded to the print(Object o) method in class A. “Line2”
is forced to be upcasted into an Object type. So, the method print(Object o)
in class A was called.
2017 S2 4
Line Output Explanation
An object of class C1 is created. The constructor of A and C1 is called.
3 Good Great C1
The print(String s) method in class C1 is called.
An object of class C2 is created. The constructor of A and C2 is called.
The print method in class C2 overrides the print method in class A as they
4 Good C2
have the same method signature. Thus, the method print(Object o) in class
C2 is called.
Upcasting of class D1 to class B. The print(Object o) method in class D1 is
5 D1 B called. b is of type B but of object reference D1. Since only the print in
class B accepts a String, the print method in B is called.
An object of class D1 is created. The print(Object o) method in class D1 is
6 D1 B called. The print(String s) method in class B is called since it takes in a
String as an argument.
Upcasting of class D2 to class B. The print(String s) method in class B is
7 B
called since there is no print method in class D2.
If a string is provided, the program will search for a print method that takes in a String first. If none is
found, it will proceed to find a print method that takes in an Object as an argument.
If the method print in A is changed to print(String s) , then the output of Line 3 will be “Good Great
C1”.
If the method print in B is changed to print(Object o) , then the output of Line 5 and Line 6 will be
“D1”.
b) For each line, from line8 to line11 indicated in the code comments, state the output and briefly
explain the reason.
Answer
Line Output Explanation
8 String instof Object “Line1” is a String. String is an Object in Java.
9 Object a1 is of type A.
Object a2’s real object type is class C1. (a2 is an object reference
10 a2 instof C
that refers to an instance of the C1 class.)
Object d1’s real object type is class D1 and D1 is a subclass of class
11 d instof B
B. Thus, d1 is an instance of class B.
c) State the output of line12 indicated in the code comments and briefly explain the reason.
Answer
Output Explanation
3GPhone d1 is of type D1. The sms() method in class D1 is called.
d2 is of type D2. The sms() method in D2 overrides the sms() method in
4GPhone
B. So, the sms() method in D2 is called.
b is of type D1. The sms() method in D1 overrides the sms() method in
3GPhone
B. So, the sms() method in D1 is called.
2017 S2 5
Question 3
a) Consider a telephone phone service provider, MTel, with the following requirements :
Each customer is identified by an ID, and has a name, gender, date of birth and address. Each
customer has one or more phone numbers and each phone number is tied to a phone plan which
the customer registers for. A phone plan has an ID and is either prepaid or postpaid. A postpaid
plan has three quotas per month: for local calls (in minutes), international calls (in minutes), and
internet data usage (in KBs). There are also different type of postpaid plans (For example,
Combo1, Combo2, etc.) which provide different quotas for each of the above mentioned usages
and their corresponding monthly subscription charge. A prepaid plan has an expiry date and a
balance amount which is used for local calls, international calls and internet data usage.
For a phone number on a postpaid plan, MTel records the start and end dates of its current phone
plan and charges a monthly subscription fee. MTel also records its remaining quotas in the
current month for each of the above mentioned usage and exceeded quotas will be charged at a
specified rate. For a phone number on a prepaid plan, MTel records its current account balance.
You are tasked to identify the Entity classes needed to build the MTel application based on the
description above.
Show your design in a Class Diagram. The Class Diagram should show clearly the relationship
between classes, relevant attributes (at least TWO, with appropriate data type), logical
multiplicities, meaningful role names, association names and constraint, if any. You need not
show the class methods.
Answer
2017 S2 6
b) The UML Class Diagram in Appendix A (page 7) shows the relationships of THREE classes:
Usage , Data and Call . Study the class diagram and the depicted details carefully.
Additional details relevant to this question are provided below.
The constructor for the Usage class has a double parameter, rate , which initializes the rate
attribute and also sets the charge attribute to zero. The getCharge() function in the Usage class
returns the charge attribute.
The Data class has TWO constructors: one can be used to initialize the dataUse attribute and the
rate in the Usage class and the other initializes only the charge and set the rate and dataUse to
zero. The getCharge() function multiplies the rate with the dataUse , adds the result to charge and
returns the total. The operator+ function has a Usage parameter, u , and returns a Usage reference.
The operator+ function does the following:
It calculates the total charge, chg , by adding together the charge returned from calling its
getCharge() method and the charge returned from calling the getCharge() method using the
Usage parameter.
It instantiates a Data object, data , using the Data(charge: double) constructor and the total
charge as the input argument.
It returns the Data object reference, data .
The Call class has a similar structure as the Data class. The getCharge() function multiplies the
rate with the callDuration , adds the result to charge and returns the total.
i) Write the complete C++ code for the Usage class in only a header file, usage.h, as depicted in the
class diagram. You should use the appropriate C++ keyword/s, pointer or reference to ensure the
code will run as expected.
Answer
#ifndef USAGE_H
#define USAGE_H
class Usage{
2017 S2 7
protected:
double rate;
double charge;
public:
Usage(double rate): rate(rate), charge(0.0);
virtual Usage operator+(Usage u) = 0;
double getCharge();
~Usage(){}
};
#endif //USAGE_H
ii) Write the complete C++ code for the Data class in only an implementation file, data.cpp, for all
the constructors and functions as depicted in the class diagram. You should use the appropriate
C++ keyword/s, pointer or reference to ensure the code will run as expected.
Answer
#include <iostream>
#include <string>
#include "usage.h"
using namespace std;
class Data : public Usage{
private:
double dataUse;
public:
Data(double rate, double dataUse): Usage(rate), dataUse(dataUse){}
Data(double charge): Usage(0), charge(charge), dataUse(0.0){};
Usage operator+(Usage u){
double chg = getCharge() + u.getCharge();
Data Data(chg);
return data;
}
double getCharge(){
return rate * dataUse + charge;
}
~Data(){}
};
Question 4
a) The UML Sequence Diagram in Appendix B (page 8) shows the objects' interaction of a scenario
flow in a musical chords validation application. Using the details depicted in the diagram, write the
preliminary JAVA code for the class ProgressionMgr and its methods. You may make appropriate
assumptions on the method parameters, return types and return value(s) if they are not stated in
the diagram.
2017 S2 8
Answer
import java.util.*;
public class ProgressionMgr {
Validator val;
ArrayList<Chord> chordList;
String progressionChords = "";
public int init() {
chordList = loadChords();
val = loadValidator();
}
private ArrayList<Chord> loadChords() {
return new ArrayList<Chord>();
}
private Validator loadValidator() {
return new Validator();
}
public void selectChord(String chordStr) {
this.addChord(chordStr);
}
private void addChord(String chordStr) {
Chord ch = matchChord(chordStr);
2017 S2 9
String sy = ch.getSymbol();
ch.playChord();
String pc = getProgressionChords();
if(pc == null)
setProgressionChords(sy);
else
setProgressionChords("||" + sy);
}
private Chord matchChord(String chordStr) {
return new Chord(chordStr);
}
private String getProgressionChords() {
return progressionChords;
}
private void setProgressionChords(String s) {
progressionChords += s;
}
public String validate() {
String pc = getProgressionChords();
String result = val.validate(pc);
return result;
}
}
b) The ProgressionMgr class uses the Validator class, shown in the sequence diagram in Question
4(a), to validate the chords progression (list of musical chords) and check for grammatical errors
in the chords sequences - similar idea to checking the grammar structure of an English sentence.
Currently, the Validator class is using a rule-based validation algorithm. In future, there will be
requirement to replace the algorithm with other more advanced ones like using artificial
intelligence. You are to apply the Open-Closed Principle, Liskov Substitution Principle and the
Dependency Injection Principle to enable the replacement of future algorithms to the current
design taking into consideration the extensibility, reusability and maintainability of the new
design.
Illustrate your new design in a Class Diagram showing relevant method/s and explain which part
of the design each of the THREE principles focuses on respectively.
Answer
Open-Closed Principle
2017 S2 10
Software entities (i.e classes) should be open for extension, but closed for modification. The
developer can introduce more advanced types of validators by extending the appropriate base
class. For example, if there is a better algorithm for a rule-based validation, the class can simply
extend from RuleBasedValidator.
Liskov Substitution Principle
Objects of a superclass shall be replaceable with objects of its subclasses without breaking the
application. In other words, the child class should expect no more and return no less than its
parent class. By implementing the Validator interface, all the classes only need to implement the
same validate method, and nothing else.
Dependency Injection Principle
High-level modules, which provide complex logic, should be easily reusable and unaffected by
low-level modules. Both high-level and low-level modules should depend on abstractions. All the
validators depend on the abstraction Validator, as an interface. This decouples the client from the
validation, allowing many different validators to be used without changing the client code.
2017 S2 11