Behavioral patterns focus on the interaction between objects, their communication, and the responsibilities of objects. These patterns help make complex communication between objects more flexible and efficient.
Memento Pattern
Purpose
Primary Purpose of Memento Pattern is to save and store previous state of an object. The Caretaker is responsible for storing and restoring the Memento without modifying it.
In this blog, we will explore how the Memento Pattern is implemented in Java through an example of a simple Graphic Editor that can save and undo shape modifications.
Key Components of the Memento Pattern:
Originator: This is the object whose state is being saved or restored. In our case, it’s the GraphicEditor class. Memento: This object stores the state of the Originator. In our example, it is the EditorMemento class. Caretaker: This class keeps the history of mementos and is responsible for saving and restoring states. In our example, the Caretaker class handles the history of shapes.
The GraphicEditor Class
The GraphicEditor class represents the originator. It stores information about the shape being drawn, such as its type, position, color, and size. It also has methods to save and restore its state using the EditorMemento class.
// The GraphicEditor class manages the properties of shapes and provides functionality to save and restore their state using the Memento pattern.packageMemento;publicclassGraphicEditor{privateStringshapeType;privateintx;privateinty;privateStringcolor;privateintsize;publicEditorMementosave(){// TODO: Create and return a new memento that captures the current state of the shape attributes.returnnewEditorMemento(shapeType,x,y,color,size);}publicvoidrestore(EditorMementomemento){// TODO: Restore the shape attributes from the provided memento, updating the graphic editor's state.shapeType=memento.getShapeType();x=memento.getX();y=memento.getY();color=memento.getColor();size=memento.getSize();}publicStringgetShape(){return"Shape: "+shapeType+", Position: ("+x+", "+y+"), Color: "+color+", Size: "+size;}publicvoidsetShape(StringshapeType,intx,inty,Stringcolor,intsize){this.shapeType=shapeType;this.x=x;this.y=y;this.color=color;this.size=size;}}
fromEditorMementoimportEditorMementoclassGraphicEditor:def__init__(self):self.shape_type=Noneself.x=0self.y=0self.color=Noneself.size=0defsave(self):# Create and return a new memento capturing the current statereturnEditorMemento(self.shape_type,self.x,self.y,self.color,self.size)defrestore(self,memento):# Restore the state from the provided mementoself.shape_type=memento.shape_typeself.x=memento.xself.y=memento.yself.color=memento.colorself.size=memento.sizedefget_shape(self):returnf"Shape: {self.shape_type}, Position: ({self.x}, {self.y}), Color: {self.color}, Size: {self.size}"defset_shape(self,shape_type,x,y,color,size):self.shape_type=shape_typeself.x=xself.y=yself.color=colorself.size=size
The EditorMemento Class
The EditorMemento class captures the state of a shape. It is an immutable object that holds the shape's properties and provides getter methods to retrieve these values.
// The EditorMemento class stores the state of a shape, allowing for the preservation and restoration of its attributes in the Memento pattern.packageMemento;publicclassEditorMemento{privatefinalStringshapeType;privatefinalintx;privatefinalinty;privatefinalStringcolor;privatefinalintsize;publicEditorMemento(StringshapeType,intx,inty,Stringcolor,intsize){// TODO: Initialize the shape's attributes with the provided parameters.this.shapeType=shapeType;this.x=x;this.y=y;this.color=color;this.size=size;}publicStringgetShapeType(){returnshapeType;}publicintgetX(){returnx;}publicintgetY(){returny;}publicStringgetColor(){returncolor;}publicintgetSize(){returnsize;}}
classEditorMemento:def__init__(self,shape_type,x,y,color,size):# Store the shape's attributesself.shape_type=shape_typeself.x=xself.y=yself.color=colorself.size=size
The Caretaker Class
The Caretaker class is responsible for managing the history of saved states. It uses a Stack to keep track of the mementos. The caretaker does not modify the state of the memento, it only stores and retrieves it when necessary.
// The Caretaker class manages the history of shape states, allowing for saving and undoing changes in the Memento pattern.packageMemento;importjava.util.Stack;publicclassCaretaker{privatefinalStack<EditorMemento>history=newStack<>();publicvoidsaveState(GraphicEditorgraphicEditor){// TODO: Save the current state of the graphic editor by pushing its memento onto the history stack.history.push(graphicEditor.save());}publicvoidundo(GraphicEditorgraphicEditor){// TODO: Restore the last saved state of the graphic editor if history is not empty.if(!history.empty()){history.pop();graphicEditor.restore(history.pop());}}}
classCaretaker:def__init__(self):# Stack to maintain history of statesself.history=[]defsave_state(self,graphic_editor):# Save the current state by appending its memento to the historyself.history.append(graphic_editor.save())defundo(self,graphic_editor):# Restore the last saved state if history is not emptyifself.history:self.history.pop()# Discard the latest stateifself.history:# Check if there’s another state to revert tographic_editor.restore(self.history[-1])
The Exercise Class (Main Application)
The Exercise class is responsible for interacting with the user. It collects the shape's attributes and uses the GraphicEditor and Caretaker to manage and save the state.
// The Exercise class allows users to input shape attributes and provides functionality to manage these shapes using the Memento pattern.packageMemento;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It is designed to gather user input and manage shape states.publicvoidrun(){Scannersc=newScanner(System.in);GraphicEditorgraphicEditor=newGraphicEditor();Caretakercaretaker=newCaretaker();for(inti=0;i<3;i++){Stringshape=sc.next();intx=sc.nextInt();inty=sc.nextInt();Stringcolor=sc.next();intsize=sc.nextInt();// TODO: Update the graphic editor with the new shape attributes from user input.graphicEditor.setShape(shape,x,y,color,size);// TODO: Save the current state of the graphic editor to the historycaretaker.saveState(graphicEditor);}sc.close();// TODO: Implement the undo operation to revert to the previous shape statecaretaker.undo(graphicEditor);// TODO: Output the current shape attributes after the undo operation to verify the restored state.System.out.println(graphicEditor.getShape());}}
fromGraphicEditorimportGraphicEditorfromCaretakerimportCaretakerclassExercise:defrun(self):graphic_editor=GraphicEditor()caretaker=Caretaker()for_inrange(3):# Take input from the usershape=input("Enter shape type: ")x=int(input("Enter x-coordinate: "))y=int(input("Enter y-coordinate: "))color=input("Enter color: ")size=int(input("Enter size: "))# Update the graphic editor with new shape attributesgraphic_editor.set_shape(shape,x,y,color,size)# Save the current state to the historycaretaker.save_state(graphic_editor)# Undo the last operationcaretaker.undo(graphic_editor)# Print the current state after undoprint(graphic_editor.get_shape())if__name__=="__main__":Exercise().run()
How the Memento Pattern Works in This Example
Setting the Shape: The user inputs the shape's attributes (type, position, color, and size). These attributes are set in the GraphicEditor object. Saving State: Each time the user changes the shape, the current state is saved by calling the saveState method of the Caretaker class. This stores the state in a Stack. Undo Operation: The user can undo the changes by calling the undo method of the Caretaker class. This restores the previous shape state from the stack, effectively rolling back to an earlier state.
Benefits of Using the Memento Pattern
Encapsulation Preservation: The state of an object is captured and stored outside of the object, allowing for state restoration without exposing the internal structure. Undo/Redo Operations: The Memento Pattern provides an easy way to implement undo and redo functionality. Separation of Concerns: The Caretaker class is responsible for managing the state history, allowing the GraphicEditor class to focus solely on the editing functionality.
// The Exercise class simulates stock price updates, registers investors, and removes an observer after the 4th update.packageObserver;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It is designed to handle user input, manage stock price updates, and control the observer notification process.publicvoidrun(){Scannersc=newScanner(System.in);doublepriceChangeThreshold=sc.nextDouble();StockMarketstockMarket=newStockMarket(priceChangeThreshold);InvestorAinvestorA=newInvestorA();InvestorBinvestorB=newInvestorB();// TODO: Register Investor A as an observer to receive stock updates.stockMarket.registerObserver(investorA);// TODO: Register Investor B as an observer to receive stock updates.stockMarket.registerObserver(investorB);intupdates=sc.nextInt();for(inti=1;i<=updates;i++){if(i==5){// TODO: Remove Investor B from receiving notifications after the 4th update.stockMarket.removeObserver(investorB);}StringstockSymbol=sc.next();doublenewPrice=sc.nextDouble();doubleoldPrice=sc.nextDouble();// TODO: Update the stock price and notify observers.stockMarket.setStockPrice(stockSymbol,newPrice,oldPrice);}sc.close();}}
// The StockMarket class tracks stock price changes and notifies observers if the change exceeds a threshold.packageObserver;importjava.util.ArrayList;importjava.util.List;publicclassStockMarketimplementsSubject{privatefinalList<Observer>observers;privatefinaldoublepriceChangeThreshold;publicStockMarket(doublepriceChangeThreshold){// TODO: Initialize the list of observers to keep track of registered observers.observers=newArrayList<>();this.priceChangeThreshold=priceChangeThreshold;}@OverridepublicvoidregisterObserver(Observero){// TODO: Add observer to the list of observersobservers.add(o);}@OverridepublicvoidremoveObserver(Observero){// TODO: Remove observer from the list of observersobservers.remove(o);}@OverridepublicvoidnotifyObservers(StringstockSymbol,doublenewPrice){for(Observerobserver:observers){// TODO: Inform each observer about the updated stock price.observer.update(stockSymbol,newPrice);}}publicvoidsetStockPrice(StringstockSymbol,doublenewPrice,doubleoldPrice){doublepriceChange=Math.abs(newPrice-oldPrice)/oldPrice*100;if(priceChange>=priceChangeThreshold){// TODO: Notify observers if the price change exceeds the thresholdnotifyObservers(stockSymbol,newPrice);}}}
// The InvestorA class implements the Observer interface and receives stock price updates.packageObserver;publicclassInvestorAimplementsObserver{@Overridepublicvoidupdate(StringstockSymbol,doublenewPrice){System.out.println("Investor A notified: Stock "+stockSymbol+" has a new price: $"+newPrice);}}
// The InvestorB class implements the Observer interface and receives stock price updates.packageObserver;publicclassInvestorBimplementsObserver{@Overridepublicvoidupdate(StringstockSymbol,doublenewPrice){System.out.println("Investor B notified: Stock "+stockSymbol+" has a new price: $"+newPrice);}}
// The Subject interface defines methods for registering, removing, and notifying observers about stock price changes.packageObserver;publicinterfaceSubject{voidregisterObserver(Observero);voidremoveObserver(Observero);voidnotifyObservers(StringstockSymbol,doublenewPrice);}
// The Observer interface defines the update method for receiving stock price change notifications.packageObserver;publicinterfaceObserver{voidupdate(StringstockSymbol,doublenewPrice);}
// Exercise.java// This class manages the formatting process of a document using various text formatting strategies.packageStrategy;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It facilitates the formatting process of a document using different formatting strategies.publicvoidrun(){Scannersc=newScanner(System.in);Documentdocument=newDocument();StringuserInput=sc.nextLine();document.setContent(userInput);// Using PlainTextFormatter// TODO: Set the formatter for the document to PlainTextFormatter.PlainTextFormatterplainText=newPlainTextFormatter();document.setFormatter(plainText);System.out.println("Plain Text:");document.display();// Using HTMLFormatter// TODO: Set the formatter for the document to HTMLFormatter.HTMLFormatterhtmlText=newHTMLFormatter();document.setFormatter(htmlText);System.out.println("HTML Format:");document.display();// Using MarkdownFormatter// TODO: Set the formatter for the document to MarkdownFormatter.MarkdownFormattermarkdownText=newMarkdownFormatter();document.setFormatter(markdownText);System.out.println("Markdown Format:");document.display();sc.close();}}
// Document.java// This class represents a document that can have its content formatted using different strategies.packageStrategy;publicclassDocument{privateStringcontent;privateTextFormatterformatter;publicvoidsetContent(Stringcontent){this.content=content;}publicvoidsetFormatter(TextFormatterformatter){this.formatter=formatter;}publicvoiddisplay(){// TODO: Print the formatted content using the chosen formatter.System.out.println(formatter.format(this.content));}}
// PlainTextFormatter.java// This class implements the TextFormatter interface to format text as plain text.packageStrategy;publicclassPlainTextFormatterimplementsTextFormatter{@OverridepublicStringformat(Stringtext){// TODO: Return the input text without any formatting.returntext;}}
// HTMLFormatter.java// This class implements the TextFormatter interface to format the text as HTML.packageStrategy;publicclassHTMLFormatterimplementsTextFormatter{@OverridepublicStringformat(Stringtext){// TODO: Wrap the input text in HTML tags: "<html><body>" and "</body></html>".return"<html><body>"+text+"</body></html>";}}
// MarkdownFormatter.java// This class implements the TextFormatter interface to format text using Markdown syntax.packageStrategy;publicclassMarkdownFormatterimplementsTextFormatter{@OverridepublicStringformat(Stringtext){// TODO: Wrap the input text in Markdown syntax: "**" and "**".return"**"+text+"**";}}
// TextFormatter.java// This Interface defines a contract for text formatting strategies.packageStrategy;publicinterfaceTextFormatter{Stringformat(Stringtext);}
// This class is responsible for creating devices, commands, and a remote control to demonstrate the command pattern functionality.packageCommand;publicclassExercise{//Do not modify the run method; it is designed to manage command execution and control the devices.publicvoidrun(){// Create devicesLightlight=newLight();Fanfan=newFan();// Create commandsCommandlightOn=newLightCommands.LightOnCommand(light);CommandlightOff=newLightCommands.LightOffCommand(light);CommandfanOn=newFanCommands.FanOnCommand(fan);CommandfanOff=newFanCommands.FanOffCommand(fan);// Create remote control// TODO: Instantiate the RemoteControl object to manage commands.RemoteControlremoteControl=newRemoteControl();// TODO: Set the command for turning the light on using the LightOnCommand using remoteControl object.remoteControl.setLightOnCommand(lightOn);// TODO: Set the command for turning off the light using LightOffCommand using remoteControl object.remoteControl.setLightOffCommand(lightOff);// TODO: Set the command for turning on the fan using FanOnCommand using remoteControl object.remoteControl.setFanOnCommand(fanOn);// TODO: Set the command for turning off the fan using FanOffCommand using remoteControl object.remoteControl.setFanOffCommand(fanOff);// Test the functionality// TODO: Press the button to turn on the light and verify the output.remoteControl.pressLightOnButton();// TODO: Press the button to turn off the light and verify the output.remoteControl.pressLightOffButton();// TODO: Press the button to turn on the fan and verify the output.remoteControl.pressFanOnButton();// TODO: Press the button to turn off the fan and verify the output.remoteControl.pressFanOffButton();}}
// This class acts as a remote control, allowing the execution of commands for controlling devices like lights and fans.packageCommand;publicclassRemoteControl{privateCommandlightOnCommand;privateCommandlightOffCommand;privateCommandfanOnCommand;privateCommandfanOffCommand;publicvoidsetLightOnCommand(Commandcommand){this.lightOnCommand=command;}publicvoidsetLightOffCommand(Commandcommand){this.lightOffCommand=command;}publicvoidsetFanOnCommand(Commandcommand){this.fanOnCommand=command;}publicvoidsetFanOffCommand(Commandcommand){this.fanOffCommand=command;}publicvoidpressLightOnButton(){if(lightOnCommand!=null){lightOnCommand.execute();}}publicvoidpressLightOffButton(){if(lightOffCommand!=null){lightOffCommand.execute();}}publicvoidpressFanOnButton(){if(fanOnCommand!=null){fanOnCommand.execute();}}publicvoidpressFanOffButton(){if(fanOffCommand!=null){fanOffCommand.execute();}}}
// This class contains command implementations for controlling the light, including turning it on and off.packageCommand;publicclassLightCommands{publicstaticclassLightOnCommandimplementsCommand{privateLightlight;publicLightOnCommand(Lightlight){this.light=light;}//TODO: Override the execute() method from the Command interface and Implement the logic to turn on the light when this command is executed.publicvoidexecute(){light.turnOn();}}publicstaticclassLightOffCommandimplementsCommand{privateLightlight;publicLightOffCommand(Lightlight){this.light=light;}//TODO: Override the execute() method from the Command interface and Implement the logic to turn off the light when this command is executed.publicvoidexecute(){light.turnOff();}}}
// This class contains command implementations for controlling the fan, including turning it on and off.packageCommand;publicclassFanCommands{publicstaticclassFanOnCommandimplementsCommand{privateFanfan;publicFanOnCommand(Fanfan){this.fan=fan;}//TODO: Override the execute() method from the Command interface and Implement the logic to turn on the fan when this command is executed.publicvoidexecute(){fan.turnOn();}}publicstaticclassFanOffCommandimplementsCommand{privateFanfan;publicFanOffCommand(Fanfan){this.fan=fan;}//TODO: Override the execute() method from the Command interface and Implement the logic to turn off the fan when this command is executed.publicvoidexecute(){fan.turnOff();}}}
// This class represents a light that can be turned on or off, providing the corresponding output.packageCommand;publicclassLight{publicvoidturnOn(){System.out.println("The light is on.");}publicvoidturnOff(){System.out.println("The light is off.");}}
// This class represents a fan that can be turned on or off, providing the corresponding output.packageCommand;publicclassFan{publicvoidturnOn(){System.out.println("The fan is on.");}publicvoidturnOff(){System.out.println("The fan is off.");}}
// This interface defines a command that can be executed, requiring an implementation of the execute method.packageCommand;publicinterfaceCommand{voidexecute();}
// This class is responsible for executing the report generation process using different report types.packageTemplate;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It manages the report generation process for various report types.publicvoidrun(){Scannersc=newScanner(System.in);// Generate Sales ReportReportTemplatesalesReport=newSalesReport(sc);System.out.println("Generating Sales Report:");// TODO: Generate the Sales Report by calling the generateReport() method.salesReport.generateReport();// Generate Employee ReportReportTemplateemployeeReport=newEmployeeReport(sc);System.out.println("Generating Employee Report:");// TODO: Generate the Employee Report by calling the generateReport() method.employeeReport.generateReport();// Generate Inventory ReportReportTemplateinventoryReport=newInventoryReport(sc);System.out.println("Generating Inventory Report:");// TODO: Generate the Inventory Report by calling the generateReport() method.inventoryReport.generateReport();}}
// This class represents a Sales Report, gathering and processing sales data from user input.packageTemplate;importjava.util.Scanner;publicclassSalesReportextendsReportTemplate{privateScannersc;publicSalesReport(Scannersc){this.sc=sc;}@OverrideprotectedvoidgatherData(){StringgatherData=sc.nextLine();System.out.println(gatherData);}@OverrideprotectedvoidprocessData(){StringprocessData=sc.nextLine();System.out.println(processData);}}
// This class represents a Employee Report, gathering and processing sales data from user input.packageTemplate;importjava.util.Scanner;publicclassEmployeeReportextendsReportTemplate{privateScannersc;publicEmployeeReport(Scannersc){this.sc=sc;}@OverrideprotectedvoidgatherData(){StringgatherData=sc.nextLine();System.out.println(gatherData);}@OverrideprotectedvoidprocessData(){StringprocessData=sc.nextLine();System.out.println(processData);}}
// This class represents a Inventory Report, gathering and processing sales data from user input.packageTemplate;importjava.util.Scanner;publicclassInventoryReportextendsReportTemplate{privateScannersc;publicInventoryReport(Scannersc){this.sc=sc;}@OverrideprotectedvoidgatherData(){StringgatherData=sc.nextLine();System.out.println(gatherData);}@OverrideprotectedvoidprocessData(){StringprocessData=sc.nextLine();System.out.println(processData);}}
// Abstract class defining the template for report generation, enforcing the structure while allowing specific implementations for each report type.packageTemplate;publicabstractclassReportTemplate{// Template method defining the skeleton of the report generationpublicfinalvoidgenerateReport(){gatherData();// Specific to each reportprocessData();// Specific to each reportformatReport();// Common across all reportsprintReport();// Common across all reports}// Steps to be implemented by subclassesprotectedabstractvoidgatherData();protectedabstractvoidprocessData();// Default methods that can be common across all reportsprotectedvoidformatReport(){System.out.println("Formatting the report with appropriate layout and style.");}protectedvoidprintReport(){System.out.println("Printing the report for final review and distribution.");}}
// This class allows users to input various types of notifications and then displays them.packageIterator;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It is designed to handle user input and manage the notification workflow.publicvoidrun(){Scannersc=newScanner(System.in);NotificationManagernotificationManager=newNotificationManager();// Add Notificationsfor(inti=0;i<2;i++){StringemailNotification=sc.nextLine();StringsmsNotification=sc.nextLine();StringpushNotification=sc.nextLine();notificationManager.addEmailNotification(emailNotification);notificationManager.addSMSNotification(smsNotification);notificationManager.addPushNotification(pushNotification);}// Print all notifications// TODO: Use notificationManager to display all the added notifications by invoking the method that prints them.notificationManager.printAllNotifications();sc.close();}}
// This class manages different types of notifications (email, SMS, push) and provides methods to add notifications and print them using an iterator pattern.packageIterator;importjava.util.Iterator;publicclassNotificationManager{privateNotificationCollectionemailNotifications;privateNotificationCollectionsmsNotifications;privateNotificationCollectionpushNotifications;publicNotificationManager(){// TODO: Initialize Email Notifications using the class responsible for handling Email Notifications.this.emailNotifications=newEmailNotification();// TODO: Initialize SMS Notifications using the class responsible for handling SMS Notifications.this.smsNotifications=newSMSNotification();// TODO: Initialize Push Notifications using the class responsible for handling Push Notifications.this.pushNotifications=newPushNotification();}publicvoidaddEmailNotification(Stringmessage){((EmailNotification)emailNotifications).addNotification(message);}publicvoidaddSMSNotification(Stringmessage){((SMSNotification)smsNotifications).addNotification(message);}publicvoidaddPushNotification(Stringmessage){((PushNotification)pushNotifications).addNotification(message);}publicvoidprintAllNotifications(){printNotifications(emailNotifications.createIterator(),"Email");printNotifications(smsNotifications.createIterator(),"SMS");printNotifications(pushNotifications.createIterator(),"Push");}privatevoidprintNotifications(Iterator<Notification>iterator,Stringtype){System.out.println(type+" Notifications:");while(iterator.hasNext()){System.out.println(iterator.next().getMessage());}}}
// This class represents a collection of Email notifications and provides functionality to add notifications and create an iterator for traversing through them.packageIterator;importjava.util.ArrayList;importjava.util.Iterator;importjava.util.List;publicclassEmailNotificationimplementsNotificationCollection{privateList<Notification>emailNotifications;publicEmailNotification(){emailNotifications=newArrayList<>();}publicvoidaddNotification(Stringmessage){emailNotifications.add(newNotification(message));}@OverridepublicIterator<Notification>createIterator(){// TODO: Return a new iterator for the Email Notifications using the EmailNotificationIterator class.returnnewEmailNotificationIterator(this.emailNotifications);}privateclassEmailNotificationIteratorimplementsIterator<Notification>{privateintposition=0;privateList<Notification>notifications;publicEmailNotificationIterator(List<Notification>notifications){this.notifications=notifications;}@OverridepublicbooleanhasNext(){returnposition<notifications.size();}@OverridepublicNotificationnext(){returnnotifications.get(position++);}}}
// This class represents a collection of SMS notifications and provides functionality to add notifications and create an iterator for traversing through them.packageIterator;importjava.util.Iterator;importjava.util.ArrayDeque;importjava.util.Queue;publicclassSMSNotificationimplementsNotificationCollection{privateQueue<Notification>smsNotifications;publicSMSNotification(){smsNotifications=newArrayDeque<>();}publicvoidaddNotification(Stringmessage){smsNotifications.add(newNotification(message));}@OverridepublicIterator<Notification>createIterator(){// TODO: Return a new iterator for the SMS Notifications using the SMSNotificationIterator class.returnnewSMSNotificationIterator(this.smsNotifications);}privateclassSMSNotificationIteratorimplementsIterator<Notification>{privateQueue<Notification>notifications;publicSMSNotificationIterator(Queue<Notification>notifications){this.notifications=newArrayDeque<>(notifications);}@OverridepublicbooleanhasNext(){return!notifications.isEmpty();}@OverridepublicNotificationnext(){returnnotifications.poll();}}}
// This class represents a collection of Push notifications and provides functionality to add notifications and create an iterator for traversing through them.packageIterator;importjava.util.LinkedHashSet;importjava.util.Iterator;importjava.util.Set;publicclassPushNotificationimplementsNotificationCollection{privateSet<Notification>pushNotifications;publicPushNotification(){pushNotifications=newLinkedHashSet<>();}publicvoidaddNotification(Stringmessage){pushNotifications.add(newNotification(message));}@OverridepublicIterator<Notification>createIterator(){// TODO: Return a new iterator for the Push Notifications using the PushNotificationIterator class.returnnewPushNotificationIterator(this.pushNotifications);}privateclassPushNotificationIteratorimplementsIterator<Notification>{privateIterator<Notification>iterator;publicPushNotificationIterator(Set<Notification>notifications){this.iterator=notifications.iterator();}@OverridepublicbooleanhasNext(){returniterator.hasNext();}@OverridepublicNotificationnext(){returniterator.next();}}}
// This class represents a notification with a message for managing notifications.packageIterator;publicclassNotification{privateStringmessage;publicNotification(Stringmessage){this.message=message;}publicStringgetMessage(){returnmessage;}}
// This defines an interface for collections that provide an iterator for notifications.packageIterator;importjava.util.Iterator;publicinterfaceNotificationCollection{publicIterator<Notification>createIterator();}
// The Exercise class demonstrates the State Design Pattern for a Media Player.packageState;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It is designed to handle user commands (Play, Pause, stop) for the Media Player. publicvoidrun(){MediaPlayermediaPlayer=newMediaPlayer();Scannersc=newScanner(System.in);Stringchoice=sc.next();switch(choice){case"Play":mediaPlayer.play();break;case"Pause":// TODO: Set the Media Player state to PausedStatemediaPlayer.setState(newPausedState());mediaPlayer.pause();break;case"Stop":// TODO: Set the Media Player state to StoppedStatemediaPlayer.setState(newStoppedState());mediaPlayer.stop();break;default:System.out.println("Invalid choice.");}// TODO: Display the current state of the Media PlayermediaPlayer.displayState();sc.close();}}
// The MediaPlayer class manages the current state of the Media Player using the State Design Pattern.packageState;publicclassMediaPlayer{privateStatestate;publicMediaPlayer(){// TODO: Set the initial state of the Media Player to PlayingStatestate=newPlayingState();}publicvoidsetState(Statestate){this.state=state;}publicvoidplay(){// TODO: Implement the functionality for pressing playstate.pressPlay();}publicvoidstop(){// TODO: Implement the functionality for pressing stopstate.pressStop();}publicvoidpause(){// TODO: Implement the functionality for pressing pausestate.pressPause();}publicvoiddisplayState(){// TODO: Implement the functionality to display the current statestate.display();}}
// The PlayingState class represents the playing state of the Media Player. packageState;publicclassPlayingStateimplementsState{@OverridepublicvoidpressPlay(){System.out.println("Starting playback");}@OverridepublicvoidpressStop(){System.out.println("Stopping playback");}@OverridepublicvoidpressPause(){System.out.println("Pausing playback");}@Overridepublicvoiddisplay(){System.out.println("Current State: Playing");}}
// The PausedState class represents the playing state of the Media Player.packageState;publicclassPausedStateimplementsState{@OverridepublicvoidpressPlay(){System.out.println("Resuming playback");}@OverridepublicvoidpressStop(){System.out.println("Stopping playback from pause");}@OverridepublicvoidpressPause(){System.out.println("Pausing playback");}@Overridepublicvoiddisplay(){System.out.println("Current State: Paused");}}
// The StoppedState class represents the playing state of the Media Player. packageState;publicclassStoppedStateimplementsState{@OverridepublicvoidpressPlay(){System.out.println("Starting playback");}@OverridepublicvoidpressStop(){System.out.println("Stopping playback");}@OverridepublicvoidpressPause(){System.out.println("Can't pause. Media is already stopped");}@Overridepublicvoiddisplay(){System.out.println("Current State: Stopped");}}
// The State interface defines the methods for different states of the Media Player. packageState;publicinterfaceState{voidpressPlay();voidpressStop();voidpressPause();voiddisplay();}
// This class simulates the flight control system, managing airplane takeoff and landing requests through a control tower mediator.packageMediator;importjava.util.Scanner;publicclassExercise{// Do not modify the run method. It demonstrates the functionality of the Flight Control System using the Mediator design pattern.publicvoidrun(){Scannersc=newScanner(System.in);ControlTowercontrolTower=newControlTower();StringairplaneId1=sc.nextLine();StringairplaneId2=sc.nextLine();StringairplaneId3=sc.nextLine();StringairplaneId4=sc.nextLine();// TODO: Instantiate an airplane with the first provided IDAirplaneairplane1=newAirplane(airplaneId1);// TODO: Instantiate an airplane with the second provided IDAirplaneairplane2=newAirplane(airplaneId2);// TODO: Instantiate an airplane with the third provided IDAirplaneairplane3=newAirplane(airplaneId3);// TODO: Instantiate an airplane with the fourth provided IDAirplaneairplane4=newAirplane(airplaneId4);// TODO: Register the first airplane with the control towercontrolTower.registerAirplane(airplane1);// TODO: Register the second airplane with the control towercontrolTower.registerAirplane(airplane2);// TODO: Register the third airplane with the control towercontrolTower.registerAirplane(airplane3);// TODO: Register the fourth airplane with the control towercontrolTower.registerAirplane(airplane4);airplane1.requestTakeoff();airplane2.requestTakeoff();airplane3.requestTakeoff();airplane4.requestTakeoff();// TODO: Mark the first airplane as having completed takeoff and free a runwaycontrolTower.completeTakeoff(airplane1);// TODO: Mark the second airplane as having completed takeoff and free a runwaycontrolTower.completeTakeoff(airplane2);airplane3.requestTakeoff();airplane4.requestTakeoff();// TODO: Mark the third airplane as having completed takeoff and free a runwaycontrolTower.completeTakeoff(airplane3);// TODO: Mark the fourth airplane as having completed takeoff and free a runwaycontrolTower.completeTakeoff(airplane4);airplane1.requestLanding();airplane2.requestLanding();// TODO: Mark the first airplane as having completed landing and free a runwaycontrolTower.completeLanding(airplane1);// TODO: Mark the second airplane as having completed landing and free a runwaycontrolTower.completeLanding(airplane2);airplane3.requestLanding();airplane4.requestLanding();// TODO: Mark the third airplane as having completed landing and free a runwaycontrolTower.completeLanding(airplane3);// TODO: Mark the fourth airplane as having completed landing and free a runwaycontrolTower.completeLanding(airplane4);sc.close();}}
// This class represents an airplane that interacts with the Control Tower mediator for takeoff and landing requests.packageMediator;publicclassAirplane{privateStringid;privateMediatormediator;publicAirplane(Stringid){this.id=id;}publicvoidsetMediator(Mediatormediator){this.mediator=mediator;}publicvoidrequestTakeoff(){System.out.println("Airplane "+id+" requesting takeoff");// TODO: Notify the mediator to handle the takeoff request for this airplanemediator.handleTakeoffRequest(this);}publicvoidrequestLanding(){System.out.println("Airplane "+id+" requesting landing");// TODO: Notify the mediator to handle the landing request for this airplanemediator.handleLandingRequest(this);}publicvoidreceiveNotification(Stringmessage){System.out.println("Airplane "+id+": "+message);}publicStringgetId(){returnid;}}
// This class serves as the mediator, managing communication and runway allocation between airplanes for takeoff and landing.packageMediator;importjava.util.ArrayList;importjava.util.List;publicclassControlTowerimplementsMediator{privateList<Airplane>airplanes;privateinttakeoffRunways;privateintlandingRunways;publicControlTower(){// TODO: Initialize the list of airplanes to manage communicationairplanes=newArrayList<>();this.takeoffRunways=2;this.landingRunways=2;}@OverridepublicvoidregisterAirplane(Airplaneairplane){// TODO: Add the airplane to the list of registered airplanesairplanes.add(airplane);// TODO: Set the mediator for the airplane to enable communicationairplane.setMediator(this);}@OverridepublicvoidhandleTakeoffRequest(Airplaneairplane){if(takeoffRunways>0){takeoffRunways--;notifyAirplane(airplane,"Takeoff approved. Runways available: "+takeoffRunways);}else{notifyAirplane(airplane,"Takeoff denied. No runways available. Please wait");}}@OverridepublicvoidhandleLandingRequest(Airplaneairplane){if(landingRunways>0){landingRunways--;notifyAirplane(airplane,"Landing approved. Runways available: "+landingRunways);}else{notifyAirplane(airplane,"Landing denied. No runways available. Please wait");}}// Simulate the completion of takeoff and free the runwaypublicvoidcompleteTakeoff(Airplaneairplane){System.out.println("Airplane "+airplane.getId()+" has taken off");takeoffRunways++;System.out.println("Runway freed. Available takeoff runways: "+takeoffRunways);}// Simulate the completion of landing and free the runwaypublicvoidcompleteLanding(Airplaneairplane){System.out.println("Airplane "+airplane.getId()+" has landed");landingRunways++;System.out.println("Runway freed. Available landing runways: "+landingRunways);}privatevoidnotifyAirplane(Airplaneairplane,Stringmessage){// TODO: Notify the airplane of the status message from the control towerairplane.receiveNotification(message);}}
// This interface defines the contract for the mediator responsible for managing airplane communication and requests for takeoff and landing.packageMediator;publicinterfaceMediator{voidregisterAirplane(Airplaneairplane);voidhandleTakeoffRequest(Airplaneairplane);voidhandleLandingRequest(Airplaneairplane);}