280 likes | 295 Views
Learn about the concepts of coupling and cohesion in object-oriented design and how they affect relationships between objects. Discover design heuristics and examples to help improve your understanding and application of these principles.
E N D
Relationships amongst Objects By Rick Mercer with help from Object-Oriented Design Heuristics, Arthur Riel
Outline • Coupling / Cohesion • The only possible relationships between objects • A few design heuristics • Consider role play in current project
Coupling • Coupling: A measure of how strongly one class is connected to, has knowledge of, or relies upon other classes • Low coupling: the class is not dependent on many other classes--good • High Coupling: class is dependent on many others--bad • A change to one class forces changes in others • More difficult to understand a type in isolation • Harder to reuse because it depends on others • Assign responsibilities so coupling remains low
Which is the better design? • Should a Point of Sale Terminal (POST) object construct an instance of Payment, then add the payment to the sale • POST would be coupled to both Payment and Sale in the following Collaboration diagram a UML diagram that will not be on test • With this design, POST needs to know 2 classes makePayment() 1: create() :POST p : Payment 2: addPayment(p) :Sale Example from Craig Larmans Applying UML and Patterns
An alternative design • Try associating Payment with Sale • Sale is coupled to Payment in either design • But low coupling favors this 2nd design • In practice coupling can’t be considered • in isolation from other design guidelines • such as Expert and High Cohesion makePayment() 1: makePayment() :POST :Sale 1.1. create() :Payment
Try to keep coupling low • Common forms of coupling • Class A has an instance of Class B • Class A send a message to an instance of Class B • Class A is a subclass of Class B (inheritance) • Class A implements interface I • Coupling does occur, in any design • A dangerous case of high coupling: • Allow Class A to use instance variable of Class B • Low coupling is a sign of good design
High Cohesion, Good • Cohesion • Synonyms: consistency, pulling together • A measure of how strongly related and focused the responsibilities of a class are • Assign responsibilities for high cohesion • High cohesion is good • Low cohesion is bad • hard to understand • hard to reuse • hard to maintain • fragile; constantly affected by change
High Cohesion • High cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module • Examples • PlayList manages the songs in the queue • BankAccount has no queueUpSong method • Any well structured class where the methods are related, String for example
High vs. Low Cohesion • High functional cohesion exists when the elements of a class "all work together to provide some well-bounded behavior" Grady Booch • Low cohesion • One class is responsible for things in different functional areas. Examples: • Model arranges Views, Listeners make the Rules • One class has sole responsibility of many tasks • Jukebox coordinate activities and determines if a song can play, plays audio files, maintains collections, manages the play list, ...
High Cohesion • A class has moderate responsibilities in one functional area and collaborates with other objects to fulfill tasks: • e.g. PokerDealer coordinates but gets help from • a deck of cards that can shuffle and deal cards • the player that can figure out what to do next and knows what he or she can bet • a view to show poker hands, pot, cards, animations • Small number of methods, highly related functionality, doesn't do too much
Benefits of High Cohesion • Design is clearer and more easily understood • Maintenance (bug fixes, enhancements, changing business rules) is easier • The fewer classes you touch the better Adele Goldberg • High cohesion can help you attain lower coupling
Relationship Between Objects 1) Object relationships occur when one object sends a message to itself publicclassStack<E> { publicbooleanisEmpty() {} publicvoidpush(E element) { if(this.isEmpty()) first = new Node(element, first); else … }
An object relationship occurs when… • 2) An object sends a message to an instance variable • private JTextArea textEditor = new JTextArea("You can edit me"); • public FirstGUI() { • this.setSize(500, 400); • textEditor.setLineWrap(true); • textEditor.setWrapStyleWord(true); • textEditor.setBackground(Color.RED); • textEditor.setFont(new Font("Courier", Font.BOLD, 12));
An object relationship occurs when… • 3) One object sends messages to other objects passed as formal parameters • publicclassJukeBoxAccountextends JFrame { • // Someone else asks this object if a song can be played by a user • publicbooleanmayQueueUp(Song song, JukeboxAccountuser) { • returnsong.canBePlayedToday() && • user.canSelect(song.getPlayTimeInSeconds()) • } • }
An object relationship occurs when… 4) One object constructs another publicclassJukeBoxAccountextends JFrame { private class SelectListener implements ActionListener { publicvoid actionPerformed(ActionEventae ) { if (mayQueueUp(selectedSong, currentUser)) PlayListplayList = new PlayList(song); // Not a good idea else … } } }
Design Guideline • Minimize the number of objects with which another object collaborates • Worst case: The design has a collection of simple • objects where each one uses all others • One class could contain several smaller objects
A Meal Class Wrap 3 Smaller Classes Melon cost() Restaurant Patron cost() Steak Meal Pie cost() Melon cost() Restaurant Patron cost() cost() Steak Pie cost()
Meal Wrapper Class • Encapsulation makes 4 messages look like one • Containment simplifies things • RestaurantPatron collaborates with one object now, instead of three
If you have instance variables, use them • Containment implies a relationship, messages should be sent to contained objects • If no messages are sent to a contained object, it doesn't belong, Put the attribute in the right class • An exception: Container classes • ArrayList, HashMap, TreeMap, and so on, do not send messages to their elements • Okay, equals or compareTo sometimes • The collection’s responsibility is to add, find, and remove elements
Don't use too manyinstance variables • Most of the methods in a class should be using most of the instance variables most of the time If not, perhaps there should be two classes • Classes should not contain more instance variables than a developer can fit in short term memory Maximum should be 7 plus or minus 2 That's about 5 to 9 instance variables
Contained objects don’t talk to their containers • A class must know what it contains, but the contained class should not know who contains it • It's okay for a bedroom to know it has a clock, but the clock should not be dependent on the bedroom • currentAccountshould not send messages to Jukebox • The JukeboxAccountCollection need not know it is contained in an instance of Jukeboxshould be standAlone • All contained objects are loosely coupled • Meaning they are not dependent on each other
Use a Mediator to Coordinate Activities • Objects that share lexical scope -- those in the same containment --need not have relationships between them • Consider an ATM that contains a card reader and a display screen • The card reader should not send a message to the display screen • better reuse--could use the card reader software for a security entrance without taking the display screen
Violation Increases Complexity • An ATM may contain • card reader, display screen, and keypad • The ATM should coordinate activities between these three objects • Avoid collaborations between instance variables if possible ATM displayPIN() HaveACard? getPIN() Display Card Reader Keypad
In our system? • The Jukebox may contain • currentUser • selectedSong • But they need not send messages to each other\ • but they could currentUser.canPlay(selectedSong) Jukebox canBePlayedToday()? allowedToPlay()? selectSong currentUser
Information Expert • The most basic, general principle of assigning responsibilities (behavior/methods) is to • Assign the responsibility to the object that has the information necessary to fulfill it. • “That which has the information, does the work.” • Who decides if the jukebox object can select? • Most important skill in OO Design • Which objects should do what? • find the objects • assign responsibilities to the correct objects
Role Play Jukebox:Now I need to determine whether or not the current user can play the selected Song. Who is responsible for knowing the playing time of any Song? Song:It seems like I should be responsible for knowing the duration of my Song. For example, it might take 3 minutes and 34 seconds to be completely played. I'll add the responsibility “know play time”. Jukebox:Okay, now I should check to make sure the user is able to select this Song before telling playlist to queue it up. I seem to remember I cannot simply play a Song without checking on a few things. I know the current user and the selected Song. What do I do now?
Alternative #1 JukeBox: So tell me Song, how many minutes and seconds do you require to be played? Song: 3 minutes and 34 seconds. JukeBox: JukeboxAccount, do you have 3 minutes and 34 seconds credit? JukeboxAccount: I'll be responsible for maintaining remaining time credit, so I can answer that, Yes, I have enough credit. JukeBox: JukeboxAccount, have you played fewer than 2 Songs? JukeboxAccount : I have not played 2 songs today. JukeBox: Okay, now we can play the Song. Here it playList. PlayList: Okay JukeBox, I will add this Song to my queue. I'll take care of the add(String fileName) responsibility
Alternative #2 • JukeBox: JukeboxAccount, can you play this Song? • JukeBoxAccount: It feels as though I should be responsible for maintaining my own time credit, it seems appropriate that I should also know how many Songs I've played today. So I should be able to do some simple calculations to give you the answer you seek. Yes JukeBox, I can play the Song you sent me. I'll add these responsibilities to my CRC card: • know how much time credit I have left • know how many Songs I've played on this date • respond to a message like this: JukeboxAccount.canSelect(selectedSong)