• Ei tuloksia

3.1 M ETHOD

3.1.1 Input

Each software system is designed to serve a certain purpose. This purpose is formulated in functional requirements. As GAs cannot synthesize the actual purpose of the software system, the specification of functional requirements must be done manually. However, only the minimal amount of effort should go into elaborating the requirements, as the idea is to exploit the power of the GA as much as possible. The functional requirements of the system under design are gathered in the way described in Section 2.1. To give a complete view of the synthesis process, I will here use a new example for ehome.

Specifying requirements begins with giving use cases. Here, I will take as an example the “adjust room temperature” use case for ehome. The use case is illustrated in Figure 5. The user simply places a command that the temperature should be adjusted (for the sake of simplicity, we can here consider elevation), and ehome adjusts the temperature by turning on the heater.

The sequence diagram for the temperature adjustment use case is given in Figure 6. The process begins with a call from the user to set the temperature to a new level. The system then calls the temperature regulation component, which measures the current temperature, and then sets the heater on. After the correct temperature is reached, the heater is turned off.

While sequence diagrams already give a good understanding of how the different operations depend on each other, a structural view still needs to be obtained, as patterns cannot be inserted into sequences of calls.

Fortunately, sequence diagrams can easily be turned into class diagrams.

At this point, the class diagram would not consist of anything but the classes, their methods and attributes, and connections between classes, as defined in the sequence diagram.

53

Figure 5. Use case for ehome (adjust temperature)

Figure 6. Sequence diagram for adjust temperature use case

Transforming a simplistic call sequence to a class diagram is straightforward. Using the temperature adjustment use case as an example, the class diagram would have three classes, the UI, the Temperature Regulation and the Heater Manager. It should be noted that

54

the “UI” considers here all user interface related matters, and handles all calls to the actual functional components. The UI may, in practice, contain several classes for handling all the user interface requirements. However, these implementation details are omitted here.

Calls between the different objects in the sequence diagram would be made into methods inside these classes. Information relating to temperature state would be an attribute in the Temperature Regulation class, and the class would contain accessor methods for it. The Heater Manager class would similarly have attributes for its state. In the class diagram, there would be “use” relations between the UI and Temperature Regulation, and between Temperature Regulation and Heater Manager classes (as according to the sequence diagram). The example class diagram for this use case is given in Figure 7.

Figure 7. Class diagram example for ehome (temperature control)

Collecting all use cases would ultimately produce a complete class diagram of the system. This complete class diagram is called here the null architecture. As described, the null architecture is extremely simplistic. It does not contain any information about implementation or any specific design choices. The null architecture is merely a picture which gives a certain view of the gathered functional requirements.

The null architecture for ehome is given in Figure 8. Although here only classes are given, each class also provides an interface which is implemented by all operations (in that class) that are required by other classes. These interfaces can be seen as part of good design/coding practice, and not a part of actual design. Similarly, while it is assumed that attributes have accessor (getX() and setX()) methods, they are not portrayed in the null architecture, as they are considered as part of the coding process and standard good practice.

55

Figure 8. Null architecture for ehome

It should also be noted that while the calls given in the sequence diagram are marked as operations (methods) in the resulting class diagram, this does not dictate the actual implementation. In practice, each operation can, and often will, be implemented as a set of more detailed methods. The operations as used here describe functional entities (responsibilities) of the system.

Two case studies are used in this thesis, one of them being ehome, which has been used as a running example. The other case study is robo, which has been briefly discussed in Subsection 1.4. The null architecture for robo can be elicited through use cases and sequence diagrams (similarly to ehome). These steps will not be repeated here, but in order to compare the resulting architectures produced by the GA to the starting point, the null architecture for robo is given in Figure 9.

56

Figure 9. Null architecture for robo

In addition to simply specifying the functional requirements as a set of operations and dependencies between them, some characteristics of the operations can also be evaluated. For example, in the case of ehome, it can quite easily be deduced that calculating an optimal positioning for drapes requires much more data than, say, turning on the heater. Similarly, music is probably played much more often than a password needs to be changed.

From the software development point of view, it is not difficult to predict that it is quite probable that several different options can be created for showing the music list or the coffee machine status. On the other hand, water will probably always be added to the coffee machine in a similar manner, and there will not be a need to provide optional implementations for that. Doing similar comparisons between all operations, relative values for parameter size, frequency of use and sensitiveness to variation (called hereafter simply variability) can be estimated. These, in turn, aid in achieving a more accurate evaluation of the architecture’s quality.

57

3.1.2 Encoding

The information given in the null architecture must be encoded for the GA to follow the biological analogies. There are two kinds of data regarding each operation oi: basic information (given as input) and architectural information. The basic input information contains the operations Oi = {oi1, oi2, …, oik} depending on oi, as can be deduced from the sequence diagram.

An operation ok depends on oi if oi is preceded by ok in the sequence diagram. Furthermore, the basic information contains the name ni, type di, frequency fi, parameter size pi, variability vi and the predetermined null architecture class MCi of the operation oi. Naturally, the null architecture class is also architectural information, but it is specified in the input and not modified by the GA. Frequency, parameter size and variability can be given relative values: 1 for low, 3 for medium and 5 for high.

The architectural information, in turn, contains data regarding operation oi’s “place” in the architecture: the class Ci it belongs to, the interface Ii it implements, the message dispatcher Di it uses, the operations ODi ⊆ Oi

that call it through the dispatcher, and the design pattern Pi it is a part of.

The dispatcher is given a separate field as opposed to other patterns for practical reasons. In practice, in the current implementation there is only one possible dispatcher that can be used, but the encoding leaves an opportunity to easily extend the approach so that several dispatchers may be present in the system.

All the information defined above is gathered together into one supergene, where each data particle is given a separate field. Thus, one supergene represents one operation in the system. Figure 10 depicts a supergene sgi. If n is the number of operations in a system, the collection <sg1, sg2, …, sgn>

of these operations defines the entire system when collected into one chromosome (the chromosome thus being a vector of supergenes).

Figure 10. Supergene sgi

Again, I use ehome as an example. In the null architecture phase, if the TemperatureRegulation class is given #ID 2 (and the interface #ID 2), the supergene for the operation “measureTemperature” (#id 9) would have the following values: O9 = #idSetRoomTemperature, n9 = measureTemperature, d9 = f (as in functional), p9 = 3, f9 = 1, v9 = 3, C9 = 2, I9 = 0, D9 = 0, OD9 = 0, MC9 = 4, P9 = 0. The interface has value 0, as measureTemperature is only required by setRoomTemperature, which is in the same class, and thus does need an interface to access this operation.

The fields for message dispatcher and pattern have values 0, as no architectural solutions are included in the null architecture. As the

58

operation is here located in the original null architecture class, the values for C and MC are the same.

Note, that the encoding is indeed operation-centered. Thus, modifications to the architecture are considered from the viewpoint of how one particular operation can be accessed, and not how two classes communicate with each other. In practice, the null architecture is encoded into a text file, which is given as input for the algorithm, with each operation in its own line.

After encoding is done, the genetic synthesis can begin. However, a single base solution is not enough, as the GA requires a whole population to work with. The initial population is created by cloning the “base chromosome”, i.e., null architecture, a desired number of times (in this work, 100 is used as the default), and then mutating each of the clones once. Thus, the initial population does not only contain clones, but has some versatility.