LESSON 7 Object Oriented Software Design Contents LESSON 7 Object Oriented Software Design ............................................................................................................... 1 Objective ..................................................................................................................................... 1 Introduction ................................................................................................................................. 1 3Class Notation ....................................................................................................................... 2 UML Class Diagram ................................................................................................................... 5 Dependency Diagram.............................................................................................................. 5 Association Diagram ............................................................................................................. 11 Cohesion ................................................................................................................................... 14 Objective 1. 2. 3. 4. 5. 6. 7. To understand the concept of Object Oriented Design To apply Object Oriented Principles to design software systems. To understand what the Unified Modeling Language (UML) is. Be able to identify the components of Unified Modeling Language (UML) diagram. Be able to identify the Unified Modeling Language (UML) notations for a class. Be able to define a class diagram using UML. Be able to develop and code a UML class diagram into a complete program. Introduction Object-oriented design (OOD) is the philosophy of developing an object-oriented model of a software system, by defining the classes and their interactions with one another. The major benefits of OOD are easy maintainability systems, easy understandable systems, easy expandable systems, and reusable components of the systems,. In this lesson you will learn about the principles of OOD through the design principle called Unified Modeling Language (UML). First we will learn what UML is, and how it relates to classes. Secondly, we will learn how it is used to model the solution to an entire system by using class diagrams. When you are designing classes, you should pay attention to those units that are highly related to each entity. One way to achieve this is by applying the concept of cohesion. The third concept that will be covered is the principle of cohesion. This will be followed by the concept of coupling – the relationship among objects. The lesson closes with a survey of misinterpretations and pitfalls that could occur during the design and implementation phases. Before studying the pitfalls, however, we will develop an entire system using the concept of Object Oriented Design along with the Uniform Modeling Language. Uniform Modeling Language (UML) The Unified Modeling Language (UML) is a standardized specification language that uses a set of diagrams for modeling objects, with the ultimate aim of solving problems. UML feature several types of diagrams for different purposes, six of which are component diagrams, composite structure diagram, deployment diagram, object diagram, package diagram, and class diagram. Of these diagrams, class diagram is the one of importance to this lesson. Class Notation Central to the UML notation is the concept of a class. Class as we know it is a blue print that models an entity. It has name, attributes and operations. In UML notation, a class is represented by a rectangle that is divided into three regions. The first region specifies the name of the class, the second region specifies the fields – instance variables, class variables, and constants – and the third region specifies the methods. Figure 6.1 shows a schematic view of class notation using UML. Class Name -attribute1: data_type -attribute2: data_type : -attribute3:data_type -attribute4:data_type: : +constructor1(<parameter>) +constructor2(<parameter>) : : +method1(<parameter>): return_type +method2(<parameter>): return_type : : +method1(<parameter>): return_type +method2(<parameter>): return_type : : Figure 6.1: A schematic view of the class notation using UML format. There are several things worth noting in this diagram. UML uses the following notation: Class name: The class name is placed in the center of the first region. The name chosen follows the naming conventions discussed in Lesson 2. Attributes: Private attributes are prefaced with the minus sign (– ), as in the case of attribute1 and attribute2. If they were public they would have been prefaced with the plus sign ( + ). The data type of each attributes follows the name of the attribute, as in all three attributes. In addition, if the attribute represents a class variable, it is underlined, as in attribute3 and attribute4. Constructors: Constructors have the name of the class as usual. If they are public they are prefaced with the plus sing (+), also. They may or may not carry parameter. Methods: The same private – public rule holds for methods. They may also have parameters; in addition, the ending clause of the specification tells the return type. Class methods are underlined. In Lesson 2, we mentioned that we would be discussing UML in this lesson. Looking back at Figure 2.19 we were on our way to building a UML diagram for the class Sales. As a matter of fact if you recount all the diagrams that we used up to now, followed the same pattern as Figure 2.19 shown below. Name: Sales Instance Variables: product daily_sale daily_quantity Class Variables total_sale total_quantity Constructor Sales(product, cost, quantity) Instance Methods getProduct() getPieces() getSale() Class Methods getTotalSale() getTotalQuantity() Figure 2.19: A depiction of the Sales report program. Because we did not have any standard by which to specify each entry in those diagrams, we had to label each in order to know what each represents. Now that we have these standard elements we can apply them to Figure 2.19 to yield the class notation as shown in Figure 6.2. Sales -product: String -daily_sale: double -daily_quantity: int -total_sale: double - total_quantity: int +Sales(product: String, cost: double, quantity: int) + getProduct(): String + getPieces(): int + getSale(): double getTotalSale(): double getTotalQuantity(): int Figure 6.2: UML notation for the class Sales. In Figure 6.2, the first region of the diagram tells the name of the class. The second region itemizes two sets of attributes. The first set – product, daily_sale, and daily_quantity – represents instance variables; and the second set - total_sale, and total_quantity represents class variables. Each of the variables in both sets is prefaced with the minus ( - ) sign. In UML it means that these variables are private variables. The third region of the rectangle features three sets of ideas – constructors, instance methods, and class methods. The constructor has three parameters. Notice that the name of each of the parameter is followed by its data type. The instance methods follow the constructor. Notice that none of them has any parameter. However, their return type follows the name of the method. Finally, the class methods, they have features similar to the instance methods. According to the UML, by underlining them, they are interpreted as class methods. Another UML feature to recognize is that the constructor and the methods are prefaced with the plus ( + ) sign. This symbolizes that these are public entries. Let us suppose that class Sales should contain two mutator methods – one for changing the amount of sales, and the other for changing the quantity of an item. The following two statements would represent the UML notation for these methods. +changeSales(amount: double): void and +changeQuantity(amount: int): void In both cases the parameters are qualified by the variable – double and int respectively. In Lesson 7 we will learn about protected modifier, and how they are used in the UML notations. If no access specifier is attributed to the fields, the constructors, or the methods, then they assume the default package level. Exercise (6a) 1. What does the term OOD means? 2. What does the term UML means? 3. Name two uses two types of diagrams that UML is used for. 4. A family corner store wants to install a computerized weighing machine in its fruit and vegetable department. The input to the system is a single character identifier for the type of produce, two numeric values, one representing the weight of the produce, and the other the cost per pound. Design a class called Produce using UML notation to reflect the solution to this problem. 5. Make necessary additions to Question 4, so that it produces the total day’s sale. UML Class Diagram As we have mentioned, one of the applications of the UML is to design class diagrams. Class diagrams model class structures and contents using design elements such as classes, packages and objects. The term UML class diagram really means UML Class Relationship diagram. There are four class relationship diagrams that can be formulated using UML notation. They are UML Dependency diagram, UML Association diagram, UML Generalization diagram, and UML Realization diagram. We will discuss class dependency and association diagrams in this lesson. Generalization and realization diagrams will be discussed in Lesson 7 after we cover inheritance. Dependency Diagram The UML dependency relationship is the least formal of the four relationship diagrams, as we will see shortly. The default of any relationship is bi-directional. However, it is recommended that when designing class diagrams you limit your design to unidirectional relationship as much as possible. The UML dependency diagram uses a broken line to show the relationship between two classes. In addition, an open arrow head is used to show directional relationship between classes. Figure 6.3 shows two classes A and B that have unidirectional dependency. The relationship between these two classes means that class A depends on class B. A B Figure 6.3: UML Dependency diagram. A depends on B some what. In Figure 6.3, the unidirectional dependency class diagram means that class A uses class B. In a situation of this kind, the unidirectional dependency relationship is restricted to the following meaning. (a) Class A receives an instance of class B as a parameter to at least one of its methods, or (b) Class A creates an instance of class B, local to one of its methods. In this kind of dependency relationship class B cannot be an attribute of class A. Hence class A cannot contain an instance of B. Listing 6.1 shows the interpretation of the UML unidirectional dependency relationship. Notice that method1 in class A accepts reference parameter b. Also, method2 in class A creates a reference of B local to method2 in A. public class A { public void method1( B b ) { public class B { public void method() { } } public void method2() { B b = new B(); } public void method1() { } } } Listing 6.1: UML dependency relationship between class A and class B. Example 6.1 ABC Company wishes to keep a computerized database of its potential customers for future reference. During its campaign it interviews several people. The interviewer collects the name and telephone number of those who meet their criteria. The program will store the information it collects in a database. The company will communicate with customers via the telephone as a source of contact at a later date. Design a UML dependency diagram outlining the possible classes and the relationship among the classes. Analyzing the problem definition there are two obvious classes – a person class, and a test database class. Figure 6.5a and Figure 6.5b show the UML class notation for each class. Person -lastnams: String -firstname:String -phone: String +Person(lname:String, fname:String, phone:String) +getLastName(): String +getFirstName(): String +getPhoneNumber(): String Figure 6.5a UML class notation Person TestDatabase +main(arg[]: String):void +get(s: String): String +display(p: Person, msg: String): void +display(s: String, msg: String): void +search(list: ArrayList, phone: String): Person Figure 6.5b UML class notation TestDatabase Figure 6.5b shows that the class Database uses the class Person as parameter in the first display method. This usage defines a dependency relationship between both classes. That is, class TestDatabase depends on class Person. Figure 6.6 shows another feature of a UML class dependency diagram. Not only does the class TestDatabase depends on the class Person, but it depends on it from one to any number of times, as indicated by the symbols above the arrow. Person TestDatabase 1 1..* Figure 6.6 UML Class Dependency diagram. It is not enough to know that one class depends on another, but equally important is to know the frequency on which one class depends on another. So in this case the class Database depends on the class Person a multiplicity of times. The following table is a frequency chart showing the possible frequency occurrences. Frequency Meaning 0.. 1 Zero or one time 1 Only once 0 .. * Zero or more times 1 .. * 1 or more times N Only n times, n > 1 0 .. n Zero or more times, where n > 1 1 .. n 1 or more times, where n > 1 Listing 6.2 shows the definition for the class person. There is no dependency in this class. In addition, if a class cannot be decomposed into entities, we can regard it as an atomic class. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. public class Person { private String lastname, firstname, phone; public Person(String l, String f, String s) { lastname = l; firstname = f; phone = s; } public String getLastname() { return lastname; } public String getFirstname() { return firstname; } String getPhoneNumber() { return phone; } } Listing 6.2 class Person Listing 6.3 shows the definition for the class TestDatabase. It shows that this class depends on the class Person. The class TestDatabase uses the class Person as local variable to the method main; hence it is not an instance variable of the TestDatabase. See Lines 20, 13, and 24. Finally the class Person appears as parameter in the display method on Line 63. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. import javax.swing.JOptionPane; import java.util.ArrayList; public class TestDatabase { public static void main(String [] arg) { boolean done = false; ArrayList <Person>list = new ArrayList<Person>(); String msg = "Get choice\n1. Add new customer\n2. Search\n3. Quit"; while(!done) { int i = Integer.parseInt(get(msg)); switch(i) { case 1: Person p = new Person(get("Lastname"), get("Firstname"), get("Phone number")); list.add(p); break; case 2: String s = get("Phone number"); Person person = search(list, s); if (person != null) display(person, "Individual is"); else display("Phone number " + s, "Individual is not in database"); break; case 3: done = true; break; default: break; } } } static String get(String s) { return JOptionPane.showInputDialog(s); } 49. 50. 51. 52. static void display(Person p, String msg) { String s = p.getFirstname()+ " " + p.getLastname(); JOptionPane.showMessageDialog(null, s, msg, JOptionPane.INFORMATION_MESSAGE); 53. } 54. 55. static void display(String s, String msg) 56. { 57. JOptionPane.showMessageDialog(null, msg, s, JOptionPane.ERROR_MESSAGE); 58. } 59. 60. static Person search(ArrayList list, String phone) 61. { 62. int i = 0; 63. boolean found = false; 64. Person person = null; 65. 66. while (i < list.size() && !found) 67. { 68. Person p = (Person)list.get(i); 69. 70. if(p.getPhoneNumber().equals(phone)) 71. { 72. found = true; 73. person = p; 74. } 75. else 76. i++; 77. } 78. return person; 79. } 80. } Listing 6.3 class TestDatabase Exercise (6b) 1. Given that A and B are two classes, and that class B depends on class A. Draw a unidirectional dependency relationship diagram between both classes. 2. Given that A and B are two classes. What must be true be true, in order to establish a unidirectional dependency between class B and class A. 3. Given that Q represents a class. Which of the following classes establish a dependency relationship of class P on class Q? (a) class P { Q q; P() { q = new Q(); } } (b) class P { P() { Q q = new Q(); } } (c) class P { P() { } void add( Q q); { } } (d) class P { P() { } } Association Diagram Association defines a relationship that is much stronger than dependency relationship. Figure 6.7 shows the UML association relationship class diagram between class A and class B. A solid line with an open ended arrow establishes a unidirectional association relationship between these classes. The strength of an association relationship class diagram means that class A will contain at least one instance variable of class B, which makes class B structurally a part of class A. A B Figure 6.7 A UML Association relationship diagram. Let us re-visit Example 6.1, and let us redefine the class Person to have a name and a telephone number, where name becomes a class. Figure 6.8 shows the class diagram representing both classes. Person -name: Name -phone: String +Person(n: Name, phone: String) +getName(): Name +getPhone(): String Name -firstName: String -lastName: String +Name(first: String, last: String) +getFirstName(): String +getLastName(): String Figure 6.8 shows the association diagram of class Person and class Name. If we were to remove the field name from the class Person then the class Person, having just the field, phone, would be deemed meaningless. Hence by adding the field, name to Person, at least intuitively adds the meaning person. Figure 6.9 shows the class Person without the field name. Person -phone: String +Person(phone: String) +getPhone(): String Figure 6.9 shows the class Person without the field name. Listing 6.4 shows the code for the association relationship diagram between class Person and class Name. Notice that the class Person has instance variable name of type Name. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. public class Person { private Name name; private String phone; public Person(Name n, String p) { name = n; phone = p; } public Name getName() { return name; } public String getPhoneNumber() { return phone; } } 1. 2. 3. public class Name { private String lastname, firstname; 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. public Name(String l, String f) { lastname = l; firstname = f; } public String getLastname() { return lastname; } public String getFirstname() { return firstname; } } Listing 6.4: UML Association relationship between class Person and class Name. Figure 6.10 shows the association diagram of class Person and the class Name. It also shows dependency relationship between the class TestDatabase and Person, and between TestDatabase and the class Name. TestDatabase Person Name Figure 6.10 the association diagram of class Person and the class Name The class TestDatabase does not require much change. That is, the only change occurs in case 1 of the switch statement, as shown in Listing 6.5, Lines 16, 17, and 18. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. import javax.swing.JOptionPane; import java.util.ArrayList; public class TestDatabase { public static void main(String [] arg) { // Same as before, no change while(!done) { int i = Integer.parseInt(get(msg)); switch(i) { case 1: Name name = new Name(get("Lastname"), get("Firstname")); String phone = get("Phone number"); Person p = new Person(name, phone); list.add(p); break; case 2: // Same as before, no change break; case 3: done = true; break; default: break; } } } static String get(String s) {...} static void display(Person p, String msg) {...} static void display(String s, String msg) {...} static Person search(ArrayList list, String phone) {...} } Listing 6.5 The class TestDatabase In the class TestDatabase, the only change to the class from what is shown in Listing 6.3, shown in Lines 16 and 17. Exercise (6c) 1. What condition(s) must exist for the class A to have an association relationship on class B? 2. If class A has an association relationship with class B, draw the association diagram between both classes. 3. Given that Q represents a class. Which of the following classes establish an association relationship of class P on class Q? (e) class P { Q q; P() { q = new Q(); } } (f) class P { P() { Q q = new Q(); } } (g) class P { P() { } void add( Q q); { } } (h) class P { P() { } } 4. There are two classes, Circle and Shape. Define both classes where the class Shape defines an association relationship on the class Circle. 5. Define two classes, Sentence and Words, where the class Sentence has an association relationship with an array of potential Word objects. 6. Using Question 5, draw a unidirectional association relationship between both classes. Cohesion The concept of cohesion is the strength of the relationship among the components in an entity. When applied to programming it is the modeling of the strength of the relationship among the fields and methods in a class. That is, all possible attributes that pertain to an entity should be included in that entity, and are not to be spread over into other entities. In return each method within that entity should be cohesive; that is, each method does only a single precise task. A high cohesion method has several advantages than a low cohesion method. A high cohesion method is simpler to understand is simpler to re-use; it is easily maintained; it is easier to code; and is easy to debug. If methods are highly cohesive, then the class itself will be highly cohesive. Hence the class is easy to understand, because it is designated to relay a single complete thought. It is easy to code and maintained; highly re-useable, easy to debug; and above all, it is easily extendable. Example 6.3 Let us re-visit Example 6.1 and see how this concept fits in context. ABC Company wishes to keep a computerized database of its potential customers for reference. During its campaign it interviews several people. The interviewer collects the name, and telephone number of those who meet their criteria. The program will store the information it collects in a database. The company will communicate with customers via the telephone as a source of contact at a later date. Design a UML association diagram outlining the possible classes and the relationship among the classes. Re-analyzing the problem we see that there are four possible classes – a name class, a person class, database class, and a test database class. The name class and the person classes are already cohesive, because all the variables and methods are highly related to the concept of name and person. Refer to Listing .64. However, the concept of data base presents an entity separate and apart from the role of the test class, and should not have been embedded in the test class, as we have seen in Listing 6.3. Figure 6.5 shows a UML class notation depicting the class Database Database -list: ArrayList -person: Person +Database() +add(p: Person): void +search(phone: String): boolean +getPerson(): Person Figure 6.5 the UML class notation depicting the class Database In Figure 6.5, notice that the field list is necessary for storing person objects. In addition, a data base stores records, hence the need for the add method. Another function of a data base is the searching for particular record(s); and it also retrieves record(s). Clearly this class is cohesive. Figure 6.7 shows the revised test class TestDatabase. This class no longer has the ole of performing data base operations, but instead it is dedicated to the specific task of generating data and displaying information, the output. TestDatabase +main(p[]: String): void +get(s: String): String +display(p: Person, msg: String): void +display(s: String, msg: String): void Figure 6.7: The UML notation for the class TestDatabase When you analyze and compare this solution with Example 6.1 you see that by removing the database related items from the main method in TestDatabase class, and set up an identifiable class that deals with data base related issues, the class Database is highly cohesive. That is, it does only what a data base does - stores data, retrieves data, and searches data. Figure 6.7 shows the revised UML class diagram to reflect the four classes – Name, Person, Database, and TestDatabase. Name Person Database TestDatabase Figure 6.7 The UML class diagram for classes – Name, Person, Database, and TestDatabase In Figure 6.7 the UML diagram shows a set of unidirectional association and a set of unidirectional dependency diagrams. The class Database forms an association relationship with the class Person, similarly classes Person with Name. In either case the class Name does not know of the existence of the class Person; similarly, the class Person does not know of the existence of the class Database. The class TestDatabase forms a dependency relationship with each of the other classes; none of which knows about the existence of the TestDatabase class. Listing 6.5 shows the class definition Database. Line 7 shows that the variable person is an instance variable of class Person. Hence the class Person and the class Database have an association relationship. That is the class Database depends on class Person. The major significance of this association is that the class Database could not work without the variable person, because the Boolean method search cannot return more than one type of information. The method search is designated to return a boolean value, therefore there must be a separate method to return the instances of type Person when they are found. Against this background, the class requires an instance variable of Person. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. import java.util.ArrayList; import java.util.Iterator; public class Database { private ArrayList<Person> list; private Person person; public Database() { list = new ArrayList<Person>(); } public void add(Person p) { list.add(p); } boolean search(String phone) { boolean found = false; Iterator i = list.iterator(); while (i.hasNext() && !found) { Person p = (Person)i.next(); if(p.getPhoneNumber().equalsIgnoreCase(phone)) { found = true; person = p; } } return found; } public Person getPerson() { return person; } } Listing 6.5 shows the class definition Database. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. import javax.swing.JOptionPane; import java.util.ArrayList; public class TestDatabase { public static void main(String [] arg) { // Same as before, no change while(!done) { int i = Integer.parseInt(get(msg)); switch(i) { case 1: Name name = new Name(get("Lastname"), get("Firstname")); String phone = get("Phone number"); Person p = new Person(name, phone); list.add(p); break; case 2: String s = get("Phone number"); if (list.search(s)) display(list.getPerson(), "Individual is"); else display("Phone number " + s, "Individual is not in database"); break; case 3: done = true; break; default: break; } } } static String get(String s) {...} static void display(Person p, String msg) {...} static void display(String s, String msg) {...} static Person search(ArrayList list, String phone) {...} } Listing 6.6 shows the class definition TestDatabase. As was mentioned earlier, a high cohesive class is easy to understand, to code, to maintain, and extend. In terms of extendibility, a cohesive class can be extended without changes to the existing code. For instance, if the necessity arises to add an entry at particular location in the database, or to remove an entry, or to determine the size of the database, or to determine if the database has any element, there is no need to alter the existing code. All that is necessary is to add the necessary methods (extendibility) to the existing code. Figure 6.7 shows the UML notation with the newly included methods. Database -list: ArrayList -person: Person +Database() +add(p: Person): void +search(phone: String): boolean +getPerson(): Person +add(index: int, person: Person): void +size():int +remove(index: int): void +remove(person: Person): void +empty(): boolean Figure 6.7: Enhancement to the class Database Example 6.4 Let us modify Example 6.1 by including the high lighted information and see how these slight changes affect the solution. ABC Company wishes to keep a computerized database of its potential customers for future reference. During its campaign it interviews several people. The interviewer collects the name, the address, date of birth, telephone, and email address of those who meet their criteria. The program will store the information it collects in a database. The company will communicate with customers either by telephone, postal mail, or e-mail as a source of contact at a later date. Design a UML association diagram outlining the possible classes and the relationship among the classes. With this small change we can identify three atomic classes – a name class, an address class, and a personal information class. These classes can be used to define the person class. Not only can they be used to define the person class, but because they are so highly cohesive, they can be used by other programs that deal with names, or addresses, or other personal information. Hence the concept of re-usable codes (write once - use often). Figure 6.5 shows the UML class notation for each class. We will see that although there are additions to the system, the class Databse will change in any way. PersonalInfo Name -firstname: String -lastname: String -dateOfBirth : Date -phone : String -email : String +Name(f: String, String l) +getFirstname():String +getLastname():String + PersonalInfo (dob: Date, phone:String, email:String) +getDateOfBirth(): Date +getEmail(): String Address -street: String -city : String -state : String -zip : String +Address(street: String, city: String, state: String, zip: String) +getStreet() : String +getCity() : String +getState( ) : String +getZip() : String Person -name : Name -info : PersonalInfo -phone: String +Person(n: Name, p:PersonalInfo, phone: String) +getPhoneNumber(): String +getName():Name +getPersonalInfo(): PersonalInfo Database -list: ArrayList -person: Person +Database() +add(p: Person): void +search(phone: String): boolean +getPerson(): Person +add(index: int, person: Person): void +size():int +remove(index: int): void +remove(person: Person): void +empty(): boolean Name Address PersonalInfo 1 1 * * 1 * Person 1 * Database Notice that although the class Person has been modified, the class Database does not require any changes. This is one of the benefits of cohesion.