Master Course in Distributed
Computing Systems Engineering

 

 

Workshop AW2

 

Module EE 5029A
Object-Oriented Systems Design

 

 

 

Assignment 2 – Part 1

Design Patterns and Evolution
of Software-Architectures

 

 

 

Marc Höschele

Contents

1.       Introduction. 3

1.1.       Software quality. 3

2.       eXtreme Programming (XP) 4

3.       Development using XP.. 5

3.1.       Main Storage. 5

3.2.       Persistent Storage. 5

3.3.       Navigational extension. 6

3.3.1.       Problems with test cases. 6

3.3.2.       Possibly unnecessary test cases. 7

3.4.       Refactoring. 7

4.       Design Patterns. 8

4.1.       The Model View Controller pattern. 8

4.2.       The Variants pattern. 9

4.3.       The Bridge pattern. 9

4.4.       The Abstract Factory pattern. 10

4.5.       The Factory Method pattern. 10

5.       Conclusion. 12

 

1.   Introduction

Design Patterns are common to well-designed Object-Oriented software designs. They make a design more flexible and easier to read – if common patterns are used in a suitable way.

If careful attention is paid to the used patterns and the collaboration among the objects there are much less traps to tap into. The software will become easier to maintain. Easier maintenance is cheaper maintenance – this is possibly the key to successful software. Nobody wants genius software that is not maintainable after the inventor has left the company.

Many problems have occurred countless times in software development history. It is a good idea to have a look for common solutions to them – this is exactly what Design Patterns are all about. The problem is to find suitable patterns for the job to be done. Nobody will be able to know all of the available Design Patterns and nobody will be able to find always the perfect one – but this is not necessary. It is simply necessary to combine well fitting pattern to do the job.

1.1.   Software quality

Software quality is an important issue. Customers ask for high quality software – programmers try to create more and more complex solutions to meet these requirements. The eXtreme Programming[1](XP) approach tries to find solutions for this dilemma.

On the other hand, Design Patterns are used by programmers for solutions to well known problems. This may also offer high-quality design of software.

So why not taking both approaches together? At the first view these two approaches are not combinable – a core directive of XP is “keep it simple”. But another idea behind XP is “Refactoring”. The idea is to start with a simple design at first and to refactor the implementation where necessary. This will result in a design that uses complex Design Patterns only where really necessary and useful to fulfil the requirements.

2.   eXtreme Programming (XP)

XP is a new way to develop software. It was introduced by Kent Beck and Erich Gamma. The basic ideas behind XP are [3]:

Ø      The planning process – The customer is heavily involved in decisions what needs to be done and what needs to be deferred.

Ø      Small Releases – XP teams put a simple system into production early, and update it frequently on a very short cycle.

Ø      Metaphor – XP teams use a common “system of names” and a common system description that guides development and communication.

Ø      Simple Design – A program built with XP should be the simplest solution meeting the requirements.

Ø      Testing – XP teams focus on a continual validation of the software at all times. The tests of new software are written first.

Ø      Refactoring – XP teams improve the design of the software on a continuing and forth going development.

Ø      Pair Programming – XP programmers write all production code in pairs – two people working on one PC together.

Ø      Collective Ownership – The code is belonging to the whole development team, not to a concrete person. Everyone can change and improve every class.

Ø      Continuous Integration – XP teams integrate and built a whole system to be developed multiple times a day and run tests.

Ø      40-hour week – Fresh programmers are an essential part of the XP idea. No overworked employees.

Ø      On-site Customer – It is essential that there is someone around who is from the customer side to help the programmers solving problems in the earliest time of the implementation.

Ø      Coding Standard – An XP team must use the same coding syntax for indentation and naming. This is a necessity for pair programming and collective ownership.

For the development of the extensions to the radio simulator it was not possible to apply all of the XP ideas. The ideas “Small Releases”, “Testing”, “Refactoring” and “Continuous Integration” had been used for the implementation of the changes to the radio simulator. Most of the others were not possible due to missing partners and time.

The XP approach should provide a faster development with higher quality software. This was validated with the implementation of the storage system for the radio simulator.

3.   Development using XP

The first steps in XP are explained below. I tried to use the XP approach to implement the storage system changes to the radio simulator from the first assignment.

3.1.   Main Storage

The already implemented source was analysed to check where the changes would take place and the according test cases were written in the RadioModelImpl_Test. The storage system was moved out of the RadioModelImpl class into the MemoryStorage class and a new class StationEntry and StationEntry_Test was added. Moving the existent RadioModelImpl_Test into the MemoryStorage_Test created the new test case MemoryStorage_Test.

After the rearrangement of the already existent source code to the new structure was finished, the TestRunner was used to check if the move of the code has succeeded. After doing some fast round trips with testing and error correction the rearrangement was proved to be working.

Figure 1 The successful test run after refactoring the storage

The storage system was extended by two exceptions for two of the faults found during the tests – NotInStorageException and StorageFailedException to ease the access to the storage.

3.2.   Persistent Storage

For the extension of the radio to support either volatile or persistent storage, the core methods of MemoryStorage were moved to the new interface Storage. The new test case PersistentStorage_Test was defined which simply mirrored the implementation of the MemoryStorage_Test. The PersistentStorage class was implemented to support exactly the same functionality as the MemoryStorage class.

With various round trips in testing and error correction the PersistentStorage class came to work pretty fast.

The RadioModelImpl_Test and RadioModelImpl were changed to reference only an abstract Storage object.

3.3.   Navigational extension

The changes for the navigational support did take more effort, as it was necessary to create a complete GPS environment and accessor for the graphical environment to access the navigational functionality.

The GPSPosition_Test class was implemented using two methods to parse and get a GPS position from/to String into a double representation of latitude and longitude. The class GPSPosition.parsePosition(String pos) was implemented and checked by the TestRunner.

The next methods to support the retrieval of readable GPS positions was generated and checked.

3.3.1.        Problems with test cases

During the implementation of these two methods a big problem with the JUnit testing came up – it was not necessary to support tolerances in the results. The given implementation used two double values to save the latitude and longitude provided in the following way as a String: “HH°MM°SS N/S HH°MM’SS W/E”. Using double, the IEEE floating point representation tolerance was too big to support all digits of this data. So the resulting string / double conversion had a known tolerance. It was necessary to implement tolerance into the test cases to support the IEEE floating-point tolerances.

Figure 2 The GPS position representation as String

After these problems were solved more or less (applying tolerance to the test cases by hand or just testing for values not having tolerance), the implementation of the navigation support was finished with a NavigationEntry using the GPSPosition class. As NavigationEntry and StationEntry are both of the type Entry, the corresponding abstraction was created. The Entry_Test was created to simply store and retrieve objects from the storage.

3.3.2.        Possibly unnecessary test cases

XP states that every class needs a test case – during the implementation of the model parts there was a lot of effort necessary to create suitable tests for the functionalities already present and the newly added functions. But as the design already in place was a complete MVC design, the implementation of the view did not have any intelligence, thus there is actually no real need for writing test cases. Due to this simplicity the tests for the view elements have not been added.

3.4.   Refactoring

After the navigational part of the radio was working, the system design was reviewed – the decision was made to use an abstract factory for the creation and the access to the storage systems. This provides a simple and unified access to the storage system and its instantiations.

The test case NavigationRadio_Test was created requesting a functionality to create a storage system of the given or already instantiated type. The StorageFactory class was implemented and each method to create a Storage object was developed using round trips with TestRunner. This did very fast emerge into an implementation that was fully checked for both, PersistentStorage and MemoryStorage.

    public void testMemoryStorage() {
        StorageFactory.setStorage("MEMORY");
        de.mhoesch.radiosim.model.Storage storage = StorageFactory.getStorage();
        assertNotNull(storage);
        MemoryStorage ps = null;
        if (storage instanceof MemoryStorage) {
            ps = (MemoryStorage) storage;
        }
        assertNotNull(ps);
        memoryStorage = storage;
    }

    public void testPersistentStorage() {
        StorageFactory.setStorage("FILE");
        de.mhoesch.radiosim.model.Storage storage = StorageFactory.getStorage();
        assertNotNull(storage);
        PersistentStorage ps = null;
        if (storage instanceof PersistentStorage) {
            ps = (PersistentStorage) storage;
        }
        assertNotNull(ps);
        persistentStorage = storage;
    }

During implementation and testing it came clear that an additional change was necessary – the implementation of a factory method pattern. The correct instantiation of a StorageEntry was no longer possible. It was not possible to cleanly determine which kind of Storage implementation was in use for which entry at which time (the abstract factory could be changed to another underlying Storage implementation without notification to the radio simulator).

The interface Storage and the implementations PersistentStorage and MemoryStorage were changed to be able to create new empty StorageEntry objects. The according test cases were adjusted and the tests did run immediately.

The complete overview of the design patterns that emerged during the development is given in the next chapter.

4.   Design Patterns

During the refactoring that took place during the implementation of the new requirements using the XP approach the system design emerged to the following usage of Design patterns.

4.1.   The Model View Controller pattern

The Model View Controller (MVC) pattern is an architectural design pattern. A detailed description of the pattern would exceed this assignment. As a result only a simple overview is given below. More information about this pattern can be found in [1] and [4].

A model can have numberless views. Those views are controlled by controllers, which will apply necessary changes to the model. The core idea behind this architecture is to separate data, processing and view into different instances or layers.

The benefits from this implementation are a very expandable software design. If new views of the same data are necessary, they can simply be added without changing any line of code in the model. If the model changes data, all views will receive a notification. The controllers will handle the processing of information that has to be sent to the model.

Figure 3 The already existent radio simulator MVC design

In the already existent radio simulator implementation (from the first assignment), the controllers are used to handle the GUI events from the views and the necessary transformation to the model. The views only display what they receive from the model.

4.2.   The Variants pattern

The Variant pattern is used for the access to the storage system, the entries and the storage entries. The reason to use a Variant pattern is to separate the specification from the implementation.

Figure 4 The Variants used to extend the radio simulator

The abstractions in the radio simulator are used to generalize the access to the storage system. As shown above, all kinds of accesses to the storage system are hidden behind the abstract classes (or interfaces).

The Variants used are: Entry, StorageEntry and Storage. They separate the implementation of the storage system and its entries from the used implementation as described above.

4.3.   The Bridge pattern

The Bridge pattern is used for the station memory. This implementation enables the later extension to other memory entries. In the given radio simulation the implementation has been extended to support persistent entries. The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently [2].

Figure 5 The Bridge implementation of the Entry implementations

The interface to store entries is provided as an abstract class, the StorageEntry class. This class does define the necessary methods to store and retrieve entries from the concrete storage, which is accessed through the Storage interface (see below).

4.4.   The Abstract Factory pattern

The Storage class defines methods to store and retrieve entries of the type Entry.

The Abstract Factory pattern is useful to abstract different implementations of a class from its interface. If a proper abstract interface to a class is given, it is easy to support different implementations of this interface – it provides an interface for creating families of related or dependent objects without specifying their concrete classes [2].

Figure 6 The Abstract Factory implementation of the Storage system

In the case of the radio simulation, the abstract factory is useful to hide the used storage system from the current implementation of the storage. At the first access to the memory the user will be asked to select a persistent or volatile storage system. It would also be possible to change the storage system during runtime.

The Abstract Factory provides methods to create concrete objects and return references to the abstract class definitions. The used concrete class implementation is hidden and never referred to. Only the abstract class definition is known and used.

4.5.   The Factory Method pattern

After the implementation of the Abstract Factory pattern a problem came into view. The creation of the necessary StorageEntry objects was not possible due to missing information of the used storage implementation. It was no longer clear, which concrete implementation of the StorageEntry class should be used (see 3.4).

The only simple and clean solution is to add a new method to the Storage interface. Each implementation of the Storage interface has to create an empty Entry object by its own. This is a pattern known as the Factory Method pattern - which simply defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer the instantiation to subclasses [2].

Figure 7 The implementation of the factory method in the storage classes

As shown in figure 8, a method createStorageEntry() (highlighted) has been added to the Storage interface, which will be executed in the concrete implementation of the storage system to create the correct object of type PersistentEntry or VolatileEntry.

This pattern was used, because a class accessing the storage can’t determine which implementation of the storage is used and the responsibility of creating the correct object is delegated to the storage implementation.

5.   Conclusion

The usage of XP for the first time is a very time consuming task. The writing of test cases, the ideas about what to be done and then to write a test for a new class implementation before creating the implementation is a real pain – if someone is used to write the class first and the test afterwards.

One the other hand, the software quality that results from the XP, especially using the refactoring approach is very good. Simple tasks are still performed using simple solutions while more complex tasks automatically morph by moving and refactoring. The test methods always ensure that the refactored code works as expected. The later implementation of the patterns was faster than expected from the amount of modifications to be done in different parts of the system design and the test cases.

Some Design Patterns are sometimes hard to understand in the first view. After having a problem just asking for one of them, it is pretty simple to understand how they work. The usage of XP also helps to eliminate the unnecessary usage of complex design and Design Patterns. Without XP it would be necessary to use Design Patterns much earlier in development to support expandability – due to the missing tests and the fear not to be able to guarantee the functionality after larger changes to the system.

But the main problem with XP is simply getting used to it and getting speed into it. Probably the speed will increase over time – but if this time is not given to a programmer, it is simply not possible to start over with XP. Design Patterns on the other hand can be included in software at the design phase without the need for much training.

 


[1]        Glenn E. Krasner and Stephen T. Pope. “A cookbook for using the model-view controller user interface paradigm in Smalltalk-80”
Journal of Object-Oriented Programming, August/September 1988

[2]        Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. “Design Patterns – Elements of Reusable Object-Oriented Software”
Addison Wesley, 1995

[3]        Erich Gamma, Beck Kent. “eXtreme Programming”, 1999-2000
www.xprogramming.com

[4]        Assignment 1
”Object-Oriented Programming in Java”
Marc Höschele, 2000



[1] eXtreme Programming (XP) was introduced by Erich Gamma and Kent Beck. It will be described in chapter 2.