bild
Skolan för
elektroteknik
och datavetenskap
 

Assignment 4 - Some design patterns

Design patterns describe well-tried and reliable solutions that may be used repeatedly in object-oriented programming. With design patterns in your "toolbox", design tasks may be solved faster and safer. Some patterns are simple and some are complicated to learn in detail.

When learning a new pattern, it is often a good idea to program the pattern as a "toy example" in order to well understand its construction and its purpose.

In this excercise we will study some common and popular patterns.

Goal

  • To understand the creation of a composite objekt according to pattern Composite and how the operations in the pattern are performed.
  • To understand the idea of Factory methods and how to use packages and modifiers to create objects of classes not directly accsssible.

 

Composite pattern

The basic idea of Composite is that a group of objects, arranged in a hierarchical tree structure can be operated on with a single method call. The call affects all parts of the object. The same method call also works on each "leaf"-object of the structure as well as any partial structure.

A composite object contains other objects which in turn may be composite objects or single objects. An example of a Composite is a file directory. A directory may hold other directories and files. The files are the leaves of the tree. Another example is an image constructed from graphical primitives (line segments, ovals, rectangles etc.). If assemblies containing primitives and other assemblies can be defined and operated on, the structure is a Composite. In a method call

       c.op()

c may refer to a leaf or a composite object. For a composite object the operation will be performed on all parts of the object.

A description of pattern Composite: www.dofactory.com/Patterns/PatternComposite.asp

 

Task 1 – A suitcase as a Composite

Implement the necessary classes for a suitcase according to pattern Composite. The class Component in the standard description of the pattern is an abstract class from which the class(es) for the single items and the class(es) for the composites inherit.

Use a String-attribute to denote names of the items, e.g. Suitcase, toiletry case, plastic bac, jeans, T-shirt, shampoo.

Pack at least ten things in your suitcase and use at least three levels.

Look carefully at the UML-diagram!

In addition to the name, let all components of the pattern (small items and containers) have a weight attribute (a number). Use an ordinary constuctor to assign values to both attributes. Also, put methods getWeight() and toString() in all components. getWeight() for a small item (leaf) must return only the weight of the single item whereas getWeight() for a container returns the weight of the whole container (the weight of the empty container plus the sum of all the weights of its contents). toString() for a small item returns its name only but for a composite item toString() must return the containers name followed by the names of all the things inside the container.

Write a test program that builds a suitcase. Calculate the total weight of the suitcase with one method call and print the value. E.g. suitcase.getWeight(). Also, print the suitcase. One method call should give a string representing all of its contents. Remove a few of things from different levels in the suitcase and print the total weight and the string of contents again.
 

Factory-technique

More than one design pattern includes the word Factory. One thing these patterns have in common is that objects are not created in the usual way, with new but by calling a method. These methods are regarded as "factories" for object creation. Typical names of such methods are getInstance() and create(). The type of the created object is sometimes determined by the method (beyond reach for the client) and not by the client who requires the object. Inside the factory method, objects are of course created with new. Reasons for using this technique are e.g. that the user need not worry about the type (usually a subtype of a class known to the user) or that is is safer not to let the user choose the type. Another reason is that it may be desirable for the class to have full control of which objects are created.
 

Task 2 – Human factory

Implement an abstract class Human and its two concrete subclasses Man och Woman. In Human, implement a factory-method :
    public static Human create (String pnr) {
        // method body
    }
We assume that the parameter is a correct swedish "personnummer" with ten digits and a dash before the last four digits (no need to check this). If the tenth digit is odd, create an object of Man, otherwise create a Woman.

Write a test program that creates objects of Man and Woman using the factory method create(). Make sure it is impossible to create objects of Man and Woman by new Man(...) or new Woman(...) in the test program. The statements must generate errors from the compiler. Anonymous subclasses of Human must also not be allowed.

Tip: Make a package human for Human and its sublcasses. Put the package in a directory called human. With appropriate modifiers for visibility (public, private etc.) the required structure can be obtained. Put your test program on the directory above the package directory. Also, make sure that the test program somehow confirms the type of the created objects, e.g. by defining toString() methods in the classes. Here is an example:

    :
    Human anna = Human.create("Anna", "xxxxxx-012x");
    Human magnus = Human.create("Magnus","xxxxxx-011x");
    System.out.println(anna);
    System.out.println(magnus);
kan ge utskriften
    I am a woman and my name is Anna
    I am a man and my name is Magnus
 

Lab demonstration and requirements

  • Implement the Composite pattern with at least two classes. The UML diagram has three classes but we accept solutions with two.
  • Demonstrate a test program whrere minimum ten things in three different levels are created. Print total weight and and a String of contents. Remove a few of the items from the structure (suitcase). Print weight and contents again.
  • If your code follows the UML diagram in the pattern description (link above), you need not draw a separate UML diagram of you classes.
  • Draw an object diagram with the created objects. It is not necessary to follow any particular standard for the drawing, just make it clear and understandable. It should be a tree!

  • Run a test program where a Man and a Woman are created by a factory method.
  • Demonstrate another version of the previous program which fails to compile when you try new Man(...) or new Woman(...).

 

Extra assignment - Pattern Iterator

The purpose of pattern Iterator is to iterate (walk through, visit) all elements of a compound structure without exposing the structure to the user. Iterators over lists are intuitive and simple and are usually easy to implement.

Iterators are useful also for structures more complex than lists. Composites and other tree structures may very well be equipped with iterators that enable a user to view the contents in a sequence without knowing the details of the structure. The programmer who writes the iterator must know all the details of the underlying structure. Possible ways to visit all the nodes of a tree are the classical pre-, in- and post-order and breadth-first. Depth first is the same as preorder.

Read more about the iterator pattern: www.dofactory.com/Patterns/PatternIterator.asp
Also read in the Java API about Iterable, Iterator and check out the following tutorial: The for statement.

Breadth first iterator

First, visit the root, then all the children of the root, then all the grandchildren of the root etc.

Let the iterator implement the interface from the API described above (and shown below). E is the type of the elements "delivered" by the iterator. In our case, it must be Component, or its equivalent in the suitcase example.

public interface Iterator<E> {
    public E next();     // return the next element of the iterator
    boolean hasNext();   // return true if there are more elements
    void remove();       // not required to implement other than empty
}
Let the Composite class implement Iterable (se above) which requires method iterator(). Define iterator(), let it return an iterator object. It will now be possible to loop over the Composite with a for-each-statement as well as reaching its contents through the methods of the Iterator interface.

Depth first iterator

Implement a depth first iterator.

Demonstration of extra assignment

Testing of the iterators may be done in the test program for the Composite structure from task 1. Show that all elements are visited by the iterators. Both iterators must be demonstrated. Decide yourself how to do this!
Copyright © Sidansvarig: Ann Bengtsson <ann@nada.kth.se>
Uppdaterad 2014-04-21