Is this approach to making these key Objects easily available when
needed good architecture? Is there a better approach to this issue?
Easy availability for objects that are needed in a context does not affect the subject architecture. Objects that are needed should ALWAYS be easy available.
The more interesting question that most of the developers do not think about: Is it really neccessary to make certain objects easy accessable?
You can have two sides. Each of them maybe neccessary or not. You have to decide proper. So there are 4 possible results:
- Easy access -> neccessary
- Easy access -> unnecessary
- Difficult access -> neccessary
- Difficult access -> unneccessary
While 1. obviously makes sense unneccessary easy access (2.) will increase the number of dependencies to manage within a system. That is with wild static access to singletons can do harm to your architecture.
The more difficult decisions are when it seems to be a problem to access a special object. You have to be very careful as to break isolation and introduce new dependencies is a very important decision. If you break isolation all the time because you want to have access to an object then this will cause harm to your architecture as well.
So "difficult access to objects" is your friend. It is an indicator for architectural decisions to make.
To your code:
public static AudioPlayer getPlayer() {
if ( audioPlayer == null ) {
throw new IllegalStateException();
}
return player;
}
Here you have a hidden state pattern as you throw an exception if the static var is not initialized by the start method. Further more you mixed class scope with object scope in a problematic way.
It is a poor design to depend on time when it comes to var initialization. Here a pure Singleton-Pattern is far better to ALWAYS have a proper initialized variable/object. Further more you abstract from the creation process of the object. But I only take these two possibilities into account as there are a lot more and better approaches. But you can go with this first:
public class AudioPlayer {
AudioPlayer () {
}
public static AudioPlayer getPlayer() {
if ( audioPlayer == null ) {
player = new Audioplayer();
}
return player;
}
}
[...] without having to pass and cache a bunch of objects around
through the various classes and methods. [...]
This is the main reason I use singletons for objects that have a global character and any other scope does not make sense. A good indicator for a singleton-object is exactly: If you have to pass the object to any class and method in your system. If security is important to your application the most obvious object to be globally accessable is the current logged in User. So there it does not make sense to pass this object around through the whole application as this will lead to a lot of boiler plate code and inexpressive method signatures.
After all you have to consider how large your application is. The larger your application is the more important architectural decisions become. If you are developing this application on your own you will face architectural problem later than when you are developing in a team.
I do not want to be rude but I do not think that a music player developed by one person matches a size where architectural decisions get relevant. I suggest to you some more basic code quality measures. Maybe you post some code on "Stack Overflow - Code Review" to get feedback on code quality.