SOFTWARE DESIGN, CHARACTERISTICS OF GOOD SOFTWARE DESIGN

SOFTWARE DESIGN

During the software design phase, the design document is produced, based on the customer requirements as documented in the SRS document. 

We can state the main objectives of the design phase, in other words, as follows. 

The activities carried out during the design phase called as design process transform the SRS document into the design document. 

This view of a design process has been shown schematically in Figure 5.1. As shown in Figure 5.1, 

the design process starts using the SRS document and completes with the production of the design document. 

The design document produced at the end of the design phase should be implementable usin

OVERVIEW OF THE DESIGN PROCESS 

The design process essentially transforms the SRS document into a design document. 

Outcome of the Design Process 

The following items are designed and documented during the design phase.

Different modules required: 

The different modules in the solution should be clearly identified. Each module is a collection of functions and the data shared by the functions of the module. 

Each module should accomplish some well-defined task out of the overall responsibility of the software. Each module should be named according to the task it performs. 

For example, in an academic automation software, the module consisting of the functions and data necessary to accomplish the task of registration of the students should be named handle student registration. 

Control relationships among modules: A control relationship between two modules essentially arises due to function calls across the two modules. 

The control relationships existing among various modules should be identified in the design document. 

Interfaces among different modules: 

The interface between two modules identifies the exact data items that are exchanged between the two modules when one module invokes a function of the other module. 

Data structures of the individual modules: 

Each module normally stores some data that the functions of the module need to share to accomplish the overall responsibility of the module. 

Suitable data structures for storing and managing the data of a module need to be properly designed and documented. 

Algorithms required to implement the individual modules:

 Each function in a module usually performs some processing activity. 

The algorithms required to accomplish the processing activities of various modules need to be carefully designed and documented with due considerations given to the accuracy of the results, space and time complexities. 

Starting with the SRS document (as shown in Figure 5.1), the design documents are produced through iterations over a series of steps that we are going to discuss in this chapter and the subsequent three chapters. 

The design documents are reviewed by the members of the development team to ensure that the design solution conforms to the requirements specification. 

Classification of Design Activities 

A good software design is seldom realised by using a single step procedure, rather it requires iterating over a series of steps called the design activities. 

Let us first classify the design activities before discussing them in detail. 

Depending on the order in which various design activities are performed, we can broadly classify them into two important stages. 

• Preliminary (or high-level) design, and 

• Detailed design. 

The meaning and scope of these two stages can vary considerably from one design methodology to another. 

However, for the traditional function-oriented design approach, it is possible to define the objectives of the high-level design as follows: 

Through high-level design, a problem is decomposed into a set of modules. The control relationships among the modules are identified, and also the interfaces among various modules are identified. 

The outcome of high-level design is called the program structure or the software architecture. 

High-level design is a crucial step in the overall design of a software. 

When the high-level design is complete, the problem should have been decomposed into many small functionally independent modules that are cohesive, have low coupling among themselves, and are arranged in a hierarchy. 

Many different types of notations have been used to represent a high-level design. 

A notation that is widely being used for procedural development is a tree-like diagram called the structure chart. 

Another popular design representation techniques called UML that is being used to document object-oriented design, involves developing several types of diagrams to document the object-oriented design of a system. 

Though other notations such as Jackson diagram [1975] or Warnier-Orr [1977, 1981] diagram are available to document a software design, we confine our attention in this text to structure charts and UML diagrams only. 

Once the high-level design is complete, detailed design is undertaken. 

During detailed design each module is examined carefully to design its data structures and the algorithms. 

The outcome of the detailed design stage is usually documented in the form of a module specification (MSPEC) document. 

After the high-level design is complete, the problem would have been decomposed into small modules, and the data structures and algorithms to be used described using MSPEC and can be easily grasped by programmers for initiating coding. 

Classification of Design Methodologies 

The design activities vary considerably based on the specific design methodology being used. 

A large number of software design methodologies are available. 

We can roughly classify these methodologies into procedural and object-oriented approaches. 

These two approaches are two fundamentally different design paradigms. 

Even while using the same design methodology, different designers usually arrive at very different design solutions. 

The reason is that a design technique often requires the designer to make many subjective decisions and work out compromises to contradictory objectives. 

As a result, it is possible that even the same designer can work out many different solutions to the same problem. Therefore, obtaining a good design would involve trying out several alternatives (or candidate solutions) and picking out the best one. 

The goal of any analysis technique is to elaborate the customer requirements through careful thinking and at the same time consciously avoiding making any decisions regarding the exact way the system is to be implemented. 

The analysis results are generic and does not consider implementation or the issues associated with specific platforms. 

The analysis model is usually documented using some graphical formalism. 

In the case of the function-oriented approach that we are going to discuss, the analysis model would be documented using data flow diagrams (DFDs), whereas the design would be documented using structure chart. 

On the other hand, for an object-oriented approach, both the design model and the analysis model will be documented using unified modelling language (UML). 

The analysis model would normally be very difficult to implement using a programming language. 

The design model is obtained from the analysis model through transformations over a series of steps. 

In contrast to the analysis model, the design model reflects several decisions taken regarding the exact way system is to be implemented. 

The design model should be detailed enough to be easily implementable using a programming language. 

HOW TO CHARACTERISE A GOOD SOFTWARE DESIGN ? 

The definition of a “good” software design can vary depending on the exact application being designed. 

For example, “memory size used up by a program” may be an important issue to Characterise a good solution for embedded software development—since embedded applications are often required to work under severely limited memory sizes due to cost, space, or power consumption considerations. 

For embedded applications, factors such as design comprehensibility may take a back seat while judging the goodness of design. Thus for embedded applications, one may sacrifice design comprehensibility to achieve code compactness. 

Similarly, it is not usually true that a criterion that is crucial for some application, needs to be almost completely ignored for another application. 

It is therefore clear that the criteria used to judge a design solution can vary widely across different types of applications. 

Not only do the criteria used to judge a design solution depend on the exact application being designed, but to make the matter worse, there is no general agreement among software engineers and researchers on the exact criteria to use for judging a design even for a specific category of application. 

However, most researchers and software engineers agree on a few desirable characteristics that every good software design for general applications must possess. 

These characteristics are listed below: 

Correctness: 

A good design should first of all be correct. That is, it should correctly implement all the functionalities of the system. 

Understandability: 

A good design should be easily understandable. Unless a design solution is easily understandable, it would be difficult to implement and maintain it. 

Efficiency: 

A good design solution should adequately address resource, time, and cost optimisation issues. 

Maintainability: 

A good design should be easy to change. This is an important requirement, since change requests usually keep coming from the customer even after product release. 

Understandability of a Design: 

A major concern while performing the design of a certain problem, assume that we have arrived at a large number of design solutions and need to choose the best one. 

Obviously all incorrect designs have to be discarded first. Out of the correct design solutions, how can we identify the best one? Given that we are choosing from only correct design solutions, understandability of a design solution is possibly the most important issue to be considered while judging the goodness of a design. 

Recollect from our discussions in Chapter 1 that a good design should help overcome the human cognitive limitations that arise due to limited short-term memory. A large problem overwhelms the human mind, and a poor design would make the matter worse. 

Unless a design solution is easily understandable, it could lead to an implementation having a large number of defects and at the same time tremendously pushing up the development costs. 

Therefore, a good design solution should be simple and easily understandable. A design that is easy to understand is also easy to develop and maintain. 

A complex design would lead to severely increased life cycle costs. Unless a design is easily understandable, it would require tremendous effort to implement, test, debug, and maintain it. 

If the software is not easy to understand, not only would it lead to increased development costs, the effort required to maintain the product would also increase manifold. 

Besides, a design solution that is difficult to understand would lead to a program that is full of bugs and is unreliable. 

An understandable design is modular and layered How can the understandability of two different designs be compared, so that we can pick the better one? To be able to compare the understandability of two design solutions, we should at least have an understanding of the general features that an easily understandable design should possess. A design solution should have the following characteristics to be easily understandable: It should assign consistent and meaningful names to various design components. It should make use of the principles of decomposition and abstraction in good measures to simplify the design. We had discussed the essential concepts behind the principles of abstraction and decomposition principles in Chapter 1. But, how can the abstraction and decomposition principles are used in arriving at a design solution? These two principles are exploited by design methodologies to make a design modular and layered. (Though there are also a few other forms in which the abstraction and decomposition principles can be used in the design solution, we discuss those later). We can now define the characteristics of an easily understandable design as follows: A design solution is understandable, if it is modular and the modules are arranged in distinct layers. A design solution should be modular and layered to be understandable. We now elaborate the concepts of modularity and layering of modules: Modularity A modular design is an effective decomposition of a problem. It is a basic characteristic of any good design solution. 

A modular design, in simple words, implies that the problem has been decomposed into a set of modules that have only limited interactions with each other. 

Decomposition of a problem into modules facilitates taking advantage of the divide and conquer principle. 

If different modules have either no interactions or little interactions with each other, then each module can be understood separately. 

This reduces the perceived complexity of the design solution greatly. To understand why this is so, remember that it may be very difficult to break a bunch of sticks which have been tied together, but very easy to break the sticks individually. 

It is not difficult to argue that modularity is an important characteristic of a good design solution. But, even with this, how can we compare the modularity of two alternate design solutions? 

From an inspection of the module structure, it is at least possible to intuitively form an idea as to which design is more modular 

For example, consider two alternate design solutions to a problem that are represented in Figure 5.2, in which the modules M1 , M2 etc. have been drawn as rectangles. 

The invocation of a module by another module has been shown as an arrow. 

It can easily be seen that the design solution of Figure 5.2(a) would be easier to understand since the interactions among the different modules is low. 

But, can we quantitatively measure the modularity of a design solution? Unless we are able to quantitatively measure the modularity of a design solution, it will be hard to say which design solution is more modular than another. 

Unfortunately, there are no quantitative metrics available yet to directly measure the modularity of a design. 

However, we can quantitatively characterise the modularity of a design solution based on the cohesion and coupling existing in the design. 

A design solution is said to be highly modular, if the different modules in the solution have high cohesion and their inter-module couplings are low. 

A software design with high cohesion and low coupling among modules is the effective problem decomposition. 

Such a design would lead to increased productivity during program development by bringing down the perceived problem complexity. 

Figure 5.2: 

Two design solutions to the same problem. Based on this classification, we would be able to easily judge the cohesion and coupling existing in a design solution. 

From a knowledge of the cohesion and coupling in a design, we can form our own opinion about the modularity of the design solution. 

We shall define the concepts of cohesion and coupling and the various classes of cohesion and coupling in Section 5.3. 

Let us now discuss the other important characteristic of a good design solution layered design. 

Layered design 

A layered design is one in which when the call relations among different modules are represented graphically, it would result in a tree-like diagram with clear layering. 

In a layered design solution, the modules are arranged in a hierarchy of layers. A module can only invoke functions of the modules in the layer immediately below it. 

The higher layer modules can be considered to be similar to managers that invoke (order) the lower layer modules to get certain tasks done. 

A layered design can be considered to be implementing control abstraction, since a module at a lower layer is unaware of (about how to call) the higher layer modules. 

A layered design can make the design solution easily understandable, since to understand the working of a module, one would at best have to understand how the immediately lower layer modules work without having to worry about the functioning of the upper layer modules. 

When a failure is detected while executing a module, it is obvious that the modules below it can possibly be the source of the error. 

This greatly simplifies debugging since one would need to concentrate only on a few modules to detect the error. 

Leave a Reply

Your email address will not be published.

Previous post LAYERED ARRANGEMENT OF MODULES 
Next post APPROACHES TO SOFTWARE DESIGN