exercise sheet with solution

Werbung
Abschließendes Übungsblatt
Dieses Übungsblatt wurde von Nora Wester im Rahmen des Praktikums in der Lehre erstellt.
Die Übungsaufgaben orientieren sich an den existierenden Übungsblättern und wurden ohne Kenntnisse über die
Klausur entworfen.
Aufgabe 1
Wie sind die CRC-Karten richtig erstellt, wenn davon auszugehen ist, dass Klasse B
1) eine Erweiterung der Klasse A ist?
2) ein Attribut der Klasse A ist?
a
)
A
b
)
B
B
B
A
c
)
A
B
Antwort:
1) c)
2) b)
A
A
B
Aufgabe 2
Bewerten Sie die nachfolgenden Anforderungen bezüglich ihrer Aussagekraft in Hinblick auf deren
Sicherstellung. Wie könnte man die Anforderungen ggf. umformulieren, damit sie sichergestellt
werden können.
a) Die Suchmaschine darf bei bestimmten, in einer Datenbank festgehaltenen Eingaben, kein
Suchergebnis liefern. Werden nur gesperrte Worte eingegeben, wird unter der Eingabezeile
eine vereinbarte Meldung eingeblendet. Sind bei der Eingabe auch erlaubte Worte dabei,
werden nur die Ergebnisse für diese Worte ausgegeben und die gesperrten ignoriert.
b) Der Algorithmus muss so effizient wie möglich sein, darf aber mit den gegebenen Variablen
nur maximal 10 Sekunden zum Ausrechnen benötigen.
Antwort:
a) Hier kann klar sichergestellt werden, dass die Anforderung erfüllt wird. Die Datenbank wird
überprüft und die Einzelwörter in der Suchmaschine getestet. Es ist auch klar geregelt, wie
die Anwendung verfahren soll, wenn ein gesperrtes Wort eingegeben wird. Dieses Verhalten
kann beim Testen mitberücksichtigt werden. Somit muss die Anforderung nicht
umformuliert werden.
b) Hier ist nicht festgelegt, unter welchen Hardware-Komponenten die angegebene Zeit
erreicht werden muss. Auch die Aussage "so effizient wie möglich" ist nicht eindeutig
definiert. Somit kann man nicht sicherstellen, dass man genau die Performance, unter den
für den Kunden relevanten Bedingungen, erfüllen kann.
Eine Umformulierung könnte zum Beispiel so aussehen, dass eine mindest Worst Case
Komplexität genannt wird. Alternativ wäre es auch möglich die konkreten HardwareKomponenten, oder einen speziellen Rechner anzugeben, unter denen man die Anforderung
sicherstellt. Damit wäre aber der Einsatz des Algorithmus (mit Effizienzvoraussetzungen)
unter Umständen sehr eingeschränkt gewährleistbar.
Aufgabe 3
Erstellen Sie zu der beschriebenen Anwendung ein Use Case Diagram und ein Domainmodel.
Online-Nachhilfe-Portal:
Im Online-Nachhilfe-Portal treffen sich Leute, um zusammen zu lernen. Dabei kann man als
Mitglied entweder die Rolle des Lehrenden oder des Lernenden inne haben.
Jedes Mitglied hat eine Liste, in der er seine Chatrooms einsehen und betreten kann. Außerdem
können sich die Mitglieder untereinander durch den systemeigenen Messenger kontaktieren.
Jeder Lehrende hat von Anfang an ein Standard-Profil, das er durch Editieren mit seinen Angaben
füllen kann. In diesem Profil ist vermerkt, wie viele Lernende er noch betreuen möchte, welchen
Abschluss er besitzt und ob er Erfahrung als Nachhilfelehrer hat. Für jedes Fach, das er unterrichtet,
gibt es einen eigenen Eintrag in seinem Profil, in dem das Fach, die Klassenstufe und die Höhe der
Entlohnung angegeben ist. Diese Einträge können ebenfalls editiert werden. Möchte er ein neues
Fach lehren, kann er direkt im Profil einen neuen Eintrag erstellen, möchte er ein bestehendes Fach
nicht mehr anbieten, muss er den Eintrag löschen.
Sucht ein Lernender einen Lehrenden, so schaut er sich die existierende Profile an. Hat er jemand
gefunden, mit dem er zusammenarbeiten möchte, schickt er ihm eine Anfrage mit Angabe von Fach,
Klassenstufe und eine kurze Schilderung seiner Wissenslücken. Der Lehrende entscheidet dann
anhand der Anfrage, ob er diesen Lernenden unterrichten möchte. Nimmt er ihn an, wird vom
System ein Chatroom mit Videotelefonie generiert, der dann in die Listen von Lehrendem und
Lernendem eingetragen wird. Außerdem wird eine Nachricht an den Lernenden geschickt. Die
Anfrage wird gespeichert und ist für den Lehrenden jeder Zeit einsehbar. Lehnt er ihn ab, so wird
vom System nur eine entsprechende Nachricht an den Lernenden gesendet.
Ein Ende der Zusammenarbeit kann dem System nur durch den Lehrenden mitgeteilt werden,
indem er die entsprechende Anfrage auswählt und löscht. Daraufhin löscht das System den
Chatroom und sendet dem Lernenden eine entsprechende Nachricht.
Lösung:
Use Case Diagram:
Domainmodel:
Aufgabe 4
Erläutern Sie kurz die Unterschiede zwischen Statement-, Branch- und Path-Coverage.
Bei welchem Kriterium kann es unter Umständen unendlich viele Testfälle geben, um vollständiges
Coverage zu erreichen und warum?
Antwort:
Beim Statement-Coverage geht es darum jede Zeile Code mindestens einmal durchlaufen zu haben.
Wie man zu den Codezeilen gelangt, ist hier grundsätzlich irrelevant.
Beim Branch-Coverage hingegen geht es darum, dass jeder mögliche Zweig bei jeder Bedingung
einmal durchlaufen wird. Hierbei werden auch die Zweige getestet, die z.B. in eine Exception
führen.
Bei Path-Coverage geht man noch einen Schritt weiter als bei Branch-Coverage und verlangt
jeglichen möglichen Pfad durch den Quellcode mindestens einmal durchlaufen zu haben.
Schleifen und Rekursion kann zu unendlich vielen Pfaden führen und somit ist Path-Coverage in
der Regel nicht zu 100 % erreichbar. In der Praxis wird deshalb häufig die Anzahl der Iterationen
begrenzt.
Aufgabe 5
Schreiben Sie für die Methode tester(int[], int, boolean, int) die minimalen Testcases für BranchCoverage, Decision-Coverage und Condition-Coverage. Geben Sie auch die Ergebnisse an und für
Branch-Coverage den Kontrollflußgraphen.
protected int tester(int[] array, int value, boolean var, int position){
int temp = array[position];
if(temp == value){
return 0;
}else{
if(var && temp/2 == value)
return 2;
}
return -1;
}
Lösung:
Branch-Coverage:
array.length <= position || position < 0
N1: int temp = array[position];
N2: throw ArrayIndexOutOfBoundsException
N3: if(temp == value)
false
true
N4: return 0;
N5: if(var && temp/2 == value)
true
N6: return 2;
false
N7: return -1;
tester ( new int[0], 0, true, 1) -> ArrayIndexOutOfBoundsException
tester ( new int[]{0,1,2}, 1, true, 1) -> 0
tester ( new int[]{0,1,2}, 1, true, 2) -> 2
tester ( new int[]{0,1,2}, 1, false, 2) -> -1
Decision-Coverage:
tester ( new int[]{0,1,2}, 1, true, 1) -> 0
tester ( new int[]{0,1,2}, 1, true, 2) -> 2
tester ( new int[]{0,1,2}, 1, false, 2) -> -1
Condition-Coverage:
tester ( new int[]{0,1,2}, 1, true, 1) -> 0
tester ( new int[]{0,1,2}, 1, true, 2) -> 2
tester ( new int[]{0,1,2}, 1, false, 2) -> -1
tester ( new int[]{0,4}, 1, true, 1) -> -1
Aufgabe 6
a) Ermitteln Sie die Kopplung und Kohäsion mit Hilfe der LCOM-Metrik der Klasse
MyIntArrayFrom<T> und beziehen Sie die statische Methode mit ein.
b) Bewerten Sie die Klasse MyIntArrayFrom<T> nach ihrer Qualität und beschreiben Sie wie
man sie ggf. verbessern kann.
c) Welche Patterns erkennen Sie in der Klasse MyIntArrayFrom<T>? Bestimmen Sie welchen
Bestandteil des Patterns die Klasse und ihre Methoden einnehmen.
d) Erweitern Sie die Klasse MyIntArrayFrom<T>, so dass angemeldete Klassen über
Änderungen der enthaltenen Werte informiert werden. Benutzen Sie dazu ein Ihnen
bekanntes Pattern.
public abstract class MyIntArrayFrom<T> {
public int[] values;
public MyIntArrayFrom(T... initalElements) {
values = new int[]{};
for(T t : initalElements)
ab(t);
}
public int[] getValues() {
return values;
}
public void setValues(int[] values) {
this.values = values;
}
/**
*
* @param position
* @return the value of values at the specified position
*/
public int xy(int position){
return values[position];
}
/**
*
* @return the length of values
*/
public int getLength(){
return values.length;
}
/**
* adds the value at the end of values
* @param value
*/
public void ab(T value){
int[] temp = new int[values.length+1];
System.arraycopy(values, 0, temp, 0, values.length);
temp[temp.length-1] = convertToInt(value);
values = temp;
}
public abstract int convertToInt(T value);
/**
* sorts values with help of the given comperator MySort, which is used to
* compare two values
* @param sort
*/
public void sort(MySort sort){
values = sort.sort(values);
}
public static void main(String[] args) {
MyIntArrayFrom<String> array = new MyIntArrayFrom<String>("4"){
@Override
public int convertToInt(String value) {
return Integer.parseInt(value);
}
};
array.ab("2");
for (int i = 0; i < array.getLength(); i++) {
System.out.println(array.xy(i));
}
}
}
Lösung:
a)
Kopplung:
java.lang.System
java.lang.Object
java.lang.Integer
java.lang.String
MySort
java.lang.Override
java.io.PrintStream (Wegen System.out)
Kohäsion:
Methoden
Instanz-Variabel
MyIntArrayFrom(...)
values
setValues(...)
values
getValues()
values
xy(...)
values
getLength()
values
ab(...)
values
convertToInt(...)
sort(...)
main(...)
values
LCOM = 3 von 9
Da der LCOM erhöht ist, ist die Kohäsion eher niedrig. Dies liegt aber zum Teil daran, dass die
zweite Methodenmenge allein durch die Auskopplung von convertToInt(...) aus ab(...) zustande
kommt. Auskopplungen von internen Berechnungen werden von der Metrik nicht erfasst und stellen
einen Schwachpunkt der Metrik dar. Die Main-Methode repräsentiert in der Tat eine dritte
Funktionalität, welche als Beispielhafte Verwendung der Klasse beschrieben werden könnte.
b)
Die Klassenvariable values sollte private gehalten werden. Auch die Methode convertToInt(T) wäre
eher als protected zu deklarieren, da sie einen Teil der internen Berechnung darstellt.
Einige Methodennamen sind nicht sehr vielsagend. So sollte man xy(int) umbenennen in
getValueAt(int) und ab(T) in add(T). Der Klassenname selbst sollte, wie in Java üblich, in
AbstractMyIntArrayFrom umgeändert werden.
Die get- und vor allem set-Methode sind hier unpassend. Die set-Methode sollte nicht vorgesehen
sein, da die Klasse ein im Konstruktor erstelltes Array verwaltet, das nicht durch ein x-beliebiges
anderes oder durch null ersetzt werden darf. Denn dies würde zu unbeabsichtigten Fehlern in
anderen Methoden führen. Die get-Methode enthält die Gefahr, dass das echte Array zurückgegeben
wird und es damit von anderen Klassen ohne Kenntnisnahme der Klasse modifiziert werden kann.
Somit könnte z.B. die Sortierung verlorengehen.
Die Klasse hätte ohne main(String[] args) nur eine Responsibility, was man daran erkennt, dass z.B.
die Verantwortlichkeit des Sortierens getrennt gehalten wird. Deswegen sollte man die mainMethode in eine eigene Klasse extrahieren.
Die Methoden sind nicht zu groß oder unübersichtlich.
c)
Strategy-Pattern:
MyIntArrayFrom<T> übernimmt hier die Rolle des Contexts und MySort ist die Strategie
verwendet in der Methode sort(...).
Template Method Pattern:
MyIntArrayFrom<T> übernimmt hier die Rolle der AbstractClass mit ab(T) als templateMethod
und convertToInt(T) als hook-Method.
d)
public abstract class AbstractMyIntArrayFrom<T> extends
AbstractObservableIntArray{
private int[] values;
public AbstractMyIntArrayFrom(T... initalElements) {
values = new int[]{};
for(T t : initalElements)
add(t);
}
/**
*
* @param position
* @return the value of values at the specified position
*/
public int getValueAt(int position){
return values[position];
}
/**
*
* @return the length of values
*/
public int getLength(){
return values.length;
}
/**
* adds the value at the end of values
* @param value
*/
public void add(T value){
int[] temp = new int[values.length+1];
System.arraycopy(values, 0, temp, 0, values.length);
temp[temp.length-1] = convertToInt(value);
int[] oldArray = values;
values = temp;
fireArrayChanged(new IntArrayEvent (this, oldArray, temp,
oldArray.length-1, temp.length-1));
}
protected abstract int convertToInt(T value);
/**
* sorts values with help of the given sorter MySort, which is
* used to sort an int array. Different sorting algorithms are realized
* in classes which implement interface MySort.
* @param sort
*/
public void sort(MySort sort){
int[] temp = values;
values = sort.sort(temp);
fireArrayChanged(new IntArrayEvent(this, temp, values, 0,
temp.length-1));
}
}
import java.util.LinkedList;
import java.util.List;
public abstract class AbstractObservableIntArray {
private List<IIntArrayListener> listener =
new LinkedList<IIntArrayListener>();
public void addIIntArrayListener(IIntArrayListener l){
if(l != null)
listener.add(l);
}
public void removeIIntArrayListener(IIntArrayListener l){
if(l != null)
listener.remove(l);
}
protected void fireArrayChanged(IntArrayEvent e){
IIntArrayListener[] listeners = listener.toArray(
new IIntArrayListener[listener.size()]);
for(IIntArrayListener l : listeners)
l.arrayChanged(e);
}
}
import java.util.EventListener;
public interface IIntArrayListener extends EventListener {
public void arrayChanged(IntArrayEvent e);
}
import java.util.EventObject;
public class IntArrayEvent extends EventObject{
private
private
private
private
int[] newArray;
int[] oldArray;
int start;
int end;
public IntArrayEvent(AbstractObservableIntArray
int[] newArray, int start,
int end) {
super(source);
this.newArray = newArray;
this.oldArray = oldArray;
this.start = start;
this.end = end;
}
public int[] getNewArray() {
return newArray;
}
public int[] getOldArray() {
return oldArray;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
}
source,
int[]
oldArray,
Herunterladen