Reputation: 75
I am a complete beginner so forgive my ignorance. I have created a project where I have used composition in some classes. In my Cinema class I have a Schedule object.
public class Cinema {
private String name; //set via constructor
private int seatCount; // set in constructor
private int rowCount; // set in constructor
private int cleanUpTime; //set via constructor
private LocalTime openTime = LocalTime.of(9, 30);
private LocalTime closeTime = LocalTime.of(23, 59);
private LocalTime peakTime = LocalTime.of(16, 30);
private int costPerHour; //set via constructor
private Schedule schedule = new Schedule(this);
//Constructors, other methods....
}
A Schedule belongs to a Cinema. It needs a Cinema object for some of its methods. A Schedule can not exist without a Cinema.
When reading about OOP I am led to believe that I have created a class that is now tightly coupled to another class and that is potentially bad.
Therefore how could I improve this design?
I have a few tightly coupled classes it seems. e.g Booking class and Customer class. A booking has a Customer and a Customer contains a list of all Bookings they have made.
I thought I was using composition and that would be good but now I am confused as I have read about coupling.
Please help me understand.
Upvotes: 3
Views: 1021
Reputation: 109567
Good sense, indeed tight coupling. Prevent it here by creating a new Interface, making Schedule more reusable probably. In the interface place all methods you want to use from Cinema. The IDE's compiler will help you there. Do not forget to add @Overridable
in Cinema, for those interface methods.
public class Cinema implements Schedulable {
private final Schedule schedule = new Schedule(this);
public class Schedule {
public void Schedule(Schedulable schedulable) { // Instead of Cinema
Upvotes: 1
Reputation: 180351
Where suitable, using composition instead of inheritance is indeed a good design practice. Your question is not really about that, though: I don't see a plausible inheritance-based alternative to the composition you have formed.
Suppose that you were trying to design classes for a system that can distinguish IMAX cinemas from other cinemas, with a consistent interface but different behavior. You might consider creating ImaxCinema
as a subclass of Cinema
and overriding methods as needed to customize its behavior:
class ImaxCinema extends Cinema {
@Override
int getScreenWidth() {
// ...
}
}
That's the the "inheritance" alternative.
On the other hand, you might create an interface ProjectorType
, with implementations Standard
and Imax
that implement the varying behavior. If you give the Cinema
class a member of type ProjectorType
then you can provide for the varying behavior by the class of the object assigned to that member:
class Cinema {
ProjectorType projector;
int getScreenWidth() {
return projector.getScreenWidth();
}
}
That's a common form of the "composition" alternative.
Your situation does not bear on inheritance vs. composition because no customization of behavior is involved.
Avoiding tight coupling between classes is a separate consideration, and also a good design principle. Your Cinema
and Schedule
classes are indeed tightly coupled, as is evident already in the fact the Schedule
's constructor requires a Cinema
argument.
Consider, however, the iterators of Collections classes. Each one is inherently specific to a particular collection class, as it must navigate the idiosynchratic internal data structures of that class to do its job properly. Each iterator's class is therefore tightly coupled to the associated collection class, and that's OK. That avoiding tight coupling is generally a good principle does not mean that the quality of every single design is anticorrelated with the degree of coupling.
In your particular case, I don't quite see what advantage you get from the Schedule
class, nor either why it needs to be tightly coupled to Cinema
. Possibly, you can break the coupling, maybe by moving members of Cinema
into the Schedule
class, and having Cinema
access them by invoking appropriate methods of Schedule
. Alternatively, it might make more sense to just merge Schedule
into Cinema
instead of having it as a separate class. If neither of those is viable, then you could consider going all the way and making Schedule
an inner class of Cinema
.
Upvotes: 1
Reputation: 51485
There has to be some coupling. A Cinema and a Schedule are not completely independent.
A Schedule belongs to a Cinema.
So far, so good.
It needs a Cinema object for some of its methods.
Nope. A Schedule object should be able to stand on it's own.
Since you haven't provided any code, I'll make the following assumptions.
So here's a Schedule class.
public class Schedule {
private final Calendar showingTimestamp;
public Schedule(Calendar showingTimestamp) {
this.showingTimestamp = showingTimestamp;
}
public Calendar getShowingTimestamp() {
return showingTimestamp;
}
public int getShowingWeekday() {
return showingTimestamp.get(Calendar.DAY_OF_WEEK);
}
}
The only field on the Schedule class holds a showing date and a showing time. I showed you how to use a Calendar method to get the weekday.
Here's a bare bones Movie class.
public class Movie {
private final String name;
private List<Schedule> showingList;
public Movie(String name) {
this.name = name;
this.showingList = new ArrayList<>();
}
public void addShowing(Schedule schedule) {
this.showingList.add(schedule);
}
public List<Schedule> getShowingList() {
return Collections.unmodifiableList(showingList);
}
public String getName() {
return name;
}
}
The Movie class knows about the Schedule class. The Schedule class does not know about the Movie class.
Finally, here's the Cinema class.
public class Cinema {
private final String name;
private List<Movie> currentMovieList;
public Cinema(String name) {
this.name = name;
this.currentMovieList = new ArrayList<>();
}
public void addCurrentMovie0(Movie movie) {
this.currentMovieList.add(movie);
}
public void removeMovie(Movie oldMovie) {
for (int index = currentMovieList.size() - 1; index >= 0; index--) {
Movie movie = currentMovieList.get(index);
if (movie.getName().equals(oldMovie.getName())) {
currentMovieList.remove(index);
}
}
}
public List<Movie> getCurrrentMovieList() {
return Collections.unmodifiableList(currentMovieList);
}
public String getName() {
return name;
}
}
The Cinema class knows about the Movie class, and indirectly, about the Schedule class. The Movie class does not know about the Cinema class.
I hope this has been helpful.
Upvotes: 3
Reputation: 339
It is fine to have a Cinema object and list of Schedule objects inside it. One to Many relationship I suppose.
Upvotes: 2