Хотя уже есть некоторые ответы, я обещал предоставить пример реализации.Начнем с того, что у нас есть общий интерфейс Playable
, который является классом, который должен быть реализован для составного шаблона проектирования.
public interface Playable {
String getSongName();
}
Далее, класс Song
представляет отдельную песню.
public class Song implements Playable {
private String name;
public Song(String name) {
this.name = name;
}
@Override
public String getSongName() {
return name;
}
}
При подготовке к классу Playlist
перечисление для представления различий режимов воспроизведения.
public enum PlayingMode {
SEQUENCE, RANDOM
}
Теперь, наконец, класс списка воспроизведения.
public class Playlist implements Playable {
private String name;
private List<Playable> playables = new ArrayList<>();
private PlayingMode mode;
private Playable currentItem;
private List<Playable> next = new ArrayList<>();
public Playlist(String name, PlayingMode mode) {
this.name = name;
this.mode = mode;
}
@Override
public String getSongName() {
if (playables.isEmpty()) {
return null;
}
if (currentItem == null) {
// initialize the playing songs
next.addAll(playables);
if (mode == PlayingMode.RANDOM) {
Collections.shuffle(next);
}
currentItem = next.get(0);
} else {
// if we have a playlist, play its songs first
if (currentItem instanceof Playlist) {
String candidate = currentItem.getSongName();
if (candidate != null) {
return candidate;
}
}
int index = next.indexOf(currentItem);
index++;
if (index < next.size()) {
currentItem = next.get(index);
} else {
currentItem = null;
}
}
return currentItem != null ? currentItem.getSongName() : null;
}
private void addToNext(Playable playable) {
if (currentItem == null) {
return;
}
// if the playlist is playing, add it to those list as well
if (mode == PlayingMode.SEQUENCE) {
next.add(playable);
} else if (mode == PlayingMode.RANDOM) {
int currentIndex = next.indexOf(currentItem);
int random = ThreadLocalRandom.current().nextInt(currentIndex, next.size());
next.add(random, playable);
}
}
public void addPlayable(Playable playable) {
Objects.requireNonNull(playable);
playables.add(playable);
addToNext(playable);
}
}
Некоторые примеры:
public static void main(String[] args) {
Song song1 = new Song("Song 1");
Song song2 = new Song("Song 2");
Playlist subPlaylist1 = new Playlist("Playlist 1", PlayingMode.RANDOM);
subPlaylist1.addPlayable(new Song("Song A"));
subPlaylist1.addPlayable(new Song("Song B"));
subPlaylist1.addPlayable(new Song("Song C"));
Song song3 = new Song("Song 3");
Playlist main = new Playlist("Main", PlayingMode.SEQUENCE);
main.addPlayable(song1);
main.addPlayable(song2);
main.addPlayable(subPlaylist1);
main.addPlayable(song3);
String songName = main.getSongName();
while (songName != null) {
System.out.println("Current song is: " + songName);
songName = main.getSongName();
}
}
Может выдать:
Current song is: Song 1
Current song is: Song 2
Current song is: Song B
Current song is: Song A
Current song is: Song C
Current song is: Song 3
Вы также можете добавлять песни во время воспроизведения:
while (songName != null) {
System.out.println("Current song is: " + songName);
songName = main.getSongName();
// add songs while playing
if ("Song A".equals(songName)) {
subPlaylist1.addPlayable(new Song("Song D"));
subPlaylist1.addPlayable(new Song("Song E"));
subPlaylist1.addPlayable(new Song("Song F"));
}
}
Это может привести к:
Current song is: Song 1
Current song is: Song 2
Current song is: Song B
Current song is: Song A
Current song is: Song E
Current song is: Song D
Current song is: Song F
Current song is: Song C
Current song is: Song 3
Некоторые заключительные примечания:
- Метод
getIndex
имеет худшее время выполнения O (n) , что может быть проблемой, если в песне много песен.плейлист.Более быстрые Collection
, такие как Set
или Map
, дадут лучшую производительность, но реализация будет немного более сложной. - Классы были упрощены, что означает некоторые getters и сеттеры , а также равно и hashCode опущены для краткости.