Meeting Scheduler
🔹 Overview
A Meeting Scheduler system allows users to schedule, cancel, and manage meetings while ensuring no conflicts arise. The system also supports features like meeting reminders, notifications, and undo/redo operations.
🔹 Design Patterns Used
1️⃣ Singleton Pattern
Ensures only one instance of
MeetingScheduler
exists.Centralized meeting management.
2️⃣ Factory Pattern
Encapsulates object creation for different meeting types (
Virtual
,In-Person
).Supports scalability when adding new meeting types.
3️⃣ Observer Pattern
Notifies users when a meeting is scheduled or canceled.
4️⃣ Strategy Pattern
Handles conflict resolution strategies dynamically.
5️⃣ Decorator Pattern
Adds meeting reminders dynamically without modifying existing meeting classes.
6️⃣ Command Pattern
Supports undo/redo functionality for meeting scheduling and cancellation.
🔹 Complete Java Implementation
import java.time.LocalDateTime;
import java.util.*;
// ==================== Singleton Pattern (MeetingScheduler) ====================
class MeetingScheduler {
private static MeetingScheduler instance;
private List<Meeting> meetings;
private CommandHistory commandHistory;
private MeetingScheduler() {
meetings = new ArrayList<>();
commandHistory = new CommandHistory();
}
public static synchronized MeetingScheduler getInstance() {
if (instance == null) {
instance = new MeetingScheduler();
}
return instance;
}
public boolean scheduleMeeting(Meeting meeting) {
if (!isConflict(meeting)) {
meetings.add(meeting);
meeting.notifyObservers();
commandHistory.executeCommand(new ScheduleMeetingCommand(this, meeting));
return true;
}
return false;
}
public void cancelMeeting(Meeting meeting) {
meetings.remove(meeting);
System.out.println("Meeting '" + meeting.getTitle() + "' has been cancelled.");
commandHistory.executeCommand(new CancelMeetingCommand(this, meeting));
}
public void undoLastAction() {
commandHistory.undo();
}
private boolean isConflict(Meeting newMeeting) {
for (Meeting meeting : meetings) {
if (meeting.conflictsWith(newMeeting)) {
return true;
}
}
return false;
}
}
// ==================== Observer Pattern (User) ====================
interface Observer {
void update(Meeting meeting);
}
class User implements Observer {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public void update(Meeting meeting) {
System.out.println("Notification: " + name + ", your meeting '" + meeting.getTitle() + "' is scheduled.");
}
public String getName() {
return name;
}
}
// ==================== Factory Pattern (Meeting Factory) ====================
abstract class Meeting {
protected int id;
protected String title;
protected TimeSlot timeSlot;
protected List<User> participants;
private List<Observer> observers;
public Meeting(int id, String title, TimeSlot timeSlot) {
this.id = id;
this.title = title;
this.timeSlot = timeSlot;
this.participants = new ArrayList<>();
this.observers = new ArrayList<>();
}
public void addParticipant(User user) {
participants.add(user);
observers.add(user);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
public boolean conflictsWith(Meeting other) {
return this.timeSlot.overlapsWith(other.timeSlot);
}
public String getTitle() {
return title;
}
}
// ==================== Concrete Meeting Types ====================
class InPersonMeeting extends Meeting {
private String location;
public InPersonMeeting(int id, String title, TimeSlot timeSlot, String location) {
super(id, title, timeSlot);
this.location = location;
}
}
class VirtualMeeting extends Meeting {
private String meetingLink;
public VirtualMeeting(int id, String title, TimeSlot timeSlot, String meetingLink) {
super(id, title, timeSlot);
this.meetingLink = meetingLink;
}
}
class MeetingFactory {
public static Meeting createMeeting(String type, int id, String title, TimeSlot timeSlot, String extraDetail) {
if (type.equalsIgnoreCase("inperson")) {
return new InPersonMeeting(id, title, timeSlot, extraDetail);
} else if (type.equalsIgnoreCase("virtual")) {
return new VirtualMeeting(id, title, timeSlot, extraDetail);
}
throw new IllegalArgumentException("Invalid meeting type: " + type);
}
}
// ==================== Decorator Pattern (Meeting Reminders) ====================
abstract class MeetingDecorator extends Meeting {
protected Meeting decoratedMeeting;
public MeetingDecorator(Meeting meeting) {
super(meeting.id, meeting.title, meeting.timeSlot);
this.decoratedMeeting = meeting;
}
public abstract void addFeature();
}
class MeetingWithReminder extends MeetingDecorator {
private int reminderMinutesBefore;
public MeetingWithReminder(Meeting meeting, int reminderMinutesBefore) {
super(meeting);
this.reminderMinutesBefore = reminderMinutesBefore;
}
@Override
public void addFeature() {
System.out.println("Reminder set: " + reminderMinutesBefore + " minutes before the meeting.");
}
}
// ==================== Command Pattern (Undo/Redo) ====================
interface Command {
void execute();
void undo();
}
class ScheduleMeetingCommand implements Command {
private MeetingScheduler scheduler;
private Meeting meeting;
public ScheduleMeetingCommand(MeetingScheduler scheduler, Meeting meeting) {
this.scheduler = scheduler;
this.meeting = meeting;
}
@Override
public void execute() {
scheduler.scheduleMeeting(meeting);
}
@Override
public void undo() {
scheduler.cancelMeeting(meeting);
}
}
class CancelMeetingCommand implements Command {
private MeetingScheduler scheduler;
private Meeting meeting;
public CancelMeetingCommand(MeetingScheduler scheduler, Meeting meeting) {
this.scheduler = scheduler;
this.meeting = meeting;
}
@Override
public void execute() {
scheduler.cancelMeeting(meeting);
}
@Override
public void undo() {
scheduler.scheduleMeeting(meeting);
}
}
class CommandHistory {
private Stack<Command> history = new Stack<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
}
public void undo() {
if (!history.isEmpty()) {
Command lastCommand = history.pop();
lastCommand.undo();
}
}
}
// ==================== TimeSlot ====================
class TimeSlot {
private LocalDateTime startTime;
private LocalDateTime endTime;
public TimeSlot(LocalDateTime startTime, LocalDateTime endTime) {
this.startTime = startTime;
this.endTime = endTime;
}
public boolean overlapsWith(TimeSlot other) {
return startTime.isBefore(other.endTime) && endTime.isAfter(other.startTime);
}
}
// ==================== Main Class (Testing) ====================
public class Main {
public static void main(String[] args) {
MeetingScheduler scheduler = MeetingScheduler.getInstance();
User alice = new User(1, "Alice");
User bob = new User(2, "Bob");
TimeSlot timeSlot1 = new TimeSlot(LocalDateTime.of(2025, 3, 10, 10, 0), LocalDateTime.of(2025, 3, 10, 11, 0));
Meeting meeting1 = MeetingFactory.createMeeting("virtual", 101, "Project Kickoff", timeSlot1, "zoom.com/meeting1");
meeting1.addParticipant(alice);
meeting1.addParticipant(bob);
Meeting meetingWithReminder = new MeetingWithReminder(meeting1, 15);
((MeetingWithReminder) meetingWithReminder).addFeature();
if (scheduler.scheduleMeeting(meeting1)) {
System.out.println("Meeting scheduled successfully.");
}
scheduler.undoLastAction();
}
}
✅ Final Features
✅ Decorator Pattern → Add reminders dynamically. ✅ Command Pattern → Supports Undo/Redo actions. ✅ Factory Pattern → Encapsulated object creation.
Would you like further refinements? 🚀
Last updated