Law of demeter - using only one dot, could I improve this logic? - java

I have the following method:
private boolean reserveSeat(int selectedRow, int selectedSeat) {
if (show.getRows().get(selectedRow).getSeats().get(selectedSeat).getReservationStatus()) {
return false;
} else {
show.getRows().get(selectedRow).getSeats().get(selectedSeat).reserve();
setRowNumber(selectedRow);
setSeatNumber(selectedSeat);
return true;
}
}
which resides in a Reservation class. This class has a Show Object (show), A show has Rows (another object), Rows have Seats (another object).
My question is could this method be improved? I have read about LoD and worried that my dot signals a bad design though I think it is logical. It is the Seat object that knows if it is reserved or not. However is going from Show to Seat talking to strangers? or is it ok because of the way each object contains the next object?
Apologies if my questing is not clear. What seems to happen with me (probably because I am self taught) is I design stuff that works then I read some OOP design principles and think crap, it works but it is not good design!
Any advice appreciated.

Yes, that chain of calls is way too long.
If show is in charge of the seats,
then it would be better if it's fully in charge.
Right now it's not fully in charge,
because seats can be reserved without the show's knowing.
This fragmentation of responsibilities is strange.
You can put show fully in charge by not exposing Seat to the Reservation,
by hiding the seat status manipulations behind helper methods:
private boolean reserveSeat(int selectedRow, int selectedSeat) {
if (show.isSeatReserved(selectedRow, selectedSeat)) {
return false;
} else {
show.reserveSeat(selectedRow, selectedSeat);
setRowNumber(selectedRow);
setSeatNumber(selectedSeat);
return true;
}
}
Or, if you don't want show to be in charge of the seats,
then it should not be aware of the seats at all,
so then you would access seats not through show,
but another class that's in charge of that.

You're using show as a data object, and putting all the logic for handling that data in the class that contains it. This makes Show a data class and the enclosing class a god class.
The logic for handling data inside of show should really be inside the Show class itself (data is smart).
You could make a method in the Show class for reserving a seat. And equally, you could make a method in the Row class for reserving a seat.
Then one just passes on the message to the next until you get to Seat.
What if you changed the implementation of Show to use a 2D array for instance? That would break the code in your reservation class.
By doing these long chained calls, and not letting classes handle their own data. You are making the user classes dependent on the implementation the used data structures.
If you wanted to change one, you would have to update all the user classes, instead of just the one class that contains the data structure.

So thanks for the suggestions, the feedback really helped with my learning. So what I went on to do was the following, based on the relationsip--> A Reservation (now called Booking) has a Show, A Show has a Row, A Row has a Seat(s).
In the Booking class I now have this: Thanks #janos
private boolean reserveSeat(int selectedRow, int selectedSeat) {
if (show.isSeatReserved(selectedRow, selectedSeat)) {
System.out.println("Sorry, that seat has already been booked");
return false;
} else {
show.reserveSeat(selectedRow, selectedSeat);
setRowNumber(selectedRow);
setSeatNumber(selectedSeat);
System.out.println("This seat has now been booked.");
return true;
}
}
In the Show class I have this:
public boolean isSeatReserved(int selectedRow, int selectedSeat) {
if (getRow(selectedRow).getSeatStatus(selectedSeat)) {
return true;
} else
return false;
}
and in the Row class I have
public boolean getSeatStatus(int selectedSeat) {
return getSeat(selectedSeat).getReservationStatus();
}
I thought it may be useful to other people just starting out (like me) to show this graphically using before and after diagrams taken from the jarchitect tool which shows what a mess my code was in! I used the same logic to tidy up some other classes that "knew too much".

Related

What is the best way to navigate a complex tree of dissimilar objects?

For example:
class Vehicle {
Collection<Axle> axles;
}
class Axle {
Collection<Wheel> wheels;
}
class Wheel {
// I think there are dually rims that take two tires -- just go with it
Collection<Tire> tires;
}
class Tire {
int width;
int diameter;
}
I have a service through which I can get a collection of all Vehicle objects I know about. Now say I have a tire of a specific width and diameter, and I want to find a Vehicle which can take it. The simplistic way is to have a set of four nested loops, like so:
for (Vehicle vehicle : vehicles) {
for (Axle axle : vehicle.getAxles()) {
for (Wheel wheel : axle.getWheels()) {
for (Tire tire : wheel.getTires()) {
if (tire.width == targetWidth
&& tire.diameter == targetDiameter) {
// do something
break;
}
}
}
}
}
Is there a good design pattern for this? Or a better data structure to use? Would it be better to just keep an index somewhere of tire information mapped to vehicles?
edit: answering questions from comments
Do you have control over the structure of the data you receive from the service?
Yes
Do you need to search for different tires multiple times in the same data?
Yes
Is performance an issue?
Not especially
When you find the tire, do you just need to know which vehicle contains it or do you also need the axle and wheel?
Sometimes just the vehicle, sometimes just the axle -- two different contexts
Do you need the reference to the tire that was found?
Yes, in the cases where I need the axle
edit2:
Extending the metaphor further, to explain the two contexts above:
Context 1 -- I want to know the vehicle, so I can send a worker out to collect the vehicle and bring it back
Context 2 -- I want to know the axle and tire, because I am at the vehicle trying to do the work
You could flatten out the loops by using Java 8 streams.
vehicles.stream()
.flatMap(vehicle -> vehicle.getAxles().stream())
.flatMap(axle -> axle.getWheels().stream())
.flatMap(wheel -> wheel.getTires().stream())
.filter(tire -> tire.width == targetWidth
&& tire.diameter == targetDiameter)
.forEach(tire -> {
// do something
});
The nice thing about streams is that you could insert additional filter, filter, findAny, etc., calls pretty easily anywhere in the sequence.
I would inverse your logic and move the question into the Vehicle, unless of course you'd like to keep your objects thin for any other reason (in which case I'd personally wrap them with a thicker object to add any behaviour needed)
class Vehicle {
...
public Tire acceptsTire(Tire tire) {
}
}
from here on there are several possibilities, depending on how important this piece of business logic is in your domain in general.
If you'll have several actions you could probably just iterate as you had done in your sample. Or possibly in the same way as I suggested, keep cascade the question to the correct component. As long as you can live with the time complexity of doing this that should be alright.
If this check is something you'd usually do then you could have a reference to the type of tires you hold in the vehicle directly, this could be either your Tire collection, or you could pass a TireSpecification instance when constructing the Vehicle if for any reason you need to keep these separated (Your intention is not very clear in the question, is the tire on the car or just an spec of what could fit?)
Without changing your data structure you won't be able to make significant difference. You can add some syntactic sugar with lambdas, but it is essentially the same solution.
Things you could look at:
Your model allows for Vehicles with zero axles or hundred. While it depends on your business model it seems to weird.
Your model allows to have different axles in your vehicle, different wheels. Is it really necessary? Make sure which elements of your model should have their separate identity (currently each object has it) and which is just a value object.
Make sure you really need such detailed model. Currently you have two classes (Axle,Wheel), which only hold collections of inner objects. If they will be just simple JavaBean object with getAllInnerTypes() then you should consider removal of this class. It may even be the case that tire information should be stored almost directly in Vehicle class.
As long as there aren't too many items and/or performance is not a big issue, I would probably just go with the nested loops (or streams from John's answer).
Since you have two contexts for the search, you could pass the appropriate action to the search method - something like this (using loops in this case):
interface TireAction {
void doSomething(Vehicle v, Axle a, Tire t);
}
void findTireAndPerform(int targetWidth, int targetDiameter, TireAction action) {
for (Vehicle vehicle : vehicles) {
for (Axle axle : vehicle.getAxles()) {
for (Wheel wheel : axle.getWheels()) {
for (Tire tire : wheel.getTires()) {
if (tire.width == targetWidth && tire.diameter == targetDiameter) {
action.doSomething(vehicle, axle, tire);
break;
}
}
}
}
}
}
void someMethod() {
...
findTireAndPerform(width, diameter, (v, a, t) -> {
// send worker to 'v'
});
...
findTireAndPerform(width, diameter, (v, a, t) -> {
// work on 'a' and 't'
});
}

JAVA AND OR and IF

basically what needs to happen is that it needs to check what the type of Champion is, and compare it to challenges to see if it can actually do that challenge, its quite hard to explain...
a wizard can fight "magic" and "mystery"
a warrior can fight "fight" and "mystery"
a dragon can fight "fight" and "mystery(only if the dragon talks)"
what I have come up with is this...
public boolean canFight(String nme, int id)
{
Challenge chal = getChallengeObj(id);
Champion champ = getChampionObj(nme.toLowerCase());
if(champ.getType().equals("wizard") && (chal.getTypeAsString().equals("magic") || chal.getTypeAsString().equals("fight"))) {
return true;
} else if(champ.getType().equals("warrior") && (chal.getTypeAsString().equals("fight") || chal.getTypeAsString().equals("mystery"))) {
return true;
} else if((champ.getType().equals("dragon") || champ.getType().equals("dragon") && champ.isMystery()) && (chal.getTypeAsString().equals("fight") || chal.getTypeAsString().equals("magic"))) {
return true;
} else {
return false;
}
}
I thank anyone who has a go at helping me, I am open to any response, even complaints on how complicated the code is!!
(It is checking everything toLowerCase() btw, just in case this isnt clear in the code, as it is in the methods that are called in different classes.
THANKS!!
EDIT
The problem is is that it keeps returning false, and I am a second year university student, so if you could give examples of what you mean by your answers, that would be greatly appreciated :)
Thanks again
The best way to get different behaviour from different types in Java is using polymorphism - basically, put a virtual method into your Champion type with different implementations in Wizard, Warrior, Dragon etc subtypes
A chain of if-else's based on the type of an object is a bit of an anti-pattern in Java - crying out to be refactored into a single call to a virtual method.
Why not use a 2D array to map FightCompatilibity between Challenge and Champ. Model each Champ and Challenge as enum. Maybe, take a look at Visitor Pattern. It might be useful.
This is exactly something, which should be done by class hiearchy. Like if you know, someone is Wizard, you should already known what he can attack or not, therefore you should move responsibility into that Wizard class instead of testing it externally.
I did an example with 4 classes :
In this enum are listed all attack types
public enum AttackTypes {
Magic, Meele, Ranged;
}
This is abstract class, it only helps you define behaviour that is same for all classes. I supposed that every class has some subset of "attack types" (or something similar). In my example, I just suppose, that each class can fight only against some types of attack. In constructor, I get all the attack I can fight and then I can chack it with this canAttack.
public abstract class Champion {
private List<AttackTypes> attackTo;
public Champion(AttackTypes... attack) {
this.attackTo = new ArrayList<>();
this.attackTo.addAll(Arrays.asList(attack));
}
public boolean canAttack(AttackTypes attackType){
for (AttackTypes attack : attackTo){
if (attack == attackType){
return true;
}
}
return false;
}
}
This is simple Warrior class, in my example, it can fight only Magic and Meele :
public class Warrior extends Champion{
public Warrior(){
super(AttackTypes.Magic, AttackTypes.Meele);
}
}
And similar to Warrior, here is Wizard :
public class Wizard extends Champion{
public Wizard(){
super(AttackTypes.Ranged, AttackTypes.Magic);
}
}
This approach is important, because it is extremely easy to add new class (like Ranger for example). You just add him and thats all, you dont have to anything in your already finished code.
Then you can use it as following :
Champion wizard = new Wizard();
Champion warrior = new Warrior();
AttackTypes attackType = AttackTypes.Ranged;
System.out.println("can wizard attack? : " + wizard.canAttack(attackType));
System.out.println("can warrior attack? : " + warrior.canAttack(attackType));
This having this output :
can wizard attack? : true
can warrior attack? : false
A much more flexible approach would be to use a pair of enums. Something like this would be a good start. I haven't exactly implemented your logic because there is a flaw in it:
enum Champion {
Wizard(EnumSet.of(Challenge.Fight, Challenge.Magic)),
Warrior(EnumSet.of(Challenge.Fight, Challenge.Mystery)),
Dragon(EnumSet.of(Challenge.Fight, Challenge.Magic)) {
#Override
public boolean canFight(Champion enemy, Challenge attack) {
// For demonstration.
return susceptible.contains(attack) && enemy == Champion.Dragon;
}
};
protected final Set<Challenge> susceptible;
Champion(Set<Challenge> susceptible) {
this.susceptible = susceptible;
}
public boolean canFight(Champion enemy, Challenge attack) {
return susceptible.contains(attack);
}
}
enum Challenge {
Magic,
Fight,
Mystery;
}
You flaw is in (champ.getType().equals("dragon") || champ.getType().equals("dragon") && champ.isMystery()) which doesn't make sense.

Java Server Client, shared variable between threads

I am working on a project to create a simple auction server that multiple clients connect to. The server class implements Runnable and so creates a new thread for each client that connects.
I am trying to have the current highest bid stored in a variable that can be seen by each client. I found answers saying to use AtomicInteger, but when I used it with methods such as atomicVariable.intValue() I got null pointer exception errors.
What ways can I manipulate the AtomicInteger without getting this error or is there an other way to have a shared variable that is relatively simple?
Any help would be appreciated, thanks.
Update
I have the AtomicInteger working. The problem is now that only the most recent client to connect to the server seems to be able to interact with it. The other client just sort of freeze.
Would I be correct in saying this is a problem with locking?
Well, most likely you forgot to initialize it:
private final AtomicInteger highestBid = new AtomicInteger();
However working with highestBid requires a great deal of knowledge to get it right without any locking. For example if you want to update it with new highest bid:
public boolean saveIfHighest(int bid) {
int currentBid = highestBid.get();
while (currentBid < bid) {
if (highestBid.compareAndSet(currentBid, bid)) {
return true;
}
currentBid = highestBid.get();
}
return false;
}
or in a more compact way:
for(int currentBid = highestBid.get(); currentBid < bid; currentBid = highestBid.get()) {
if (highestBid.compareAndSet(currentBid, bid)) {
return true;
}
}
return false;
You might wonder, why is it so hard? Image two threads (requests) biding at the same time. Current highest bid is 10. One is biding 11, another 12. Both threads compare current highestBid and realize they are bigger. Now the second thread happens to be first and update it to 12. Unfortunately the first request now steps in and revert it to 11 (because it already checked the condition).
This is a typical race condition that you can avoid either by explicit synchronization or by using atomic variables with implicit compare-and-set low-level support.
Seeing the complexity introduced by much more performant lock-free atomic integer you might want to restore to classic synchronization:
public synchronized boolean saveIfHighest(int bid) {
if (highestBid < bid) {
highestBid = bid;
return true;
} else {
return false;
}
}
I wouldn't look at the problem like that. I would simply store all the bids in a ConcurrentSkipListSet, which is a thread-safe SortedSet. With the correct implementation of compareTo(), which determines the ordering, the first element of the Set will automatically be the highest bid.
Here's some sample code:
public class Bid implements Comparable<Bid> {
String user;
int amountInCents;
Date created;
#Override
public int compareTo(Bid o) {
if (amountInCents == o.amountInCents) {
return created.compareTo(created); // earlier bids sort first
}
return o.amountInCents - amountInCents; // larger bids sort first
}
}
public class Auction {
private SortedSet<Bid> bids = new ConcurrentSkipListSet<Bid>();
public Bid getHighestBid() {
return bids.isEmpty() ? null : bids.first();
}
public void addBid(Bid bid) {
bids.add(bid);
}
}
Doing this has the following advantages:
Automatically provides a bidding history
Allows a simple way to save any other bid info you need
You could also consider this method:
/**
* #param bid
* #return true if the bid was successful
*/
public boolean makeBid(Bid bid) {
if (bids.isEmpty()) {
bids.add(bid);
return true;
}
if (bid.compareTo(bids.first()) <= 0) {
return false;
}
bids.add(bid);
return true;
}
Using an AtomicInteger is fine, provided you initialise it as Tomasz has suggested.
What you might like to think about, however, is whether all you will literally ever need to store is just the highest bid as an integer. Will you never need to store associated information, such as the bidding time, user ID of the bidder etc? Because if at a later stage you do, you'll have to start undoing your AtomicInteger code and replacing it.
I would be tempted from the outset to set things up to store arbitrary information associated with the bid. For example, you can define a "Bid" class with the relevant field(s). Then on each bid, use an AtomicReference to store an instance of "Bid" with the relevant information. To be thread-safe, make all the fields on your Bid class final.
You could also consider using an explicit Lock (e.g. see the ReentrantLock class) to control access to the highest bid. As Tomasz mentions, even with an AtomicInteger (or AtomicReference: the logic is essentially the same) you need to be a little careful about how you access it. The atomic classes are really designed for cases where they are very frequently accessed (as in thousands of times per second, not every few minutes as on a typical auction site). They won't really give you any performance benefit here, and an explicit Lock object might be more intuitive to program with.

How to refactor a big function with many if constructs?

We have App A as main app. Now we build from it App B which uses a subset of App A's functionality.
App A stays like it is whereas app B only uses a subset of A
So I want to refactor the function without or with as little dublication as possible and with maximum readability.
So the function looks like this (it is actually longer, this is an excerpt):
class SomeClass {
Data prepareData() {
if (this.bothId==1 || this.appAid=2 /*or only relevant for appA*/) {
if(this.data==null) { /*appA*/
appAdoSmth(); /*appA*/
}
boolean merge=false; /*appA*/
if (this.data==null) { /*appA*/
merge=appAanalyze(data); /*appA*/
}
bothPrepare(merge);
} else if (bothIsRelevant()) {
if(appArelevant()) { /*appA*/
data=appAprepare(); /*appA*/
} else {
data=prepareBoth();
}
bothUpdateSomeValue();
}
}
How would you do it?
Other Answers address the general question of how to refactor code. They offer good advice, but I don't think it is what you are asking.
I think you are asking about possible refactorings of the code in your question.
It is hard to give an answer that is generally applicable, or even specifically applicable. (The sample code isn't your real code, and it is a little difficult to understand what it actually "means").
AndreasD gives one approach: break the big complicated nested if into separate methods.
Another approach is to use the Stragegy design pattern. Separate the code that is specific to each app into strategy classes. For example:
interface Strategy {
Data prepareData();
}
class GeneralStrategy implements Strategy {
Data prepareData() {
// do general preparation
}
}
class App1Strategy extends GeneralStrategy {
Data prepareData() {
// do app1-specific preparation
super.prepareData();
// do more app1-specific preparation
}
}
and so on.
I ideal world develop unit test that validates that existing implementation of your function works.
Then start changing code incrementally and run your test after every change.
It is hard to give your formal recommendation without knowing your code structure. But generally try to find duplicate code fragments, write methods that implement this logic with parameters and replace the duplicate fragments to your new method. Etc, etc.
Good luck.
Readbility can be improved by extracting some logic in separate methods. That is a refactoring method.
Data prepareData() {
if (this.bothId==1 || this.appAid=2 ) {
handleCase1(); // <- you'll find better names for the methods
} else if (bothIsRelevant()) {
handleCase2();
}
}
private void handleCase1() {
if(this.data==null) {
appAdoSmth();
}
boolean merge=false;
if (this.data==null) {
merge=appAanalyze(data);
}
bothPrepare(merge);
}
private handleCase2() {
if(appArelevant()) {
data=appAprepare();
} else {
data=prepareBoth();
}
bothUpdateSomeValue();
}
This doesn't reduce the number of if/else, of course, but it keeps the "main" method simple.
If I were you I would run a coverage report on this class. (e.g. http://ecobertura.johoop.de/ or http://www.eclemma.org/) This way Eclipse can show covered lines green and this helps you to identify the cases. With this aid it's much easier to separate green lines and pull them into methods.

Java: For loop and If algorithm

I've this question from an assignment to create a Store which rent out books, using a Store.java and Book.java. I've finished this assignment, but I'm curious for better algorithm to a specific part.
--
Book.java
public class Book {
private String name;
Book(String name)
this.name = name;
public String getName()
return name;
}
Store.java
Inside main();
Book bookObj[] = new Book[3]; //Create 3 Array of Object.
bookObj[0] = new Book("Game Over");
bookObj[1] = new Book("Shrek");
bookObj[2] = new Book("Ghost");
Scanner console = new Scanner(System.in)
input = console.nextLine();
Assuming, input = Devil.
Now, I need to do a simple search to check whether the specific book exist.
Example:
for(int i = 0; i < bookObj.length; i++) {
if(bookObj[i].getName().equals(input))
System.out.println("Book Found!");
}
Apparently, this is a for loop that cycles through the array of object and checks whether such Book exist. Now, the problem arise when I want to give an output that the Book was not found.
Example:
for(int i = 0; i < bookObj.length; i++) {
if(bookObj[i].getName().equals(input))
System.out.println("Book Found!");
else
System.out.println("Book not Found!");
}
The problem with the above code is that Book not Found would be printed thrice. My goal is to avoid such problem. I do have solutions to this, but I'm still in search for a better one to use that utilizes getName(), which in my opinion still has room to improve.
Usually, in structural programming, I would do the following,
for(int i = 0; i < bookObj.length; i++) {
if(bookObj[i].getName().equals(input))
System.out.println("Book Found!");
else if(i == bookObj.length - 1)
System.out.println("Book not Found!");
}
This is useful to tell whether it's the end of the loop, and the search has ended, but there was no successful result from the search.
How should I think of it in Object Oriented way?
All in all, my question is,
Is there a better way to write the above code rather than checking that it's the end of the line?
Is there a better way to utilize getName() method or to use other methods?
You should loop through the array and use an index / boolean flag to store whether or not the book is found. Then print the message in the end, based on the index / flag value.
int foundAtIndex = -1;
for(int i = 0; i < bookObj.length; i++) {
if(bookObj[i].getName().equals(input)) {
foundAtIndex = i; // store the actual index for later use
break; // no need to search further
}
}
if(foundAtIndex >= 0)
System.out.println("Book Found!");
else
System.out.println("Book not Found!");
Alternatively (unless your assignment specifically requires using an array) you should prefer a Set, which can do the search for you with a single call to contains().
How should I think of it in Object Oriented way?
When looking at a single method, there is not much difference between procedural and OO style. The differences start to appear at a higher level, when trying to organize a bunch of conceptually related data and methods that operate on these.
The OO paradigm is to tie the methods to the data they operate on, and encapsulate both into coherent objects and classes. These classes are preferably representations of important domain concepts. So for your book store, you may want to put all book related code into your Book class. However, the above search method (and the collection of books it operates on) is not related to any particular book instance, so you have different choices:
put both the collection of books and the search method into Store (probably as regular members), or
put them into Book as static members.
The first choice is more natural, so I normally would prefer that. However, under specific circumstances the second option might be preferable. In (OO) design, there are hardly ever clean "yes/no" answers - rather tradeoffs between different options, each having their own strengths and weaknesses.
You could introduce state and remember whether you have found the book or not.
If you're not using Java 1.4 or earlier, you could also use the foreach loop syntax:
boolean bookFound = false;
for(Book currentBook : bookObj) {
if(currentBook.getName().equals(input))
//TODO: see above
}
Also, I would suggest looking into the Collections library, and replace your array with a list or set:
Set<Book> books = new HashSet<Book>();
books.put(new Book("Game Over"));
books.put(new Book("Shrek"));
books.put(new Book("Ghost"));
And, while were at it, you could also think about when two books are equal and override equals() and hashCode() accordingly. If equal() would be changed to check the title, you could simply use books.contains(new Book(input)); and have the libraries do the work for you.
To solve the problem in a better way you must understand that the power of Java comes not from the language itself but from the Java Framework.
You should learn the usage of the Java Collection classes (never work with arrays anymore). Then you will be able to solve the search with just one line of code:
ArrayList<Book> listOfBooks;
// init your list here
listOfBooks.contains(new Book(input));
To make this work, you must also learn how to correctly implement the equals() method of your Book class.
Happy learning!
Here is a working solution :
import java.util.Scanner;
public class Store {
private static class Book {
private String name;
Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
String input;
Book[] bookObj = new Book[3];
bookObj[0] = new Book("Game Over");
bookObj[1] = new Book("Shrek");
bookObj[2] = new Book("Ghost");
Scanner console = new Scanner(System.in);
input = console.nextLine();
boolean found = false;
int i = 0;
while(!found && i < bookObj.length) {
if(bookObj[i].getName().equals(input)) {
System.out.println("Book Found at position : " + i);
found = true;
} else {
i++;
}
}
if(!found) {
System.out.println("Book not Found!");
}
// Here i contains the indice of the element found in the array.
}
}
You've gotten some pretty good advice thus far. You asked if there was a more Object Oriented way of thinking about the problem so I thought I'd try and shed some light on it. As Peter already mentioned at this level of the design it's a single method implementation so the approach is going to be fairly similar as say a procedural approach. What's the advantage? In a word reuse. If you needed to find a book by name in lots of places then moving the code to it's own class will help.
So what you have is a single Book instance to encapsulate behavior around a single book, but you want to have behavior about multiple books, or a collection of books. You can keep the data (array of books), and the method that account on them separate as you outlined in your program. However, if we wanted to collect a place for doing behavior on a collection of books we can define a new class. Let's call it Library, and we might do something like the following:
public class Library {
private Book[] books;
private bookCount = 0;
public Library( int numberOfTotalBooks ) {
books = new Book[numberOfTotalBooks];
}
public boolean addBook( Book book ) {
if( bookCount < book.length ) {
books[bookCount++] = book;
return true;
}
return false;
}
public Book findByTitle( String title ) {
for( int i = 0; i < bookCount; i++ ) {
if( books[i].getTitle().equals( title ) ) {
return books[i];
}
}
// didn't find one
return null;
}
}
So a couple of things to note about doing things this way. One is that when we work with a Library we don't know there is an Array back there. We could use an array, a Set, a List, or a database (most common). The point being the code that calls these functions just works with the interface of Library (not a literal Java interface, but the method signature of Library). Also this is a higher level interface. We don't worry about iterating over the books, doing for loops, if statements, etc. We just call a method saying "Hey find this book title in the Library". How that's done we don't care. This is the basic tenant of Object Orientation called encapsulation, and it's deceptively powerful. It's really about how we delegate responsibility in our program, and give the details of a job to individual class or classes. If Library had only public members (i.e. books and bookCount), or getter/setters then the client wouldn't be getting any advantages because the client would still have to do all the heavy lifting. The trick to OO is figuring out what can be delegated out of an object, without creating problems. This takes practice, and experience.
The second thing here is we've separated the presentation from the act of finding a book. The method you wrote assumed the next step which was to print "Hey we found it." However, Library object simply returns the Book to you when it finds it, or null if it didn't. That makes it possible to print to the console, display in a GUI, or serialize it to a JSON stream in a server. The act of finding a book is separate from the visualization. This is another important aspect of programming in general, but some what related to object orientation and encapsulation. This is typically called separation of concerns. The console application has concerns about supporting the UI, and printing the console. While the Library just manages cataloging and managing the book collection. How those details are performed neither cares.
In the end Library is a reusable class. We can use it in a console application, desktop, web, or middleware server. More importantly is we can also reuse the calls to findByTitle or addBooks from multiple locations within a single program. Also by putting the methods with the data we create a barrier to where that function can be used. You can't do it anywhere in your program. You have to have a reference to Library. If you don't have reference to a Library instance then you shouldn't be calling it. This can be troublesome to new developers because they lack the experience to properly organize their programs to not get into trouble with this (then they start doing value objects, creating statics, singletons, etc and things turn into a big ball of mud). It's a double edged sword.
One more thing I'd like to point out is say we wanted to model two Libraries. We have a Library uptown and downtown, and we want to allow people to check out books from either Library. With OO that's really easy to represent:
Library uptown = new Library( 50 );
Library downtown = new Library( 100 );
Now we can check out books from one or the other. And I didn't use statics (i.e. global variables) so reusing that logic is really easy. These are the basics of OO so they are really deep topics. Strange how I can write so much on very simple topics. Anyway I hope this helped you understand your program a little deeper, and see how you can use OO to help you.
chubbsondubs came closest to giving a correct answer to this question
What he missed is that his algorithm is incorrect because it contains two tests, when only one is needed. The correct code requires only 3 statements and is as follows:
public boolean zLibaryContains( String title ) {
books[bookCount] = title;
int xBook = 0;
while( true )
if( books[xBook].getTitle().equals( title ) )
return xBook != bookCount;
else xBook++;
}
Noticeably smaller and faster than all other solutions. Simplify, simplify, simplify.
Object-oriented code is a crutch to support poor designs that would otherwise be too complex to understand. The goal is write code that is so easy to understand and maintain that OO is unnecessary and would make the program worse. When your program can be improved by adding OO, it means you are doing something wrong to begin with.

Categories