to build up programs by accretion.
What this often means, in practice, is that
it provides a structured way to write spaghetti code.
— Paul Graham
In a room full of top software designers,
if any two of them agree, that is a majority.
— Bill Curtis
Object—Oriented (OO) development is entirely possible without the use of OO design patterns, but if OO design patterns are used, they must be applied within the realm of OO development. This chapter gives a short presentation to OO development and to the process of designing OO systems with focus on how OO and design patterns interact, especially in a Java context. The "Gang of Four" design patterns we evaluate in this thesis can be used as a tool to aid the design of OO systems, regardless of the Object—Oriented Method (OOM) used. The patterns represent solutions to problems related to the design of OO systems, but at the same time express this knowledge using OO concepts and principles. Hence, the general OO concepts must be understood in order to understand the design patterns and to perform the evaluation in a consistent manner, including understanding the approach utilised by Gamma et al. in the "Gang of Four" design patterns themselves. We also link the themes and concepts described by Gamma et al. to Java. To understand how and when design patterns can be utilised when designing OO systems, we present abridgements on Object—Oriented Analysis (OOA), Object—Oriented Design, and Object—Oriented Programming (OOP) as well.
The Object—Oriented (OO) approach to software design attempts to manage the complexity inherent in real—world problems by abstracting out knowledge and encapsulating it within objects [WirfsBrock90, p.5]. Identifying the proper objects, relationships, and interactions are the key objectives to any successful OO design, but this is no trivial matter. The granularity of the design is thus a (complex) object, but an object may also represent an interaction with a complete sub—structure, for example a reusable component or a software design pattern such as a "Gang of Four" pattern. Numerous OO methods have been developed, each offering more or less proprietary procedures on how to approach the design and development in order to fulfil these objectives, but no common standard exists. Up until deployment, and regardless of the method used, the OO development life—cycle generally consists of analysis, design, implementation, and testing phases in some form. The phases may be overlapping or re—iterated, each time refining the design and implementation. This is dictated by the OO method and procedures used or more likely by (ever) changing demands and specifications. Compared to other forms of software development the design phase is considerable larger, because OO systems are designed for easy reuse, maintenance, and modification [WirfsBrock90, p.9].
As the design phase is so central to OO development, it is paramount that the design is sound and durable. While the OO method may guide the design process, it cannot offer the specific knowledge represented by a pattern. Patterns known by the designer can be used as a tool in the design process because they offer proven solutions to common problems, which ideally heighten the quality of the design. Part of the pattern knowledge is describing the objects and their relationships relevant for the given scenario, thereby making the job of the designer a little easier. As a benefit, the application of well—known patterns will probably make the design seem more familiar to other designers as well. Figure 2.1 illustrates the OO software development life—cycle commonly used excluding phases such as deployment and evaluation and the relation to patterns.
Figure 2.1 — OO development life—cycle and patterns (modified from [WirfsBrock90, f.1—2]) | ||
|
Different categories of patterns are used at different times in the development process, but their usage can overlap as illustrated in figure 2.1 above. As explained in section 2.5.1, design patterns are patterns targeting design problems with medium granularity, used to refine the sub—systems or components of an OO system, or the relationships between them [Buschmann96, p.13]. The "Gang of Four" patterns are classified as design patterns, which is thus the category of patterns this thesis investigates. From a practical point of view, design patterns are also closely related to the implementation because their descriptions contain source code and must in any case be implemented. Any type of pattern used in OO development inherently reflects OO concepts such as objects, classes, inheritance, encapsulation, polymorphism, etc. To understand such patterns these concepts need to be understood as well. Hence, the next section presents an introduction to OO concepts as understood in this thesis before we describe the processes pertaining to OO development and the relation to patterns.
2.1. Object—Oriented Concepts
The general lack of consensus regarding fundamental OO concepts is clearly illustrated by a recent survey of existing literature related to OO development performed by Armstrong [Armstrong06]. Two hundred and thirty nine articles, books, and conference proceedings related to OO development were examined by Armstrong to try to identify the essential elements of OO development. Thirty—nine concepts were identified, but only eight of these were utilised by the majority of the sources reviewed [Armstrong06, p.124]. Armstrong argues that the lack of consensus may be because we do not yet thoroughly understand the fundamental concepts that define the OO approach. Many authors suggest concepts that define OO, taking for granted that the concepts are known, or that no universal concepts exist; others acknowledge the need for a consensus [Armstrong06, p.123]. Few works offer methods of precise specification for OO design, and none are commonly recognised as standards.
Armstrong defines a two—construct taxonomy containing the eight fundamental concepts identified, also known as quarks [Armstrong06, t.3]. The taxonomy is reproduced in table 2.1 below.
Table 2.1 — Armstrong's two—construct OO taxonomy (modified from [Armstrong06, t.3]) | ||
Construct | Description | |
Structural Construct | ||
Abstraction | Creating classes to simplify aspects of reality using distinctions inherent to the problem. | |
Class | A description of the organisation and actions shared by one or more similar objects. | |
Encapsulation | Designing classes and objects to restrict access to the data and behaviour by defining a limited set of messages that an object can receive. | |
Inheritance | The data and behaviour of one class is included in or used as the basis for another class. | |
Object | An individual, identifiable item, either real or abstract, which contains data about itself and the descriptions of its manipulations of the data. | |
Behavioural Construct | ||
Message | A way to access, set, or manipulate information about an object. | |
Message Passing | An object sends data to another object or asks another object to invoke a method. | |
Polymorphism | Different classes may respond to the same message and each implement it appropriately. |
By using an OO perspective to classify the individual concepts, they are placed in one of two constructs, namely the Structural or Behavioural construct. Armstrong describes Structural concepts as focused on the relationship between classes and objects, as well as the mechanisms that support the class/object structure. A class is an abstraction of an object. The class/object encapsulates data and behaviour and inheritance allows the encapsulated data and behaviour of one class to be based on an existing class [Armstrong06, p.127]. On the other hand, Behavioural concepts are focused on object actions. Armstrong describes message passing as the process in which an object sends information to another object, or asks the other object to invoke a method. Last, polymorphism enacts behaviour by allowing different objects to respond to the same message differently [Armstrong06, p.127]. Behaviour and structure are interconnected in the sense that behaviour is a way of manipulating structure, but behaviour must also support the actions of the system. The OO perspective used in the taxonomy to identify concepts as either Structural or Behavioural matches very well with the "Gang of Four" classification concerning pattern purpose, namely Structural, Behavioural, or Creational as described in section 3.7.1. It also matches quite well with the types of UML diagrams targeting Structural and Behavioural conduct as described in section 2.3.
In order to perform a meaningful evaluation of the "Gang of Four" design patterns, the general concepts and themes inherently expressed by the design patterns must be understood. The pattern authors understanding of OO concepts will naturally be reflected in the pattern descriptions, but pattern users may have a different understanding as Armstrong's survey explains. We must therefore establish the basic concepts and themes reflected in the "Gang of Four" patterns. Luckily, this is not as difficult as it sounds. Several concepts related to OO development in class—based languages are summarised in chapter one of the "Design Patterns" book [Gamma95, p.11-28]. Obviously, this thesis adapts the concepts and themes described by Gamma et al., especially because Java is a class—based language like C++ and Smalltalk. Not doing so would be a topic for a different thesis altogether, for example an evaluation targeting prototyped—based languages, where OO concepts such as classes and inheritance has no or at least a different meaning.
2.1.1. Concepts
The OO concepts described in chapter one of the "Design Patterns" book [Gamma95, p.11-28] are explained in relation to the languages used, i.e. C++ and Smalltalk, as well as the problems the "Gang of Four" patterns are designed to solve. For example, the concept of mixin classes seems only relevant in a language like C++ that allows multiple functional inheritance (as opposed to mixin types, in Java in form of interface implementation that requires composition). The delegation and acquaintance concepts directly refers to one of the general "Gang of Four" design principles as described in the next section. The number of concepts is around forty in total of varying granularity, though many of them have fine granularity. Thirty—eight is the number of bold—faced words, i.e. concepts, with associated explanations on pages 11-28 in [Gamma95]. Some have identical meanings, though, for instance request and message. In addition, a few concepts are introduced as part of a figure or section heading, for example application. Table 2.2 lists the identified concepts in alphabetical order. Because the concepts are described in relation to C++, the table also supplies comments related to Java.
Table 2.2 — "Gang of Four" concepts | |||
Concept | Description | Java 6 Remarks | |
Abstract class | A class whose main purpose is to define a common interface for its sub—classes [Gamma95, p.15]. | Supported. | |
Abstract operation | The methods an abstract class declares but does not implement [Gamma95, p.15]. | Supported. Abstract methods can only be declared in abstract classes. Interfaces also declare methods with no corresponding implementation. | |
Acquaintance | An object uses another object in a loosely coupled fashion [Gamma95, p.22]. | Composition, supported. | |
Aggregatee | The object owned by the aggregator [Gamma95, p.23]. | Composition, supported. Also called Aggregate Member. | |
Aggregation | An object owns or is responsible for another object [Gamma95, p.22]. | Composition, supported. | |
Aggregator | The object owning the aggregatee [Gamma95, p.23]. | Composition, supported. | |
Application | Type of program where internal reuse is important [Gamma95, p.25]. | ||
Black—box reuse | Reuse by object composition [Gamma95, p.19]. | ||
Class | An object's implementation is defined by its class [Gamma95, p.14]. | Supported since Java is a class—based language. Java also provides access to an object's class at runtime. | |
Class inheritance | Defining new classes in terms of existing classes for code and representation sharing [Gamma95, p.15,17]. | Java supports single inheritance only, but a class can implement several interfaces. | |
Client | The object that issues a request [Gamma95, p.11]. | ||
Concrete class | A class that is not abstract [Gamma95, p.15]. | Supported. | |
Delegate | The object being forwarded a message in delegation is called a delegate [Gamma95, p.20]. | Another form of composition, supported. | |
Delegation | Using object composition, an object receiving a message forwards the message to its delegate passing itself along as an argument [Gamma95, p.20]. | Supported. Delegation implies composition, but composition does not imply delegation as aggregation and acquaintance could also be used. | |
Dynamic binding | Runtime association of a message to an object and one of its methods [Gamma95, p.14]. | Supported via polymorphism. The signature of the method is determined at compile—time, but the actual type of the (polymorphic) object is determined at runtime [Sierra06, p.111]. | |
Encapsulation | The internal state of an object cannot be accessed directly, and its representation is invisible from outside the object [Gamma95, p.11]. | Supported, but must be enforced by access modifiers. | |
Framework | A set of cooperating classes that makes up a reusable design for a specific class of software [Gamma95, p.26]. | ||
Generics | Parameterised types as used in certain languages [Gamma95, p.21]. | Supported, including support for bounds and wild—card types (not found in C++). Type information is not always present at runtime (erasure), and generics do not allow (static) template specialisation as in C++. Corresponds to parameterised types. | |
Instance | A created object is a unique instance of its class [Gamma95, p.15]. | Supported. Instances can be compared based on identity or based on equivalence (equals). | |
Instance variable | The internal data of an object are represented as instance variables [Gamma95, p.15]. | Supported. Can also be accessed via reflection. | |
Instantiation | Objects are created by instantiating a class [Gamma95, p.15]. | Supported. Objects can also be created reflectively. | |
Interface | The set of all signatures for a given object [Gamma95, p.13]. | Interface as a type is supported, but a class may also represent the set of all signatures of an object. | |
Message | An object invokes a method when it receives a message. Messages are the only way to get an object to invoke a method [Gamma95, p.11]. | Supported. | |
Method | A typical name used to describe the procedures that operate on object data. If encapsulation is enforced, methods are the only way to change the internal state of an object [Gamma95, p.11]. | Supported. Can also be accessed and/or invoked reflectively. | |
Mixin class | A class providing an optional interface or functionality to other classes, but it is not intended to be instantiated and requires multiple (functional) inheritance [Gamma95, p.16]. | Mixin classes are not supported, but mixin types in form of interfaces that require composition are1. Java supports dynamic proxies that allow implementation of interfaces at runtime (reflection). | |
Object | An object packages both data and procedures that operate on the data [Gamma95, p.11]. | Supported. All classes inherit java.lang.Object. | |
Object composition | An alternative to class inheritance that composes (assembles) objects to obtain complex functionality [Gamma95, p.18]. | Supported. | |
Operation | Synonym for method. | Supported. | |
Override | A sub—class may override a method defined in its parent class [Gamma95, p.16]. | Supported unless the method is declared final. Java supports covariant return types. | |
Parameterised type | A type that is declared without specifying all the types it uses until the point of usage [Gamma95, p.21]. | In Java a synonym for generics. | |
Parent—class | A parent—class defines data and methods sub—classes can inherit [Gamma95, p.15]. | Supported, also called super—class. Java provides access to the super—class at runtime as well as the actual instance. | |
Polymorphism | Substitution of objects with similar interfaces at runtime using dynamic binding [Gamma95, p.14]. | Supported. All non—primitive classes are polymorphic in Java as they inherit java.lang.Object and define their own type. See dynamic binding. | |
Request | Synonym for message. | ||
Signature | The name, parameter, and return type of a method [Gamma95, p.13]. | Supported. Can be accessed reflectively. | |
Sub—class | A sub—class inherits (all) data and methods from its super—class [Gamma95, p.15]. | Supported, but access modifiers determine data and methods inherited. | |
Sub—type | A type is a sub—type of another type if its interface contains the interface of its super—type [Gamma95, p.13]. | Supported. | |
Super—type | A type is a super—type of another type if its interface is included in the interface of a sub—type [Gamma95, p.13]. | Supported. | |
Template | Parameterised types as used in C++ [Gamma95, p.21]. | Not supported by Java. | |
Toolkit | A class library [Gamma95, p.26]. | ||
Type | A name used to denote a particular interface [Gamma95, p.14. | Supported, but type is usually used to describe the functionality listed under Interface. A type is thus a class or interface. | |
White—box reuse | Reuse by sub—classing [Gamma95, p.19]. |
Of the eight fundamental concepts identified by Armstrong listed in table 2.1, all but message passing are described as a distinct concept in some form by Gamma et al., though some using slightly different names and meanings, for example polymorphism and dynamic binding. However, message passing is implicitly part of the message (request) and method (operation) "Gang of Four" concepts. This is similar to method invocation not being described either. We therefore conclude that the concepts are encompassed by the taxonomy suggested by Armstrong. As the "Design Patterns" book predates Armstrong's taxonomy, it is possible that the tight resemblance is an indication of how influential and/or how widely used the "Gang of Four" patterns have been - and still are. On the other hand, many of the concepts described are well—known OO principles that any developer has to know to design and implement durable OO designs. Concepts such as classes, inheritance, polymorphism, etc., cannot be ascribed to Gamma et al.
There is only one concept we disagree with the definition of, namely encapsulation. From our perspective, the merging of the different meanings of encapsulation and information hiding by Armstrong is flawed, even though Gamma et al. do the same [Gamma95, p.11]. Consequently, the Gamma et al. definition of sub—class is faulty as well, because information hiding will determine the data and methods to inherit (see Java remark). We consider encapsulation and information hiding as two distinct concepts as explained by Rogers [Rogers01]:
Encapsulation is a language construct that facilitates the bundling of data with the methods operating on that data. Information hiding is a design principle that strives to shield client classes from the internal workings of a class. Encapsulation facilitates, but does not guarantee, information hiding. Smearing the two into one concept prevents a clear understanding of either. |
The remarks regarding Java 6 functionality in table 2.2 clearly indicates that the concepts are well—suited for a Java environment. Hence, the concepts are adapted to represent our understanding of OO concepts as well, keeping the distinction between encapsulation and information in mind.
2.1.2. Themes
The first chapter of the "Design Patterns" book also describes a set of reoccurring themes that permeate the "Gang of Four" approach to OO development and their design patterns [Gamma95, p.11-31]. The concepts listed in the previous section facilitate the themes, but these themes must also be understood in order to understand the "Gang of Four" design patterns. Two important principles summarise their ideas:
- Program to an interface, not an implementation [Gamma95, p.18]; and
- Favour object composition over class inheritance [Gamma95, p.20].
Perhaps more than the design patterns themselves, we consider these principles evidence of how significant the "Design Patterns" book has been in OO development. They cover the concepts listed in table 2.2, and express the need for abstraction, loose coupling, and flexibility in OO (re—) designs. By using interfaces, clients remain unaware of the specific types (and classes) of objects they use [Gamma95, p.18]. Interfaces are directly supported as a concept in Java. Gamma et al. promote indirection as a mean to achieve decoupling, flexibility, and reuse, and encapsulation, information hiding, and parameterised types may aid in achieving this as well [Gamma95, p.19,22]. They prefer dynamic (e.g. runtime) relationships as opposed to static ones and thus favour object composition over implementation inheritance [Gamma95, p.20]. Delegation is the extreme example of composition, which can always be used to replace inheritance [Gamma95, p.21]. However, dynamic, highly parameterised software is harder to understand than more static software [Gamma95, p.21], which thus may influence the pattern descriptions. The need for re—design may still arise, but by following the two principles and utilising relevant design patterns expressing them, the process of re—design becomes easier, because then aspects of a system structure may vary independently of other aspects [Gamma95, p.24].
As Java 6 supports the concepts from the previous section, these themes can be expressed in Java providing a prudent designer. Infact, Java can even help enforce these principles via built—in support for interfaces and final classes. By returning interface types, e.g. types declared using the interface keyword, the client has no choice but to program to an interface as opposed to an implementation class, e.g. a (non—abstract) type declared using the class keyword. Generally exposing interfaces instead of implementation classes will also help promote composition as the client will have no class to inherit. Secondly, by declaring a class to be final, the class cannot be inherited and its usage by the client must therefore be in some form of composition. This, however, can severly limit the usage and polymorphism of the class in question.
Many of the principles and themes described by Gamma et al. are represented by the General Responsibility Assignment Software Patterns (GRASP) [Larman04]. Grand provides a Java version of these patterns in [Grand99, p.51-87]. These patterns are not design patterns as such. They do not target a specific problem, but provide insight into how responsibilities should be assigned to classes to achieve a well—structured design, which is easily understood and maintained [Grand99, p.52]. For example, Low Coupling and High Cohesion [Grand99, p.53] is closely related to both of the above principles, and Polymorphism [Grand99, p.69] is naturally related to concepts such as polymorphism, super—class, sub—class, inheritance, etc. Several of these themes have by some been promoted to design patterns. Grand provides Delegation [Grand98, p.53] and Interface [Grand98, p.61] patterns, but whether such fundamental concepts are best expressed as design patterns is doubtful in our view.
2.2. Object—Oriented Methods
An Object—Oriented Method (OOM) provides a set of techniques for analysing, decomposing, and modularising software system architectures [Schmidt, p.4]. The techniques may be applied in different phases of the software lifecycle, e.g. in the analysis, design, and implementation phases (see figure 2.1) [Schmidt, p.6]. An OOM can for example describe how the requirements found in the analysis can be transformed into a software model consisting of objects [SEI]. Despite the widespread use of OO as explained in section 2.1, there is not only a lack of consensus regarding the formalisation of the relevant concepts and principles inherent in OO, but also on how to approach the overall design process. Hence, numerous OO methods have been developed, each trying to remedy this, for example Rational Unified Process (RUP) [RUP] or Model—Driven Architecture (MDA) [MDA], but none are an accepted industry standard. Different software development processes are used in various OO methods, such as the sequential Waterfall model, or the Iterative, Spiral, or Agile development. All but the first are based on the idea of repair and evolution and are in some form iterative in nature, while the Waterfall model is more static and employs replacement. RUP, for example, uses iterative development.
2.2.1. Patterns
The traits of a given OOM and the procedures used will guide the OO development. It is difficult to speculate on the impact a given OOM has on the application of design patterns, if any, without in—depth knowledge and experience with each method. Vlissides, one of the "Gang of Four" members, argues that patterns do not need tools or methodologies to be effective [Vlissides97, i.4]. Based on experience we agree. However, certain methodologies directly address the use of patterns or other techniques, such as UML. Responsibility Driven Design has no mention of patterns what so ever [WirfsBrock90], while Extreme Programming (XP), for example, de—emphasises or even ignores the need for patterns [Fowler04].
Nevertheless, we do not even see XP as incompatible with design patterns. XP is a software engineering methodology developed mainly by Kent Beck and Ward Cunningham, the duo that also introduced software design patterns [Beck87] as described in section 3.2. It is typically used in Agile development, and is iterative in nature. It advocates the use of Evolutionary Design contra to Planned Design under certain preconditions [Fowler04]. Central is the use of several enabling practices, such as testing, refactoring, and continuous integration that embodies and encourages certain values, such as simplicity and communication. This allows changes to be performed much faster and cheaper, thus reinforcing the enabling practices [Fowler04; PPR]. Due to the evolutionary nature of this methodology, it is often believed that Object—Oriented Analysis (OOA), Design (OOD), and design patterns are incompatible with XP. Others, such as Fowler, think that patterns are underrated within XP, and are in no way contradictory to the paradigms of XP and that program code developed using the methodologies can evolve into patterns during refactoring. We agree, and conclude that the enabling practices of XP to some extent can be viewed as a form of pattern discovery, or mining (see section 3.8.1).
Designers often feel strongly about their preferred development method, OO or otherwise, sometimes to the point of a religious belief. In many respects, we see design patterns as orthogonal to OO methods, because the objects and knowledge they represent are independent of which method produced the (initial) context to which a pattern can be applied. While design patterns can be grouped in collections, such as pattern systems and languages as explained in section 3.7, the effect of this in our experience rarely influences their practical application when used in a specific process. Their application is thus largely independent of the OOM used.
2.3. Unified Modelling Language
Regardless of OO method and processes used, the Unified Modelling Language (UML) is generally used for object modelling and illustration [UML05]. UML is an extensible general—purpose object modelling and specification language used to create abstract (design) models illustrated graphically. It is not limited to modelling software, but is widely used in various OO methods. The model of the system can be described using a Functional Model (user's point of view); using an Object Model (structural); and/or using a Dynamic Model (internal behaviour). Different models use different types of diagrams, for example a Use Case Diagram for the Functional Model; a Class or Object Diagram for the Object Model; and a Sequence Diagram for the Dynamic Model [UML05].
UML can be used in various development phases. Use Case Diagrams can specify demands the analysis must adhere to (see also [Cockburn01]). Class and Object Diagrams can be used in the design phase to describe the identified classes and objects, and Sequence Diagrams can illustrate the behaviour of classes, objects, and methods. As the design evolves, so must the diagrams. UML does not have built—in notations for all features found in Java 6, such as annotations, but can be adapted by user—defined extensions.
2.3.1. Patterns
Patterns related to OO development commonly use UML models, because the pattern participants (i.e. classes and objects) are easily illustrated using the UML models. Graphical illustrations of pattern functionality are a requirement to ensure proper quality of the pattern as well as a meaningful description of its functionality as explained in section 3.3 and 3.5, respectively. The "Gang of Four" patterns predate UML, but use other forms of closely related types of illustrations. In this thesis, only UML Class diagrams are used. Section 6.2 details the usage, but the evaluation produces a Class diagram for each pattern implementation.
2.4. Object—Oriented Analysis
As illustrated in figure 2.1, Object—Oriented Analysis (OOA) is the first phase in OO development, excluding mundane tasks such as sale, legal affairs, project planning, and management in real corporate environments. A typical scenario is that a given client has produced a (far from complete) list of demands identifying the overall behaviour of the system that must be built. The demands can be specified in a number of ways, for example as Use Cases [Cockburn01, p.1-3]. The analysis is concerned with developing software engineering requirements and specifications from these demands, often expressed in form of a conceptual object model, as opposed to the traditional data or functional views of systems [Larman04; SEI]. The analysis is a discovery process that determines what is to be built, and the design determines how it is done [Schmidt, p.6; SEI; WirfsBrock90, p.5]. This is done by identifying the (real—life) abstractions, concepts, responsibilities, and relationships present in the system in order to form a conceptual model of the system while adhering to the demands. The practical procedures on how to do this as well as how the model is described are typically dictated by the Object—Oriented Method (OOM) used.
2.5. Object—Oriented Design
Object—Oriented Design (OOD) is the process of defining the software objects and collaborations forming an OO model of a software system in order to implement the identified requirements found during the analysis [Larman04; SEI; WirfsBrock90, p.10]. The design phase is thus the second phase in OO development and where the analysis determines what is to be built, the design is a process of invention and adaptation that determines how it is done [Schmidt, p.6; SEI; WirfsBrock90, p.5]. While the conceptual model identified during the analysis describes conceptual objects unrelated to software terminologies, the OO model describes the computational software objects needed to implement the functionality of the model instead. The mapping between objects is rarely or never one—to—one. The system is decomposed into (complex) software objects of relevant granularity, some perhaps mapping to existing re—usable components. Detailed descriptions consisting of message protocols ("patterns of communication"), attributes, and methods ("public behaviour") at the level of individual objects should be specified [WirfsBrock90, p.10,28].
Example 2.2 — To implement a design for the notification mechanism described in example 2.1 the conceptual model must be transformed into a model of collaborating software objects. Model objects such as User, Subscription, Notification, Message, Formatter, and Delivery may map directly to similar software objects, or types, e.g. to User, Subscription, Notification, Message, Formatter, and Delivery software objects, respectively. A software object may be designated as abstract, which will require specific implementations for usage as well. For example, the Delivery object could map to a Delivery interface with specific implementations such as EmailDelivery, SMSDelivery, and SNMPDelivery, which in turn could require abstract Message, Formatter, and Subscription types as well. Coarse or complex model objects may require numerous software objects or even libraries to represent the functionality. For example, an object doubling as both a Scheduler and Processor must implement a Scheduler and a Processor interface. The UML Class diagram below shows such a scenario. Conversely, certain model objects may not even require a structural counterpart such as a class/object; this could be the case with an Event object, which could be defined as the executing context creating and scheduling a Notification object simply using method invocations. On the other hand, some software objects may have no direct conceptual counterpart, as for instance a NotificationRelation object expressing a specific relationship between a Notification scheduled for later processing using a certain Delivery type. Once the software objects have been identified, their responsibilities and relationships must be established and described ("fine design granularity"). As seen in the UML diagram above, the Scheduler object could have a schedule(Notification) method as well as a getScheduledNotifications() method to return the Notification objects scheduled by that Scheduler contained in NotificationRelation objects. Similar, the Processor could have a process(NotificationRelation) method as well as a getProcessedNotifications() method to return relations processed by that Processor. Here, the Scheduler and Processor are the same type (and instance), but that is not a requirement. The design will not only identify the attributes and methods, but also the overall internal logic of the methods. Finally, depending on the demands at hand, the mechanism could be designed as a standalone library used in a large OO systems, or as part of the system itself ("large design granularity"). If designed as a library, it could be used in other design scenarios, but this raises the need for a good, durable, and flexible design even more |
The practical procedures on how to execute the design phase, i.e. how objects and responsibilities are identified as well as how the design is presented, are typically described by OOM used combined with personal experience, for example using the Responsibility Driven Design process suggested by Wirfs—Brock et al. [WirfsBrock90]. However, Fowler states that it can be hard to distinguish between the analysis and design phase in practice [Fowler97]. Wirfs—Brock et al. do not even label the initial phase as the analysis phase, but as part of the design phase. Nevertheless, the design phase requires the specification of concepts nonexistent in analysis, such as the logic of object methods or the types of the attributes of an object or class [SEI], as for example a name attribute of the User class identified in example 2.2 having the type java.lang.String. Furthermore, the design may seem closely related to the implementation, and in particular OO Programming Languages (OOPL), because it will typically be represented by diagrams such as UML Class and Object Diagrams sharing similar notions [UML05]. The design does not require an OOPL for implementation, but an OOPL will facilitate the implementation considerably, though the variant of the OO paradigm supported by it will also play a role.
The choice of programming language is important already in the design phase. The language may implicitly affect the design if it affects the design patterns used. In [Norvig96], Norvig differentiates between three types of programming relevant for the design: a) Programming In a language; b) Programming Into a language; and c) Programming On a language [Norvig96, p.58]. In case a), the design is constrained by what the language offers. In case b), the design is done independently of any language, then implemented using features available in the chosen language. In case c), the design and language meet half way. Norvig explains this as programming into the language you wish you had; a language built on the actual language chosen. Ideally, patterns represent case b). Unfortunately, the choice of programming language may already have been made, for example based on client demands, and this may force a given type of design and programming relation.
2.5.1. Patterns
In our experience, real—world problems seldom map to software objects representing real—life entities, but rather to programmatic abstractions, i.e. objects of varying granularity, of required functionality. As stated, identifying the objects, their relationships, and interactions is no trivial matter. This is where software patterns come in handy, because they provide solutions to many of the problems faced while designing such objects (or "components"): a given pattern has already identified a set of objects, relationships, and responsibilities required in a given scenario. The knowledge represented by the patterns can be adapted and utilised by the designer yielding familiar variants of already well—known scenarios. Chapter 3 provides a thorough introduction to pattern theory, not necessarily related to OO.
Patterns can describe solutions to various areas and the pattern concept originated within the field or architecture. According to Vlissides, a common misconception is that software patterns are just for OO design and implementation, but patterns can be applied in numerous areas [Vlissides97, i.7]. Furthermore, it is commonplace that any pattern related to design, software or otherwise, is dubbed design pattern. However, according to Lea, within computer science, the term design pattern reflects a categorisation to identify a specific range of patterns related to the design of software systems [Lea00, i.3]. In accordance with this, Buschmann et al. suggest a pattern taxonomy that categorises patterns pertaining to design as architectural patterns, design patterns, or idioms depending on their range of scale or abstraction [Buschmann96, p.12-15]. Others, for example Hohmann [Hohmann98], extend the taxonomy to include analysis patterns as described by Fowler [Fowler97]. Table 2.3 illustrates the extended and slightly modified taxonomy used in this thesis.
Table 2.3 — Pattern taxonomy | |||
Category | Description | Target | |
Architectural Patterns | An architectural pattern expresses a fundamental structural organisation schema for software systems. It provides a set of predefined sub—systems, specifies their responsibilities, and include rules and guidelines for organising relationships between them [Buschmann96, p.12]. | Entire (sub—) systems, applications, and frameworks | |
Analysis Patterns | An analysis pattern reflects the conceptual structures of business processes rather than actual software implementations [Fowler97, p.XV]. | Domain and Business Object Model | |
Design Patterns | A design pattern provides a scheme for refining the sub—systems or components of a system, or the relationships between them [Buschmann96, p.13]. It does so by describing communicating objects and classes that are customised to solve a general design problem in a particular context [Gamma95, p.3]. | Micro—architectures within sub—systems or components | |
Idioms | An idiom is as a low—level pattern, specific to a particular programming language that describes how to implement particular aspects of components or the relationships between them using the features of the given language [Buschmann96, p.14]. An implementation of a design pattern that is unique to the language chosen is also considered an idiom in this thesis. | Classes, Objects, and Methods |
Architectural patterns have large design granularity and are thus used early in the design phase. Analysis patterns are related to the domain and business object model of the system, if any, while design patterns can be used throughout the entire design phase. Though design patterns target sub—systems, they are only used to define specific and encapsulated functionality within the systems. Hence, the granularity of analysis patterns if often larger than design patterns. Idioms have fine granularity and are closely related to the implementation phase. This is illustrated in figure 2.1. Design patterns (indicated with the grey row in table 2.3) are the category of patterns evaluated in this thesis, and unless explicitly stated otherwise, the term design pattern as used in this thesis indicates this category of software patterns.
2.5.1.1. Architectural Patterns
Patterns categorised as architectural express fundamental structural organisation schemas for software systems. They have large granularity and their introduction into the early stage of the design phase will greatly influence the system, including the detailed design of sub—systems and how different parts collaborate and communicate [Buschmann96, p.25-26]. Architectural patterns are still only applicable for a given scenario and do not represent a complete software architecture. Hence, several patterns may need to be applied to form the entire system. As is the case with design patterns, architectural patterns may be classified according to their overall purpose, for example as Adaptable Systems, Interactive Systems, or Distributed Systems [Buschmann96, p.26]. Buschmann et al. also suggest a number of patterns, known as the "POSA" patterns, for instance the architectural Model—View—Controller Pattern [Buschmann96, p.125], which we have used extensively in the design of numerous applications2.
Example 2.3 — The notification mechanism described in example 2.1 can be designed as a standalone library, or even framework allowing for customisation in form of an API. It is reasonable to assume that Users, Subscriptions, scheduled Notifications, and Messages must be serialised to a perhaps permanent store to handle identification of pre—existing users and subscriptions, application shutdown, and re—deliveries. The Layers [Buschmann96, p.31] architectural pattern suggest to divide the architecture into layers dedicated to different tasks, for example a database layer handling the persistence of the objects and a layer handling the application logic. An API can by it self be considered a variant of the Layers pattern [Buschmann96, p.46]. Fowler identifies specific variants of the Layers pattern, for example Two—Tier Architecture [Fowler97, p.240] corresponding to the scenario in this example |
While architectural patterns can have tremendous impact on the design of the software system, we for the most part see design patterns as autonomous from their application. Of course, a design pattern such as the View Handler Pattern [Buschmann96, p.291] is a refinement relevant to the infrastructure offered by the Model—View—Controller Pattern, and Creational "Gang of Four" design patterns behaviour could be affected if the architectural Reflection Pattern is applied [Buschmann96, p.293]. However, the granularity of design patterns and their general versatility makes them useful and relevant in many different architectural contexts, for example the "ever—applicable" Iterator [Gamma95, p.257] and Decorator [Gamma95, p.175] patterns.
2.5.1.2. Analysis Patterns
At the core of many Information Systems (IS) is the business object model that do represent real—world entities, for example a mapping of a company to a Company object. The business object model is said to represent the domain of the system. Hence, the business object model is often closely related to the conceptual model constructed in the analysis phase, but it is often just a relative small part of the entire system. However, the business object model is a truly pivotal part because it effectively defines the system behaviour: instances of model objects are to be manipulated by the system while adhering to the business rules, thus defining the overall behaviour. Because a well—designed business model is so important, patterns have even been developed targeting the domain specifically. Fowler presents a comprehensive set of analysis patterns targeting reusable parts of business object models [Fowler97], some even at a granularity level corresponding to architectural patterns as illustrated with the Two—Tier Architecture [Fowler97, p.240] pattern in example 2.3. However, a business object model cannot stand by it self, or may not even be utilised in a design. The total functionality required to manipulate the business object model, directly or indirectly, will in terms of objects vastly out number objects in the model. For example, in Internet applications auxiliary objects are required to handle of incoming browser requests, security, logging, persistence of data, errors, rendering, etc. Such objects rarely have real—world counterparts. Examples could be a Logger object to log diagnostic messages; a Request object to represent input to the application; or in the case of example 2.2, a NotificationRelation object. Even if a system does not use a business object model as such, it will always have a core functionality that requires many auxiliary objects with additional functionality. Hence, the design of business system is not just about designing the business object model, but naturally about designing the entire system. The use of analysis patterns does not exclude the need for design patterns.
2.5.1.3. Design Patterns
The design pattern categorisation is almost directly based on the "Gang of Four" definition of design patterns. The precise definition used in this thesis is shown in table 2.3. The "Gang of Four" patterns [Gamma95] are a collection of patterns targeting the domain of design problems closely related to pragmatic problems found in general OO designs. Gamma et al. define design patterns as [Gamma95, p.3]:
Descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context. |
Gamma et al. further explain that the domain of design patterns is describing concepts and structures beyond individual objects and classes up to the granularity level of refinement of OO sub—systems. Algorithms are not considered a pattern by this, or other, definitions; they solve computational problems, not design problems. This definition of design patterns is roughly equivalent to the domain of the design pattern categorisation described by Buschmann et al. [Buschmann96, p.13], except that Buschmann et al. do not explicitly mention OO. Our definition implies an overall OO domain. Borchers [Borchers99, p.2] offers a broader definition that does not require class—based languages, or even a specific domain:
A software design pattern is generally considered to be a proven solution of a recurring software engineering problem that balances the competing design constraints optimally for a certain type of situation. |
This broader definition implies the choice of pattern has consequences. This is an important aspect of patterns as explained in chapter 3. As the "Gang of Four" patterns are evaluated in this thesis, we see no reason not to use the "Gang of Four" definition. Hence, design patterns both describe the "Gang of Four" patterns, but also the category of patterns targeting the same domain. Many other commonly used design patterns have been published as well, for example the "POSA" patterns3 [Buschmann96]. The problems design patterns address arise more frequently than issues purely related to the business object model as targeted by analysis patterns.
Example 2.4 — In example 2.2, we identified the need for an abstract Delivery type with concrete implementations representing different means of delivery mechanisms, such as EmailDelivery, SMSDelivery, and SNMPDelivery to deliver messages via email, SMS, or the Simple Network Management Protocol (SNMP), respectively. The design must ensure that only the proper types of Message objects will be delivered using a given delivery mechanism; that the messages will be formatted to a representation suited for such a delivery; and that additional means of delivery could fairly easy be added - but how? A designer familiar with the "Gang of Four" patterns will immediately recognise that the Abstract Factory [Gamma95, p.87] and Factory Method [Gamma95, p.107], and perhaps the Singleton [Gamma95, p.127] and Template Method [Gamma95, p.325], design patterns could be utilised here. The Abstract Factory pattern can be used to ensure that the Delivery and Formatter types used together are to correct ones, making use of the Factory Method to defer the actual creation elsewhere, which also allows for easy introduction of new Delivery and Formatter types. The Singleton pattern can be used to ensure that the notification mechanism creates Delivery and Formatter objects in a uniform way not breaking the loose coupling offered by the factory patterns by ensuring that only a single factory is available. Finally, if the notification mechanism is designed as a library, the Template Method pattern can be used to define hooks in various objects that the client can override to add additional functionality or means of delivery |
2.5.1.4. Idioms
Buschmann et al. describe an idiom as a low—level pattern, specific to a particular programming language that describes how to implement particular aspects of components or the relationships between them using the features of the given language [Buschmann96, p.14]. The classification is based on the work by Coplien in [Coplien91]. We furthermore claim that any design pattern is implemented as an idiom if the specific implementation is unique to the language. An example is a Java implementation of the Singleton pattern using the synchronized statement to ensure that only a single instance is created. While the implementation can be considered a Java idiom, the abstraction is still a design pattern. This indicates a closer relation between design patterns and idioms and thus the implementation, which is illustrated in figure 2.1. Buschmann et al. also note that certain design patterns provide a source for idioms [Buschmann96, p.350].
2.6. Object—Oriented Programming
The objects described in the design phase must be transferred into program code. Languages supporting an OO paradigm will facilitate this process, for example by directly offering language constructs such as objects, classes, and inheritance. Other types of languages can be used as well, but will require more work during implementation. The implementation phase is often called Object—Oriented Programming (OOP), but OOP is also commonly used to denote OO in general. This thesis refers to the implementation phase as OOP. More so, Sethi describes OOP as a programming paradigm, where execution is normally imperative and data is conceptualised in cooperating and communicating objects representing logical entities [Sethi96, p.15-16], i.e. closely related to the programming language chosen. Languages supporting an OO paradigm are called OOP languages (OOPL).
Example 2.5 — To implement the design of the notification mechanism described in example 2.2 a programming language must be chosen. If the design is described using UML Class Diagrams, the conceptual model entities are represented by classes. All User objects are thus represented by the User class, Delivery objects by a Delivery interface, etc. A class—based language like Java would be the obvious choice to transform the design to program code, because the language directly support classes and inheritance as part of the syntax. The User type could directly be defined as class User, and a specific Delivery implementation as class EmailDelivery implements Delivery, for example. On the other hand, if a prototype—based language is chosen as the programming language, the relationship between Delivery and EmailDelivery would have to be expressed differently |
Implementation is not the last phase in the software life—cycle, but the last phase relating to the design. Testing, deployment, and evaluation are key phases that might spawn new demands, which in turn may cause the development cycle to re—iterate.
2.6.1. Object—Oriented Programming Language
Ideally, the relation between the design and implementation should be in form of Programming Into a language as described by Norvig [Norvig96, p.58], i.e. the design should be designed independently of any programming language (see section 2.5). A programming language that has built—in support for an OOP paradigm is an obvious choice to use when the design must be implemented, because such languages directly support the object notions of objects, encapsulation, information hiding, polymorphism, and in case of class—based languages classes and inheritance [SEI; WirfsBrock90, p.10]. In short, most of the concepts from the concepts presented in section 2.1, which Java 6 does. Below, table 2.4 offers a quick comparison of some of the more interesting features found in C++, Smalltalk, and Java 6 based on [Gamma95; Gosling05; Stroustrup91].
Other types of languages can be used as well to implement a design, though not as easily. For example, a class—based design implemented in a language not supporting classes must utilise, or even invent, means to represent classes and inheritance. This corresponds to Programming On a language [Norvig96, p.58]. Programmatic features must be introduced to support the requirements of the design. OOP languages will be easier to use in conjunction with design patterns as well because design patterns build on the fundamental OO concepts.
2.6.2. Patterns
If design patterns were utilised in the design phase, the patterns will supply canonical implementations or at least examples on how to implement the required functionality. As Gamma et al. already note, the choice of language will affect the pattern application because the language will ultimately decide what can and what cannot be done (easily) in light of supported programming paradigms [Gamma95, p.4]. In case of a Programming In a language or Programming On a language relation between the design and programming language [Norvig96, p.58], design patterns can help establish wanted features. That is, to avoid limitations of the implementation language [Norvig96, p.4]. However, the pattern examples must be modified to the language chosen and to the scenario at hand, which may raise issues in case the language does not support features utilised in the examples or the problems inherent to the scenario. We have already established that Java 6 supports practically all concepts from section 2.1.1. Still, the concepts do not describe all specific programmatic features used in the examples, such as multiple inheritance in C++ or codeblocks in Smalltalk. The Java 6 implementations must find alternative ways to implement the desired functionality.
The patterns used may also reflect part of the author's approach to OO development, for example the two important principles for OO development defined by Gamma et al. that are listed in section 2.1.2: 1) program to an interface, not an implementation [Gamma95, p.18]; and 2) favour object composition over class inheritance [Gamma95, p.20]. By applying the "Gang of Four" patterns, these principles will be reflected in the developed source code. By repeatedly using the "Gang of Four" patterns, these principles may be promoted by the developer to core principles that will be applied elsewhere in the design process as well.
The knowledge represented by some design patterns can be implemented as reusable components. The process of implementing patterns as reusable components is called "componentization" by Meyer and Arnout [Arnout06]. This is discussed in chapter 4. Pattern components make the implementation phase much easier, but also fixate the behaviour to the functionality available. Certain design patterns are so universally applicable that programming languages offer implementations of them as part of the language or its core libraries. For example, it is widely known that Java has built—in support for the Iterator, Observer, and Proxy patterns. The java.util.Iterator<E> interface describes the Iterator pattern functionality as understood in Java with numerous standard implementations in the Java Collections Framework4. Iterators in Java, for example, are defined to fail immediately in case of concurrent modification, thus addressing, but fixating behaviour only discussed in [Gamma95, p.261]. Additionally, any class implementing the java.lang.Iterable<T> interface must return an iterator, which can be used directly in the for—each loop introduced in Java 5. The java.util.Observer interface combined with the java.util.Observable class describes the functionality needed to implement the Observer pattern, but is in our experience rarely used. Perhaps because it utilises deprecated collection types; that the default implementation is too simple; or because developers prefer unique method names to identity different types of events. We do not think it is unreasonable to consider that poor implementations may cause developers to become biased towards not using a pattern at all or at least in the given language. As a side note concerning the Observer pattern, the Java thread notification model described by the wait() and notify() methods in java.lang.Object can be viewed as a variant of it. Finally, the java.lang.reflect.Proxy class combined with the java.lang.reflect.InvocationHandler interface are an advanced implementation of the Proxy pattern that exploits Java's reflection mechanism in a manner totally different from the canonical implementation supplied in [Gamma95, p.210-215].
A programming language should be chosen from the device "the right tool for the right job" once the initial design has been established. In reality, the choice is often made beforehand. The regular usage of a programming language supporting certain design patterns will affect the way the developer thinks of the specific design patterns. It may ease the development process, but it may also fixate how the developer perceives pattern behaviour. The choice of programming language is therefore important to establish as early as possible.
2.7. Summary
By abstracting out knowledge and encapsulating it within objects, the OO approach to software design attempts to manage the complexity inherent in real—world problems. Identifying the proper objects, their relationships, and collaborations is the key to a successful design of any OO system.
Object—Oriented analysis (OOA), design (OOD), and implementation (OOP) is part of OO development and the software lifecycle for OO systems. The analysis develops the software engineering requirements and specifications, often expressed in form of conceptual object model. The design must define the software objects and collaborations forming an OO model of a software system to implement the identified requirements. Compared to other forms of software development, the design phase is larger, emphasising the need for good and durable designs even more. The analysis determines what must be built; the design determines how it should be done. The implementation must implement the design using a programming language. A programming language that has built—in support for an OOP paradigm (OOPL) will be easier to use, for example C++ and Java supporting class—based programming, but other languages can be used as well. Different OO methods (OOM) can be used to guide the design and development process, offering procedures and principles to follow within the realm of OO development. A given method may dictate that the development phases may be re—iterated and/or overlapping.
Software patterns can be used as a tool in the design and implementation process regardless of the OOM chosen, because we view patterns as orthogonal to the OOM used in many respects. Different pattern categories may be utilised in different phases of the design. Architectural patterns have large design granularity and are thus used early in the design phase, while design patterns can be used throughout the entire design phase. Analysis patterns are related to the business object model, or domain, of the system. Idioms are closely related to the implementation phase. The "Gang of Four" patterns are classified as design patterns. Regardless of the OOM chosen, UML is often used to model the design, including pattern implementations, visually. The strength of patterns is that they represent well—proven solutions to commonly known and re—occurring problems based on empirical knowledge, thus aiding and facilitating the design process. Several languages have built—in support for commonly applied patterns, such as the Iterator pattern in Java, which makes the implementation and usage easy, but may also fixate pattern behaviour and affect the way the developer perceives the patterns in question. This is an indication of patterns and programming languages influence each other.
This thesis investigates the "Gang of Four" design patterns described by Gamma et al. As the design patterns build upon OO, the fundamental OO concepts must be understood. We adapt OO concepts identified by Gamma et al. because they are inherent to the pattern application and very well suited for Java environments.
1. | The java.io.Serializable and java.lang.Cloneable interfaces are each a hybrid between a mixin class and a mixin interface. Java has built—in support for both of these special interfaces that cannot be described by standard interface semantics, for example default serializable behaviour in form of private inherited methods. |
2. | The "POSA" pattern system contains architectural patterns, design patterns, as well as idioms. |
3. | The Command Processor [Buschmann96, p.277] design pattern implemented as part of the Command pattern in section 8.3.2.3 is in fact a "POSA" pattern. |
4. | See http://java.sun.com/javase/6/docs/technotes/guides/collections/overview.html. |