Chapter 8 & 9: Object Design.
The main goal of object design is to close the gaps between application objects and offthe-shelf components by identifying additional objects and refining existing objects. That is, we are refining the analysis and system design models as well as identifying new objects.
Object design includes four groups of activities:
1.
Service specification: The subsystem services are specified via class interfaces, which includes operations, arguments, type signatures, and exceptions. Missing operations and objects are identified that are needed to transfer data among subsystems. A complete interface specification for each subsystem is the result of this activity, which is called the API (Application Programmer Interface).
2.
Component Selection: Off-the-shelf components may be used to realize each subsystem. Class libraries and basic data structures are identified.
3.
Restructuring: Leads to increase code reuse. All u-nary associations are reduced to binary associations, merging two similar classes from different subsystems into a single class, collapsing classes with no significant behavior into attributes, splitting complex classes into simpler ones, and rearranging classes and operations to increase inheritance.
4.
Optimization: Address performance requirements – changing algorithms to respond to speed or memory, reducing multiplicities in associations to speed up queries, rearranging execution orders, etc.
Object Design Concepts:
1.
Application objects versus solution objects.
2.
Types, signatures, and visibility.
3.
Preconditions, postconditions, and invariants.
4.
UML’s objects Constraint Language.
Application objects versus solution objects:
Application objects arer found at the client’s domain. Solution objects are objects that do not have a counterpart in the application domain. They pertain to the solution of the system and includes such aspects as persistent data stores, user interface or middleware
(existing distributed applications and applications).
Types, signatures, and visibility:
The type of an attribute specifies the range of values the attribute can take and the operations that can be applied to the attribute. Example, if we declare num of type int
1
Then num will at some time have a particular value ((-2147483648 <= num <=
+2147483647) or 2
31 with one bit for the sign
)
and we could add, subtract, etc. any integer number to or from num . The return type is typed in a similar fashion as the attributes.
The signature is the return type and the types of the parameters. Example: int add (int x, int y) where the return type is integer as well as the parameters type.
The visibility indicate whether if the attribute or operation can be used by other classes.
Thus an attribute or operation is declared as public (accessed by any class), private
(accessed only within the class in which it is declared) or protected (accessed in class in which it is defined or by descendent classes). These are prefixed in the UML diagram by
– for private, # for protected and + for public. There are times when we need to reduce the range of a value (for example and integer) to the positive amount only – signed int
Preconditions, postconditions, and invariants:
Invariant is a predicate that is always true for all instances of a class. These are constraints associated with classes or interface.
Precondition – a predicate that must be true before an operation is involved. Associated with specific operations.
Post Conditions – a predicate that must be true after an operation is involved. Associated with specific operation.
Thus invariant are generally associated with attributes – applying constraints to them. Pre and Post conditions are more associated with methods (operations) stating the conditions that are needed to be existing before an operation and the conditions that will exist after the operation.
UML’s Objects Constraint Language:
OCL is used to formally specify constraints on single model elements (e.g. attributes, operations, classes). The diagram of page 355 gives a good example. Note the use of
Invariant, Preconditions and Postconditions. Also, rather than using a diagram for OCL, it could be expressed in textual format, with the key words context, inv, pre and post being used. Anything word following the context key word refers to a class, an attribute or and operation.
Object Design Activities:
Bearing in mind the four groups indicated above:
1.
Specification:
(a) Identify missing attributes and operations.
2
(b) Specify type signatures and visibility.
(c) Specify constraints.
(d) Specify exceptions.
2.
Component Selection:
(a) Identify and adjust class library
(b) Identify and adjust application framework.
3.
Restructuring:
(a) Realizing associations.
(b) Increasing reuse.
(c) Removing implementation dependencies.
4.
Optimization Activities:
(a) Revisiting access paths.
(b) Collapsing objects: turning objects into attributes.
(c) Caching the results of expensive computations.
(d) Delaying expensive computations.
Using the an example JEWEL to illustrate these 4 activities:
JEWEL enable end users to simulate and visualize air pollution as a function of point source, area source and mobile source. Examples of point source include factories, and powerplants. Examples of area source include cities. And examples of point source include cars, trucks and trains. The area under study is divided into grid cells. Emmisions are then estimated for each grid cell for each hour of the day, thus giving 24 sets of data per grid cell per day.
Users are eventually able to see the concentration of pollutants , and the emissions source of the pollutants , on a map. Because it is using a map for a geographical area, JEWEL is using a Geographical Information Subsystem (GIS), which is responsible for storing and manipulating maps in the form of polygons and segments.
Roads, rivers and political boundaries are organized as different layers so that they can be displayed separately. Thus a high level view of the map may only display major roads whereas a low level view may include secondary roads as well.
Specification:
Figure 7.7 gives a UML class diagram describing the object view of the GIS layering of the 3 layers. ( Remember UML class diagrams are used to describe the structure of a system, in terms of objects, classes, attributes, operations and their associations. Note:
Classes are abstractions that specify the common structure and behavior of a set of objects. Objects are instances of classes that are created, modified and destroyed during the execution of the system. Objects have state that includes the values of its attributes and its relationships with other objects.
) Also note that this is a class diagram that models only the GIS subsystem (category). There should be similar class diagrams that model all of the other subsystems (categories) in the application. Figure 7.8 describes the
3
ZoomMap use case that the users use to zoom in or around a selected point. Figure 7.10 gives a Category Interaction Diagram (CID) of the application with a brief description of each category.
To now apply the activities of Specification:
1.
Identify missing attributes and operations: Need to identify the service description of the subsystem (category) and then identify missing attributes and operations.
Because we concentrate on the functionality of the system during analysis (using use cases), many attributes may be missed. Now it is time to focus on the solution domain and thus all of the ignored details (when we concentrated on the analysis model – the application domain) must now considered. Example, in JEWEL, the layers are created, deleted and organized in the layer class. In the solution domain, we now concentrate on operations such as zooming activities. So an Interaction Diagram (ID) is developed to address this activity. In this example, a new class is identified, the
LayerElement class, which interact with the elements of the layer class to access the roads, rivers, etc. but is going to use these to allow for zooming in and out. As is the layer class will implement the original three layers and interact with the layer element class via the getOutline() method to accomplish the zooming on the roads, rivers and political boundaries. The new subsystem is given as Figure 7.12
. Note: only a class is added - a new category (subsystem) was not created.
The parameters for the getOutline() is the bounding box that the user will select by clicking a point and the detail would be factor to scale by. And there are two getOutline() methods, one in the layer class and one in the layerelement class. Further levels of details were lacking, thus the classes for Point, Polyline and Polygon were revised to accommodate these details, as shown in Figure 7.14.
2.
Specify type signatures and visibility: The types of attributes, the signatures of operations and visibility of operations and attributes are specified. Figure 7.15 gives an example. Note the use of + and - to declare public and private attributes and operations respectively. Also note the relationship between classes (association). For example a Layer class (object) can have many layerElement objects and a Polyline class (object) can be made up of many Point objects whereas there is a one-to-one relationship between LayerElement and PolyLine . We should also indicate the type of library packages that will be used in the application and a description pertaining to what they would be used for.
3.
Specify Constraints : Constraints are attached to classes and operations to remove any ambiguities. To achieve this, Invariants are applied to attributes, and preconditions and postconditions are indicated. OCL could be used or UML class diagram, page 357.
4.
Specify Exceptions : Exceptions are usually associated with violation of preconditions. In JAVA and Ada, there are built in mechanisms to handle exceptions.
In C++, the developers usually have to hard code exception handling. Note the inclusion of the requirements on the UML class diagram of Figure 7.16.
4
To apply the Component Selection activities:
1.
Identify and adjust Class Libraries: If an off the shelf package is used to implement part of the system, example JFC or MFC, then they may implement certain objects differently (may require different amount of parameters in their methods, or perhaps different types of parameters, etc.) This must be taken care of before the package could be used properly. Two solutions: (a) Change the interface of the package (if the developers have control of the package) to suit the application; or
(b) Write a utility method to change the data type (for example) in our application to match that of the package. If both were packages (which means we have control over neither interface), then we need to use the Adapter pattern to address the mismatch.
2.
Identify and adjust application framework : An application framework is a reusable partial application that can be specialized to produce custom applications.
Such frameworks are more used in data processing, real-time avionics, etc. The key benefits of application frameworks are reusability and extensibility. Hook methods are overwritten in the application to extend the application framework. Application frameworks are classified into three groups dependent upon their use:
(a) Infrastructure framework : Usually used within a software project and is not delivered to the client. It is aimed to simplify the software development process.
Example frameworks are for operating systems, debuggers, user interface and communication tasks.
(b) Middleware framework : Used to integrate existing distributed applications and components. Examples are MFC, CORBA, etc.
(c) Enterprise application framework : Focuses on application domain. Examples are telecommunications, avionics, etc.
Types of frameworks dependent to the techniques used to extend them are:
(a) Whitebox framework : Rely on inheritance and dynamic bounding. Existing functionality is extended by subclassing framework base classes and overriding predefined hook methods using patterns such as the template method patterns.
Requires intimate knowledge of the internal structure of the framework. This produces systems that are tightly coupled via inheritance. Requires recompilation of the application.
(b) Blackbox frameworks : Support extension by defining interfaces for components that can be plugged into the framework. Existing functionality is reused by defining components that conform to a particular interface and integrating these components with the framework, using delegation. Easier to use than whitebox because rather than using inheritance, they use delegations. But harder to implement.
Design pattern vs. frameworks : The former could be considered building blocks for frameworks.
Class libraries vs. frameworks : Frameworks use class libraries to simplify the development of frameworks. The classes inside of the framework cooperate to make the framework work as well as to produce a reusable architectural skeleton. The class libraries provide a smaller scope of reuse.
5
Components vs. frameworks : Frameworks could be used to create components where the components interface provide a façade for the internal class structure of the framework. They (components) are actually instances of classes that are plugged together to form complete applications. Compared to frameworks, they are less tightly coupled. They are used to simplify the development of end-user application whereas frameworks are used to simplify the development of infrastructure and middleware software.
3.
To apply the Restructuring activities:
1.
Realizing associations : Refers to bi-directional links between two or more objects.
OO does not provide the concept of association; instead it provides references whereby one object stores a handle to another object. This reference is unidirectional.
(a) Unidirectional one-to-one associations: Figure 7.21 – The ZoomInAction accesses the MapArea, but the MapArea does not access the ZoomInAction –
Unidirectional. And it is a one-to-one relation in that the ZoomInAction will modify one area (given the detail for that area) at a time. To achieve this reference, an attribute is declared of type MapArea in the ZoomInAction class.
The process in this case is that the user will choose a point on the MapArea to zoom, then he/she will click a zoom menu that will provide the numeric values by which to decrease or increase (for example using MSWord, we could choose a single word in a document and increase or decrease the size).
(b) Bidirectional one-to-one associations: This is when we are given the opportunity to either use the menu or click directly on a point on the MapArea, using perhaps the left mouse button to locate the point and then click the right mouse button to increase and then the left mouse button to decrease. Hence the association between these two objects is now bidirectional. So we need to create references in both objects to each other. Figure 7.22. Note the use of the set and get methods.
(c) One-to-many associations: In this case we cannot use reference as in the above two cases but need to realize the many part via a collection of references. If the constraints on the many part is unordered, we could use a set data structure, else we may have to use arrays or vectors . Note the Figure 7.23 – in the Layer class, a private attribute, LayerElements of type set is declared and in the LayerElement class, a private attribute, ContainedIn of type Layer is declared. These provide the connection via the use of the methods in each class.
(d) Many-to-many associations: In this case, both classes would consist of a collection of references. If ordering is required, then vectors or arrays could be used in both classes. It is more difficult to implement unordered references in both classes. One could be ordered and the other unordered. Figure 7.24 shows the example of the PolyLine and Points which have a many-to-many association, but which is an ordered association, thus the use of vectors. Note, only if absolutely necessary should many-to-many association be used, and when used, try for an ordered association. Otherwise, try to collapse such association to one-to-many, unidirectional.
6
(e) Associations as separate objects: Can be used when testing the application.
Create a class (something like a driver) to run the application. Associations are created between the class used for testing and the object that is tested. The Test class would normally contain data such as the author’s name, the date, CPUtime and would be connected with the class that is tested via a method. See Figure
7.25.
(f) Qualified associations: Similar to the above except that a qualification is added to the object via a key that will initiate the simulation (test).
2.
Increasing reuse: Inheritance allows developer to reuse code. The classes should be organized into well-defined inheritance hierarchy to provide maximum code reuse.
Furthermore, a well-defined inheritance hierarchy also increases the possibilities for adding new types of objects with less coding being required.
3.
Removing Implementation dependencies: By developing abstract classes that serves as the base class, we could achieve inheritance. There are two types of inheritance: a) implementation inheritance – leads to fast reuse of code by subclassing an existing class and refining its behavior. Should avoid this type of inheritance. And b) interface inheritance – used for subtyping – the superclass is called supertype and the subclass is called subtype. Thus, delegation is used. Here an object of the superclass is declared in the subclass and that object is used to invoke the characteristics of the superclass.
4.
To apply the Optimization activities:
1.
Revisiting Access Paths: Inefficiency could be achieved by repeated traversals of multiple associations. We should consider for each operation – how often is the operation called? What associations does the operation have to traverse? Note: frequent operations should not require many traversals. A direct association should be established thus reducing the amount of traversals. We should also consider for each association – if it is a one-to-many or many-to-many? If a many side of a relation is frequently involved, it should be reduced to a one relation. Also often in system analysis, many classes may be modeled that may not provide any meaningful behavior, or behaviors that are be duplicated. These should be taken care of in object design.
2.
Collapsing objects : Turning objects into attributes: During analysis, many classes are identified associated with domain concepts. During object design, especially, restructuring may leave many of these classes with only a few attributes (classes may provide only one or two functions and may be associated with one other class). These classes could be collapsed into an attribute thus reducing the overall complexity of the model. A class that is a prime candidate for such collapsing is one that may only consist of a login ID which is also required by another class. The class that consists of the ID could be removed and the ID attribute included in the other class that may require the ID as well as providing more functionalities.
7
3.
Caching the result of expensive computations : Expensive computations need to be done once only, especially if the result would be used more than once in the application, and the base values do not change or change very seldomly. Expensive computation refers to the time it takes to compute something. If this is the case, then the result should be placed in a private attribute (caching).
4.
Delaying expensive computations : Figure 7.31 shows how delaying can be implemented. The example pertains to displaying an image, which obviously, takes a lot of processing time. It is suggested in this case to create a proxy object in which some attributes of the image is loaded (width() and height()). No data is loaded as yet.
Only when the image needs to be drawn will data be loaded from disk. Then when the paint() method is invoked, the real image is created.
Documenting Object Design:
Object design is documented in the Object Design Document (ODD). It describes object design trade-offs made by developers, guidelines they follow for subsystem interfaces, the decomposition of subsystems into packages and classes, and the class interface. The
ODD is used to exchange interface information by team members and as a reference during testing. An ODD should therefore satisfy the following creteria:
1.
Restrictiveness: A specification should be precise enough that it excludes unwanted implementations. Preconditions and postconditions specifying border cases is one way to achieve this.
2.
Generality: A specification should not restrict its implementation. This enables developer to substitute better (efficient or/and elegant) implementations (providing they adhere to the specification or even enhance the specification) that were not thought of during design.
3.
Clarity: A specification should be easily and unambiguously understandable by developers. A specification is absolutely useless if it cannot be understood or even difficult to understand. Therefore, wherever possible, use natural language to describe and use constraints and exceptions to describe boundary cases.
These documentation could be placed (embedded) in the source code, or often if a tool is used to develop these models, then the tools may provide a good portion of the documentation. The most important issue is that documentation is made and that whatever method is chosen, then that method must be the only method used.
Being consistent is of utmost importance, which leads to a few naming conventions:
1.
Classes are named with singular nouns.
2.
Methods are named with verb phrases, fields and parameters with noun phrases.
3.
Error status is returned via an exception only, not a return value.
8