Summary: An introduction to design and software design, in order to set up an underlying, supportive foundation to help the reader recognize both the relevance and the advantages of software design.
Before setting up to the task of studying and applying Software Architecture, it is convenient to know where – within the range of Software Engineering Body of Knowledge – Software Architecture should be placed as a discipline. Besides, the implementation of Software architectural design is the first of two activities that comprise the Software Design Knowledge Area (the other activity is that of software detailed design). As part of Software Design, Software Architecture is a mixture of method and creativity. Software Architecture lies beyond the range of this book whose purpose does not include the teaching of creativity. Creativity is something that results from experience, thus it is not the purpose of this book teach it. However, the authors seek to impart the knowledge needed for you to create software system architectures.
A sound knowledge of the fundamentals of Software Design is certainly needed for you to take up the material covered in this book. Therefore, this chapter will provide you with an overall view of these fundamentals, expounding them further by setting up an underlying, supportive foundation to help you recognize both the relevance and the advantages of software design. In other words, it will provide you with the necessary knowledge that will enable you to:
The relevance of the practice (and, therefore, of the study) of Software Design can be explained by the ever-increasing complexity of software systems. Because of such complexity, the risk of building a system that will not meet its goals is indeed too high.
In order to avoid such a risk, the rule of thumb of any engineering process for building a complex artifact is to build it according to a pre-established plan, e.g., to design it before building it. Therefore, the purpose of design is twofold. One is to allow you to build models that can be analyzed and evaluated against their aimed objectives before they are actually built; and the other is to serve as a guide for the artifact assemblage after it has passed both the analysis and the evaluation phases.
There are several reasons for designing an artifact before building it. The following paragraphs describe some of them.
Design enables early evaluation. The building process of an artifact can be very expensive in terms of time and money. Because it helps to ensure that the right thing is being built, those involved in this process desire the possibility of early evaluation. Since the products of design process often permit this kind evaluation and it often costs less than actually building the artifact, designing and early evaluating the artifact before building are recommended in order to save time and money.
Design demands modeling. When modeling, the designer's focus is on the problem, letting apart some complexity inherent to the final product. Since complexity is one of the greatest difficulties of artifact development 1, any practice that helps to prevent is also highly recommended.
Design involves planning. Because of this, the design process helps on estimating costs, e.g., how long it will take to build the artifact, how many men will be necessary to build it, what existing parts can be reused in current design, etc., and adds control to development.
Lastly, design facilitates communication. A design provides a common language in which knowledge about the artifact can be recorded, transmitted and discussed. So, if the artifact development involves more than one person and communication among the involved is desired, it is recommended to leverage design in order to assist communication.
In addition to these characteristics, we introduce two important points, which must be kept in mind while designing.
First, designing an artifact may be cheaper than building it, but is not cheap. Design takes time and money and must be performed with care and method. We will further see that design is made to achieve goals, but nothing ensures that the design goals will remain the same by the end of the design process. Consequently, this possible transient nature of design goals can bring risk to the design process and lead to failure, especially when performed by those not aware of this aspect of design goals.
Second, the design of an artifact is not the true artifact. This means that design evaluation against its goals cannot be perfect and its results must be analyzed carefully – always keeping in mind that there will be some intricacies out of the design that can deeply impact its success and, so, must not be completely ignored.
Please notice that these points above will be better illustrated when we restrict the subject of this chapter to design of software.
Since design is a rather vast discipline, it is wise to pin down our subject. Authors usually define design in two steps: when it is used as a subject and when it is used as a verb. When used as a subject, design “indicates the [product] that emerges from the design process, some physical document or other kind of representation that articulates the intent of the designer. This product results from the choices the designer made, choices that form an abstraction of that what is eventually desired in the real world.” Moreover, as a verb, to design “indicates the process by which a design is achieved. It is a human-centered process, involving varied stakeholders. It is also understood to be strongly goal-driven and drawing upon established knowledge of the designer and the field at large.” Entry 9
Turning our attention to software design, we must make just a small (and obvious) specialization on design definition: the product of software design turns out to be a software system representation. This way, a software design may typically describe many aspects of the software system. Budgen identifies some of these aspects Entry 6:
Because designing all these aspects of a software system might be hard work, its benefits must be brought to light. We soon see that these benefits are analogous to the benefits of general design. Software development consumes time and money. Also, it is quite obvious that no one wants to spend his or her resources in developing software that solves the wrong problem. Considering this, just like when we were generally describing design, the possibility of early evaluation is desirable because it helps to ensure the stakeholders the development of the right program. Software design enables this early evaluation, because its product describes several aspects of the final program. Also, it is often less expensive to obtain than the whole program.
When modeling a software system, thus designing it, the designer focuses on the domain problem. This enables the designer to distinguish the problem's essential complexity from the accidental one. As stated by Brooks Entry 5, this separation is known to impact positively on the quality of the final program.
Just like general design, software design helps on estimating costs, e.g., how long all the development process will be, how many implementers will be necessary for a specific module, should a module be implemented or bought, what group is responsible for implementing a specific module, and so on.
Finally, because it contains knowledge from the system that can be recorded, transmitted, and discussed, software design can act as a communication vehicle. For example, if a software system is to be presented to a new member of the development team, before making her dive into the code, valuable information can be passed to her via the software design. Moreover, if the audience of this presentation is a program user, information (often abridged) from the design is even more necessary, since it may not make sense showing software source code to this kind of audience.
Nevertheless, some important observations related to these characteristics must be made.
While the design process occurs, it is possible that the person who wants the software to be built for solving her problem (the client) (1) change her mind about the very problem, (2) describe her problem incorrectly, or even (3) consider that the problem itself changed, or have been solved, during the design process. All these three possibilities must be kept in mind because they may lead the designer to fail on achieving the client's objectives and eventually to waste time and money.
Since a software design is just a model, its detail level may not be adequate for certain kinds of evaluation. In fact, evaluating a software design without enough details can lead to wrong results and then to wrong programs. This is quite common when evaluating performance, especially when bad designers consider insufficient detail for this purpose. For example, a performance design evaluation stated that a specific system was able to cope with a thousand users accessing it simultaneously. However, this very design did not add enough details on the actions performed by users. The case was that the performance analysis was made considering that each action consumed less resources than they in fact did after implemented. As expected, this system failed miserably when the number of concurrent users started to grow near the expected limit of a thousand users.
Finally, although a software design is correct, it may not be followed as such during its implementation. So, we must be aware that wrong implementation of a design can lead to wrong software just like a wrong design would.
We understand that software design can lead to some drawbacks that are not to be ignored, but its advantages, as we have seen, are very relevant too. Considering all this, we believe that understanding the fundamentals of (software) design, as well good practices, guidelines, and enabling techniques are essential to an aspiring architect.
The design process can be described as the process of choosing a representation of a solution from a set of alternatives, given the constraints towards a set of goals. It can be divided in two phases Entry 2: diversification and convergence.
The diversification is the phase of generating alternatives. Not necessarily documents describing a possible solution, but, at least, on the designer's mind. These alternatives are the solution candidates and are generated/obtained from knowledge, catalogs, or previous experience. During the convergence phase, the designer chooses the alternative (or the combination of alternatives) that meets the intended goals. The alternative selected will be the solution, which will meet the constraints imposed by the problem domain. The solution then may be described using some representation. The representation chosen must fit its purpose: describe the solution and the process to build the artifact that reaches the intended goals.
From the paragraph above, we must emphasize the following elements: goals, constraints, alternatives, representations, and solutionsEntry 8. These elements together define a conceptual framework that helps to understand the software design process.
Design begins with a need. If something is to be designed, though to be built, it is because the outcome of the design process will fulfill this need. In Software Engineering, the necessity starts from a customer who specifies what are her needs and therefore what are the goals to be achieved with the software system to be designed. So, the goal of the design process is to achieve a solution that will solve the customer's needs. In software design, goals are also referred to as requirements. Software design is mainly concerned on two types of requirements: functional and nonfunctional requirements.
A functional requirement specifies the functionality that a system will exhibit. In other words, what the system is to perform according to the customer. For example, a functional requirement for a sorting program is to provide the ability to sort integers. Another example could be related to a software system that manages the inventory of a movie rental store. If we were to enumerate the functional requirements of a system like this, among them would be: the ability to search for a movie by its keywords, the ability to perform a movie rental, the ability to perform a movie return, and many others.
On the other hand, a nonfunctional requirement specifies properties or characteristics that a software system must exhibit other than the observable behavior Entry 10. Specifically, it is concerned on how the software will function. Back to our sorting program example, a nonfunctional requirement is the customer's concern with the running time of the sorting function (e.g., it is acceptable that the sorting algorithm execution time has the growth rate of
As nonfunctional requirements play an important role on software architecture, we may return to them in the chapter on Nonfunctional Requirements, where they will exemplified, described, and categorized in detail, as well they will be correlated to their imposers, the system's stakeholders.
A design product must be feasible. Considering this, a design constraint is the rule, requirement, relation, convention, or principle that define the context of design, in order to achieve a feasible design product. Smith and Browne gave a detailed description of the role of constraints on design Entry 8:
The notion of constraint is central to design. [...] This centrality derives from the nature of design problems: something new must be created; human imagination is able to generate various possibilities; but this capacity and the alternatives it proposes must be managed by consideration of what is feasible. Constraints serve this purpose. They express relations among properties or variables of the proposed artifact and its environment or context.
It is important to know that constraints are related to goals, and sometimes they can even be exchanged. However, in order to differentiate them, it is also important to understand that are not only the goals that rule what is to be designed. In other words, a software system may have clear goals, but its design, or some possibilities of it, may not be feasible because of its constraints.
In order to grasp the role of constraints in design, let us consider two examples. In the first example, despite the software system has a clear goal, its design is not feasible due to some of its constraints. In the second, a software system also has a clear goal, but just a clear design possibility is constrained.
First, please consider that a customer describes a simple goal to be achieved by a system: it must be able to decide whether its input, a description of another program, finishes running or not. An inexperienced software designer might even try to find a design possibility for this requirement – but this would be in vain. There is a theoretical constraint in computer science, widely known as the halting problem, which forbids a program to decide whether or not another program halts after its execution, thus not allowing achieving a solution. So, a design was not allowed due to its constraints even with a clear stated goal.
In the second example, the customer requires the following for a software system: the system must read input data from a specific card reader from a specific manufacturer. Besides the goal, the designer finds the following restriction. The specific manufacturer does not provide the card reader drivers for one of the operating systems the system must execute on. If it werenât for this restriction, the design for the input data module would be quite straightforward: just rely on the hardware driver to fetch the input data from cards. But now the designer will have to work out an alternative design to handle this restriction. One design possibility could be emulating one of the operating systems supported by the manufacturer when the system is executing in a non-supported environment. This would mean the creation of a layer of abstraction that would represent the supported environment and eventually be implemented by a native or an emulated system. Another possibility could be just designing and implementing the missing driver.
A design alternative is a possibility of solution. Since design problems often have multiple solutions, since design problems often have multiple solutions, the designer is expected to generate multiple design alternatives for a single problem. We must understand that the designer, after understanding the problem's goals and constraints, has two concerns: the alternative generation and the solution election among the alternatives.
Alternative generation poses the real challenge for a designer. Unlike decision problems area where decision alternatives are known or can be discovered through search methods, design alternatives must be created. This creation process then must be controlled by design enabling techniques, and designer's experiential knowledge and creative imagination Entry 8. "Enabling Techniques" will present some essential techniques for a designer.
The solution election is simply the choice of one of the alternatives that, according to the designer, will best solve the problem. This choice must be made employing reasoned analysis and experience. The following subsections better explain solutions and their representations.
Back to our sorting program example, let us only consider one aspect of it, the sorting algorithm to be used, and see how many alternatives a designer could generate. A quick search on the Internet returned nine algorithms that respect the growth rate of
Representations are the language of design. Although the true product of design is a representation for artifact construct, representing the solution is not the only purpose of representation. It also supports the design process. This support happens by allowing communication between stakeholders and by serving as a record of commitments.
A design representation allows communication because it turns alternatives into manipulable products, so they can be described, analyzed, and discussed not only by their author but also by others. Please observe that there are multiple dimensions to be represented on a single software design alternative. These dimensions may comprise runtime behavior, structure, and relation between logical entities to physical entities just to name a few. These dimensions are often exhibited on different types of representations, which later we will name them views.
In order to illustrate design representations, let us present two dimensions of our sorting program example by using two different representations. The first representation, Figure 1, shows the structure from a design possibility of our example using Unified Modeling Language. Observing this representation, we see how the solution was decomposed, how each class of the structure relates to each other, or even see what points could be replaced by ready-made components, which must implement the same interfaces specified on the representation. One thing that must be noticed is that this representation is not self-contained, since knowledge on UML is needed by the reader to fully understand this representation.
|
The second representation, Figure 2, shows the example's runtime behavior when sorting with high level of detail. Although we cannot see from this representation the same information presented on the previous one, this enables us to analyze how the program will asymptotically behave according the growth rate of its input. Also, we can have the notion of how much space is needed to perform the algorithm.
|
Both representations show important aspects of a system's design, nevertheless one involved on its development might be interested on aspects other than its structure or algorithm asymptotic analysis. As a matter of fact, other representations might be wanted for other purposes and it is the role of the design process to provide them.
In addition, if we have considered multiple versions of a single representation, e.g., multiple versions obtained until the pseudocode shown on Figure 2, we could see the evolution of the design decisions made over time, e.g., the evolution from a standard merge sort to an in-place merge sort. The recording of multiple versions are important to understand what decisions led to the design's current state.
A design solution is nothing more than a description enabling system construction that uses one or many representations in order to expose sufficient details. Its characteristics are listed in the following paragraphs.
Design solutions mirror the problem complexity, usually having many attributes and interrelationships. We can observe this characteristic, for example, when designing a solution for managing the inventory of a DVD rental store. Whatever the solution is, it must contain attributes such as movies, DVDs, clients, and genres, which will represent the elements inherent to the problem. However, this is not enough. It must also contain relations such as “a client can rent one or more DVDs”, “a movie have one or more genres”, or “a DVD must contain one or more movies”, which will behave just like the relations on the problem domain. Consequently, when many different attributes have different interrelationships between themselves, complexity emerges.
It is difficult to validate design solutions. The complexity of the solution renders many points of possible validation against design goals. The very problem resides on how well the design goals are described. Usually, only high-level goals are specified for very complex problems, so validation is hardened.
Lastly, most design problems have many acceptable solutions. This is intrinsic to design problems. Since many alternatives can be generated for a single design problem, as many design solutions are eventually reproduced.
The product of the design process is always a design solution. However, despite being a description enabling system construction, nothing is said about the level of detail a solution must incorporate. It comes out that the software design process can occur in many levels of detail. These levels of software design are the subjects of the next section.
According to the Guide to Software Engineering Body of Knowledge (SWEBOK) Entry 1, software design consists of two activities: top-level design and detailed design.
The top-level design, also called architectural design, is concerned on describing the fundamental organization of the system, identifying its various components and their relationships to each other and the environment in order to meet the system's quality goals. In contrast to top-level design, detailed design is more concerned on describing each component sufficiently to allow for its construction according to the top-level design.
The clear separation of these activities may not always occur during software development process. Sometimes a designer performs both activities in parallel, conceiving a design that will both meet the quality requirements and also allow for the construction of the system. Nevertheless, we primarily consider this conceptual separation to keep the focus only on software architecture, which is the main subject of this book and will be discussed in the following chapters.
However, just before starting our studies on Software Architecture, we would like to present some principles essential to every design activity, which are called enabling techniques.
Enabling techniques are guidelines, approaches, and key notions that are known to result on generally good and sound software designs. Since there are many great books and articles on enabling techniques, the purpose of this section is just to scratch the surface of this subject by giving an overview of it. The essential enabling techniques for an aspiring designer are:
Abstraction is an essential principle used by designers to deal with complexity. This strategy recommends representing an element only by its “essential characteristics that distinguish it from all other kinds of objects and thus provide crisply defined conceptual boundaries relative to the perspective of the viewer” Entry 3. Considering this approach, the element representation tends to be simpler, since unnecessary details are put aside, and eventually easier to understand, communicate, and analyze.
As a matter of fact, most of the techniques presented help the designer to raise the level of abstraction of the design process in order to lower the level of complexity of the solution.
“Information hiding involves concealing the details of a [system entity's] implementation from its clients” Entry 4. By doing this, the coupling between entities is minimized and their contribution to system complexity is restricted to what information they expose.
There are several approaches to perform information hiding: by modularizing the system, by separating its concerns, by separating interface and implementation, or by separating policy and implementation.
Modularization is the meaningful decomposition of a system into modules. It introduces well-defined and documented partitions (modules) within a system by deciding how to physically package the logical structure entities of an application. Some benefits of modularization follow:
Separation of concerns is tightly coupled to the modularization technique. Actually, it is a rule of thumb to define how modules should be separated to each other: different or unrelated concerns should be restricted to different modules.
Coupling and cohesion are principles used to measure whether the design was well divided into modules.
Coupling is the strength of interdependence between software modules. The more dependent is module A to module B internals, the stronger is the coupling between A and B. Strong coupling implies the modules involved are harder to understand because they must be understood together, harder to change because these changes will impact more than one module, and harder to correct because the problem may be spread over the tightly coupled modules.
Opposed to coupling, cohesion is an intra-module measure. Cohesion is the strength of relation between tasks performed by a module. The tasks of a module can be related to each other by several levels, then converted on types of cohesion:
In order to achieve sound designs, we can also sort the types of cohesion from the most to the least desirable on design Entry 7: functional, sequential, communicational, temporal, procedural, logical, and coincidental.
This technique enables separation of concerns. The separation of policy and implementation technique simply states: “A [module] of a software system should deal with policy or implementation, but not both.” Entry 4 Modules responsible for implementation should only execute algorithms without any context/domain-sensitive decisions. These decisions must be left to policy modules, which are often application-specific and are often responsible of supplying arguments for the implementation modules.
This separation eases reuse and maintenance of implementation modules, since they are less specific than policy modules.
Separation of interface and implementation eases modularization. This technique recommends the description of functionality by means of contracts, also called interfaces. Modules will eventually implement these interfaces in order to be part of the system.
Using this technique, the coupling between modules and clients is lowered, since clients are only bound only to interfaces – not to implementations.
The purpose of this chapter was to provide the fundamental knowledge on Software Design in order to enable the reader to be ready for the study of Software Architecture. We expect that, by now, the reader is able to understand:
As a matter of fact, since there are many great books on Software Design for the same audience of ours – the inexperienced reader, it was not our intention to go deeper on the subject covered in this chapter. Our intention was just to introduce it. For more detailed information, we recommend the reader to check the books on Software Design indicated in this chapter's bibliography.