Hello, and welcome to this new lesson. In this lesson, you will learn more about how to become a pro at writing classes. We will start out with how you can discover which classes you should be implementing. Have a look at this description of a task that should be programmed. In a social network, people can send messaged to their friends and they can see pictures that their friends like. How do we know what classes might make sense of this situation? There is a very simple rule. Look for nouns in the problem description. Many of them can be good candidates for classes. So we might want to make a class person or message. What about friend? Well, a friend is just a kind of person so we might not be using that one. So, it's not that every noun turns into a class, but the nouns give a good idea where to start. What about methods? Well, methods are verbs. For example, here, sending would correspond to a method. And in object-oriented programming, there's this core belief that a method belongs to a class. There is a single class that is responsible for sending, and so we would want to make a choice. Is it the person class that has a send method, or is it the message class that has a send method? Or maybe there's another class that we haven't yet discovered, maybe a mailbox class. We're not going to get into the details about this application. Here I just wanted to point out the noun-verb rules. Nouns are good candidates for classes, verbs are good candidates for methods. And I wanted to remind you, that each method is the responsibility of a single class. In this lesson will be implementing a fairly complex program, and we'll start out by just covering the classes that we need. The program that we want to develop deals with car sharing. So, we know car sharing is great, means less waste, less environmental damage, people travel together when they can. So, we want to write an application that simulates car sharing so that we can learn how to make it better. Here is a problem description. So, cars can pick up people whose destination is along the way of their own. The simulation should read information about cars and passengers and print out who has been driven where. What I'd like you to do is to read through this description and tell me what might make some good classes to implement in a program that does this car-sharing simulation. Let me give you a few choices. Which of these do you think would work out? Check all that apply.
Well, I'd certainly check car and person. Those are nouns, they're current in the problem description. drive is a verb, so that probably wouldn't work out. And now car sharing, yes it is a noun, but how would a car sharing class help us? What would an object of class carsharing look like? So, that's not so clear. So, this is just a technique to give us some candidates for classes, so that one can get started designing a solution. It's not perfect but it's pretty useful. Now, I want to shift focus a bit and talk about the challenges of implementing a single class.
Many lessons ago, we've talked about how to develop the public interface of the class, the methods that one might want to call on objects of the class. But we've always pretty much given to you the instance variables, because it turns out that picking the right instance variables isn't all that simple. While now, the time has come for you to do more of that yourself. And I'd like to give you a few rules of thumb, on how you might discover instance variables. To see how those rules of thumb work, let's have another look at the Car class from Lesson three. That class had an instance variable to hold the amount of gas in the tank. That was a method to add gas where the parameter value would be added to the gasInTank. Now the point that I want to make is that there is a certain pattern here. You have a quantity here. Here you make that quantity bigger. It also gets smaller when the car is driven. I like to call that pattern keeping a total. And it occurs in many classes, not just the car. So then lots of times we have some class and you have some variable, which may be called total or not. And then there's some methods that add to the total. Maybe some methods that clear the total, or subtract from it. I'll give you a few situations, and I'll ask you whether the pattern of keeping a total applies. So, would you keep a total to model the account balance in a bank account? Friends in a social network? A car odometer? Stripes in a flag. Check all where you think the pattern makes sense.
With a bank account balance, the total makes perfect sense. The balance can be added to when you deposit money, and it gets smaller when you withdraw money. It's just a number that gets incremented and decremented in times. With friends in a social network, I probably wouldn't be using this pattern. Because in a social network, you don't just con, collect the number of friends that someone has, you want to collect the actual friends. And that would not be a variable that gets incremented or decremented. A car odometer? That again makes sense. As miles accumulate, this not total just gets bigger. Notice that this one here doesn't get smaller. that's fine. And finally, what were flag stripes. I don't know, it's, a flag has a constant number of stripes, they don't change. So, even though you may want to count the stripes, it's not the total pattern where you have a quantity that has some method that adds to it. And maybe some method that subtracts from it. The point is that, once you've seen a couple of classes that keep a total of gas consumed, of miles driven, of money in the bank, whatever. Then you start recognizing that particular pattern, and it becomes easy for you to put in a method that adds something to the total that takes something away. So, let's look at a few other patterns that you haven't seen so much, but they are very useful in practice.
When you write code as part of a team, picking good class and method names makes it possible to work together. When I pick good names for my classes, it makes it easier for other engineers I'm working with, to read my code, and know what it does. It also makes it easier for me to remember what I did. So, this process is important, but how do you get good at it? The answer is practice. Lots of practice. Keep writing code, and when you read code, ask yourself, what patterns does this code follow to make it easier to read. Or, what patterns could this code have followed to make it easier to read. These questions will be very hard to answer, at first. But as you critique more examples, it'll become more natural. We'll talk about a few more patterns in this lesson. But for now, let's do another example of designing class and method names.
Let's say you've banded together with a group of friends to create a freelance photography company. You've taken on the role of manager and your friends are the photographers. Customers describe photography assignments to you. And you label the assignments with priorities. Each day you have a team meeting, where you give out the highest priority assignments to your friends. Each photographer can do one assignment per day. And every so often, you like to review all of the pictures in the company portfolio. What are some of the methods that might be carried out by objects of those classes? This is a very open ended question. What you come up with will depend on how you imagine this situation. There are a lot of correct answers here. I haven't specified every single step of the program. So everything's open to interpretation. What nouns do you want to make classes out of? And what are the methods that might go with those nouns? I won't give an automatic grade but will be collecting the answers and analyzing them.
These are the classes that I came up with. There's a manager and photographers, assignments and a portfolio or maybe multiple portfolios. The manager is responsible for labelling or prioritizing and photographers are completing assignments. So I think the manager has a method something like prioritizing the assign that takes in a new assignment and figures out what to do with it, and photographers have probably a method complete assignment. Thinking about the methods that an assignment has, might be a little harder. Mostly, an assignment should have a priority and a description. So it will probably be possible to set the priority of an assignment. We'll also probably need to be able to get the priorities, or compare them, so that the manager can figure out which ones are the highest priority assignments. Maybe when an assignment is finished, something happens with it. The portfolio probably needs to be able to collect the finished assignments, and there should be a way to look at the finish assignments as well. There are they things that you could have written here, it's possible that customer would also be a good class or may be priority. This would depend on how complicated you wanted to make your priorities
Let's talk about the thought process in more detail. The first question I asked myself was, which nouns in this problem description are doing things? Well, the manager is getting assignments, and marking them with priorities, and assigning them to photographers. Photographers are getting the assignments from the manager, and completing them. Assignments don't seem to do too much, but we need to keep some information associated with the assignments. In particular, I thought an assignment would have a priority, and maybe, whether it's finished or not. They would also have the original description. Perhaps I would even want to store the finished work in the assignment. So maybe the finished method would take in an assignment. You might also want customer objects. Maybe the customers are doing something interesting when they create the assignments or maybe the manager keeps a list of customers and assigns the priorities based on who the customer is. Maybe you would want an object to represent a priority. If you imagine the priorities as just numbers and higher numbers mean higher priorities then you probably wouldn't need a special priority class. But if you wanted priorities to age over time, and increase the longer you have the assignment, then you might want to class your priorities, because they would have behavior. This kind of design work can be very difficult, because there's so many ways you might start. But it also becomes a lot of fun as you get better at it. This process becomes a kind of story telling. You're using the code to document the relationships between all of the actors in the story. And creating a model for how to solve the problem.
We'll start with a pattern of a counter. That pattern teaches us to make an integer variable that counts whatever it is that we want to count. You want to have another method that increments the counter when whatever interesting event happens that you're counting. And you'll usually want a method that returns the current counter value so that the user of the class can get at it. Okay, that's all really abstract, so let's look at a concrete example. Here, I have the beginning of a Car class. A car has a given number of seats. That of course is not my integer counter, because that number doesn't change. And then I have a method here called addPassenger. Whenever that method gets called, another passenger enters the car. And then I have a method for counting the number of passengers in this car, not counting the driver. What I'd like you to do is, use this pattern and add an instance variable for the counter, which you can call anything that you think is appropriate. And to implement the methods that increment the counter and that returns current value.
Our task is to add an instance variable that counts the number of passengers. Here it is. Here I want to add a passenger to this car, so we will increment our counter. But, of course, there's a twist. You can only add a passenger if there's room. So, we need to check if the number of passengers plus the one that I want to add is less than the number of seats. Why not less equal than the number of seats? I still need one seat for the driver. So only in that case do I increment. So when I talk about patterns in general, the pattern that I described here says increment the counter, but it doesn't mean that you'll always have to increment the counter. The patterns gives you the broad outline of what you have to do, and the details are different, in every situation. After all if it was that simple, then you wouldn't be making the big bucks figuring out the answers. Finally, the getPassengerCount really is as simple as you think, here we just return the numberOfPassengers. The point is again, knowing that many classes keep a counter can help you when you have a situation where that is appropriate. Here, at the counter, you figure how does the count change, that's the case here. Here we increment the counter, and then here we have a simple and boring method to return. In this example, we just counted the number of passengers. What if we wanted to know who's actually in the car? We'll turn to that next.
Many classes needed to collect objects of another class. Use an array list or maybe an array for the collecting. Supply a method for adding to the collection so that it can get bigger over time. And depending on your problem, maybe you want to have a method from removing items from the collection. You'll want to supply some methods to find out about the contents. Maybe something that prints the contents, maybe to test whether a given value is in the collection. That depends on the individual situation. And here's a bonus tip when you declare that array list or array, don't forget to initialize it in the constructor. because otherwise it will be null, and when you add the first element, you'll get a null pointer exception. Sadly that happens to me a lot. So let's practice that with another variation of the car class. In this case we want to collect the names of the passengers. And I've already declared an array list of strings for you. So that's the instance variable. And your task is to update that instance variable in a couple of methods.
Here we're practicing the pattern for collecting values in a class. Here's is already the instance variable that does the collecting. Before we forget, let's make sure it's initialize in the constructor, here we go. By the way, I did forget that when I prepared the solution. Let's see here, here we need to add a strain to the collection, here's the code for that. Now sometimes, the code really is as simple as this. It's still necessary to have a separate method because of course passengers is a private instance variable that the public can't use. In our example, the add passenger method is a bit more complex because we should only add when there's space. Last time around we had a counter for the number of passengers. We don't have that this time, but we have a method, down here. So I'll just call that method. And again if there's room, then I'll do the adding. I don't expect that we actually have to implement that method, that's simply the size of the collection. The last method here, gives us more information about the contents of our collection. Unfortunately it's already been implemented. It simply returns the array list converted to a string, which, has this characteristic form. All of the strings inside, surrounded by brackets. So that's the general collection pattern, any time that an object wants to collect other objects. Using the array list or an array such as this one, and did I mentioned, that you should never forget initialize the array list or array in the construction. You will get to practice these patterns with another example that's dear to Sarah's heart.
Lets practise applying patterns. Remember the HomeworkScores class from before, here's a slightly modified version of it. We had it collect all of the scores, so that we could do competitions with them later. But if all that we want is to add all of the scores in and calculate the total without the lowest one, we don't need to collect of the scores. So, here's the outline of what we want to build. A HomeworkScores class with an addScore method and a getTotal excluding lowest method. The only method that should be a mutator is the addScore method. In earlier lessons, we had a method for removing the minimum score. But in this version we wont remove any. We will just find a way to no to count them when we are calculating the total. There are few corner cases I'd like you to think about, they have special behavior. If there are no scores yet, say that the total is 0. If there is one score, say the total is that score. If there are more, say the total is the total excluding the lowest. Now remember, I'd like you to do this without collecting all of the scores in an array list, or an array. Try this yourself. And if you'd like a hint, there will be hint links over here. They'll show you more videos.
To answer this question, I'll start with some of the code that I was using in the hint videos. Here's the homework scores class. And so far, it's keeping track of a sum and a number of scores. This code returns a sum but doesn't actually think about excluding the lowest. The way I see this problem, there are two key insights. The first is that to return the total excluding the lowest. We don't necessarily need to permanently remove the low score. If we can find the low score, we can return the total minus the low score, just subtract it off at the last moment. The second insight is that we don't need to save all of the scores to know what the lowest score is. We only need to save the value of the lowest scores itself, so instead of a method that finds the lowest score I will create an instances variable to track the lowest score so far, I will define it as private double lowest which probably make these private to, now I am going to need some special cases in my getTotalExcludingLowest because if the number of scores is zero, I want to return zero, but if the number of scores is one, I want to return that one score which is currently the sum. And if there are more then I want to return the sum without the lowest score. To keep track of how many scores there are, I'm using the counting pattern. I start with the number of scores being zero and when I add a score, I increment the number of scores by one. Now, for this new variable, lowest score. Or lowest, as I'm calling it. I need to come up with an initial value. Ideally I would initialize it with the first score. But I don't know what that is yet in the constructor. So I'll initialize it with minus one, and promise never to look at it until I've added at least one score. This is a good example of why you want to keep your instance variables private. People using the homework scores class might not realize that lowest isn't always initialized. Only the homework scores class knows about this implementation detail. And when you mark this as private, it shows that the homework scores objects are responsible for interpreting the data inside of the lowest score variable. So outsiders don't need to remember things like, I shouldn't look at the lowest score until I've added scores. So now I've initialized the lowest score, but I need to think about how it's changing. I'll need to make sure that in add score, when there aren't any scores yet, I should initialize lowest to be the given score. Then I need to think about, what happens when I add additional scores. The lowest score seen so far can change whenever I add a score. This is a lot like looking for the minimum score in a loop. When I see a new score, if the score is less than the lowest so far, I update the lowest to be score, now it looks like under both of these conditions numScores is zero or the score is less than the lowest, I change the lowest to be the score, so let's make this into one if statement, if num score is zero or score is less than lowest, then lowest equal score making get rid of this repetition and then these two statements happen no matter what whenever I see a new score I count that I've seen another score and I add the score to the sum. So now if I compile and fix my syntax errors, I can run the homework scores tester. And the homework scores class behaves as it should.
And I'd like to talk about another pattern that's very common when designing classes. We'll say that the class has a property if it has a value that one can simply get and set. Look at a person, a person has a name. That's a private instance variable, so we'll have a getter that returns the name and here we have a setter that you can use to set the name to a new name incase the person for whatever reason is unhappy with the previous one. It's very very common to have this pattern instance variable getter setter there some variations sometimes you do some error checking in the setter here we check that the new name, is not the empty string, cause no one's name should be the empty string. Or, maybe, in a particular application it just shouldn't be allowed to change the name, in that case, there wouldn't be a setter. This is a read only property in this example where we just have, the instance variable and the corresponding getter. Now, properties, meaning private instance variables with getters and maybe setters are very common. But that doesn't mean that you should make every instance variable a property with a getter and setter. Only use the getters when there is a need for the user of the class. To read the instance variable and only supply a setter when there is a need for the user to modify it, many instance variables do not have getters and setters. Well let's practice that with yet another variation of the car class, this car class has a number of instance variables and I'd like you to look it over and add getters and setters for those where it's appropriate, and not add them for those where it's not.
This class has three instance variables. The number of seats, the driver name, and the list of passengers. For which are these should we have getters and setters. The number of seats certainly shouldn't have a setter. You can't really change the number of seats in a car. Should it have a getter? if you added one, it's not a mistake. but there's probably no grade reason for having it. I didn't add one. Now, the driver name definitely needs a setter, because, when you look through the methods, there is no way of setting it otherwise. So, let's add a setter. Here is my setter. Do I need a getter? I think I do. There's no other way of finding out who's driving this car. None of the other methods make any use of the driver name. I'm assuming it was important since someone added the instance variable. So, let's add the getter as well. Here is my getter. And as is so often, the setter and the getter have very simple implementation. What about the third instance variable? You definitely don't want to have getters and setters for this variable. It would be very odd to have a method that holds and places an internal array of the class. And you also wouldn't really want to return it, because then you would give the user of the class the possibility of changing its content. So there is a rule of thumb that says that in general, a class that does not want to return any internal parts of its implementation that a third party could modify. Now, you've seen a bunch of useful patterns that hopefully will help you designing the implementation of a class. I will put those to good use when we implement our car share application. But before we do that, there is another technical topic that we need to tackle. Namely, variables and methods that don't belong to objects, but to the class as a whole.
And the beginning of this lesson, we talked about classes and methods, and about the fact that a method should always belong to a particular class, who's objects are responsible for carrying it out. Sometimes that's not as easy as it sounds. Let's go back to a program that you saw in lesson five. In the elevator demo, we write in a number, and then we first checked whether it was a valid integer, whether it was within range, and only then were we able to do any work with it. It would be nice if we could put all of this code in a separate method. And then I would like to call that method, in somewhat this fashion, so I want to read a number between 1 and 18, I want to read it from this scanner and I want to have the prompt to be floor. And of course the reason I want that is, then I can reuse the readBetween method again, when I want to read some other value that's maybe between one and 100 and maybe it's the temperature. So like so many of us, the readBetween method is probably wondering, where do I belong? Well in which class do you think the readBetween method should be put? The system class? The scanner class? The ElevatorDemo, or maybe somewhere else. Just think about it and give me your best guess.
So we want to find a home where we can add the readBetween method. Now, it can't be the System and the Scanner class because you and I can't add methods to these classes. They're classes that belong to the standard Java library and we can't just add our own methods to them. What about ElevatorDemo? So, lets try this, I would put all of this functionality in a method, give me a minute to arrange that and that then I'll show you. As promised here is my method. This method keeps reading an integer until it finds one that is within the desired range and then it returns it. The exact code isn't really important right now, you can have a look at it in the code distribution if you want to. And now here, I'm calling it, but there's a problem. When I compile it, I get an error message and that error message right now must surely appear like gibberish to you. Where it talks about non-static methods and static methods. So, let me explain what's happening here, and why the compiler is unhappy. Normally, we call a method on an object. And I didn't do that here because I figured, well, I'm in Elevator Demo, so I should be able to call any Elevator Demo method as well. But the trouble is I'm calling it from main. So there are no Elevator Demo objects in the compiler complaints. So going back here, that is the crux of the problem. ReadBetween is not called on an object, and there really isn't a great object to call it on. It would have been nice to call it on the scanner, but, like I already said, we can't do that. Whenever you're in this kind of situation, where you have a method that can't really be called on an object, then you make a static method. A static method is a method, that's not called on any object. So one remedy is simply to go back into Elevator Demo and declare the helper methods Static, like this. Now, all will be well and the syntax error is gone. So, to answer our original question, it's perfectly legitimate to put readBetween into Elevator Demo as a static method, but maybe it's not optimal. Because, what if I want to use that proflinifty method in another class. So, really readBetween is a method that seems, it would be useful in any situation that you want to read a value from some range. So, we, we might really want to put this somewhere else. And the class that I'm going to put it on, I'll call IOHelper. I've prepared that class for you, let me show you. Here is the IOHelper class. Here is the readBetween method. And again it's a static method, because I'm not going to actually make any objects of the IOHelper class. I just want it to be the home for this method. That means when I call the method, I have to specify the name of the class, like this. Here, you see the name of the class, IOhelper, the name of the method. Static methods are not incredibly common, because, like I said, most of the time an actual object is responsible for carrying out a method, but they do happen. In fact you've seen a few of them. In lesson four, you saw a good number of static methods in the math class. Again, the syntax is the name of the class, a period, and then the name of the method. And once again, there is no object of the math class. We simply call the square it method directly on the class. Now you may wonder, why did we have to use a static method? Why couldn't we just call the method on the number 2? Well that's, what it's like in Java, and Java numbers are not objects. So, you can never call a method on an object. A call such as 2.sqrt might be more logical, but it's an error. So to summarize, a static method belongs to a class, it's not called an object, and they are not actually all that common. Why do you call them static methods? There's no good reason actually. That's a hold over from another much more ancient programming language. There's nothing particularly static about them, in addition to static methods there are static variables, let's have a look at them next.
In lesson four, you wrote a class to compute some ugly fractions and averages. These methods don't need any instance variables. They should have been static methods. We could easily make them static. And then in the tester, instead of creating a math and Java object and then calling that, we could remove this step and call the methods directly on the class. Which would look like this. This would make it more obvious that these methods do computations based only on their parameters. Their behavior won't be influenced by factors like instance variables. So, go ahead and convert these three methods to static methods, so that the tester can work without creating an object.
I don't have to change very much. If I run the tester initially, it won't compile. Because right now, andahlSpeedup isn't a static method. And I can't call non-static methods from the static main method. But if I go here and declare this as public static double andahlSpeedup and public static double crossRatio, and public static double average. Now, I can compile and run the tester method. Looks good.
A static variable is a variable that belongs to a class but not to any object in that class. They're even less common than static methods. You're most likely to see them for constants. For example the math class defines a static variable called PI. Can see it's a static, can see it's a constant because it's declared as final and it's the value of PI. If you ever need to use it, you would call it as Math.Pi. Here is another example, there is a class that defines a variable called out. And again, it's static. What do you think? Which class does this? I copied this declaration from the source code of one of the Java classes. Tell me which one.
The answer is, it's the system class. You've been using system.out a lot in this course, probably much more often than Math.PI. But anyway, it is a variable that doesn't belong to any object of this system class. It just belongs to the system class itself. Now, Sarah is going to show you another static variable that she would add to the person class, that gives another example where static variables can be useful.
Often when you're writing code for a class like the person class, you want to give each person a unique ID. That might look something like this, you would have a private int ID, and each person object would have one. Now, there are a few ways to generate a different ID for each person. One would be to create a static variable. LastAssignedId and use the lastAssignedId to set each person's ID when they are created, we would also want to increment the lastAssignedId every time we used it. Try this out yourself. How would you give each person a unique ID starting from 1 and going up one at a time, so the first person would have ID 1, the second person would have ID 2 and so on.
If I decided to use a static variable to do this, then in the constructor, I would want to start with last assigned ID as zero. Now whenever a person is created, the constructor is called. We increment the last assigned ID and use the value of last assigned ID as the ID for the person being instantiated.
The solution we just saw has some problems, it had wires the ID generation into the person class, so if somebody is writing code and they want to use the person's class in their code, but they want ID's to be generated differently. Maybe they want ID's to start at a 1000 or may be they only want prime ID's they can't change the way the ID's are being assigned so what would be a better way to assign ID's to our persons? We would create a new interface, IdProvider which would have a method next to Id. You can think of an interface sort of like a class for now. We'll talk about these more in lesson nine. But now that we have this when we create a person, we can take in an ID provider, and use it to set the ID. Then we don't need the static variable anymore. This is what objects are all about, if you want different policies, you create different objects. There's no reason to be tied to a static variable in order to count the ID's. In production code you should never use static variables unless you declare them final and capitalize them and use them as constants, static variable seem convenient, but when you write real code they cause bugs because they don't get cleaned up when you are done the way objects do
At the beginning of this lesson we talked about car sharing and what classes one might be able to use to implement a car sharing simulation. And we hit upon the classes car and person. Let's go ahead and actually implement those classes. So, we'll imagine a bunch of people that all gather at the departure location. There's a long road, with destinations, that we'll just label 1, 2, Maybe this one wants to go to three, same with the others. And then, we'll have cars, that also have destinations. Let's say this car wants to go to destination 4, then of course, this car can pick up whatever passengers want to go destination 1, 2, 3, or 4. But not one that wants to go further. And, of course, the car can only load as many passengers as they have seats. Make that one less because the driver also needs a seat. So, now we need to figure out what are the responsibilities of the classes. Let's start with the person class. A person should know their destination. In our stimulation we also want to give names to people so, of course the person should know that too. A car should be able to pick up a passenger provided, of course, and only the car will know that whether there is room and whether the destination is on the way. And, of course, there is another thing that a car should be able to do, it should be able to drive. It looks like the person is the easy of the two, let's look at that first. Here's the Person class. You can see we have two properties, a person has a name and a destination property with a getter for each of them. These are read-only properties. Once a person has been constructed with a particular name and a particular destination, that doesn't change. Remember, it's just a simulation, these are not real people but, just objects for use in our simulation program. Here is the Car class. Most importantly, a car collects passengers. A car also knows how many seats it has. The name of the driver, we'll just use that for reporting. The current location, it starts at zero and goes up by one every time that the car drives to the next location. And that is destination. We construct a car with a driver name, number of seats and the destination. Initially it has no passengers and is at location 0. So, now it's your turn. I'd like you to implement the tryToAdd method. Where we add a person to the car, at least we try because if there's no room or the passenger wants to go further than the car wants to go, then it's not going to work. In that case, you shouldn't add p to the passenger array list, but you should return false. On the other hand if you can't accommodate the person. Go ahead and add it and return true.
Here's what I did. I checked how many passengers I already have, now I want to add a new person. And of course, there's the driver. So if passengers size plus seats, then I'm good. I also have to check the destination. So, and if the destination is below the destination of this car, in that case I can add the passenger and return true. Otherwise I won't add and return false.
Now next I'd like you to implement the drive method where the car goes to the next location, one more than the current location. And then when it gets there it should drop off any passengers whose destination in that new location. Now when dropping off, that just means remove them from the list of passengers. So go ahead and implement that.
Here is the drive method. We increment the location and now we want to remove all passengers who wanted to get to that location. That turns out to be a bit tricky so I'll want to find the passenger and then remove it. To remove a passenger I need to know the index. So I have an index i starts at zero, up to but not including the passenger size. I kept the i passenger, I test whether the destination of that person equals to current location and then I remove it. That's all fine, but there's something that one needs to be very careful about and that is when you remove the passenger you can't increment the index. Here's what I mean. Here are my passengers and let's say i is 1 and now Jane has reached the destination, she gets removed then of course Mary now ends up in place one and then I need to come back to the loop with i staying at 1 so that I can examine whether Mary should also, leave the car. So that's why there're two branches here. In this branch, I removed the passenger, and in the other branch, I increment i, to get to see the next one. Now the car, and the person class, have most of the functionality that I need, but somewhere I still need to put the entire simulation together.
I've designed another class, that I call the Simulation class, that puts all the information together. It keeps a list of all the cars in the simulation and all of the people. I'm giving you a method to read in all of those data, and that's not the part that I'm most interested right now, so we'll glance right past it. Now the simulation has two phases. In the first phase one loads all the passengers into the cars and in the second phase we drive all the cars until they have arrived. So go ahead and implement this method, what you going to have to do is for each passenger you want to find a car that can hold it, that means you need a loop over all the cars until one of them says yes, this passenger can be added
Here is how I did that. In the outer loop, I just go through all of the people. This inner loop is one of the algorithms that you've seen in lesson seven. It's the algorithm to find something. I keep on iterating over all the cars until I've been successful. In each step, I look at the ith car, I tried to add my passenger, that method will tell me whether I was successful or not. If I am, this variable gets to be true and I fall out of the loop.
Now go ahead and implement the driveCars method. What I want you to do is drive all the cars to location 1, then all to location 2 and so on. Of course if the car has already reached its destination then you shouldn't drive that car again
There are many ways of doing this, here is one that I think is fairly simple. Let's look at the inner loop first. So we want to go through all of the cars and drive them. And we want to keep doing that until all of the cars have reached their destination and that's the challenging part. All of the cars started at location zero and then they moved to location one. And now maybe this one here doesn't need to go any further. And I need to, have to have some way of stopping this car from moving. So what I'm simply going to do is I'm going to remove him from the list of cars. So that's the part here. If this car has arrived, then I'll just remove that car. And remember, when I remove an element from the array list, then I don't reccomend the index. But I do it over here. And I keep doing that while I have cars to drive.
Now you've written a lot of code and mercifully the application is nearly done. I'll give you the final piece. Here in the CarShareApp class, you make a simulation object, we ask it to read it's input. Then we load the passengers, and drive the cars. Also, so that you see what's going on, I've added a couple of print statements to the car class. Here in the tryToAdd method, we print out whenever the driver has picked up a passenger. And in the drive method, we print out, whenever the driver has dropped off a passenger. Now I would like you to, run the car share app, with these inputs here. So Jane has a car, that can hold four people including herself, wants to go to destination four. Fred has a two seater wants to go to destination two. And these are all passengers, they want to go to these destinations. The minus one signifies the end of input. Go ahead and run the app, and tell me in which order do passengers get dropped off.
Here is the answer. And you simply get it by running the app. [SOUND] Here I've pasted in the input, and here you see the drop offs. Well that's great our simulation really works. It was hard work but we're essentially done. There's just one other step that I'd like to do. And that is to reorganize the code in a more professional way.
Remember that manager that we were talking about earlier? Maybe it was you, maybe it was somebody else. Well, that manager is still handing out assignments manually. Let's write the program that will assign them automatically. I've got it started for you. I wrote a class simulation. You don't need to look at it too hard. There's a lot of stuff in here that you don't need to know yet. Mostly what it's doing is it's reading in a story. The story is a text file, inside the directory with the code. It's called story.txt. The simulation class reads these lines of the story on a manager object. Let's look again at this story. First, the manager hires photographers. There's a photographer Danny, then a photographer Leslie. Each day, there's a meeting, where the manager gives out assignments. The manager's going to start with the first photographer and give out the highest priority assignment available first. So in this case, that would be the parrot assignment, which has priority 5. In between daily meetings, the manager gets assignments. These assignments need to be added to the to-do list. Since choosing a priority is a complicated task, I just included the priorities for you. The priorities will be Ints, and a higher number means a higher priority assignment, so the assignment should be done sooner. When we come to the end of the story, we'll check the portfolio and display all of the pictures. Your task will be to complete the methods inside the manager class, using whatever other classes you need to create. So that when you run the main method in the stimulation class, it will print out the finished photos from left to right with no space between them and the name of photographer under each picture. So when you run the main method of the finished code, you should see something like this. The first photo is of a parrot and it was done by Danny, the second one is of the mountain done by Leslie and so on. Make sure that their signature has its top left corner, right at the bottom left corner of the photo. Now I've written one other thing for you, the photographer has the complicated job of creating a photo based on a description. If this were real life, a person would be taking the photo and loading it into the system, but we don't have time to wait for that. So I've included some photos, and a method in the photographer class, that gives the name of a file with a photo, when you give it a description. All of the descriptions in the story, so all of these ones already have photos for them. And the photographer knows how to find those photos. Aside from that, you have free reign. You can add whatever classes you need to make this work. Though to submit your answer, you'll need to find a way to use the classes that are in the Udacity IDE. You can't make new files in the Udacity IDE. But if you use BlueJ, you can create whatever classes you want. I'll give you a little tour of how I designed my solution at the end. But there are a lot of ways to do this, and many of them are good for different reasons. One last thing. I understand that this is very open ended. So if you'd like more guidance, check out the hint links that will show up here. And if you can't make it work, it's not your fault. And if you're satisfied with your progress and what you've learned from the question it's okay to move on.
I'm going to start by getting the portfolio working because I kind of want to see some pictures already. It looks like I need to finish up the addFinishedWork method, alright. Now, I need to actually create that FinishedPhoto class. So, I'll come back to my overview and make a new class FinishedPhoto. I'll move this down here since it's going to be used by the portfolio. Now, basically I just want the FinishedPhoto to have the location of the file and the name of the photographer. So, I'll create instance variables for them, initialize them in the constructor and make it possible for the portfolio to get the information back out later. Now, I can go back to the portfolio and compile again, and if I were to actually use the correct class names, I now have the collecting of finished works working. To display them, I'll have to fill in the bits that I left out before. I'll make the picture based on what's saved in my finished work object. Translate it to the correct position and draw it. The signature will have the same x coordinate. Whoops, looks like I have a bit of a name collision here. I've called the picture object photo. And I've also called the finishedPhoto, photo. So I'm going to call this one work. And I'll need to be careful to change it in exactly the right places. And now I can actually get the height of the Picture object, so that I'll know where to place the text. And I'll get the actual text for the signature from the work object not the picture object. Let's see how we were doing. I should probably actually call the constructor when I'm making a Text object. And now it's better. So let's see how this is working. I'll go back to the overview and create a class PortfolioDemo. PortfolioDemo will have a main method where I'll create a Portfolio object and then add some finished work to it. Here's one of the images I know is in the package. And I'm actually going to use the original photographer's name here. And I'll add two more. And at the end, I'll want to display the work. Looks like I should capitalize correctly. Try this again. Now, if I run my PortfolioDemo, it prints out the three photos with the names just as I wanted. Looks like I'm ready to start using the portfolio in the Photographer class. The photographer will need to able to accept assignments. When a photographer accepts an assignment, they'll take a photo, which in this simulation will mean finding a file name based on a description for the assignment. Then the photographer will add the finished work and their name into the portfolio. There are a few ways that we could get a hold of the portfolio. One is that we could just have it passed in as a parameter. In this case, I'll just put it in as an instance variable. Either one works. But I'm going to need to take it in as a parameter in the constructor. And I'm also going to need the name of the photographer. Which will also come in as a parameter in the constructor. Now, that should be all set. But my photographer's probably not working all that well yet. I keep using assignments. But assignments haven't been defined yet. So I'll make a new class. This is all getting pretty squished. That's a bit better. Now, the Assignment class isn't going to have a whole lot in it. Basically, the Assignment object is what's going to be stored in the to do list for the manager and then used by the photographer to know what assignment they should do. I know that I'll need a description for the photographer, and I'll eventually need a priority but I'll come back to that. And here's my get a method for the description. Now, the photographer should compile. Once I fix the typos in the assignment and now I can write a PhotographerDemo that shows that the Photographer class works. I'll make a new Photographer And give the Photographer portfolio. I'll fix a little typo, take out the s that I had here in the Photographer class. And then I'll make sure that all of the assignments made it into the portfolio by displaying the finished work. When I run the PhotographerDemo, I'll get three photos with me pretending to have taken them. So that's looking pretty good. Now, I only need to fix the Manager class though the Manager does have some of the most complicated logic. The most interesting part is when we give out the assignments. The Manager will need a list of unfinished assignments. I already defined that earlier in one of the hint videos along with a list of photographers. Now, there's this detail about assigning the photographer who was hired first the highest priority assignment. So, it looks like I'll be iterating over all of the photographers. And I'm going to want to look at the one who was hired first, first. Luckily, this won't be too difficult since I'm storing all the photographers in an array list. They're going to be stored in the list in the order that I hired them. So if I iterate over the list, I'm going to iterate over the photographers, starting with the one that was hired first. Now, for trickier part. I'll want to find the highest priority assignment but you already mostly know how to do this. So I'll quickly make a helper method that does this. Now, there is one hiccup here, I'll need to add the getPriority method to the Assignment class. But that wasn't especially complicated and now I'm back. Looks like I tried to index into an array list using parentheses. Alright, now I'm using the get method and correcting spelling errors. And I should be able to use my getHighestPriorityAssignment method. So, for each photographer, I'll get the highest priority assignment, give it to a photographer and remove it so I don't do it twice. And one more gotcha. I only want to do these things if there's actually another assignment to be done. So, if there are no assignments in the list of assignments, I'll skip all of this. I can add an else return here and the moment I realize there are no more assignments, I would stop looking through the photographers. This isn't necessary but it might skip a lot of steps if our company gets really, really big and we have thousands of photographers. I think I'm done with the giveOutAssignments method. Now, I'll make sure I've implemented all the other methods. I'll need to add a photographer to the list when I hire a photographer and make sure to initialize all of my array lists, as well as a new portfolio. When I get a new assignment, I'll need to add the new assignment to the list of assignments to be done. And at the end when I'm ready to check the portfolio, I'll use the portfolio to display the finished work. Did it work? Looks like it. If you stuck with that problem all the way through and got it, great job. And if you didn't hope that you learned something by seeing me go through the exercise. Now, that I have this working I would probably want to go back and clean up all of the documentation. But I wont make you spend any more time on this example. Ky has some important concepts to show you with car share program.
Let me tell you about a useful concept that you can use to organize your classes effectively. It's called the concept of coupling. And we'll say that a class is coupled with another if it uses the other class. In fact, [INAUDIBLE] shows us this relationship, with these arrows that you may have noticed before. In this example, the Car class. Is coupled with a Person class, because the code of the Car class uses personal objects. But the Person class is not coupled with the Car class because the personal object actually knows nothing about cars. Let me show you. When you look at the code of the Person class you'll see there are no cars inside, but when you look at the code for the Car class, there's a person here, and a Person class is used up here. Like I said, the Car class knows about the Person class, but the Person class doesn't know about the Car class. It's a good idea if not every class knows about every other, it makes it easier to make changes. For example, if we make a change in the Car class, the Person class doesn't care. It never knew the class in the first place. One wants to keep the coupling between classes as low as possible. Let's see what other classes the Car class might be coupled with. Go ahead, go through the code of the Car class, and give me a list of all of the classes that the Car class uses. Just put the names of the classes into this box.
Here are the classes that I found. Person, we know about that one. String and Array List, I'm not worried about those, many classes use them. System right here, where we have system.out. And Print Stream, that's a hard one actually, because the print stream class is never mentioned explicitly. One would just have to know that system.out is an object off the print stream class. Now, I am a bit bothered about that dependency, because it means that the car class can only work in an environment where I actually have a console, that would mean any computer. And these days, does your cell phone have a console, does a toaster oven? I don't really like to build on a dependency on system.out and print stream if I can avoid it.
As you've just seen in our solution, the car class was coupled with the system and print stream classes simply because the car class does an output. And we'd like to reorganize the code so that that's no longer the case. Let's get going. In the tryToAdd method, we have a print statement, let's instead move that to the point where tryToAdd is called. That would be in the loadPassengers method of the simulation class. Go ahead and move the print statement.
I've moved print statement into the loadPassengers method. There's just one thing to be careful about. You should only print the message, when the passenger has actually been added. Now we're half done, and in the next step, we'll finish the job of decoupling the car class from system.out.
Let's finish the job of decoupling the car class from the use of System.out. There's a second print statement in the drive method where we print something out every time that a passenger gets dropped off. So we want to move this again to the simulation class that caused the drive method. There's just one problem and that is that this statement occurs in a loop. There may be more than one printout per call to drive. So let's think a bit out of the box here. We drive the car to the next location and what could we tell the caller? We could tell the caller what the passengers are that have been dropped off. So go ahead, take this out. Change the return type so that it now returns an array list of person each time you remove a passenger, stick it into that array list. And at the end of the method, return that array list. In the drivesCars method of the stimulation class, look at where you called drive. Now that returns a value namely an array list of person. Save that array list in a variable. Then make a loop over all of the people in that list and for each of them, print the drop off message. Go ahead and implement that now.
We had to make changes in two classes, the car class and the stimulation class. In the car class, we now return an ArrayList which is allocated here, we add a passenger to that ArrayList, whenever one has reached his destination. And all the way in the end, we return that ArrayList as the return value of the method. Did you modify the Java Doc, to add an add return statement. If so, pat yourself in the back. In the drives car method, we call the drive method. That returns an ArrayList. For each person in that ArrayList, we print the drop off message. Note, that I didn't even bother to save the return value of the drive method in a variable. You can of course and you may find that it makes the code easier to understand. But here I say c.drive returns an ArrayList. And for every person in that ArrayList, I do something with this loop. Now, we have achieved the goal of our reorganization. The car class no longer knows anything about system.out. The Simulation class does, but that doesn't bother me so much because that simulation class also needed to know about system.in to read the user input. So the simulation class deals with input and output, the car and person class are blessedly unaware of that. Congratulations for having worked through such a complex example. Real life code does look like that, with lots of classes. And in the next unit, you will see the Java way of organizing related classes.
What did coupling look like in the photography example? Here's the BlueJ dependency diagram for the code that I wrote for the photography example. Which classes are coupled with which?
The answer is, Simulation is coupled with Manager and Manager is coupled with Portfolio. You can see this because Simulation points at Manager in the diagram and Manager points at Portfolio. But Portfolio doesn't point at Simulation, or back at the Manager. Similarly Assignment doesn't have an arrow to Portfolio. So Assignment isn't coupled with Portfolio. And remember in the code, this dotted arrow means that Simulation uses Manager. So somewhere in Simulation, a Manager object or a Manager method is used.
And Java use packages to organize related classes. Now, everyone likes packages particularly on Christmas, but in Java, we, we use them all year long. Classes that have one responsibility, get put in one package and classes with a different responsibility, in a different one. For example, you've seen the package called java.util quite a bit. It contains, various utility classes such as Scanner, ArrayList, Arrays, Random, and so on. That's one aspect of packages, to group related classes together. There's another useful aspect, the second aspect has to do with names. Unfortunately, in real life, names aren't always unique. There may be more than one person called Fred. And if you refer to Fred, they may all feel that they are the one. That's of course why last names were invented. So that hopefully the first and last names are unique. Now with people that's no longer the case, but with packages and classes it actually is. A package name is intended to be unique around the entire world. There's only 1 java.util package, and even though there may be other packages that have a scanner class, java.util.Scanner is unique. And you can actually reference in your programs, the scanner class as java.util.Scanner everywhere. And say things like java.util.Scanner in equals new java.util.Scanner. That's a little tedious, of course, which is why the import statement was invented. When you say import java.util.Scanner, that means that from then on, you can drop the family name, and just use Scanner. These two statements then mean the exact same thing. This is the sole purpose of the import statement, to allow you to use only the class name, or the first name so to speak, without having to use the package name. Now let me show you, how to use packages in your own code. So let's suppose that you have a class that you want to put into a package. All you have to do, is put this statement starting with the word package, and then the name of the package, on the top of the source file. Package names are supposed to be unique, and most companies use their domain name written in reverse order. Such as com.udacity instead of udacity.com. Because domain names are guaranteed to be unique. Maybe you have your own domain name. But if you're not. Udacity, in its infinite generosity, it will allow you to use theirs for the following exercises. Now, when you want to use this class, you do what you've always done, when you imported a class. You just use the import statement, and then you get to use the class in the normal way. Now Sara will practice this with you. And you get to reorganize the car pool classes.
For the CarShareApp, we crated four classes. There was the CarShareApp, which used the simulation, which used cars and persons. The simulation and car classes go together and are specific to the CarShareApp. The person is more general and could possibly be used for other situations. These three classes could be organized into packages. The person class might go into a more general, com.udacity package, and the simulation and car, would go in a more specific, carshare package. The CarShareApp, is just a main program, that we're writing that uses these other things, so we'll keep it in the default package. Creating packages in BlueJ is a little bit funny, so I'll show you how to do this. From the car share project. I'm going to look up at the menu. It's at the upper left for me, but it might be right along this bar for you. I'll go to Edit> New Package, and then put in the package name. Com.udacity.carshare. Now the package appears here in Bluejay. I can open it up. This is the com package. This is the Udacity package and this is the Car-share package in the Car-share package I want to put the car in simulation classes. So to do that i will go to that Edit menu again and then I select Add Classroom file and then inside the folder that contains all of the Car-share I'll select the car and add it. Now the car shows up in the Car-share package. But I need to add the simulation too. Edit > Add Classroom File > Simulation > Add. Now in the Udacity package I want to add the person. Edit > Add Classroom File and this time I'll select Person. And now its here, so this compiles but if I go back to the Car-share package and try to compile these or find cannot find symbol class. That's because person is no longer right beside simulation so i have to tell Java where to look for the person class. Where do I look for the person class? In com.udacity. Now before I ask you a question, I've got one more clean up task. Right now there are actually two persons. There's one in com Udacity, and there's one in Car-share seven. So, all of the classes that I copied into different packages, I need to remove. So I'll remove car, and it's okay that I'm permanently removing it because I copied it. And then I'll remove person and I'll remove simulation. So like I just mentioned, right now Car isn't compiling. What import statements would you need to add to this project to make it compile again? Go ahead and reorganize all of the classes into the right packages. Then to get the CarShareApp running again. To which class should I add the import com.udacity.person? Write none if I never need to import this. To which class should I add the import com.udacity.carshare.car? And to which close should I add the import com.udacity.carshare.simulation? And remember write none if you never use a particular import statement. And separate answers with commas, if you find that there are two classes that use the same import.
We import person in car in simulation. We import car nowhere, because it's only used by simulation. And simulation and car are in the same package already. We import simulation in the CarShareApp. Here's what that looks like in BlueJ. In the car class, we'll import com.udacity.person. In the simulation class, we'll also import com udacity person, and in the CarShareApp, we'll import com.udacity.carshare.Simulation. Now when I go back and compile the CarShareApp, it'll work the way it did before. But now the code is organized into the correct packages.