Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 17 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
17
Dung lượng
1,75 MB
Nội dung
Contents • INTRODUCTION • STARTING OUT FINDING CLASSES AND THEIR RELATIONSHIPS • MAXIMIZING COHESION • SEPARATION OF RESPONSIBILITY • DUPLICATION AVOIDANCE • COMPLETE AND CONSISTENT PROTOCOLS • MUTABILITY VS IMMUTABILITY CHAPTER Elegance and Classes NNL – Khoa Toán Tin ĐHKHTN Contents INTRODUCTION • In this chapter, we will discuss how and when to use O.O features and we will introduce other guidelines to help design and build elegant systems “from scratch.” • We will compare various possible competing designs of systems in terms of the classes involved, their responsibilities, their relationships with their collaborators, and their implementations • DESIGNING FOR CHANGE • LAW OF DEMETER NNL – Khoa Tốn Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN STARTING OUT FINDING CLASSES AND THEIR RELATIONSHIPS Designing and building a software system from scratch • “The hard part about object-oriented design is decomposing a system into objects.” • Suppose that we are asked by a customer to create a new application Here is a simplified outline of the steps that should be taken: • Develop a precise specification of the system • Determine the classes, their responsibilities and collaborators • Determine the precise protocol or interface of the classes • Implement the classes NNL – Khoa Tốn Tin ĐHKHTN STARTING OUT FINDING CLASSES AND THEIR RELATIONSHIPS Project specification Consider a very simple project: developing a program for a customer that allows us to gather statistics on the frequency of occurrence of words in text files NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN When the Java program starts, it analyzes the specified text file It constructs a summary report regarding each word that occurs in the file and the number of times that word occurs, sorted from most frequently occurring word to least frequently occurring NNL – Khoa Toán Tin ĐHKHTN Clearer specification Clearer specification The user starts up the program by typing the following command on the command line of a console window: “java,” followed by the name of the program, followed by the full path name of the text file For example, the command might be: java WordCounter /users/smith/Hamlet.txt The program checks that the file exists If not, it reports an error message and exits The program traverses the file, treating each byte as a character and treating each sequence of characters not containing any delimiters as a word The delimiter characters are Java’s default whitespace characters The program keeps track of the number of occurrences of each word Two words are considered equal if and only if they contain exactly the same sequence of characters The program prints to the console window a list of all the words and their frequencies, with words with the highest frequency first and with one word and frequency (separated by a tab) per line The program quits NNL – Khoa Toán Tin ĐHKHTN Clearer specification 10 Use cases • If two words have the same frequency, they should be reported in alphabetical order using the ASCII ordering • The efficiency of the program is important enough that only one read-through of the text file is permitted by the program Furthermore, the program is not allowed to copy the whole text file into memory, since it might use up too much memory • The program is going to be enhanced later to deal with other inputs and outputs of an as-yet-unknown form NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN 11 • A use case is a sequence of steps indicating how the program is to behave in a certain situation to achieve a particular goal • Create a use case for each of the ways the system is expected to behave NNL – Khoa Toán Tin ĐHKHTN 12 Finding the classes (first attempt) • Let each noun in the specification correspond to a class and each verb to a method of a class • Add concepts from the application domain A use case diagram for a word frequency counter application NNL – Khoa Toán Tin ĐHKHTN Finding the classes (first attempt) CRC cards The user starts up the program by typing the following command on the command line of a console window: “java,” followed by the name of the program, followed by the full path name of the text file For example, the command might be: java WordCounter /users/smith/Hamlet.txt The program checks that the file exists If not, it reports an error message and exits The program traverses the file, treating each byte as a character and treating each sequence of characters not containing any delimiters as a word The delimiter characters are Java’s default whitespace characters The program keeps track of the number of occurrences of each word Two words are considered equal if and only if they contain exactly the same sequence of characters The program prints to the console window a list of all the words and their frequencies, with words with the highest frequency first and with one word and frequency (separated by a tab) per line The program quits NNL – Khoa Tốn Tin ĐHKHTN 14 15 • CRC stands for “Class, Responsibilities, and Collaborators” • Use one note card for each class • Use role playing to determine responsibilities and collaborators NNL – Khoa Toán Tin ĐHKHTN 16 CRC cards Figure 5.2 A CRC card for a WordFrequencyAnalyzer class CRC cards Figure 5.3 A WordFrequencyCollection CRC card CRC cards Class Protocols public class WordCounter { public WordCounter(); //constructor public void checkFileExistence(String filename); public File createFile(); public WordFrequencyAnalyzer createAnalyzer(); public void initiateAnalysis(); public getAndPrintResult(); Figure 5.4 A WordCounter CRC card } Class Protocols public class WordFrequencyAnalyzer { public WordFrequencyAnalyzer(); //constructor public void analyzeText(File file); public WordFrequencyCollection getResults(); } Class Protocols public class WordCounter { public static void main(String[] args); } public class WordFrequencyCollection { public class WordFrequencyCollection { public WordFrequencyCollection(); //constructor public void editCollection(); public String toString(); public WordFrequencyCollection(); //constructor public void add(String word); public String toString(); } } Class Protocols Class Protocols public class WordCounter { public class WordFrequencyCollection implements Iterable { public static void main(String[] args) { //create the file File file = new File(args[0]); //test to see whether the file exists and can be read try { new FileReader(file); } catch (FileNotFoundException e) { System.out.println(“error: file cannot be read”); System.exit(0); } //analyze the file WordFrequencyAnalyzer analyzer = new WordFrequencyAnalyzer(); analyzer.analyzeText(file); //get and display the results WordFrequencyCollection collection = analyzer.getResults(); for(String word: collection) { System.out.println(word + “:” + collection.getFrequency(word) }; } public WordFrequencyCollection(); //constructor public void add(String word); public Iterator iterator(); public int getFrequency(String word); } } } Guideline • When working in the early high-level design phase, keep the discussion at a high level In particular, avoid wasting time on implementation details and low- level data representations Figure 5.5 The word frequency analyzer classes NNL – Khoa Tốn Tin ĐHKHTN MAXIMIZE COHESION OF CLASSES Guideline • A class should model one concept • All its methods should be related to and appropriate for that concept • Promotes understanding and reusability of the class • Example: The Java String class NNL – Khoa Toán Tin ĐHKHTN 26 27 • Every class should be responsible for doing one thing only and doing it well NNL – Khoa Toán Tin ĐHKHTN 28 SEPARATION OF RESPONSIBILITIES • Example: Traversing a Graph object Who keeps track of which nodes of the Graph have already been visited? – The nodes themselves – The Graph object – A different object Figure 5.6 An ill-formed God class with all the responsibilities and slave (data-only) classes NNL – Khoa Tốn Tin ĐHKHTN 30 Guideline • Different kinds of responsibilities should be separated among different objects Figure 5.7 A graph with nodes and edges NNL – Khoa Toán Tin ĐHKHTN 32 Guideline (Expert pattern) More about duplication - Guideline • The object that contains the necessary data to perform a task should be the object that performs the task • “Ask not what you can to an object, ask what an object can to itself.” • “What is it I’m doing with this data and why doesn’t the class it for me?” • Avoid duplication • This is also known as the DRY principle (“Don’t Repeat Yourself.”) NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN 33 More about duplication Guideline • Duplication can occur in many forms: – duplicate copies of same data – duplicate code within a method or between methods – duplicate processes (duplicate execution of a piece of code) • Code with duplication is less readable and less maintainable than code without duplication NNL – Khoa Tốn Tin ĐHKHTN 34 35 • Only one class should be responsible for knowing and maintaining a set of data, even if that data is used by many other classes • Corollary: Avoid duplicate copies of data • (choose one class as the “gatekeeper” for the data) • Question: Does the length field of an Array cause duplication of data? NNL – Khoa Toán Tin ĐHKHTN 36 Behavior of a class Guideline • Should a class have only the minimum necessary behavior to accomplish its tasks? • Should we add more behavior to increase the reusability of the class? • Should we add lots of auxiliary methods to make the class easier to use? • What should the protocol be for each method? Give classes complete and consistent interfaces • By “complete,” we mean that the class should have the full set of appropriate behavior so that it can perform any reasonable action related to the role that it plays NNL – Khoa Toán Tin ĐHKHTN 37 Examples of completeness and consistency 38 MUTABILITY VS IMMUTABILITY • When should classes be made immutable? General answer: Whenever possible • Advantages of immutable classes: • GUI components could have setSelected() and setUnselected() methods, but better to have setSelected(boolean b) and isSelected() methods • If there is a getXXX method, consider a setXXX method unless the XXX property is read only NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN – can easily share immutable objects – they have guaranteed behavior 39 NNL – Khoa Toán Tin ĐHKHTN 40 Creating an immutable version of a mutable class Implementing immutability Make sure a class is mutable: • Make all instance variables private or final • Exclude any modifier methods • Prevent overriding by making the class final or making all the methods final NNL – Khoa Toán Tin ĐHKHTN 41 ways to design a FixedPoint class • Consider the java.awt.Point class, which is mutable • How can you create a FixedPoint class that is identical to the Point class but is immutable? NNL – Khoa Toán Tin ĐHKHTN Subclassing • Design it independent of the java.awt.Point class • Make it a subclass of Point • Have it delegate to Point Figure 5.8 FixedPoint class implemented as a subclass of java.awt.Point NNL – Khoa Toán Tin ĐHKHTN 43 42 Using Delegation Using Delegation public class FixedPoint //attempting immutability using delegation { private Point pt; public FixedPoint(Point p) //constructor { this.pt = p; } public FixedPoint(int x, int y) //constructor { this.pt = new Point(x,y); } public double getX() { return pt.getX(); } public double getY() { return pt.getY(); } public Point getLocation() { return pt; } } Figure 5.9 FixedPoint class implemented using delegation to java.awt.Point NNL – Khoa Toán Tin ĐHKHTN Problem with FixedPoint (1) Problem with FixedPoint (2) FixedPoint fp = new FixedPoint(3,4); System.out.println(fp.getX()); //prints Point loc = fp.getLocation(); loc.x = 5; System.out.println(fp.getX()); //prints Fixed by implementing public Point getLocation() { return pt.getLocation(); } NNL – Khoa Toán Tin ĐHKHTN 46 Point p = new Point(3, 4); FixedPoint fp = new FixedPoint(p); System.out.println(fp.getX()); //prints p.x = 5; System.out.println(fp.getX()); //prints 47 NNL – Khoa Toán Tin ĐHKHTN 48 Solution DESIGNING FOR CHANGE public final class FixedPoint //corrected version { private Point pt; public FixedPoint(Point p) { this.pt = new Point(p); } //copy constructor public FixedPoint(int x, int y) { this.pt = new Point(x, y); } public double getX() { return pt.getX(); } public double getY() { return pt.getY(); } public Point getLocation() { return pt.getLocation(); } } NNL – Khoa Tốn Tin ĐHKHTN • Classes often need to have bugs fixed, be optimized, or have behavior added • Therefore design classes to make it easy to maintain, modify, and extend them • “Change happens Deal with it.” 49 Guideline 50 Open-Closed Principle • Design your classes so that they are easy to extend if new behavior is needed, but design them in a way so that existing classes never need to be modified • The benefit is that it allows you to avoid cascading chains of changes to existing classes, which can be time consuming an introduce new bugs • Particularly useful for software libraries Design your classes and interfaces so that they can handle change NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN 51 NNL – Khoa Toán Tin ĐHKHTN 52 Guideline Coding to interfaces • Follow the Open-Closed Principle when designing software That is, design software to be open for extension but closed for modification • One way to make code able to handle change is to code to interfaces instead of classes • Example: If a variable v of type LinkedList uses only the iterator, add, and size methods of the LinkedList class, it would be better to declare v to be of type List and even better to declare v to be of type Collection This allows many more types of values to be assigned to v without modifying any of the existing uses of v NNL – Khoa Toán Tin ĐHKHTN 53 54 More ways to design for change Guideline Code to interfaces, not classes That is, wherever possible write your code so that objects are referred to by the interfaces they implement instead of by the concrete class to which they belong NNL – Khoa Toán Tin ĐHKHTN NNL – Khoa Toán Tin ĐHKHTN 55 • Minimize coupling—reduce the number of dependencies of classes on each other • Use encapsulation—group related items and protect them from access by others • Use information hiding—keep implementation details hidden NNL – Khoa Toán Tin ĐHKHTN 56 Example • Suppose a Point class has a public nonfinal field x that is accessed by many other objects • If you need to continually display the current value of x in a window, the window must repeatedly poll x, since its value can be changed in many places Figure 5.10 The Window class may have no way of knowing the user changed the value of p.x since x is a public field NNL – Khoa Toán Tin ĐHKHTN 57 Example (continued) • If the field x were private with getX and setX methods, then the system could be modified to avoid polling x • The modification would consist of subclassing the Point class so that the setX method notifies the Window whenever it is called Figure 5.11 By making x and y private in the Point class, we can ensure the Window is notified the NotifierPoint notifies the Window of the change in x NNL – Khoa Toán Tin ĐHKHTN 59 ATM example • To get an account balance from the bank name b, branch number r, customer name c, and account number a, an ATM could execute Balance balance = centralControl! getBank(b).getBranch(r).getCustomer(c)! getAccount(a).getBalance();! Figure 5.12 The UML sequence diagram for the first approach at getting the balance ATM example (continued) • Alternatively, the ATM could issue Balance balance =! centralControl.getBalance(b,r,c,a); • CentralControl could in turn find the bank and ask it for the balance, giving it r, c, and a • The bank in turn could find the branch and ask it for the balance, giving it c, and a • Similarly, the branch could find the customer and ask it for the account, etc Figure 5.13 The UML sequence diagram for the second approach at getting the balance ATM example (concluded) • Which approach is best? • The second approach better hides design and implementation details and reduces coupling • There is “inappropriate intimacy” among the classes in the first approach Guideline Obey the Law of Demeter This principle can be summarized as “talk only to your immediate neighbors” or “don’t talk to strangers.” Law of Demeter Inside a method body, an object should send messages only to objects in the following categories: this object itself this object's instance variables the method's parameters any object the method creates any object returned by a call to one of this object's methods the objects in any collection that falls into these categories