Baixe o app para aproveitar ainda mais
Prévia do material em texto
Table of Contents 1. Introduction 2. Domain Object 3. Parts of Speech Technique 4. Case Study : Buffet R Us 5. How to Identify Services 6. CRC Technique 7. Interviewing Domain Experts 8. Conceptual Category List 9. Static Modeling 10. Class Design 11. Finding Operations from the Static Model 12. Abstraction 13. Choose Good Names 14. Encapsulation 15. Polymorphism 16. Interfaces 17. Domain Model 18. Effective Use of Inheritance Object Oriented Analysis This book covers the basics of Object Oriented Analysis. We will discuss about domain objects, identifying services, classes, static modeling, finding operations from the static model, parts of speech and CRC techniques. We will apply the concepts to case studies to make the learning process easier. About the Author Bala Paranj has a Master's degree in Electrical Engineering from Wichita State University. He began working in the IT industry in 1996. He started his career as a Technical Support Engineer and then became a Web Developer using Perl, Java and Ruby. He is available for freelance work. Please contact him at support@zepho.com or via Ruby Plus. He is also working on screencasts based on this book. If you want notification about the release, please contact him. https://www.rubyplus.com/contact Domain Object Domain object is an object that consist of domain information only, it usually represents a logical entity in the problem domain. How to Identify Domain Objects? There are several techniques to identify domain objects and the services provided by them. They are: Parts of speech technique CRC technique Interviewing domain experts Conceptual Category List / Common Associations List Analysis Patterns [Fowler] Analysis Patterns is beyond the scope of this book. In the upcoming chapters, we will discuss the first four techniques in detail. Parts of Speech Technique In Abbot's linguistic analysis approach we identify the noun phrases and the verbs in the problem description or system specification to find the classes and their operations. Verb extraction is used to find the methods. Identify the verbs in the problem statement. These are good candidates for the actions that must be performed by the classes to carry out its responsibilities. Booch finds Abbott's method to be useful due to its simplicity and the fact that it forces the developer to work in the vocabulary of the problem space. The drawback is that the quality of the domain objects depends on the informal specification document. Coad and Yourdan suggest looking for key nouns and verbs in the widest possible variety of domain-related documentation, not just developer-generated domain descriptions. Example for Parts of Speech Technique Let us now see an example that illustrates the parts of speech technique for finding the domain objects. Consider the following statement of requirements for the first iteration of a Library System. Books and Journals : The library contains books and journals. It may have several copies of a given book. Some of the books are for short term loans only. All other books may be borrowed by any library member for three weeks. Members of the library can normally borrow up to six items at a time, but members of staff may borrow up to twelve items at one time. Only members of staff may borrow journals. Borrowing : The system must keep track of when books and journals are borrowed and returned, enforcing the rules described above. Requirements statement with noun and noun phrases in bold is shown below. Books and Journals : The library contains books and journals. It may have several copies of a given book. Some of the books are for short term loans only. All other books may be borrowed by any library member for three weeks. Members of the library can normally borrow up to six items at a time, but members of staff may borrow up to twelve items at one time. Only members of staff may borrow journals. Borrowing : The system must keep track of when books and journals are borrowed and returned, enforcing the rules described above. According to the Merriam-Webster dictionary, noun is something that is an entity, quality, state, action or concept. Books, Journals, copies of a book are all entity therefore they are nouns and is in bold. Staff member and library member are noun phrases. Short term is an adjective. Loan can be a noun as well as a transitive verb. Loan is a concept in the above context. We don't have enough information about the loan concept in the description. From the description, we can say "Book is loaned to a library member". This will be modeled in the domain model as a relationship between the Book and LibraryMember with loaned to as the name of the association. Therefore we will eliminate it. Here is a simple and easy way to find out if a given word is a noun or not. Go to Webster and just type in the word, the dictionary shows the meaning of the entry and also tells you if it is a noun or not. http://www.merriam-webster.com Case Study - Buffet R Us Let's identify the domain objects for the following system. Problem statement We are required to develop a payment system for Buffets R Us. Buffets R Us is a restaurant specializing in serving buffets to its customers. Lunch buffet is priced at $ 9.59 per person and dinner buffet is priced at $ 14.79 per person. Beverages are priced separately. Buffets R Us advertises at a local movie theaters to build its customer base. The customers can turn-in the used movie tickets for a 10% discount on their order. A senior discount of 15% is available for people over the age of 55. Only one discount is applicable at any one time. Kids less than five years old eat free on Tuesdays. Payment can be made using credit card, personal cheque or cash only. The receipt contains the date and time stamp of the purchase, restaurant name, name of each item and its price, discount type, discount amount, sales tax of 6.5% and the total amount for the purchase. Answer Lunch, Dinner, Payment, CreditCard, Cheque, Cash, Beverage are the set of domain objects for this system. All of these objects represent concepts that are part of the domain model of the payment system. Examining the nouns in the problem statement can help discover candidate domain objects, but not all nouns will relate directly to the system being designed. Candidate objects that do not have attributes or behavior required by the system are rejected. For example, “theaters” is a noun, but does not have any behavior or attributes required by the system, so it is rejected. The remaining objects are the core domain objects that will be part of the system. How to Identify Services Case Study - Automated Movie Ticket System This case study is used to illustrate how to identify services. We are commissioned to develop an Automated Movie Ticket System (AMTS) for Movie Brothers R Us. This system will allow the movie goers to browse for the current showings that are playing Users can also search based on show time, artist name or movie name. Users will buy tickets after selecting a movie and a show time, and after inserting any valid discount coupon. This system will only accept credit card as the payment method. Students can insert their student discount coupons to receive a discount of 20%. Matinee shows are priced at a 40% discount in order to fill up the empty seats. Once the card is authorized, the system prints the movie tickets with the show time, movie name, date & time and the theater screen number where the movie is played. Identify the services provided by this AMTS system. Answer browse(), search(), buy(), pay(), charge(), print() This is the complete list of all services to be provided by the SuD (system under discussion). Examining the verbs or verb phrases in the problem statement can help discover candidate services that must be provided by the system, but not all of them will relate directly to the system being designed. Candidate verbs or verb phrases thatdo not describe behavior required by the system are rejected. For example, “insert” is a verb, but it does not describe a behavior to be implemented by the system. The “matinee” shows get a discount and this is actually a temporal event; the system does not need any input from the actor. The verb “accept” is not a service to be provided by the system under development. Accepting a credit card is not the service to be provided, the desired service is to charge the credit card. The verb “receive” is not a service to be provided by the system under development. Receiving a discount is not the service to be provided. Applying the discount is part of the pay service. The verb “play” is not a service to be provided by the system under development. Playing the movie is the responsibility of the theater, not the system. It is out of scope. CRC Technique CRC technique can also be used to find the domain objects and services. CRC stands for Class-Responsibility-Collaborator. Let's now see how to identify objects using CRC Technique. Responsibility-Driven Design is a technique that can be used to identify objects. Responsibilities become attributes and operations after we refine our model. This technique is very helpful to teach beginners about OOAD. Once you become familiar with this approach it becomes easy to think in terms of objects and collaborations. Consequently, developers will be able identify objects mentally. This can be used during both the analysis and design phases. The emphasis will shift from what to how when we move from analysis to design. So, the CRC cards will be different for these two phases. In order to make it easier for the reader to understand the CRC technique, we will use a mini-case study to illustrate the concepts. The emphasis will be on identifying objects and we will make simplifying assumptions to make it easier to swiftly move to implementation stage. This means we will not have any realistic non-functional requirements, legacy constraints etc. Let's now look at a case study. Customer Credit Card Account Management Program System Description The program facilitates a credit card customer service representative (CSR) to maintain and access information about credit cards of the customer. Credit card type, holder’s name, credit card number and expiration date describe each credit card. Customer needs access to their credit card related information such as account balance, available credit, last payment date, last payment amount, any current minimum amount due, due date, past due amount if any. Data is saved in XML format. A CSR can run the program by logging on using their user id and password. This is due to the need for knowing who made changes or notes on a customer’s credit account. Notes are made to record notification of late payment by the customer, request for credit line increase, report of lost or stolen credit card, report of unauthorized charges, dispute related information etc. Once the CSR logins into the system, they can pull up the customer related information by entering the credit card number and doing search to get the related information. The screen shows customer’s authentication information such as their social security number, security question and answer and date of birth. A CSR must authenticate the customer before making any modifications or giving out any information related to that particular customer account. Customer record includes their current address, contact number and account holder name. Let us now move on from the initial specification to writing main success scenario for most frequently used use cases. We will defer the alternative path related work to the next iteration. The interaction between the system and the actor will be documented in a user interface intensive terms at a high level of abstraction. This is because we will know what data is going into and out of the system. This is a short-cut that we are taking for the purpose of our case study. In reality the users and domain experts will provide input on what information they will be providing and expecting from the system. We have selected Record Dispute as the most frequently used use case because we are making the assumption that the use cases that are most frequently used are Retrieve type of use case and this information can be given by the automated system over the phone. The phone interface is not capable of handling dispute charges. Our program therefore will focus more on functionality provided to the CSRs. CSR is capable of resolving the dispute and in some cases provide immediate resolution in favor of the customer depending on the situation. Scenarios Scenario 1 CSR selects “Record Dispute” from the menu. High-level interaction 1. CSR selects “Record Dispute” from the menu. 2. System displays a new screen that can capture all the dispute related information. 3. Customer provides the merchant name, transaction amount, transaction date and the reason for dispute. CSR enters this information into the system and saves it. Scenario 2 : Customer wants to update address. High-level interaction 1. CSR pulls up the account information related to the customer by searching on the account number. The customer record contains social security number, date of birth, current address and the security question 2. CSR requests the customer to identify himself and checks the information with that of the system. CSR selects change address. 3. System prompts for new address. 4. CSR enters the new address and updates the system. We will use the above two scenarios to illustrate the CRC technique. Identifying Candidate Classes Let us use parts of speech technique to find the candidate classes. Underline the nouns and noun phrases in the specification document to get a list of potential classes. Merriam-Webster dictionary defines noun as any member of a class of words that typically can be combined with determiners to serve as the subject of a verb, can be interpreted as singular or plural, can be replaced with a pronoun, and refer to an entity, quality, state, action, or concept Step 1 - Identify candidate classes from specification document. The candidate classes are in bold in the following specification document. The program facilitates a credit card customer service representative (CSR) to maintain and access information about credit cards of the customer. Credit card type, holder’s name, credit card number and expiration date describe each credit card. Customer needs access to their credit card related information such as account balance, available credit, last payment date, last payment amount, any current minimum amount due, due date, past due amount if any. Data is saved in XML format. A CSR can run the program by logging on using their user id and password. This is due to the need for knowing who made changes or notes on a customer’s credit account. Notes are made to record notification of late payment by the customer, request for credit line increase, report of lost or stolen credit card, report of unauthorized charges, dispute related information etc. Once the CSR logins into the system, they can pull up the customer related information by entering the credit card number and doing search to get the related information. The screen shows customer’s authentication information such as their social security number, security question and answer and date of birth. A CSR must authenticate the customer before making any modifications or giving out any information related to that particular customer account. Customer record includes their current address, contact number and account holder name. Step 2 – Identify candidate classes from scenarios. Parts of speech technique has the draw back of looking at only the specification for the candidate classes. We will miss classes that will be required for implementation if we do not look at other sources. Therefore let us look for candidate classes in our scenarios as well. Scenario 1: CSR selectsRecord Dispute from the menu. High-level interaction: 1. CSR selects Record Dispute from the menu. 2. System displays a new screen that can capture all the dispute related information. 3. Customer provides the merchant name, transaction amount, transaction date and the reason for dispute. CSR enters this information into the system and saves it. Scenario 2: Customer wants to update address. High-level interaction: 1. CSR pulls up the account information related to the customer by searching on the account number. The customer record contains social security number, date of birth, current address and the security question 2. CSR requests the customer to identify himself and checks the information with that of the system. CSR selects change address. 3. System prompts for new address. 4. CSR enters the new address and updates the system. Step 3 – Create Candidate Class List Candidate Classes 1. Credit card 2. Customer service representative 3. Customer 4. Credit Card type 5. Holder’s name 6. Credit card number 7. Expiration Date 8. Account Balance 9. Available Credit 10. Last Payment Date 11. Last payment amount 12. Current minimum amount due 13. Due date 14. Past due amount 15. User id 16. Password 17. Credit Account 18. Notes 19. Credit Line 20. Social Security Number 21. Security Question 22. Security Answer 23. Date of birth 24. Current Address 25. Contact Number 26. Account Holder Name 27. Record Dispute 28. Merchant Name 29. Transaction Amount 30. Transaction Date 31. Dispute Reason 32. Account Number 33. Account 34. New Address We have combined the candidate classes from the specification and the scenarios. We have also eliminated any duplicate entries in our list. Now we are ready to move on to the next step, which is filtering the candidate classes to find the final list of classes. Step 4 – Triage the Candidate Classes Let us now go through our selection of nouns and check if they can be moved from a candidate class list to required class list. Credit card. The Credit card consists of account holder’s name, credit card number, expiration date and credit card type. We need this class for implementation therefore we will move this to our class list. Customer service representative. This is just an actor. Is this part of our system? Does the system keep track of anything related to CSR? The answer is yes, but only for the purposes of keeping track of who made changes or added notes to a customer account. We will keep this class. Customer. We maintain the customer information in the system. These are cardholder’s name, current address, date of birth, social security number and credit card number owned by the customer. We will implement this as a class. Credit Card type. This is just a String (Master Card, Visa, Amex, Discover etc) and is an attribute that belongs to Credit card class that we discovered above. Holder’s name. This is the same as the Account Holder’s name listed below. Credit card number. This is a number that can be represented as an Integer. It is an attribute that belongs to Credit Card class. Expiration Date. This is a Date object and is an attribute that belongs to Credit Card class. Account Balance. This is a number and can be represented by a real number. This is an attribute of Credit Account class (listed below in this list). Available Credit. A real number represent amount of credit available. It is another attribute of Credit Account class. Last Payment Date. A Date is an object and is an attribute of Last Payment class. Last Payment will be a new class that will hold all the information related to the previous statement. Last payment amount. A real number and an attribute of Last Payment class. Current minimum amount due. A real number and an attribute of Current Payment class (a new class to hold all the relevant attributes and behavior). Due date. A Date object and an attribute of Current Payment class. Past due amount. A real number and an attribute of Current Payment & Last Payment class. User id. This is a String and is an attribute that belongs to Login Account used by CSR to login to the system. Password. Same as above. Credit Account. This is an important concept and must be implemented as a class. This will hold all the data and behavior associated with the credit card account of the customer. Notes. This is String object holding the information related to the communication between the CSR and the customer. Unknown during first pass on this list. After the first pass is made on this list we have made a decision to let this be part of Record Dispute class (which is below). Credit Line. A real number that belongs to the Credit Account class discovered above. Social Security Number. A number that is an attribute of the Customer class. Security Question. A String that is an attribute of some class. We don’t know what it will be yet. We might define a Authenticate or Identify class. Security Answer. Same as above. Date of birth. A Date class and is an attribute of Customer class. Current Address. This is a class by itself; we will implement a class called Address that will have street name, zip, city and state attributes. This is required due to the application requirements. Contact Number. An Integer that is an attribute of Customer class. Account Holder Name. A String that is an attribute of Credit Card class. Record Dispute. This is a class that will have all the data and behavior related to the dispute. Merchant Name. A String that is an attribute of Record Dispute class. Transaction Amount. A real number and an attribute of Record Dispute class. Transaction Date. A Date object and an attribute of Record Dispute class. Dispute Reason. A String that is an attribute of Record Dispute class. Account Number. This is the same as the Credit Card Number. We will combine the two names representing the same concept with just one name, Credit Card Number. Therefore, this is eliminated. Account. This concept is already captured by Credit Account. We will eliminate this candidate class from the list. New Address. This is the same type as Address that we have already discovered. So, we don’t need a new class to implement this concept. Step 5 – Required Class List We now have the following list of classes that we need to design and implement in our system. Credit card Customer service representative Customer Last Payment Current Payment Credit Account Address Record Dispute Step 6 – List of Attributes Identified During the Triage of Candidate Classes Credit Card type. Credit card class Credit card number. Credit Card class. Expiration Date. Credit Card class. Account Balance. Credit Account class Available Credit. Credit Account class. Last Payment Date. Last Payment class. Last payment amount. Last Payment class. Current minimum amount due. Current Payment class Due date. Current Payment class. Past due amount. Current Payment & Last Payment class. User id. Login Account class. Password. Login Account class. Notes. Record Dispute class. Credit Line. Credit Account class. Social Security Number. Customer class. Security Question. Authenticate or Identify class. Security Answer. Same as above. Date of birth. Customer class. Contact Number. Customer class. Account Holder Name. Credit Card class. Merchant Name. Record Dispute class. Transaction Amount. Record Dispute class. Transaction Date. Record Dispute class. Dispute Reason. Record Dispute class. Step 7 – List of Attributes Categorized According to the Class where it belongs Credit Account Account Balance Available Credit Credit Line Last Payment Last Payment Date Last payment amount Past due amount Current Payment Current minimum amount due Due date Past due amount Login Account User id Password Identify Security Question Security Answer Credit Card Account Holder Name Expiration Date Credit Card Number Credit card type Expiration Date Record Dispute Merchant Name Transaction Amount Transaction Date Dispute Reason Notes Customer Contact NumberDate of birth Social Security Number Step 8 – Identify Class Responsibilities The functionality required by the system is described in the use cases. The classes that collaborate together will implement the functionality and each class will have some responsibilities that it carries out during this activity. We can expand the high-level interactions into collaborations between classes and document the responsibilities that emerge. Each class will have a CRC card with the following information on it. 1. Class name 2. Attributes 3. Responsibilities 4. Collaborators 5. Short description of the purpose of the class The short description is written on the back of the card. For instance for Credit Card class, it will be: "I maintain all the information related to Credit Card and provide any required operations on my data to my clients". When you trace the use case scenarios with the CRC cards record all the information indicated above. During the trace if you find that you need a new class, we can create a new card for the new class and record the related information on it. Scenario 1: CSR selects Record Dispute from the menu. High-level interaction: 1. CSR selects “Record Dispute” from the menu. 2. System displays a new screen that can capture all the dispute related information. 3. Customer provides the merchant name, transaction amount, transaction date and the reason for dispute. CSR enters this information into the system and saves it. Class-level interaction: 1. CSR select “Record Dispute” from the menu. 2. Record Dispute UI is displayed on the screen. 3. Record Dispute Controller saves the Credit Account and the corresponding Record Dispute in the system. As we work at a lower level of abstraction we find that we must associate an account to a dispute. Otherwise we will not know which customer filed a dispute. We also have discovered that we need a new class called Record Dispute Controller. This class hooks up the UI to the domain classes Credit Account and Record Dispute. Let us now document the responsibilities on the CRC cards. Class: Record Dispute Controller Responsibilities: To respond to the user-interface events. To delegate work to the appropriate domain classes. To display the results to the user. Collaborators: RecordDisputeUI CreditAccount RecordDispute Back of the card: Description: I am the use case controller responsible for connecting the user interface to the domain classes. I encapsulate control logic. Attributes: Let us leave this blank for now. We do not need to be exhaustive while filling out the CRC card. We must avoid analysis paralysis. CRC card format Front of the CRC card Back of the CRC card You can think of a CRC card with the front as the public view and the back as the private view with implementation details. The CRC should contain Class name, Responsibilities and Collaborators section as the minimum. Attributes are considered implementation details and must be shown on the back of the card. Class: Record Dispute Responsibilities: Create a dispute record in the system. Provide access to existing dispute record. Collaborators: Record Dispute UI Description: I maintain all the information related to dispute and provide any required operations on my data to my clients Attributes: Merchant Name Transaction Amount Transaction Date Dispute Reason Notes Note: We can rename the RecordDispute to DisputeRecord (verb phrase is changed to noun phrase due to the naming convention for classes). Scenario 2: Customer wants to update address. High-level interaction: 1. CSR pulls up the account information related to the customer by searching on the account number. The customer record contains social security number, date of birth, current address and the security question 2. CSR requests the customer to identify himself and checks the information with that of the system. CSR selects change address. 3. System prompts for new address. 4. CSR enters the new address and updates the system. Class-level interaction: 1. CSR enters account number of the customer and does a search for the account. 2. Credit Account UI detects the user-interface search event and delegates the search functionality to Credit Account Manager class. 3. Credit Account Manager searches to find the account information corresponding to the account number of the customer. 4. Credit Account Manager returns the Credit Account class. 5. Credit Account UI queries the Credit Account class for the required fields and displays the information. 6. CSR selects the change address from the menu. 7. Credit Account UI responds to the change address user-interface event and prompts for new address. 8. CSR enters the new address and clicks update address. Credit Account UI sends the new address data and delegates the update operation to the Account Manager class. 9. Account Manager class updates the system with the new address. Class: Credit Account UI Responsibilities: Provide the UI for the credit account related information. Delegate work to the Credit Account Manager class. Collaborators: Credit Account Manager Credit Account Description: I provide the user interface to the credit account information and delegate all the functionality to a controller class. Attributes: none (for now) Class: Credit Account Manager Responsibilities: 1. Provide access to the existing credit account information. 2. Provide search by account number functionality to find credit account. Collaborators: Credit Account UI Credit Account Description: I encapsulate the control logic for searching and retrieve account related data used by my clients. Attributes: none (for now) Class: Credit Account Responsibilities: Provide access to the credit account information. Collaborators: Credit Account Manager Credit Account Description: I provide the data and access to the credit account related information to my clients. Attributes: Account Balance Available Credit Credit Line We can repeat the procedure outlined above to all other scenarios. Once we have a complete set of CRC cards documented we need to verify that each class is capable of providing the functionality documented in the scenarios. If we had missed any responsibility then we need to add them. If there are responsibilities that are not required then it must be removed from the classes. The main advantage of the CRC cards is their flexibility. It is very easy to move the CRC cards around to see how they collaborate and make changes to the CRC cards. This is very much like Agile Modeling. This technique is extremely useful during the analysis phase when the requirements are fluid. During the design phase we can add more details to the CRC cards such as define the data types of the attributes, include classes to satisfy non- functional requirements etc. Keep in mind that we work with cards that are limited in size and we may not prefer to work with CRC cards during the design phase at all. The real question is Where does the CRC technique fit in the big picture? The answer is that they find their use in modeling the vocabulary of the system. In other words, CRC cards in conjunction with use case-based analysis can aid us in the discovery of abstractions in both the problem and the solution domain. They are also used when we are modeling structural relationships. By using CRC cards in combination with use case analysis we can identify the classes that must interact. These interacting classes must have an association between them. Larman calls this as need to know associations. It is noteworthy to mention that CRC technique forces us to look at the structural and behavioral scenarios. This concludes our simple introduction to the CRC technique. Interviewing Domain Experts Domain objects can be identified by interviewing domain experts or from requirements document. The domain objects can be categorized into following three types: 1. Business objects that are things used in abusiness. These are abstractions such as invoice, account etc. 2. Real-world objects or concepts that a system keeps track. E.g., Book, Boat etc. 3. Events that occur such as gate closing, train arrival etc. This technique can also be used to find the services provided by the objects. Domain experts have very good knowledge in their domain. Interview domain experts and use abstraction to identify classes and services. This approach utilizes their domain expertise and is likely to result in a high quality domain model. Also this technique can be used in conjunction with other techniques to find the missing domain objects and services. This will improve the quality of the domain model. If this is the case then we get feedback from the domain experts for the results we got from other techniques. Major drawbacks include that it can result in analysis paralysis (how many experts? what if they disagree?) and the inability to trace the abstractions back to requirements. Conceptual Category List A conceptual category list [Larman] can be used to identify the domain objects. In this technique we can use the list of categories as a crutch to discover the domain objects. Study the requirements and determine items from the problem that belongs to a certain category. This is also called as Common Associations List. Similarly common associations list can be used to identify the relationships between the domain objects. Seek common relationship patterns in the interactions between classes and objects and invent ways to use these patterns. Evaluate the semantic relationships and seek to maximize coupling among things that are semantically related while minimizing coupling among things that are semantically distant and subject to change. The disadvantage is that we may get stuck at some point when something does not seem to fit well in the category list. In such a situation remember that no there is no such thing as the perfect domain model. It is just more or less valuable tool in understanding the domain. If we capture all the important concepts and their relationships in the domain then we can get a good domain model. All of the above techniques can be combined to identify classes and services depending on the situation, so that we optimally use the different approaches based on their strengths to arrive at a good domain model. Static Modeling Identifying Attributes To find the attributes of a class ask yourself the following questions: What does the class know? What information should be stored by the class? We will discuss this in further detail below when we develop a domain model. Developing a Domain Model For very small business domains, domain model is not required; a glossary of terms will be sufficient. Domain model should only be concerned with the problem domain and should not include anything related to solution domain. The main purpose of the domain model is as a communication tool between the developers and other non-technical staff. This is the reason why we do not include anything related to the solution domain. The sequence of activities shown in this eBook is for learning purposes only. In real world, OOAD skills that you apply will not be rigid or follow a linear structure. Identify Classes Class is a template that defines the structure and behavior of an object. It defines the data (state) and methods (behavior) that all objects of that class will possess. An object is characterized by responsibilities, semantic integrity constraints, types and relationships. Meriam Webster defines the term semantic as related to meaning. An example of a semantic integrity constraint on a class called Reservation would be Number of rooms reserved cannot be zero or negative. These rules are the constraints on the data values of the object that impose the business rules. From a programming standpoint, you must throw an exception to indicate invalid data passed to the methods in a class. This prevents violation of the data integrity in the system. To identify classes, make a list of key nouns from use cases. These are candidate classes. Synonyms must be merged into one term and documented. Classes can be found by using the following categories: 1. Tangible things (e.g., Book, Apple, Credit card application form) 2. External systems (i.e., actors, e.g., Credit Bureau) 3. Devices the system interacts with (library card scanner, receipt printer) 4. Locations of things (Concert Hall, Exhibit Room) 5. Roles of people or systems (Teacher, Credit Authorizer) 6. Organizations (Hospital, Finance department) 7. Events (signing a lease or shifting a gear) 8. Remembered events (time of delivery of a package, date of signing a lease) Identify Attributes of a Class Find the data that a class needs to maintain its state. The decision whether to represent the data as an attribute or derive it when required depends on speed vs space trade off. E.g., total debts owned to all creditors by a credit counseling service customer. Keep the data in the class where it is used most of the time. During the triage on the candidate classes, eliminate any classes that provide the functions of a primitive data type such as string etc. It becomes an attribute. Identify methods Make a list of all the verbs in the use cases. These become a list of probable methods. Similar to the classes any synonyms must be merged into one term and documented. We have discussed about identifying responsibilities and attributes in the requirements modeling chapter. This is during the analysis phase, therefore each responsibility of a class may have to mapped on to several methods when we design. The attributes that we found in that chapter are related to the problem domain only. Class Design Following are the important concepts to know about class design: Operations, parameters, attributes etc use the programming language syntax that has been selected for implementation. Visibility of attributes and operations are specified using the keywords of the selected programming language (If it is Java; public, protected, private are used) The relationships between design classes have meaning when it is implemented. Egs: Generalization becomes inheritance in the programming language. Associations and aggregations often maps to variables of classes in the implementation phase. This provides references among objects. A design class can represent an interface if the programming language supports it. A class interface is the list of operations that is provided by the class to its clients. Note: This is different from interface that specifies a contract that must be implemented by a class when it extends (implements) the interface. When we identify the operations the resulting set of operations may not be sufficient for other applications. This is due to our use case driven development process. Since our use cases are specific to the application under development. It may not require certain operations that might be required in other applications. Use case driven approach results in minimal set of operations for a class. At the end of the project, there can be a separate phase allocated for looking for objects that can be reused. This topic is beyond the scope of the exam. Before we discuss the various techniques to find operations, we must keep in mind that the discovery of operations is done during the design phase. Therefore, we must communicate with the stakeholders and ask them if they need this functionality. If they had overlooked or could not foresee the need this requirement then we need to update other artifacts such as use cases, design class diagram and anything else that might be affected. If it is not required then we can discard this operation. Of course no update would be needed in that case. Design of operations depends on the trade-off priorities dictated by the requirements. Some of the factors involved during this trade-off are: memory usage, efficiency, re-usability, maintainability etc. From thespecification perspective the associations in the class diagram represent the responsibilities of the participating classes. Following are the types of operations that we have to consider during this activity. 1. Setting the value of an attribute. 2. Accessing the value of an attribute. 3. Checking the value of any derived attribute. 4. Creating relationships. 5. Querying an association. 6. Setting an association. 7. Accessing all objects that have contains relationship. Setting Attribute Values For each attribute in a class we must decide whether we need an operation that sets the attribute’s value. This decision is based on whether we should allow a client to change the attribute or not. For example, once we create an order number during the purchase of an item in a restaurant, we should not change it. Therefore, when the Order object is created, the order number is initialized and we do not provide a method to set the order number. In UML this constraint is called as frozen. An attribute which is frozen cannot change its value during the lifetime of the object in which it is declared. Consider the Guest class in Hotel Guest Management System. From now we will use the abbreviation HGMS to refer to this system. The marketing department is taking a survey to target its customer effectively. The result of this survey produces customer profile. The profile consists of answers to questions like their age, sex, number of children, income range etc. In this example, the attribute sex cannot be changed and we do not define any setter method for it. You might ask: What happens if a careless mistake was made? This is assumed to be a rare case, if this happens then we have to delete the object and create a new one with the correct attributes from the beginning. Syntax: void setAttribute(type : attribute); or boolean setAttribute(type : attribute) Accessing the Value of an Attribute Attribute values are accessed by defining accessors. We must minimize the getters. Because the class must operate on its data and provide services and not act as a data holder. There are certain situations where a class can be a data holder, ValueObject is an example in Java. Checking Derived Attributes If the value of an attribute is calculated from the values of other attributes in the object then it is called as derived attribute. Therefore, we cannot set the value of a derived attribute. It may be required to allow clients to access the derived attributes. If so, we can define accessor for this purpose. For example we can calculate the number of days over due on an invoice by operating on the invoice due date. The value may either be calculated everytime it is accessed or cached for faster retrieval. Decision must be made based on the time vs space trade off requirements. Creating Associations Guest can stay in one room only. Either 0 or 2 guests can occupy room. Let us now consider the scenario where the room is vacant. The HGMS needs this object in order to set the value of the Boolean attribute isClean. After the housekeeper has finished cleaning the room, the list is given to the front desk clerk who updates the system using the user interface to indicate which rooms are clean. When a guest checks in to a room, the association between the Guest object and Room object must be created. We will put an operation in the Room class that allows us to set the guest name, namely, setGuestName(String : name). For the purpose of illustrating the concept we will assume guest names are unique. Later we will see how to model this without making this assumption during our discussion on qualified association. Sometimes, the associations will be created when we create the object. In the example, when we create the Performance object we pass in the Dancer object and Event object to the constructor. The constructor creates the association between Dancer object and Event object. Association classes will be discussed in detail later in this chapter. Querying an Association We can query an association for two different purposes. They are: 1. To check for the existence of an association. 2. To find out which object is associated for a given object. Case 1: Checking to see if the relationship exists: We can add an operation called isTeamMember() to the Dancer class to check if the Dancer is on a team or not. But we will not know the name of the team. Case 2: Accessing the value of each association: If we are interested in knowing the value of an association, we can query the association to find out. We can add an operation getGuestName() to the Room object. Given an object, the query tells us which object has an association with it. If we want to query previous association then we can add getPreviousGuestName(String : roomNumber). For example, this can allow us to know which guest had forgotten their belongings. A dancer can be a member of one or more teams. This model is shown from history over time perspective. It means that a dancer can be part of a team only one at a time but could possibly be a member of many teams over time. Each team consists of at least 2 to at most 10 dancers. We are not showing the possibility of a dancer not being a member of the team (i.e., in 0..* notation, the case when 0 value is taken by the association) because our application requirements dictate that a dancer has to be part of a team in order to compete in the event. Changing Associations Case 1: Go through the associations one at a time and see if there is a need to change the association to a different object. Add an operation to change the association if necessary. joinTeam() allows the Dancer object to join different teams. addDancer() allows the Team object to add different Dancers. Case 2: Check if an operation that deletes the association is required. This operation is valid only if the multiplicity can take on the value of 0 (0 means no association). Otherwise, the integrity of the artifacts will be lost. quitTeam() allows the dancer to leave the team. dropDancer() allows the dancer to be removed from the team. Iteration over Associations When you have a one to many association in the design class diagram, it gives us a clue that clients may want to iterate over all the objects to do something. In this example, RoomScheduler object needs to iterate over all the rooms and make a list of all rooms that are clean. Therefore we define a new operation called isClean() in the Room object for this requirement. Reference Objects and Value Objects Objects have identity. This is much more important to reference objects than it to value objects. Reference objects are called so because you will get hold of that object through a reference or pointer in the code. For example it could be a thing like Invoice. The identity is important because of the constraint that there can be only one object to designate an invoice in the real world. All variables that point to this invoice will reference the same invoice object. This means any changes to a particular Invoice object will be seen by all the variables pointing to it. In order to check if two references to an Invoice is the same you have to compare their identities. If this object is cloned for some reason then we have to synchronize the changes. In simple terms the Value objects are objects that represent constants. Once they are created they cannot be changed. So they are immutable. We could have many value objects that represent the same object in the real world. For example a Date object that has a value of 21-July-05. We could have an application where two different person might have that date as their birth date. If we had two copies of this particular date object then they can be interchanged. The date object is created when required and destroyed when it is no longer needed. In order to check if two dates are the same you must compare their values. So an equality test method must be implemented. In this case the method will compare the values of year, monthand day for both the objects. The implementation of this method depends on knowing what data must be same for two objects to be the same. Let us look at another example where we have a BreadCrumb object that represents the bread crumb of an user's navigation through a web site. In this case, the BreadCrumb object must compare the hyper links of the two BreadCrumb objects to determine if they are the same. The BreadCrumb object is a custom data type and is an example of how you can extend the type system with your own classes. Value objects are immutable therefore we do not provide any setters. Of course there are always exceptions to any rule. So, if you are using a persistence framework like Hibernate and you want to persist a value object, you could provide a private setter. Each value object is a separate object but sharing is allowed since it will not lead to any bugs in the software due to accidental updates. If you want another date with a different value then instead of updating the existing date object you must create another date object with the required values. This will prevent update of a date object that belongs to some other object leading to bugs in the software. If frozen is applied to a class it means that all attributes and association ends corresponding to that class can never change. There is a difference between frozen attribute and a read-only attribute. Read-only means the client code cannot change the value and it may change value due to the implementation inside the class. For example the attribute legalDrinkingAge can be frozen to a value of 21 and this can never change whereas the age attribute can be read- only but it cannot be frozen because it changes with passage of time. If a new law is passed and the legal age for drinking is changed then it can be changed only by the implementing class and not the client code. As far as the client is concerned the attribute legalDrinkingAge is frozen. The same argument holds if an inadvertant mistake is made in setting the initial value of this variable. If a class has a mutable object as one of its attribute and we have a setter due to the requirements then the implementation of the setter must clone the mutable object and return it. This will prevent any of the clients accessing the mutable object from accidentally changing its value and introducting bugs in the code. Let us look at an example to illustrate this concept. Consider a Reservation object which has check in and check out date as its mutable attributes. There is a semantic constraint imposed by the application requirements that once the guest checks in at a particular date the check in date cannot be changed. The client code can call the getter of the check in date and once it gets the date object it can change its value by calling the setter in the Date object. This will become a bug because it violates the constraint. So eventhough you do not provide a setter for the check in date in the Reservation object the setter in the Date object allows the value to be changed. Since the getter for check in date returns the reference for the check in date object the client code can use this reference to change the value of the check in date. To prevent this bug we must clone the check in date object and return it. Client code can access this value maybe to generate reports but it will not be able to change its value. Abstraction In this chapter, you will learn how to determine when a new class is needed. To quote Booch in Object-Oriented Analysis and Design with Applications: An abstraction denotes the essential characteristics of an object that distinguish it from all other kinds of objects and thus provide crisply defined conceptual boundaries, relative to the perspective of the viewer. The term perspective of the viewer needs an explanation. Let us consider a House object, when a banker sees this house, he thinks in terms of the value of the property, opportunity for appreciation, etc whereas when a decorator views it, he thinks in terms of what color the house should be painted, total area to be painted, etc. The same object House can be viewed from different perspectives and can lead to entirely different abstractions by different people. Booch, Fairsmith, Henderson-Sellers define abstraction as: Any model that includes the most important, essential, or distinguishing aspects of something while suppressing or ignoring less important, immaterial, or diversionary details. Coad, Fairsmith, Henderson-Sellers, Rumbaugh define abstraction as: The cognitive tool for rationalizing the world by considering only those details necessary for the current purpose". So, abstraction is about what details we choose to emphasize and what details we choose to ignore. What we choose to emphasize is dictated by the application. It simplifies the things that we look at in the real world. For example, a chair can be made up of different kinds of material, height adjusting knobs, reclining adjustment knobs etc. If every time we looked at the chair, if we had to deal with what material it is made up of, how the height adjustment knobs are designed and other irrelevant details related to our purpose using a chair to sit, our brains will be exhausted. So, the abstraction process simplifies things and allows us to manage complexity during problem solving process. Interface of a Class and its Role in Abstraction The interface of a class should offer a set of methods that belong together. Let us consider a class that represents Ticket. It would contain data describing the ticket, date of event, name of the event, seat number, and so on. It would offer services to initialize and use ticket. Here is the code showing how it looks. public class Ticket { public String seatID; public Date eventDate; public String eventName; public Ticket(String seatID, Date event Date, String eventName) {...} public String getSeatID() {} public Date getEventDate{} public String getEventName{} } The ticket object cannot be modified after it is created because the values of the attribute cannot be changed. The interface of the class has methods that provide access to the attributes that are relevant to the abstraction that the Ticket object represents. This class might have additional methods and data to support these services but the clients using this class don't need to know about these implementation details. For example, the seatID could be accessed from a relational database. This is an example of a well defined interface with good abstraction because every method in the interface is focused on expressing a single concept. Conversely, an example for a class with poor cohesion would be a class called MegaBitePizzaAndAutoService which consists of unrelated methods such as deliveryTotal(), dineInTotal(), oilChangeTotal() etc. To correct the mistake all operations related to auto service can be moved to a new class called AutoService. The public methods are the basis for evaluating class abstraction. This does not mean that methods private to the class can have poor abstraction. The private methods should also be designed with good abstraction in mind. Guidelines for Creating Class Interfaces Guideline 1: Each class should implement one and only one abstraction. If a class implements more than one abstraction or if we cannot determine what abstraction the class implements, then the class must be split into one or more well defined abstraction. The following example shows a class with inconsistent interface. It is inconsistent because level of abstraction is not uniform. public class DanceTeam extends ArrayList { public void addDancer(Dancer aDancer) {...}; public void removeDancer(Dancer aDancer) {...} ; public Dancer getFirstItem() {...}; public Dancer getLastItem() {...}; public Dancer getNextItemInList () {...}; } Java Example of a Class Interface with Mixed Levels of Abstraction The abstraction of the first two methods is atthe "Dancer" level. The abstraction of the last three methods is at the "List" level. The DanceTeam class is capturing two abstraction: a dancer and a Collection utility. The ArrayList class is a utility class found in the Java's java.util package. The fact that we have chosen a particular collection class to implement the functionality should be hidden from clients. This gives us the flexibility of changing the implementation where we could use a different collection utility class which for example gives us better performance under multi-threaded environment. The following code shows the class after it has been refactored to improve its abstraction. public class DanceTeam { public void addDancer(Dancer aDancer) {...}; public void removeDancer(Dancer aDancer) {...}; public Dancer getFirstDancer() {...}; public Dancer getLastDancer() {...}; public Dancer getNextDancer() {...}; private Collection dancerList; <----- Collection class now is hidden. } In the code shown above, the abstraction of all the methods is at the "Dancer" level. We now have the variable that is of type Collection interface. We could implement the dancerList as ArrayList, Vector or any other type of collection class. It is actually a design flaw for the DanceTeam to inherit from the ArrayList class. It fails the inheritance test question: Is DanceTeam a type of ArrayList? If the abstraction of the DanceTeam object requires searching and sorting then it should be made explicit by making it part of the class interface. Guideline 2: Have a clear understanding of the abstraction. Sometimes there might be two classes representing two similar abstractions. Choosing the right class depends on clear understanding of the abstraction it represents. During development if two classes are very similar then having a clear understanding of the abstraction will help us to come up with the correct interface for the class. Data dictionary helps us to clarify our understanding. Guideline 3: Refactor unrelated information to a new class. If you find that roughly half of the methods work with half of the class's data and the other half of methods work with the remaining other half of the data then you actually have two classes combined into one. Split the class into two separate classes. This leads us to the rule: Most of the methods defined for a class should be using most of its attributes most of the time. Guideline 4: Analyze classes that has more than approximately 7 attributes. It has been discovered that 7 + 2 is the number of things an individual can remember while performing other tasks. If a class exceeds the 7 + 2 limit then check if the class can be decomposed into multiple smaller classes. If the attributes are primitive data types like integers and strings then you set the limit as 9, if it is complex objects then the limit is 7. List of Reasons to Create a New Class The following list shows good reasons to create a new class: 1. Create a class to represent each real-world object. This object must be within the scope of the system that we are modeling. 2. Create a class to model abstract objects. Abstract object is not concrete real-world object. It provides an abstraction of other concrete objects. For example, Chips and Fruits really exist in the real world whereas Food is an abstraction of other specific foods. If I ask you to point me to a food, you only point to a specific type of food such as apple, orange etc. So if you consider a Food abstract class and various sub-classes such as Cake, Orange etc. There can be no instance of Food class. It is abstract. We are modeling the real world and abstraction is one way by which we deal with complexity. We can only create instances of the sub-classes Cake, Orange etc. These instances will be named birthDayCake, anniversaryCake etc. The sub-classes are concrete and you can create instances. 1. Create a class to reduce complexity. This is the most important reason to create a class. Java Server Faces API is a good example. JSF API has made it simple to write GUI code for server based applications by hiding implementation details such as communication related details from the client and server. 2. Create a class to isolate complexity. For instance if we have to deal with complicated algorithms, it will be easier to locate and fix bugs if it is localized within a class. It will also be easier to replace algorithms. 3. Create a class to minimize the impact of changes. Separate the areas that are most likely to change. Our aim is to reduce the number of classes that has to be modified when changes occur. 4. Analyze parameter passing to check if a new class is required. Minimize passing data to several objects. This will require reorganizing the class structure. If a parameter is being passed to many methods it may be time to refactor so that the parameter becomes the attribute of the new class and all the methods which used it as a parameter now becomes the interface of the new class. The method now can access the attributes declared within the new class instead of receiving it as a parameter. 5. Create new class to centralize control of tasks. You might be accessing a directory server, database or other external source of data. Creating a class that is responsible for the external data access makes it easier to make changes. 6. Create a new class to enable re-use. We might refactor existing classes so that it could be re-used in a different application. The classes that are easy to design for re-use are utility classes such as Collection classes, JNDI connection pooling classes etc. User interface related classes can also be easily designed for re-use if we architect the system to use an architectural pattern like MVC. 7. Create a new class to represent one abstraction. A class that represents multiple abstractions should be split into multiple classes until each one represents a single abstraction. Keep related data and behavior in one place. Two classes related by a one-to-one relationship can be combined to form a single class only when it results in a single abstraction. Spin off non- related information within a single class into another class. 8. A data item needs additional data or behavior. A data item contained in a class that requires additional behavior or additional data of its own should be made into its own class. 9. An array contains certain elements that mean different things. Each element of the array should become a field in a new class. This eliminates the need for conventions about the meaning of each array element. Each field can be named appropriately for its purpose. 10. A class has a numeric type code that does not affect its behavior. A numeric type code that does not affect a class's behavior should be made into its own class so that the compiler can do type checking on it. This has an advantage over numbers because you can ensure that only valid codes can be constructed, reducing bugs. [Fowler - Refactoring ] 11. When we find that a class has two different definitions during OO analysis, split the class into two different classes having different names. Each class should represent a single abstraction. If the class has two different definitions, it is really two abstractions given a common name. Each abstraction should be given a different name and made a separate class. These definitions will be included in the Glossary. Choose Good Names In this chapter, we will discuss about choosing good names for classes and methods Choose Good Names for Classes The name of the class should clearly indicate the abstraction represented by the class. Noun is used to name classes. It should begin with an uppercase letter. The names can be as long as required to make it understandable. For example Ticket is a good class name where Tick is a bad class name for a class that is representing the ticket abstraction. A class that has a name which contains the data structure that it uses internally as part of the nameis also a bad naming convention. Because it is exposing the implementation details and violates encapsulation. It is a good idea to name a subclass as Xsuperclass where X denotes the specialization this class implements. For example, if the superclass is ClubMember, a good name for a class that represents the specific type of ClubMember in a club for golfers would be GolfClubMember. This helps developers to quickly know where in the inheritance hierarchy we can find this class. For classes in the domain layer, choose names that correspond to real world entities. This will allow us to directly map the analysis class to classes in the design model thereby resulting in low semantic gap between the domain model and the design model. This makes communicating the design clear. When we encounter the same definition for two classes during the OO analysis, we need to settle on a common name for the object. The name should be chosen from the real world entities in the problem domain. This will allow us to directly map the analysis class to classes in the design model thereby resulting in low semantic gap between the domain model and the design model. So, we can resolve this conflict by choosing the class name that is close to the problem domain. Choose Good Names for Methods The name of the methods should explicitly tell the reader what it does. It should be meaningful and clear. It should start with a verb. The names can be as long as required to make it understandable. Since the methods are invoked using a class name, part of the name is already available. For example, instead of author.getAuthorBookList(), it is better to have author.getBookList(). If a function returns a value then name the function for the returned value. For example ticket.isAvailable() is a good method name. The question is available? returns a boolean value. When a class has opposite operations, name them explictly instead of using obscure names. Example: addDancer(), removeDancer(). Consistent Abstractions You can look at concepts at several different levels of details. For example let us consider a "Seat" object from a flight reservation system. When you consider the seat in a aircraft, you might be interested in attributes of the seat such as its number, whether it is window or aisle type, and its comfort level such as first class or coach class. public class Seat { public String number; public String location; public String type; public Seat(String number, String location, String type) { initialize all parameters here... } public String getNumber() { ... } public String getType() {... } public String getLocation() {...} } As a side note, Seat object is immutable because its attributes cannot be changed after it is created. We provide only getters (or accessor) and do not provide settors (or mutator) in the Seat class. If you are considering an abstraction that is about collection of seats as described below then you are viewing the problem domain space at a different level of abstraction. public class Seats { public int totalSeats; public int availableSeats; public int reservedSeats; public boolean isSeatAvailable () {...} .... } The name of the class representing the abstraction described above should actually be named something that reflects the abstraction it is capturing. Therefore instead of Seats we can call it Seating or SeatAllocation. As a side note, this object falls under the abstract noun concept in the conceptual class category list. Encapsulation In this chapter, we will discuss about how to maintain encapsulation of attributes and visibility of operations effectively. All attributes should be hidden inside a class. As always, there are exceptions to any rule, in this case it is very rare for an attribute to be declared public. The only way for clients to access the attributes within a class is via methods. If clients access the attribute very often then we should re-consider the decision to declare the attribute within the class. Why does someone need it? Is it possible to perform the required operation on the data by the class owning it and provide the service to the clients? If this is possible then we also need to ensure that the operation logically is related to the abstraction that the class represents. Otherwise, move the data to another class and define a new method that operates on it. This increases the cohesion of the class, which is desirable. What is Encapsulation? Encapsulation is achieved by means of information hiding. Information hiding means hiding of all the implementation details of a class from other classes. Data is hidden from the clients and and it can only be accessed through a well defined interface. The interface is well defined in the sense that the interface defined is stable and hides the design decisions that are likely to change. As long as the method does not change its signature and fulfills its contract, the implementation of internal details can change without affecting classes using the method. By doing this we hide the internal data representation as well as the internal complexities of an implementation. It is closely related to abstraction because encapsulation is the process of hiding the details of an object that do not contribute to its essential characteristics. The goal is to eliminate the changes propagating to other classes when changes are made to the class. This isolates the changes to just one class avoiding global changes which makes maintenance easier. Encapsulation prevents accidental updates. Allowing direct access to internal data of a class permits any class to change it. Encapsulating the data by allowing access only through methods allows rules to be applied to attempts to change data and prevents accidental updates to invalid or unexpected values. To prevent this, we could return read-only copy of the internal data when a collection class is accessed. The client is not aware whether it is getting a copy or the actual data. This decision is hidden from the client. It allows re-use of abstraction without depending on the implementation. Hiding the internal implementation of a class allows an abstraction to be implemented in different ways without affecting the classes that use that abstraction. Classes only depend on the abstraction that has published a stable interface and is not wired to a specific class that implements the abstraction. In other words we program to the interface not the implementation. Encapsulation also provides opportunities to find simpler and elegant design. This makes code easier to understand. It permits team members to work independently on state and functionality and allows classes to be understood in isolation. Since encapsulation hides variable that are internal to an algorithm, allowing access to variable only through public methods, the Project Manager can use encapsulation to deal with the complexities during the early stages of the project and avoid a maintenance nightmare during later stages of the product life cycle. The Project Manager can make sure that stable interfaces are defined between the different layers of the system in the beginning of the project. This will avoid problems integrating the different layers and enforces consistency in the interaction between layers. People disagree on the distinction between encapsulation and information hiding. Arguing that encapsulation means bundling of the data and methods together into the same class. This may not necessarily hide the data from external view. From the exam perspective, keep this in mind and use the elimination process to answer any ambiguous questions on encapsulation. Visibility One of the primary task during class design is making decisions about which methods should be made public and which must be made private. This is called as visibility. For example, a class might implement 10 methods and expose only 4 of them, the remaining 6 methods are used internally by the class.Visibility concept differs with languages. So let us briefly discuss some of the visibility concepts that are applicable to Java. Public elements are visible to any class; private elements are visible only to the owning class and protected elements are visible only to the subclasses. In UML, attributes or operations can have a visibility indicator. The meaning of the marker is language dependent. So we could have + (public), - (private), and # (protected) for Java. Encapsulation of Attributes A class might use several attributes that cannot be accessed. In this case, the attribute is declared private to the class and no accessor method is provided for other classes to access. During design make the visibility of the attribute as strict as possible i.e., private. Always avoid making an attribute public. An attribute which is declared public violates the encapsulation principle. For example, a Book class with a public int edition declared within the class will make clients free to modify it even without the Book class knowing that it has been changed. However, a Book class that exposes: public int getEdition() {...}; public void setEdition(int edition) {...}; has good encapsulation. The clients do not know whether the implementation uses integer to store the edition or Book is storing the edition as string and converting it to integer. For instance, due to performance / space trade off decisions, it could be implemented differently. This leads us to the rules: Do not change the state of an object without going through its public interface. Classes should depend only on the public interfaces of other classes. Semantic Violation of Encapsulation It is easy to avoid syntactic violation of encapsulation by declaring the internal methods and data private. Some examples of semantic violation of encapsulation are: Failure to call database.connect() before calling author.retrieve(database) because you know that the author.retrieve() method will establish a new connection to the database in the absence of a database connection. Not calling done() method in class A because you now that performLastOperation() method in class A has already called it. Using class B's MAXIMUM_POINTS constant instead of class A.MAXIMUM_POINTS because you know they have the same value. Ignoring to call class A's init() method because you know that class A's executeFirstOperation() method automatically calls it. All of the examples above makes the client code dependent on the private implementation of a class. One of the main theme of the Design Patterns [GoF] book is that developers should program to an interface. This means we should avoid looking at the implementation of a class to program. When the code is dependent on the implementation, encapsulation is broken and this makes the client code brittle since it has to change whenever modifications to the implementation are made. Inheritance and Encapsulation An element that is coupled to another has a dependency on that other element. If the other element changes, the dependent element may also need to change. A well-designed superclass is not coupled to its subclasses, but a subclass is inextricably tied to its superclass. If a super class declares attributes that are visible only to its sub-classes, (in Java we use protected keyword) it results in coupling between them. So, from a strict point of view, Inheritance violates encapsulation. Do not allow the subclasses to access the protected attributes directly. This will avoid maintenance nightmare when the attributes are changed in the super class. At the other extreme you can declare the attributes as private in the superclass, this means the subclasses will not inherit the attributes. When the derived class needs access to the private attribute declared in the base class provide a protected method that can be used by the sub classes for that purpose. Changes to the superclass, visible outside that class, are automatically inherited by the subclass without having to change the code of the subclass. All design decisions require human judgement that solves the problem at hand. You have to weigh the advantages vs. disadvantages for your specific situation before deciding either to follow this rule or not. In this case, some of the things that you may look at are : How many sub-classes are we likely to extend from this super-class? How many attributes require visibility in the sub-classes? Answering these questions will help you to make the trade-offs between different designs. You could also start with the simplest design that will work for your current requirements and refactor the design in each iteration. If you are using inheritance and the derived class needs access to the private attribute declared in the base class then provide a protected method that can be used by the sub classes for that purpose. Arguments and Encapsulation A properly encapsulated class prevents its clients from knowing the implementation details. All the public methods defined in a class is called as the interface of a class. The interface of a class should hide as much implementation details as possible. Therefore, the number of arguments in the interface must be minimal. Because more the number of arguments, the more the clients know about how the class is implementing the functionality behind the signature of the method. Visibility of Operations At the class level this means minimizing the number of methods that are public and available for clients to use. This raises the level of abstraction of the services provided by the class to the correct level. Do not clutter the public interface of a class with methods that users of that class are either not able to use or are not interested in using. Implement a minimal public interface that all classes understand. You can minimize the public interface by following the rule : Do not put implementation details such as common- code private functions into the public interface of a class. Let us look at an example for information hiding. Consider a website dedicated to independent publishers. The system needs to allow profile to be created for the authors. One of the data provided by the author is their age. The system stores the age attribute in a class called Author. After a year elapses the profile still shows the same age and the author has not aged at all! By the way this is how Yahoo Profile is implemented. The management now realizes that it is a mistake and wants to provide an operation to calculate age of a given author. The problem is that code has author.age in several different places (direct access to the public age attribute). Now the code has to be modified to author.getAge() and recompiled after the getAge() is implemented. This may not be a critical issue in this system but if it was a retirement fund account which allows withdrawal of money only after a certain age is reached by the member then it is definitely a critical issue. This leads to the rule: All data should be hidden (private) within its class. Information hiding is useful for not only attributes and operations but also for class design and sub system design. Let us look at different type of information hiding example for an attribute. In MyTicketCo system the maximum number of tickets that can be purchased is 50. Therefore do not use 50 in the code, instead define a constant MAXIMUM_TICKETS to hide the information in one place. This makes it easy to change since it is found in only one place. This leads to the rule: Always use named constants instead of using literals. Encapsulation at System Level In subsystem design we minimize the number of classes exposed to the clients to provide a minimal interface. If this is not possible we use a Facade [GoF]. Coupling and its Relation to Encapsulation Coupling is a quality metric used to describe amount of interconnection between classes as well as between methods. It is basically a measure of amount of encapsulation. Coupling is applicable to both
Compartilhar