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!
|