Spustit jako prezentaci

Návrh systémů

Today we will discuss design of whole systems. We will start with small pieces - classes. Then we will look at common linking of classes (design patterns). There is plenty on that topic in the lecture.

Návrh tříd

Příklad: PHPMailer

Again we will discuss what is wrong with the given code, this time, however, with real pieces of code.

There will be shown a code, we will discuss what is good and what is bad, and at the end there will be a task to design reasonable API according to the example.

Příklad: PHPMailer

Kód (PHP)


$mail = new PHPMailer();

$mail->From = "ja@muj.skvely.server.cz";
$mail->FromName = "Já";
$mail->addAddress("ty@tvuj.skvely.server.cz", "Ty");
$mail->Subject = "Spam";

$mail->isHtml(true);
$mail->Body = "<p>...</p>";
$mail->AltBody = "...";

$mail->isSmtp();
$mail->Host = "smtp.muj.skvely.server.cz";

if ($mail->Send()) {
  echo"Odesláno";
} else {
  echo"Neodesláno, chyba: " . $mail->ErrorInfo;
}
				

Příklad: PHPMailer

Kód (PHP)


$mail = new PHPMailer();

$mail->From = "ja@muj.skvely.server.cz";
$mail->FromName = "Já";
$mail->AddAddress("ty@tvuj.skvely.server.cz", "Ty");
$mail->Subject = "Spam";

$mail->IsHtml(true);
$mail->Body = "<p>...</p>";
$mail->AltBody = "...";

$mail->IsSmtp();
$mail->Host = "smtp.muj.skvely.server.cz";

if ($mail->Send()) {
  echo"Odesláno";
} else {
  echo"Neodesláno, chyba: " . $mail->ErrorInfo;
}
				

There are wrong several things with the design of PHPMailer:

  • Some things are being set by adjusting class attributes directly (e.g. From), others using auxiliary methods (e.g. addAddress).
  • Setting the sender is unnecessarily uneasy (From and FromName could be merged into one attribute; they represent only one mail header anyway.
  • The methods with names that indicate boolean (isHtml, isSmtp) are being used to set the attributes, and more to that inconsistently. (isHtml takes a parameter, isSmtp does not).

Příklad: PHPMailer

Kód (PHP)


$mail = new PHPMailer();

$mail->From = "ja@muj.skvely.server.cz";
$mail->FromName = "Já";
$mail->AddAddress("ty@tvuj.skvely.server.cz", "Ty");
$mail->Subject = "Spam";

$mail->IsHtml(true);
$mail->Body = "<p>...</p>";
$mail->AltBody = "...";

$mail->IsSmtp();
$mail->Host = "smtp.muj.skvely.server.cz";

if ($mail->Send()) {
  echo"Odesláno";
} else {
  echo"Neodesláno, chyba: " . $mail->ErrorInfo;
}
				

The class however suffers for much serious problem: It is responsible for two different things, constructing a mail and sending it. Sign for that is setting attributes, which do not have any reflection in mail header (e.g. Host).

Better design would be to either move the "mail sending" into its own class, or design it at least in such a way, that the call will not contain attributes related to sending (necessary information would be passed directly to the method Send as parameters).

Příklad: PHPMailer

Naprogramujte použití rozumného rozhraní na posílaní e-mailu

Příklad: API pro práci s cestami

Zadání

Vytvořte jednoduché rozhraní (tj. třídu/třídy) pro práci s cestami v souborovém systému. Musí podporovat následující operace:

Příklad: Vztah tříd v API pro práci se soubory a adresáři

Zadání

Jsou dány třídy File a Directory reprezentující soubor a adresář. Obě mají metody umožňující základní operace s nimi (vytváření, kopírování, přesouvání, mazání, apod.). Jaký by podle vás měl být vztah mezi těmito třídami (z pohledu dědičnosti)?

Návrhové vzory

Show slides with design patterns (lab05.pdf), then there will be example for visitor.

Zadání

Doprogramujte vypsaní aritmetického výrazu v postfix notaci pomocí návrhového vzoru visitor.

Leave time to write the methods accept + the visitor. After that move to the next part of the task without showing the implemented code.

Rozšíření zadání

Doprogramujte vypsaní aritmetického výrazu v postfix a prefix notaci pomocí návrhového vzoru visitor.

The task had changed, this happens often during development. Not only we want postfix, but prefix (eventually infix, but there needs to be parentheses) as well.

The whole example is a teaser - the visitor will be doing nothing else than output values and signs (plus spaces), two different traversal of the tree needs to be implemented by two different accept methods.

The visitor has to take the data structure, on which it operates, as a blackbox.

Příklad existující knihovny

Java Collections Framework

An example of a relatively well functioning API.

Historie

Java 1.0

Java 1.2

Java 1.5

We have come into contact with Vector + Stack and Enumeration + Iterator as an example of poorly designed classes. These classes are part of Java Collections Framework. This framework is a beautiful example how bad design of a critical API can impact negatively the whole platform and how hard it is to fix the API. On the other hand it is a nice example of how well designed API (after fixing) looks. Therefore we will have a closer look at it.

If you are interested in details I recommend the document Java Collections API Design FAQ, which describes in detail choices and compromises, which had to be done by the developers of the API.

Generiky = komplikace?

Příklad (Java)


public static <T extends Object & Comparable<? super T>> T
  min(Collection<? extends T> coll);
				

Schéma Java Collections Framework

Java Collections Framework

Zajímavá rozhodnutí

Použití interfaců

Zajímavá rozhodnutí

Použití skeletálních implementací

Zajímavá rozhodnutí

Implementace nejsou thread-safe

Dekorátory pro thread-safe kolekce a immutable kolekce

Zajímavá rozhodnutí

Příklad dekorátoru zajišťujícího thread-safe chování (Java)

List list = Collections.synchronizedList(new ArrayList());
				

Příklad dekorátoru zajišťujícího immutabilitu (Java)

List list = Collections.unmodifiableList(new ArrayList());
				

Zajímavá rozhodnutí

Odsun obecných algoritmů mimo jednotlivé třídy

Zajímavá rozhodnutí

Konstanty/metody pro prázdné kolekce

Zajímavá rozhodnutí

RandomAccess marker interface

Iterátory jsou fail-fast

Hádanka

Hádanka (Java)

Co vypíše následující kus kódu?


public static void main(String[] args) {        
  Set<String> col = new HashSet<String>();
  col.add("Dagi");
  col.add("NkD");
  for (String key : col) {
    col.remove(key);
    System.out.println(key);            
  }
}
				

The correct answer is "NkD" and than there will be thrown an exception ConcurrentModificationException. The iterators in Java collections are fail-fast, i.e. they are watching for collection modification while they iterate through it. If the collection is modified, they alert the user with an exception, which is better behavior than nondeterministic crash somewhere in the future.

The puzzle is taken from Roman "Dagiho" Pichlík.