Java Programming Code

User Generated

Nqnz6Nqnz

Programming

Description

Unformatted Attachment Preview

Software Engineering Design: Theory and Practice by Carlos E. Otero CRC Press. (c) 2012. Copying Prohibited. Reprinted for Personal Account, Drexel University none@books24x7.com Reprinted with permission as a subscription benefit of Skillport, All rights reserved. Reproduction and/or distribution in whole or in part in electronic,paper or other forms without written permission is prohibited. Software Engineering Design: Theory and Practice Chapter 6: Creational Design Patterns in Detailed Design CHAPTER OBJECTIVES Understand the importance and role of creational design patterns in detailed design Identify, understand, and model common creational design patterns Become proficient in implementing models of creational design patterns Understand the benefits of creational patterns when implementing software systems CONCEPTUAL OVERVIEW During detailed design, software engineers spend a great deal of time devising component design solutions that fill in the gaps in architectural designs and provide the necessary internal design of components and their interfaces to achieve system functionality. At the detailed design level, common patterns in object-oriented designs have emerged that provide detailed design solutions to problems that recur many times over in different systems. A particular problem in these systems involves the efficient creation of objects so that concepts highlighted in previous chapters (i.e., interfaces, types, dynamic binding, and polymorphism) can be used effectively to generate reusable and maintainable software. Creational design patterns help identify problems that deal with creating quality detailed designs that are efficient in the creation of object in the system. They prescribe the classes required for their design solution and interrelationships required to support object creation. These patterns allow designers to quickly and systematically identify structural layouts of systems (or subsystems) and provide avenues for examining system interactions and quality evaluation within the operational system. This chapter explores several well-established creational design patterns and examines the problems they are designed to address, together with their exhibited quality attributes. Identifying and designing using creational design patterns can improve the efficiency of the development process and the quality of the final system. CREATIONAL DESIGN PATTERNS Creational design patterns are patterns for abstracting and controlling the way objects are created in software applications. They play a key role in the design of systems by making them independent of how objects in the system are created, composed, and represented (Gamma, Helm, Johnson, and Vlissides 1995). Therefore, parts of the system responsible for creating (or instantiating) objects do so through a common creational interface without knowledge of how the actual object or group of objects are created. In addition, by controlling the creational process with a common interface, enforcing creational policies becomes easier, therefore giving the system the ability to create product objects that share a common interface but vary widely in structure and behavior. Examples of creational patterns include the abstract factory, factory method, builder, prototype, and singleton. ABSTRACT FACTORY The abstract factory is an object creational design pattern intended to manage and encapsulate the creation of a set of objects that conceptually belong together and that represent a specific family of products. According to the Gang of Four (Gamma et al. 1995, p. 87), the intent of the abstract factory is to Provide an interface for creating families of related or dependent objects without specifying their concrete classes. In the abstract factory pattern, the terms family of products or family of objects are used to denote a group of objects that belong together and therefore must be created together. When designing software that uses a group of objects that need to be created and used together, problems can arise when there is no consistent way for managing the creation of these objects. For example, consider two distinct families of computers: one representing standard computers made up of standard computer parts; and another representing advanced computers made up of advanced computer parts. Assume that the family of standard computers can be composed only of standard computer parts, such as a standard monitor, standard keyboard, and standard CPU, and that the family of advanced computers can consist only of advanced computer parts, such as an advanced monitor, advanced keyboard, and advanced CPU. When left unmanaged, designing software that instantiates both standard and advanced computers can be prone to various problems. First, there is the possibility that advanced computer objects can be created using standard products or vice versa, that is, standard computer objects created using advanced computer parts products. Moreover, without a standardized common interface identified for the different computer parts, the code inside the computer classes would be required to know about the correct computer parts type to use; therefore, the addition of new products or new product types would require changing the code inside the computer classes, a clear violation of the openclosed principle discussed in Chapter 5. These problems result in code that is hard to maintain and reuse. The abstract factory Reprinted for BE9S6/hf353, Drexel University Page 2 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice design pattern addresses these problems by encapsulating the creation of these families of products in such a way that they can be interchangeable at run time and by ensuring that products that belong to a specific group, or family, are instantiated together. Problem A computer store needs stand-alone software that keeps track of computer inventory, which includes computers and different computer parts made by different hardware manufacturers. The software for the computer store needs capabilities for displaying information about computer parts, including information from the manufacturer's site and various other sites that contain customer satisfaction reviews and other relevant information. Therefore, each computer part object needs to be capable of extracting information in real time from a list of predefined remote locations, of aggregating the information, and of providing this information upon request. Upon requesting the information for a particular computer, all computer part objects communicate over the network and find out the latest comments and statistics about a component from all predefined sources and make this information available to the requesting object. Initially, the store supports only two types of computers: standard computers and advanced computers, composed of standard and advanced computer parts (i.e., CPU, monitor, and mouse), respectively. Due to store policy, standard computers cannot be composed of advanced computer parts and advanced computers cannot be composed of standard computer parts. The software solution needs to provide a maintainable and modifiable design for creating these families of objects, to support easy addition of new families of computers, and to promote consistency with computer products. Structure The general and applied structure for the abstract factory design pattern is presented in Figure 6.1. As seen, the abstract factory is presented in general and applied form, and for simplicity the details of both abstract and concrete computer part classes are omitted in the applied form. The general structure of the abstract factory design pattern serves as a blueprint that depicts the participants and relationships required to design abstract factories; it presents the essence of the pattern, which needs to be fitted for the particular problem at hand. The applied view of Figure 6.1 presents an instantiation of the general structure for depicting the solution for the particular computer store problem, which consists of abstract products (e.g., monitor), concrete products (e.g., standard monitor), abstract creators (e.g., computer parts factory), and concrete creators (e.g., standard computer parts factory). These participants are interrelated via realization and association relationships. From this example, it should be evident how a new family of computer parts can be added to the design by adding the required pattern classes that make up the new family of computers. Reprinted for BE9S6/hf353, Drexel University Page 3 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Figure 6.1: Abstract factory design pattern Implementing the abstract factory design pattern can be confusing because of the number of classes required. However, when looking closely at the Unified Modeling Language (UML) diagram for the application view, it is evident that regardless of the number of computer factories or computer products, the structural relationship among these classes is always the same. This is true for all other patterns studied throughout this and other chapters. Therefore, a step-by-step approach can be taken to design abstract factories: 1. Design the product interfaces (e.g., CPU, monitor, keyboard). 2. Identify the different families or groups required for the problem (e.g., standard and advanced computers). 3. For each group identified, design concrete products that realize the respective product interface (e.g., standard monitor vs. advanced monitor) identified in Step 1. 4. Create the factory interface (e.g., ComputerPartsFactory). The factory interface contains n interface methods, where n is the number interfaces created in Step 1. Reprinted for BE9S6/hf353, Drexel University Page 4 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice 5. For each group identified in Step 2, create concrete factories that realize the factory interface created in Step 4. 6. Create the factory client (e.g., Computer) which is associated with both products and factory interfaces created in Steps 1 and 4, respectively. Abstract factory designs contain two fundamental sets of class designs: products and factories. Once both products and factories portions of the design are complete, dynamic binding can be used at run time to allow the computer client to create different computer objects (e.g., standard vs. advanced computers) and use them throughout programs without actually knowing the specific type of the object. This way, adding other computer types to the system can be done by extending the design and not by modifying already working code. Implementation When studying the abstract factory (and all other design patterns), it is important that UML models are translated to code, compiled, and executed. When necessary, it is also helpful to step through the code with a debugger to keep track of dynamic binding throughout the software's execution. This provides additional insight and helps further the understanding of both concepts and benefits of using a particular design pattern. Once the structural design of the abstract factory is created, its translation to code is straightforward. Listing 6.1 presents the C++ implementation for the ComputerPartsFactory class. As seen, the ComputerPartsFactory abstract class simply defines interface methods required for creating each computer part designed for the system. Therefore, there is a one-to-one relationship between interface methods and product interfaces. Since the code for the abstract factory interface is presented in C++, each interface method is defined as pure virtual, which includes the virtual keyword and is set to 0. In the Java programming language, the ComputerPartsFactory would be defined as a Java interface, using the interface keyword. Once the interface for the computer parts factory is established, all other concrete factories can be implemented to create products of specific computer types upon request. Consider the implementation for creating advanced computer parts, where the advanced computer part factory instantiates advanced computer products. The header file for the advanced computer parts computer factory is presented in Listing 6.2. Listing 6.1: C++ Code for the Computer Parts Factory Interface // Computer parts factory interface. class ComputerPartsFactory { public: // Define the interface to create a monitor object. virtual Monitor* createMonitor(void) = 0; // Define the interface to create a keyboard object. virtual Keyboard* createKeyboard = 0; // Define the interface to create a CPU object. virtual Cpu* createCpu = 0; }; Listing 6.2: C++ Header File for the Advanced Computer Parts Concrete Factory // Forward references class Monitor; class Keyboard; class Cpu; // Concrete advanced computer parts factory. class AdvancedComputerPartsFactory : public ComputerPartsFactory { public: // Create and return an advanced monitor. Monitor* createMonitor(); // Create and return an advanced keyboard. Keyboard* createKeyboard(); // Create and return an advanced cpu. Cpu* createCpu (); }; Reprinted for BE9S6/hf353, Drexel University Page 5 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice As seen, the AdvancedComputerPartsFactory realizes the ComputerPartsFactory interface by inheriting from it and defining concrete methods for each of its defined interface methods. Each interface method is implemented in terms of advanced computer parts, such that the createMonitor() method returns an instance of type AdvancedMonitor, the createKeyboard() returns an instance of type AdvancedKeyboard, and so on. This behavior is presented in the source file for the AdvancedComputerParts factory, as presented in Listing 6.3. Listing 6.3: C++ Source File for the Advanced Computer Parts Concrete Factory #include #include #include #include "AdvancedComputerPartsFactory. h" "AdvancedMonitor.h" "AdvancedKeyboard.h" "AdvancedCpu.h" // Create and return an advanced monitor. Monitor* AdvancedComputerPartsFactory::createMonitor() { // Caller is responsible for cleaning up the memory. return new AdvancedMonitor; } // Create and return an advanced keyboard. Keyboard* AdvancedComputerPartsFactory::createKeyboard() { // Caller is responsible for cleaning up the memory. return new AdvancedKeyboard; } // Create and return an advanced cpu. Cpu* AdvancedComputerPartsFactory::createCpu() { // Caller is responsible for cleaning up the memory. return new AdvancedCpu; } In Listing 6.3, it is assumed that the concrete products for the advanced computer parts factory have been defined. In fact, before any concrete computer parts factory code can be compiled, its parts need to be defined. Using this pattern, the implementation for the standard computer parts factory is similar to the advanced one, but instead of creating advanced concrete products it implements the creational methods in terms of standard computer parts products. Listings 6.4 and 6.5 present the generated code for the UML model of the standard computer parts factory together with its implementation. In a similar fashion, the creation of all other required computer parts factories in the system can be designed and implemented the same way. The final piece of the abstract factory design pattern includes the factory client object. In this example, the client is a computer object that is composed of different computer part products. The concept employed in this example is that computers are composed of different computer parts; therefore, by configuring computer objects with computer parts factories they can delegate the creation of computer parts to run-time objects mapped using dynamic binding. This allows the creational code for computer objects to be open to extension but closed for modification. The header and source code for the Computer class are presented in Listings 6.6 and 6.7. Listing 6.4: C++ Header File for the Standard Computer Parts Concrete Factory // Forward references class Monitor; class Keyboard; class Cpu; // Concrete standard computer parts factory. class StandardComputerPartsFactory : public ComputerPartsFactory { public: // Create and return a standard monitor. Monitor* createMonitor(); // Create and return a standard keyboard. Keyboard* createKeyboard(); // Create and return a standard CPU. Cpu* createCpu(); }; Reprinted for BE9S6/hf353, Drexel University Page 6 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Listing 6.5: C++ Source File for the Standard Computer Parts Concrete Factory #include #include #include #include "StandardComputerPartsFactory.h" "StandardMonitor.h" "StandardKeyboard.h" "StandardCpu.h" // Create and return a standard monitor. Monitor* StandardComputerPartsFactory::createMonitor() { // Caller is responsible for cleaning up the memory. return new StandardMonitor; } // Create and return a standard keyboard. Keyboard* StandardComputerPartsFactory::createKeyboard() { // Caller is responsible for cleaning up the memory. return new StandardKeyboard; } // Create and return a standard CPU. Cpu* StandardComputerPartsFactory::createCpu() { // Caller is responsible for cleaning up the memory. return new StandardCpu; } Listing 6.6: C++ Header File for the Computer Client // Forward references. class ComputerPartsFactory; class Monitor; class Cpu; class Keyboard; class Computer { public: // Constructor parameterized with a computer parts factory. Computer(ComputerPartsFactory* ComputerPartsFactory); // Display detailed information about the monitor. void displayMonitorInfo(); // Display detailed information about the CPU. void displayCpuInfo(); // Display detailed information about the keyboard. void displayKeyboardInfo(); // Display computer cost. void displayCost (); // All other computer methods. // Destructor needs to clean up memory. private: Monitor* _monitor; // Pointer to the monitor interface. Cpu* _cpu; // Pointer to the Cpu interface. Keyboard* _keyboard; // Pointer to the Keyboard interface. }; The key concept presented in Listing 6.6 is the reliance of the Computer class on four interfaces, namely, the ComputerPartsFactory, Monitor, Cpu, and Keyboard interfaces. This highlights a desirable attribute of component designs, which is to rely on interfaces instead of on concrete implementations. Together, these interface references (i.e., pointers) will hold the addresses of run-time objects that adhere to the particular interface. Therefore, upon creation, objects of the computer type will use these interface references to save the addresses of concrete products created by a particular computer parts factory object. The implementation code for this behavior is presented in Listing 6.7. Once a concrete factory is passed into the constructor of the Computer class, the responsibility for creating each product is delegated to the factory object. This way, a Computer object can be created the same way for all computer types, simply by Reprinted for BE9S6/hf353, Drexel University Page 7 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice changing the factory that creates the products. Consider the code the computer store software that provides and manages inventory information using the ComputerStore class. Using the abstract factory design pattern, the implementation code for displaying computer information is presented in Listing 6.8. As seen, the display computer method is passed in a string to determine the type of computer to be displayed. Once the type is identified, the appropriate computer parts factory object is instantiated and passed into the constructor of the computer object. From this point forward, all operations called on the computer object are implemented in terms of the factory used to create its parts. Similarly, many different computer types can be supported by creating additional factories to parameterize computer objects with newly supported computer types. Listing 6.7: C++ Source File for the Computer Client #include #include #include #include #include "Computer.h" "ComputerPartsFactory.h" "Monitor.h" "Cpu.h" "Keyboard.h" // Constructor Computer::Computer(ComputerPartsFactory* ComputerPartsFactory) { // Delegate the creation of the monitor object to the Factory. _monitor = ComputerPartsFactory->createMonitor(); // Delegate the creation of the keyboard object to the Factory. _keyboard = ComputerPartsFactory->createKeyboard(); // Delegate the creation of the Cpu object to the Factory. _cpu = ComputerPartsFactory->createCpu(); } // Display detailed information about the monitor. void Computer::displayMonitorInfo() { // Display monitor's info. _monitor->displayInformation(); } // Display detailed information about the CPU. void Computer::displayCpuInfo() { // Display Cpu's info. _cpu->displayInformation(); } // Display detailed information about the keyboard. void Computer::displayKeyboardInfo() { // Display keyboard's info. _keyboard->displayInformation(); } // Display computer cost. void Computer::displayCost() { // Use all computer products to compute total cost and display it. } Listing 6.8: C++ Implementation of the Computer Store // Assume that the ComputerStore class has been defined and contains // the method displayComputer and the member attributes used below. // Method to display a computer's information. void ComputerStore::displayComputer(string type) { // Determine which computer needs to be created. if( type.compare("standard") == 0 ) { // Create the standard computer factory object. computerPartsFactory = new StandardComputerPartsFactory; } else { // Create the advanced computer factory object. computerPartsFactory = new AdvancedComputerPartsFactory; } Reprinted for BE9S6/hf353, Drexel University Page 8 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice // Create the computer object using the appropriate factory. _computer = new Computer(computerPartsFactory); // Display the computer information, including its cost. This // information varies according to the factory object used to create // the computer. _computer->displayMonitorInfo(); _computer->displayCpuInfo(); _computer->displayKeyboardInfo(); _computer->displayCost (); // Do more stuff with the computer object here. // Clean up the all memory allocated when done. } When applying the abstract factory design pattern, depending on the problem, a large number of classes may be required, causing confusion to those who are new to the pattern. However, it is important to keep in mind that the core classes and relationships identified in the general structure of the pattern remain the same, regardless of the problem. That is, an abstract factory has one or more factory objects defined by an abstract factory interface and one or more products defined by one or more product interfaces. Benefits Isolates concrete product classes so that reusing them becomes easier Promotes consistency within specific product families Adding new families of products, which requires no modification of existing code Skill Development 6.1: Abstract Factory Design Pattern Using the UML tool of choice, replicate the UML model presented in Figure 6.1 and generate code from the model. Using the Integrated Development Environment (IDE) of choice, fill in the gaps in the code generated using Listings 6.1 through Listings 6.8 and compile and execute the software. Once the software executes, go back to the UML model and add a third factory, named SpecialComputerFactory, together with special computer products (e.g., SpecialMonitor, SpecialKeyboard, SpecialCpu). Repeat the code generation process, fill in the gaps of the new generated code, compile, and execute. When complete, explain the steps required to make this new modification and how the abstract factory design pattern made this change easy or hard. FACTORY METHOD The factory method design pattern is a class creational pattern used to encapsulate and defer object instantiation to derived classes. Structurally, the factory method can be modeled as a simplified version of the abstract factory design pattern, since both patterns require creator and product interfaces. However, unlike the abstract factory design pattern, in which the creator objects (i.e., factories) are responsible for instantiating a plurality of products that belong to a specific family type, creator objects in the factory method design pattern are responsible for the creation of a single product of specific type. Therefore, the creator interface for the factory method design pattern provides only one creational method, whereas the creator interface for the abstract factory design pattern provides two or more creational methods. In addition, unlike the abstract factory design pattern, the factory method design pattern defers object creation to subtypes that realize the creational interface; a relationship specified by inheritance, therefore, the factory method design pattern is classified as a class creational design pattern as opposed to an object creational design pattern. These fundamental differences are essential for understanding the difference between both patterns. According to the Gang of Four (Gamma et al. 1995, p. 107), the intent of the factory method is to Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. The factory method design pattern provides the ability for designers to model and implement code in terms of the factory method and product interfaces. The factory method design pattern is mainly characterized by one creational method, which is used to instantiate and return objects of a specified product interface. This creational method is made abstract at the factory base class so that objects of the factory base class cannot be directly instantiated. This is done to create a framework that allows the factory base class to define a series of operations that rely on the product interface; however, before executing operations, object creation is delegated to derived classes that are required to implement the factory (creational) method. This Reprinted for BE9S6/hf353, Drexel University Page 9 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice way, new derived factories can override the method to instantiate and return the appropriate product for the particular situation that is then used to carry out the operations specified in the factory base class. This way, through inheritance, new factories can be added to the design of the system to extend the factory base class without modifying its code. With the factory method in place, reasoning about application logic can be made in terms of the product interface and not on concrete products, therefore resulting in code that can be extended easily. Problem The computer store from the previous example has expanded its operations to have three different stores at different locations. Because of demographics at each location, particular types of computers are offered at specific locations. The computer store in Location 1 supports standard computers only, the computer store at Location 2 supports advanced computers, and the computer store at Location 3 supports a new type of special computer. The software system is now required to display information about computers carried at specific stores. Therefore, the software design requires modification so that the display of computer information is site-specific. A desired feature for the redesigned software is the ability to keep the logic code separate from specific types of computer stores so that future stores, carrying different computers, can be added to the system with minimal effort. Structure The generic and applied structure of the factory method design pattern is presented in Figure 6.2. As seen, the pattern requires both creational and product classes, similar to the abstract factory design pattern. However, unlike the abstract factory design pattern, creator classes in the factory method design pattern require only one creational interface method for creating products that share the same interface. Therefore, for each product in the system, the design incorporates concrete product and concrete creator classes. Reprinted for BE9S6/hf353, Drexel University Page 10 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Figure 6.2: UML class diagram for the factory method design pattern A step-by-step approach to designing the factory method design patterns includes the following: 1. Identify and design the product interface (e.g., computer). 2. Identify and design the concrete products that realize the interface from Step 1 (e.g., StandardComputer, AdvancedComputer). 3. Design the factory base class (e.g., Computer Store), which contains one abstract factory interface method for delegating product creation to derived classes. Product creation must conform to the interface defined in Step 1. 4. Design one or more concrete factories for each product identified in Step 2. As seen in the applied version, a standard computer store capable of displaying computer information is added to the design. Displaying computer information is performed with the displayComputer() method that performs operations and requests Reprinted for BE9S6/hf353, Drexel University Page 11 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice services using the computer interface. Two creator classes have been added to the design: one for the standard computer store and another for the advanced computer store. As seen, each computer store is associated with the specific product that it supports. In addition, a NullComputer class is added to ensure that computer stores do not return null pointers to clients in the case that a requested computer is not supported by a particular computer store. Implementation Once the structural design is complete, translating it to code is straightforward. Listings 6.9 and 6.10 present the C++ implementation for the ComputerStore class. As seen in Listing 6.10, the computer store displays a computer's information by relying on the computer product interface; therefore, once derived computer stores override the factory method to instantiate and return objects of the computer types supported in particular stores, the displayComputer() method will display the computer information according to the object bound to it at run time. Listing 6.11 presents the factory method code for a standard computer store. Listing 6.9: Header File for the ComputerStore Class #include // Forward reference. class Computer; // The computer store creator class. This is an abstract class, // therefore to instantiate computer stores, specific derived // computer store classes are required. class ComputerStore { public: // The standard factory method for creating computer products. virtual Computer* createComputer(std:: string type) = 0; // Method to display a computer's information. void displayComputer(std:: string type); }; Listing 6.10: Source File for the ComputerStore Class #include "Computer. h" // Method to display a computer's information. void ComputerStore::displayComputer(string type) { // Delegate the responsibility of creating a computer object to // derived classes using the factory method. Computer* computer = createComputer(type); // Display the computer information, including its cost. This // information varies according to the factory object used to create // the computer. computer->displayMonitorInfo(); computer->displayCpuInfo(); computer->displayKeyboardInfo(); computer->displayCost(); // Do more stuff with the computer object here. // Clean up the pComputer and pFactory objects when done. } As seen, the implementation for the factory method for standard computer stores supports only standard computers. Internally, objects of type StandardComputer can use the abstract factory design pattern from Listings 6.4 and 6.5 to create the standard computer, which is common in many practical applications. To remain concise, this part of the problem is not presented in this example; however, to accomplish this, the design of the StandardComputer class would require an association with the StandardComputerPartsFactory from the previous section. With this association, the standard computer parts factory is used inside the StandardComputer object to create standard products, such as standard monitor, keyboard, and CPU. In many practical applications, both factory method and abstract factory design patterns are used in conjunction for providing extensible and reusable code. To support new products at the standard computer store, the factory method can be modified with different conditional statements for creating the new product; therefore, changes to support new products at a specific computer store are compartmentalized and do not affect all other computer stores. Reprinted for BE9S6/hf353, Drexel University Page 12 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Benefits Separates code from product-specific classes; therefore, the same code can work with various existing or newly created product classes. By separating the code, development becomes efficient, since different developers can work on the different parts of the project at the same time. By separating the code, it becomes easier to reuse and maintain specific parts of the code. Listing 6.11: Implementation of the Factory Method for the Standard Computer Store // Implement the factory method. Computer* StandardComputerStore::createComputer(string type) { // Pointer to a computer object. Computer* computer; // Determine which computer needs to be created. if( type.compare("standard") == 0 { // Create the StandardComputer. Clients are responsible for // cleaning up the memory for the computer object. Internally, // StandardComputer uses StandardComputerPartsFactory to create // a standard computer. computer = new StandardComputer; } else { // Create and return a null computer. computer = new NullComputer; } // Return the newly created computer object. Clients are responsible // for cleaning up the computer object. return computer; } Skill Development 6.2: Factory Method Design Pattern Using the UML tool of choice, replicate the UML model presented in Figure 6.2 and generate code from the model. Using the IDE of choice, fill in the gaps in the code generated using Listings 6.9 and Listings 6.11 and compile and execute the software. Once the software executes, go back to the UML model and add a third factory, SpecialComputerStore, that carries Specialcomputers. Repeat the code generation process, fill in the gaps of the new generated code, compile, and execute. When complete, explain the steps required to make this new modification and how the factory method design pattern made this change easy or hard. BUILDER The builder design pattern is an object creational pattern that encapsulates both the creational process and the representation of product objects. Unlike the abstract factory design pattern, in which various product objects are created all at once, the builder design pattern allows clients to control the (multistep) creational process of a single product object, allowing them to dictate the creation of individual parts of the object at discrete points throughout software operations. To accomplish this, the builder design pattern introduces a creator class (i.e., the builder) that species the (abstract) interface methods required to build a particular product. These methods are used to build parts of a product, and once all parts of the product are created (using the builder interface) clients can request the builder to return the created object as a whole. Since the creation of the product object is delegated to the concrete builder objects, the product's representation can vary according to the specific concrete builder creating the object. This provides added flexibility for managing the creational process and representation of products that is not present in other creational patterns. According to the Gang of Four (Gamma et al. 1995, p. 97), the intent of the builder is to Separate the construction of a complex object from its representation so that the same construction process can create different representations. The idea of separating the process of constructing objects from their representations is essential when considering using the builder design pattern. Consider a client-server system consisting of three clients that work in a distributed fashion and report Reprinted for BE9S6/hf353, Drexel University Page 13 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice to a centralized server. The server can request status from all three clients, compile the status received into an object of the Message type, and forward it for further processing within the server system. In this scenario, the creational process of the Message object depends on receiving information from three different clients. Once information from the first client is received, the part of the Message object that contains information about this client can be created. Similarly, once information from the second and third clients is received, the parts of the Message object that require this information can be built. Once all parts of the Message object are built, the Message object can be forwarded to the rest of the system. In this example, two important characteristics should be noted. First, the Message object cannot be instantiated all at once; therefore, a finer-grained method of Message construction is required so that events occurring at discrete points in time can be used to build individual parts of the Message object. The second important, and perhaps more important, characteristic is the fact that the multistep process that allows for the construction of the Message object is the same, regardless of the representation of the Message object. In this example, the Message object can be forwarded within the server system using a binary format, Extensible Markup Language (XML), or any other custom-defined format. A major practical benefit can be gained by separating the construction process of objects from their representation. Since objects using the builder design pattern can be constructed one step at a time, complex algorithms necessary to create these objects can be separated from the actual construction of objects so that the same algorithms can be used to build different representations of an object. This results in code that can evolve to provide new representation of objects by adding new builder classes without modifying the code for the algorithm used for creating objects. Problem A company develops software to monitor and control custom-built hardware developed by a separate vendor. The equipment supports 100 different messages, defined using a custom-defined interface control document (ICD). Every time the messaging specification changes, the code that represents all 100 messages has to be changed manually. The company has decided to develop a message generator that reads the message specification document, finds the appropriate information for each message, and generates code to represent them. This way, once the ICD changes all the company has to do is regenerate the messages. Currently, the software for monitoring and control is developed in C++ so the message generator generates C++ code. However, the need for creating message libraries for both Java, C#, and other languages is being evaluated. This means that changes to the ICD will require message regeneration for all supported languages. The software company wants a design for the message generator that separates the algorithm for parsing the messaging specification from the code that generates classes for the messages in specific programming languages (e.g., C++, Java). This way, changes to the messaging specification will not affect the code that generates the messages in specific programming languages and vice versa; changes or addition to support new programming language generation will not affect the code that parses the messaging specification. To keep track of changes in the ICD and messaging libraries, the company also wants to keep history and statistics of code generation, such as build number, build date, number of methods generated, and number of classes generated. Structure The general and applied structural view of the builder design pattern for the message builder problem is presented in Figure 6.3. In the general view of the pattern's structure, notice that the association with the Product class and the method for returning the product are specified in the ConcreteBuilder class. In many practical situations, the products being built can have dissimilar interfaces, therefore making it difficult to add the interface method that returns the product in the AbstractBuilder base class and reducing flexibility in code. In the message builder example, since the products generated can be represented using the same interface, the method that returns the product (i.e., getGeneratedProduct ()) is moved to the base class so that client code can reason about builders in terms of the AbstractBuilder interface and not based on concrete builders. The creational process is represented using the builder base class, which delineates the creational process using abstract interface methods. For each identified representation of the product, a class is created that inherits from the builder base class. These classes provide the specific creational details for their particular representation. Finally, builders are associated with the product class to generate the information required to build the product as a whole. Reprinted for BE9S6/hf353, Drexel University Page 14 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Figure 6.3: UML class diagram for the builder design pattern There are two main driving forces behind the application of the builder design pattern to the message builder problem. First, since messages are generated from a document, the sections in the document that specify the parts of a particular message may be scattered throughout the document; therefore, the message object may not be able to be created all at once. Second, the processing of the ICD file is the same, regardless of the target programming language used for generating messaging code. This means that the same parsing algorithm can be used to identify the key document elements used for code generation, regardless of the target language. Once these characteristics are identified, a step-by-step approach can be used to design the builder design pattern. The steps for applying the builder design pattern are as follows: 1. Identify and design the product class (e.g., GeneratedProduct). 2. Identify the product's creational process and algorithm, and design a class for its execution (e.g., CodeGenerator). Each creational step (when necessary) must be made in terms of a standard product builder interface (instead of concrete product interface). 3. Using the knowledge acquired from Steps 1 and 2, design the builder interface, which specifies the parts that need to be created for the whole object to exist. These are captured as abstract interface methods that need to be implemented by derived concrete builders. 4. Identify and design classes for the different representations of the product (e.g., CppMessageBuilder and JavaMessageBuilder). These classes realize the interface from Step 3 in terms of the particular representations. When using the builder design pattern, the representation of each object is encapsulated in one or more concrete builder Reprinted for BE9S6/hf353, Drexel University Page 15 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice classes; therefore, adding new representation can be done easily by extending the design and not modifying existing working code. Implementation Using the structural design from Figure 6.3, the implementation for the MesageBuilder class is presented in Listing 6.12. As seen, the purpose of this class is to specify the creational steps required for the generation of each message. Since messages in the ICD are generated as classes, the MessageBuilder has methods for creating new classes, adding attributes, adding methods, and generating code for closing classes (e.g., "};" in C++ and "}" in Java) and retrieving the extension used for the generated file. These interface methods serve as indication of the parts required to create a whole GeneratedProduct object. By separating these parts into multiple interface methods, the creational process can call upon them individually to create the product object at discrete points during software execution, when information becomes available. Once the creational parts are identified and incorporated into the MessageBuilder base class, different derived classes can be implemented to provide the required object representation. Since this behavior is incorporated into the design using inheritance, the number of representations can be extended easily in future versions of the software. Listing 6.13 presents the code for the concrete message builder that generates C++ code for the messages. For each creational method in the CppMessageBuilder class, the parameters passed in are processed and transformed to C++ code before adding it to the product being generated. The same process is used for any other language that needs to be supported, such as Java or C#. Finally, the algorithm for product creation is created and encapsulated using the CodeGenerator class. As seen, by varying the builder object passed to the code generator's constructor, message generation can occur in different languages, as presented in Listing 6.14. Benefits The builder separates an object's construction process with its representation; therefore, future representations can be added easily to the software. Changes to the existing representation can be made without modifying the code for the creational process. The builder provides finer control over the construction process so that objects can be created at discrete points in time. Listing 6.12: C++ Implementation of the MessageBuilder Class #include // Forward reference. class GeneratedProduct; class MessageBuilder { public: // The interface method for building a new class. virtual void buildNewClass(string name) = 0; // The interface method for building class attributes. virtual void buildClassAttributes(string attributeList) = 0; // The interface method for building class operations. virtual void buildClassOperations(string operationList) = 0; // The interface method for closing a new class. virtual void closeClass() = 0; // The file extension for the target programming language. virtual string getFileExtension() = 0; // Return the generated product. GeneratedProduct* getGeneratedProduct() { return _codeProduct; } private: // The product containing generated code and stats about code // generated. GeneratedProduct* _codeProduct; }; Reprinted for BE9S6/hf353, Drexel University Page 16 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Skill Development 6.3: Builder Design Pattern Using pencil and paper, modify the UML class diagram for the message builder problem to include message generation using the C# programming language. Explain the steps taken and how the addition of the new program feature impacts the existing design. List the pros and cons of using the builder design pattern for this problem. Listing 6.13: C++ Implementation for the Message Builder that Generates C++ class CppMessageBuilder : public MessageBuilder { Public: // The interface method for building a new class. virtual void buildNewClass(string name) { // Generate code for creating a class using CPP style and the // name argument. // Once code is generated, add it to the product. getGeneratedProduct()->addCode(/*new C++ class code*/); } // The interface method for building class attributes. virtual void buildClassAttributes(string attributeList) { // For all items in attributeList, generate attributes using CPP // style and add them to the generated code. // Once code is generated, add it to the product. getGeneratedProduct()->addCode(/*C++ attributes*/); } // The interface method for building class operations. virtual void buildClassOperations(string operationList) { // For all items in operationList, generate operations using CPP // style and add them to the generated code. // Once code is generated, add it to the product. getGeneratedProduct()->addCode(/*C++ operations*/); } // The interface method for closing a new class. virtual void closeClass() { // Generate code to close a class in Cpp, and add it to the // generated code. // Once code is generated, add it to the product. getGeneratedProduct()->addCode("\n};\n\n"); }; Listing 6.14: C++ Implementation for the CodeGenerator class CodeGenerator { public: // Constructor. CodeGenerator(MessageBuilder* pBuilder) : m_pBuilder(pBuilder) { // Assume a valid builder pointer. Notice that m_pBuilder is // initialized in the constructor's initialization list above. } // The interface method for building a new class. virtual void generateCode(string fileName) { // Open file for reading: fileName. while ( /* not end of file */) { // read next token in file. if( /*class name found*/) { Reprinted for BE9S6/hf353, Drexel University Page 17 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice // Assume that variable className holds the name. m_pBuilder->buildNewClass(className); } else if( /*attribute list found*/) { // Assume that attributeList contains the attributes m_pBuilder->buildClassAttributes(attributeList); } else if( /*operation list found*/) { // Assume that operationList contains the operations. m_pBuilder->buildClassOperations(operationList); // Close the class. m_pBuilder->closeClass(); } } // end while ( /* not end of file */) // Close file: fileName. // // // // Create file using file extension from generated product object. write(m_pBuilder->getGeneratedProduct()->getCode()); Close file. logCodeGeneration(m_pBuilder->getGeneratedProduct()); } // end generateCode(...) private: MessageBuilder* m_pBuilder; }; PROTOTYPE The prototype design pattern is a class creational design pattern that allows clients to create duplicates of prototype objects at run time without knowing the objects' specific type. Previous creational design patterns, such as abstract factory and builder, required two distinct set of classes: one or more creational classes and one or more product classes to support the creational process. Unlike these, in which creator and product classes were separate, prototype objects are both creators and products. This characteristic allows them to support a generic interface for object creation while having the capability to access internal product data to create (deep) copies of prototypical objects. These copies are returned to clients and used independently from the original prototype object. According to the Gang of Four (Gamma et al. 1995, p. 117), the intent of the prototype design pattern is to Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. The prototype design pattern is typically used when clients need to duplicate products at run time without regarding how products are created or their specific concrete types. This allows copies from a variety of objects within the same class hierarchy to be created uniformly, which simplifies client code and adds flexibility to designs. Problem Consider the enemy component created for a gaming system. The detailed design of the enemy component includes a wide variety of enemy specifications defined for the game, each including different profiles and weapons. The game designers have identified the need to have each character provide a method for creating copies of themselves so that at any given point during the game a character clone can be made including identical energy level, weapons, and profiles. This functionality is required to develop an enemy registry of different Character subtypes to create and add enemies at any point during the game. Consider the initial proposed solution to the problem, as presented in Listing 6.15. As seen, both TerrestrialEnemyCharacter and AerialEnemyCharacter have been designed to support an interface method for duplication of objects at run time. The problem with the code in Listing 6.15 is that clients are required to know about the specific concrete class to create a copy of each different character. Therefore, it is inefficient to design an enemy character registry that can be used throughout the game to create characters uniformly, since the creational process requires knowledge about the particular interface method for duplicating a run-time object. This decreases the design's flexibility, since it does not support object extension without code modification. That is, for every new type of enemy character, the enemy registry code for creating and adding characters to the game needs to be modified for including the new character. This problem is presented in Listing 6.16. Reprinted for BE9S6/hf353, Drexel University Page 18 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice Listing 6.15: C++ Implementation for the Terrestrial and Aerial Enemy Characters // The character interface. class Character { public: virtual void attack() = 0; // Other methods such as defend, move, etc. }; class TerrestrialEnemyCharacter : public Character { public: // Method definitions for terrestrial attack, defend, etc. // Duplicate this object. TerrestrialEnemyCharacter* duplicateTerrestrial() { // Use the copy constructor to create a copy of this object and // return it. return new TerrestrialEnemyCharacter (*this); } }; class AerialEnemyCharacter : public Character { public: // Method definitions for aerial attack, defend, etc. // Duplicate this object. AerialEnemyCharacter* duplicateAerial() { // Use the copy constructor to create a copy of this object and // return it. return new AerialEnemyCharacter(*this); } }; As seen, the code required to create copies of enemy characters using the enemy registry with only two characters is quite large. Consider the case where 100 different enemy characters are designed for the gaming system. In such a case, the size of the function would increase significantly, since a conditional statement is required for each character. This problem can be solved easily by using a prototypical interface for creating copies that does not require clients—in this case, the function to create the enemy character—to know which object they are creating. Listing 6.16: C++ Implementation of the Client that Creates New Enemy Character // Pre-Condition: A registry of 2 Enemy Characters has been created. Character* createNextEnemyCharacter() { // Randomly pick the location of the next enemy character to be // created. int nextEnemyLocation = rand() % MaxNumberOfEnemies; // Make sure that nextEnemyLocation is within proper bounds. // Retrieve the character at the nextEnemyLocation. Character* pCharacter = enemyRegistry[nextEnemyLocation]; // The enemy character to be returned. Character* pNewCharacter = 0; // Determine if the character located at nextEnemyLocation is // Terrestrial. if( dynamic_cast(pCharacter) != 0 ) { // Terrestrial Character, downcast it so that the // duplicateTerrestrial method can be used to duplicate the // terrestrial character. TerrestrialEnemyCharacter* pTerrestrial = dynamic_cast(pCharacter); // Create the copy. Clients are responsible for cleaning up memory // allocated for the copy. pNewCharacter = pTerrestrial->duplicateTerrestrial(); Reprinted for BE9S6/hf353, Drexel University Page 19 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice } // Determine if the character located at nextEnemyLocation is Aerial. else if( dynamic_cast(pCharacter) != 0 ) { // Aerial Character, downcast it so that the duplicateAerial // method can be used to duplicate the aerial character. AerialEnemyCharacter* pAerial = dynamic_cast(pCharacter); // Create the copy. Clients are responsible for cleaning up memory // allocated for the copy. pNewCharacter = pAerial->duplicateAerial(); } else { // Invalid Character. pNewCharacter = new InvalidEnemyCharacter; } // Return the newly created enemy character. return pNewCharacter; } Structure The general and applied structural view of the prototype design pattern for the gaming system problem is presented in Figure 6.4. As seen, from the structural point of view the only thing required to implement the prototype design pattern is the addition of the clone interface method to the Character type. By adding the clone() method in the character interface, the behavior for creating character duplicates throughout the game is standardized and delegated to derived classes. Therefore, by deriving from this base class and implementing the clone interface, derived classes can abstract the process of creating a copy of themselves. Figure 6.4: UML class diagram for the prototype design pattern Reprinted for BE9S6/hf353, Drexel University Page 20 of 26 CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited Software Engineering Design: Theory and Practice The driving forces behind this design are the ability of client objects to create duplicates of character objects without knowing the object's internal true composition and providing clients the ability to reason about character objects using the character interface. Once these characteristics are identified, applying the prototype design pattern is straightforward. 1. Identify and design the common interface that needs duplication. As part of the interface, the clone interface method needs to be specified. 2. Identify and design concrete products, which realize the interface created in Step 1. 3. For each concrete product created in Step 2, implement the clone method in terms of that particular concrete product. Listing 6.17: C++ Implementation of the Terrestrial Character and New Character Interface // The character interface. class Character { public: // Interface method for initiating an attack. virtual void attack() = 0; // Interface method for duplicating objects at run-time. virtual Character* clone() = 0; // Other methods such as defend, move, etc. }; class TerrestrialEnemyCharacter : public Character { public: // Method definitions for terrestrial attack, defend, etc. void attack() { // Display to the console the type of attack. cout
Purchase answer to see full attachment
Explanation & Answer:
code
User generated content is uploaded by users for the purposes of learning and should be used following Studypool's honor code & terms of service.

Explanation & Answer

This is a Java Project created...


Anonymous
Great! Studypool always delivers quality work.

Studypool
4.7
Trustpilot
4.5
Sitejabber
4.4