|< Day Day Up >|
An object-oriented project has several phases, beginning with a design phase. The phases are iterative, so we may find ourselves going through the same process many times. As shown in Figure 8.1, we will repeat the same stage many times, through deployment and beyond.
Figure 8.1. Iterative project cycle.
Before we can even begin the design phase of a project, a full analysis of the business requirements is required. This usually involves nontechnical representation from the business domain, often referred to as the business owners. The business owners are our customers. They provide us with information about the business problem as well as insight into how we will eventually solve the problem. This is the most important interaction or relationship that will exist in the project, as final acceptance of the application is dependent on how well we have communicated with the business owners .
Note that the business owner will have the final say as to whether we have successfully solved or satisfied the need of the business. This relationship will exist long after the application is deployed. This group will drive and support user acceptance as well as enhancements to the application.
For each phase, you will need to identify three components :
Identifying the Problem Domain
With a well-defined working relationship with the business owners, we can begin the process of clarifying the problem domain. By identifying the problem domain, we will uncover some fundamental requirements of the application. Some questions you should be asking at this point are as follows :
Examine Figure 8.2.
Figure 8.2. Possible application requirements.
Based on the results of this discovery, you can develop premises or assumptions about the application. These assumptions will carry through the development process and into deployment. Any changes in these assumptions can alter the development cycle and, in most cases, delay deployment.
After the assumptions have been gathered, you are ready to begin defining the solution.
Defining the Solution
There are many techniques available that aid in defining business solutions. In this section, we look at a specific use-case scenario and some sequence diagrams.
Understanding how the user will interact with the application can help us design objects and object communication to support the usage. We start with a full analysis of how the application will be used by building use-case diagrams, which identify the objects of the system and system behaviors. Use-case diagrams are used for documenting existing process, analyzing new processes, identifying points of integration and repurposing opportunities, and addressing the nontechnical requirements for some scenarios.
A use case describes the way in which a real world actor ”a person, organization, or external system ” interacts with an organization. They provide a high-level view of the intended functionality of the application that is understandable by business owners and developers alike.
A use case is a generic description of an entire application involving many objects. It can also describe the behavior of a set of objects. A use-case model thus presents a collection of use cases and is typically used to specify or characterize the behavior of a whole application or a part of a system, together with one or more actors or users that interact with that application. An individual use case is usually named and often written as an informal text description of the users and the sequences of events between objects that make up the interaction. Instances of this behavior might be formally specified using scenarios, but iteration and conditionality within scenarios is usually best expressed as informal text.
Each use case describes the sequence of events of an actor (user) using the system to complete a process. This description views the system as a black box. That is, the internal design and functionality of the software are not captured. As a result, use cases typically represent business processes. They capture key uses of the application and are an excellent means of communicating requirements.
An actor is a role a user plays with respect to a system, but a user can be a human being or another part of the system. If there is more than one user of the same type, all are represented as a single user. The standard icon for an actor is the stick man figure with the name of the actor below the figure, as shown in Figure 8.3.
Figure 8.3. Standard icon for an actor in a use case.
A use case is a collection of possible sequences of interactions between the application under discussion and its users (or actors) that relate to a particular goal. The collection of use cases should define all system behaviors relevant to the actors to assure them that their goals will be carried out properly. Any system behavior that is irrelevant to the actors should not be included in the use cases. Interactions are represented by ellipses with actions written inside.
A concrete use case is one that describes a fundamental process performed by the system. An actor of the system always invokes a concrete use case to achieve the requested result. Abstract use cases describe functionality common to two or more concrete use cases. Unlike concrete use cases, abstract use cases are not invoked directly by an actor, but are invoked only by other use cases.
Defining Use-Case Relationships
Use-case relationships can be categorized in the following ways:
Figure 8.4 shows a use case for the online loan application scenario.
Figure 8.4. Use case for the loan application.
Normally, at least one scenario should be prepared for each significantly different kind of use-case instance. Each scenario shows a sequence of interactions between the actors and the system, with all decisions being definite.
In the use-case scenarios, you will develop sequence diagrams. A sequence diagram maps the flow of the application, identifying points of interaction and uncovering entities that were missed in the use-case scenarios.
Sequence diagrams are models that describe how a group of objects collaborate in some behavior ”typically a single use case. An interaction diagram shows a number of example objects and the messages that are passed between these objects within the use case. In sequence diagrams, objects are shown as vertical lines with the messages presented as horizontal lines between them.
A sequence diagram has two dimensions: the vertical dimension represents time and the horizontal dimension represents different objects. Normally, time proceeds down the page. (The dimensions can be reversed , if desired.) Usually, only time sequences are important, but in real-time applications, the time axis could be an actual metric. There is no significance to the horizontal ordering of the objects. Figure 8.5 shows the sequence diagram for the loan application.
Figure 8.5. Sequence diagram for the loan application.
With the sequence diagram and the use-case scenarios defined, we can now begin to derive the class structures. First we will take a look at design patterns and determine which pattern will suit the application.
An object-oriented design builds an application out of a set of objects. Now that you have analyzed the usage scenarios of the application, you can begin to map the application needs to a design. Starting with a known design pattern can be very helpful. Design patterns are application patterns that have been designed and tested . They support the notion that one should not reinvent the wheel.
Of course, there are many patterns that have been used in object-oriented design and new patterns arise occasionally. This book uses one pattern that is particularly useful for Flash, Model-View-Controller (MVC), but there are many others. For more on Flash patterns, search for articles at www.macromedia.com.
The MVC pattern works well for an application with a clearly defined visual layer and a language that supports object-oriented development. Traditionally, we think of Flash as a presentation tool, but with the introduction of ActionScript 2.0 and the ability to create objects (both visual and nonvisual), partitioning an application using a pattern such as MVC becomes easy.
To fully understand MVC, let's consider the loan application. Each object has a very clear set of responsibilities. One of the tenets of object-oriented design is that the same job should not be done by two different types (or classes) of objects. One way to ensure uniqueness is to partition the behaviors. The application can be partitioned into three types of services:
A class should belong to one and only one partition. In other words, a class should have a well-defined role related to user interface logic, business/problem domain logic, or system management logic. As shown in Figure 8.6, each partition maps to Model, View, or Controller.
Figure 8.6. Application partitions mapped to MVC.
Working with the Model
The model of an application is responsible for data access and management. Experience tells us that data can come in many forms and from many sources. The more you can hide the complexity of the data from the rest of the application, the better off you will be in regards to scalability and modification. If, for example, the application data source changes from a static text file to a dynamic Web Services call, the user interface will never know the difference as long as the model can still make the data available. Depending on the complexity of the application, you might have many data objects partitioned by a problem domain.
In our application, the data access is done in the Flash file. To implement the MVC pattern, move the data access into its own object, thus creating a model object for the application. The model object will acquire the data, parse the data, and store the data, making it accessible to the application through accessor methods.
Working with the View
The view is often a set of objects that represents the visual part of the application. Each member of the view partition will probably have a class definition in an ActionScript 2.0 file as well as a Library entry in a FLA file. Using good object-oriented practice and encapsulation, each view object should have all the methods it needs to display itself. There will also be a messaging schema defined whereby the controller will notify the view to display itself. This might happen when some condition, such as the arrival of new data or user navigation, has been met that warrants a repaint.
Working with the Controller
The controller acts as the ringmaster for the entire application. The controller instantiates the objects just as the ringmaster introduces the acts. The controller handles application initiation. The controller also handles communication between the view and the model, notifying the view when data is available or has changed. The controller might also notify the model when new data is required.
The following is an example of controller communication:
Figure 8.7 illustrates the communication that takes place between the model, view, and controller.
Figure 8.7. Model, view, and controller communication.
Identify Classes (and Inheritance)
As we begin to derive the classes, we will find that some classes are more obvious than others. In the Loan application, we will need a Loan class and a Customer class. With further investigation, it becomes obvious that there can be multiple kinds of loans and therefore we will need a loan ancestor with some descendants to represent the specialized loans, cars , and mortgages. Figure 8.8 shows the Loan object hierarchy that we created in Chapter 5, "The Meek Shall Inherit the Earth."
Figure 8.8. Loan object hierarchy.
Customers might also require a generalized and specialized definition. All customers have basic information, but some specialized customers might have requirements that are unique. For example, there might be a category for business customers that might carry additional fees or require a tax id number. By defining a customer ancestor and subclassing for the specialized case, we build a flexible hierarchy that enables us to add new customer types without reinventing the customer.
Figure 8.9 shows the customer hierarchy that enables commercial customers to be distinguished from noncommercial customers while both still have all the properties and methods of the customer ancestor.
Figure 8.9. Customer object hierarchy.
Other objects that can be derived from our use-case diagram are the bank objects. The bank object would be responsible for publishing or advertising loans. The bank will need objects that manage loans and billing. A series of use cases for the banking side will flesh out the requirements.
Identifying class hierarchies in the design phase is not an easy task. Often we find ourselves in the midst of development when we realize that we missed something. We might need to alter the design to include an unforeseen inheritance hierarchy, or we might have overlooked a unique class structure. That is why we refer to this process as an iterative process. In a perfect world, we would begin development with a complete design that considers every possible scenario and defines every object. However, it is more often the case that we are back to the drawing board ”so to speak ”periodically during the development of the objects.
Identify Packages of Classes
Classes often form groups with a common theme. An example might be shipping, where the group would include all objects needed to perform and support the shipping process. To group classes with a common theme, we use a package concept. A package is equivalent to a folder, but it is used to group classes that are related, usually through inheritance. A package might include files that are not related if they are needed by the other classes in the package.
As an example of creating a package, the Loan class and all its descendants will be located in a package called loan . This package can then be used by other applications by importing the package. Because the loan calculation logic is encapsulated in these objects with a well-published application program interface (API), they can be reused by other applications.
If you recall from Chapter 6, "Understanding Interfaces in ActionScript 2.0," interfaces provide a type of multiple inheritance. Interfaces solve the problem when two different types of classes require the same functionality but there is no common ancestor between them. In a sense, we want each object to inherit from a new class outside its hierarchy. Because classes can have only one direct ancestor, we can't add an ancestor to each hierarchy, so we will have to use an interface. Interfaces provide a structure for each class hierarchy that contains the additional functionality required.
Using Inter-Object Communication
Referring back to the sequence diagram in Figure 8.5 reveals the inter-object communication. The sequence diagram tells us which objects need to communicate. It also reveals bi-direction communication and the need for call-back functions.
In Figure 8.10, we can see how the Loan object will communicate with the controller and in turn notify the client. Figure 8.10 also identifies some of the inter-object communication needed for the Loan application.
Figure 8.10. Inter-object communication sequence diagram.
Implementation (Iterative and Incremental Development)
Now it's time to implement. Using the road map created in the design phase, objects can be created. Each object hierarchy is defined using the interface specified in the design. In object-oriented programming (OOP), each object hierarchy can be built in isolation, tested, and then integrated. Given the conceptual plug-and-play model of OOP, each well-defined object can be plugged into the application as it is implemented.
Testing occurs at the unit level as well as at the system level. Unit level testing identifies the performance of a part of the application in isolation while system testing reveals the application as a whole with all units working together. Each object of the application is tested thoroughly in isolation and then again as part of the application after it is implemented. There are many other levels of testing that occur beyond implementation. They include load testing, stress testing, and fringe testing. Each reveals application strengths and weaknesses.
Again, new classes might be discovered in the coding process. This means further modeling activities are required. It's imperative that developers understand that they shouldn't explore these new classes in the code. Instead, they should allow them to be modeled first.
In this methodology, as in all others, testing verifies technical and business functionality. As the lifecycle's components differ , so too do their validation and verification. The goals are the same, but the verification focus is different.
There are four unique types of testing associated with object-oriented applications:
During deployment, the team must address appropriate platforms for specific functionality. For example, the presentation logic or user interface will be built into the Flash application, while some of the business objects reside on a mid- tier server or database tier. The technical architecture defined in the early stages of design takes into account the chosen infrastructure technologies to support intelligent object communication.
Note that data access requests and validation will more than likely live in the client, but much of the business logic will reside on a server in the form of Web Services, .NET, ColdFusion, Java, or some other server-side technology. During deployment, the Flash application as well as any server-side support objects must be deployed together.
Adapting to Change
Maintenance in an object-oriented solution is the reapplication of the lifecycle. Its primary concern is identifying when modification and enhancement requests suggest another development effort. The best course is to map those requests to use cases and assess the level of effort. Although the planning of changes to the application will follow the same best practices by using the lifecycle methodology, development and implementation should take considerably less time.
|< Day Day Up >|