# Object Orient Programming-Course2 Week1 ###### tags: `OOP` `Coursera` <style> .red { color: red; } </style> <style> .blue { color: blue; } </style> <style> .green { color: green; } </style> ## What is Design Pattern? In software engineering, you will often come across the same design problem many times. There are a bunch of solutions for these recurring problems but over time some solutions are prefered over others due to three main reasons: making more **reusable, flexible and maintainable** program. * Design pattern: A practical proven solution to a recurring design problem. ## Catagory 1. **Creational Pattern**: Design patterns that tackle how you handle creating object. 2. **Structural Pattern**: Design patterns that describe how objects are connected to each others. EX: Decomposition and Generalization. 3. **Behavioral Pattern**: Design patterns that focus on how objects distribute(分配) work. They describe how each function does a single cohesive function. Also, behavioral patterns focus on how independent objects work on a common goal. ## Creational Pattern1: Singleton Pattern: The singleton pattern refers to having only one object of a class. * Goal: > 1. Enforces one and only one object of a Singleton class > 2. Has a Singleton object globally accessible ### Singleton Pattern Implementation: private constructor ```java= public class ExampleSingleton{ private static ExampleSingleton mUniqueInstance = null; private ExampleSingleton(){ ... } public static ExampleSingleton getInstance(){ if(mUniqueInstance == null){ mUniqueInstance = new ExampleSingleton(); } return mUniqueInstance; } } ``` <span class = "red">Trade off:</span> There can be issues when using the code above in the situation when multithread is accessing the object. ## Creational Pattern2: Factory Method Pattern: * How do we order Knife? This is what a naive programmer do: ```java= public class Knife{ ... ... } public Knife orderKnife(string _knifeType){ Knife knife; //create Knife object object - concrete instantiation if(_knifeType.equals("steak")){ knife = new SteakKnife(); } else if (_knifeType.equals("chefs")){ knife = new ChefdKnife(); } //prepare the knife knife.sharpen(); knife.polish(); knife.pack(); return knife; } ``` * Concrete instantiation: The act of actually instantiating a class to create an object of a specific type. In java, <span class = "blue">concrete instantiation</span> is usually done by <span class = "red">new</span> operator * sharpen, polish, pack method do not care what type of knife created. * When new type of knifes are added, the conditional list of the code above grows. Instead of makeing knifes in a store, we can make them somewhere else. ### Factory Object The preparing knife processes will stay where they are, but the concrete instantiations for knife and its conditional blocks will be delegated to another object, Knife Factory. ```java= public class KnifeFactory{ public Knife createKnife(string _knifeType){ Knife knife = null; //create Knife object object - concrete instantiation if(_knifeType.equals("steak")){ knife = new SteakKnife(); } else if (_knifeType.equals("chefs")){ knife = new ChefdKnife(); } return knife; } } public class KnifeStore{ private KnifeFactory factory; //require a KnifeFactory object to be passed to this constructor public KnifeStore(KnifeFactory _factory){ this.factory = _factory; } public Knife orderKnife(string _knifeType){ Knife knife;; //use the create method in the factory knife = factory.createKnife(_knifeType); //prepare the knife knife.sharpen(); knife.polish(); knife.pack(); return knife; } } ``` Now, the knifeStore is the client of the KnifeFactory, which is similar to how the knife produced and sold in real world. The KnifeStore may not be the only client of the KnifeFactory(ex: IKEA or Telly House). Using KnifeFactory object, when new types of knife are intruduced, you can simply add it to the KnifeFactory without modifying the client, KnifeStore. If there are multiple clients that want to instantiate the same set of classes, then by using a Factory object, you have cut out redundant code and made the software easier to modify. ### Factory Method Pattern The Factory Method design intent is to define an interface for creating objects, but let the <span class = "red">subclasses decide</span> which class to instantiate. ```java= public abstract class KnifeStore{ public Knife orderKnife(string _knifeType){ Knife knife;; //use the create method in the factory knife = factory.createKnife(_knifeType); //prepare the knife knife.sharpen(); knife.polish(); knife.pack(); return knife; } abstract Knife createKnife(string _knifeType); //abstract and emypy, should be implemented by subclass } public class BudgetKnifeStore extends KnifeStore{ Knife createKnife(string _knifeType){ if(_knifeType.equals("steak")){ return new BudgetSteakKnife(); } else if (_knifeType.equals("chefs")){ return new BudgetChefdKnife(); } else return null; } //emplementation } ``` The full UML diagram of the code above: ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; Knife [label = "{<f0> Knife|<f1> \n }"]; BudgetChefKnife [label = "{<f0> BudgetChefKnife|<f1>\n }"]; KnifeStore [label = "{<f0> KnifeStore|<f1> +orderKnife()\n +createKnife()=0\n }"]; BudgetKnifeStore [label = "{<f0> BudgetKnifeStore|<f1> +createKnife()\n }"]; BudgetChefKnife->Knife [dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; BudgetKnifeStore->KnifeStore [dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] BudgetKnifeStore->BudgetChefKnife [dir="forward",arrowhead="normal",arrowtail="normal",headlabel=" ",taillabel=" "] } } ``` Instead of using another object to create objects, we separate out object creation into another method, a factory method. Now instead of working with the factory object, we specialize or subclass the class that uses the factory method. Each subclass must define its own factory method. By separating the actual object creation from other behavior using factories, the code becomes cleaner to read and easier to maintain or change. The client code is simplified and the details of object creation are moved into the factories. ## Structural Pattern1: Facade Pattern: The Facade Pattern provides a single simplified interface for client class to interact with subsystems(Usually in complicated system).A Facade simply act as a point of entry into subsystems(As what waiters or salespersons do). **A Facade is a wrapper class that encapsulate the subsystems in order to hide the complexity of the subsystems.** ### Naive way to interact with subsystems(<span class = "red">Not a good idea</span>): ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; IAccount [label = "{<f0> \<\<Interface\>\>\nIAccount|<f1> \n }"]; Investment [label = "{<f0> Investment|<f1>\n }"]; Chequeing [label = "{<f0> Chequeing|}"]; Saving [label = "{<f0> Saving| }"]; Client [label = "{<f0> Client| }"]; Saving->IAccount [style="dashed,"dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Investment->IAccount [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Chequeing->IAccount [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] Client->Saving [dir="back",arrowhead="diamond",arrowtail="diamond",headlabel="0..*",taillabel=" "] Client->Investment [dir="back",arrowhead="diamond",arrowtail="diamond",headlabel="0..*",taillabel=" "] Client->Chequeing [dir="back",arrowhead="diamond",arrowtail="diamond",headlabel="0..*",taillabel=" "] } } ``` Without a facade class, the client should have instances of Investment, Chequeing and Saving classes.This means that the customer is responsible for properly instantiating each of these constituent classes and knows about all their different attributes and methods.(<span class = "red">Not a good idea</span> due to **cohesion** and **information hiding** ) ### Facade pattern to interact with subsystem Since all kinds of account implement the IAccount interface, the **bank's service class(facade class)** is effectively wrapping the account interfacing classes, and presenting a simpler interface for the customer client class to use. ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; IAccount [label = "{<f0> \<\<Interface\>\>\nIAccount|<f1> \n }"]; Investment [label = "{<f0> Investment|<f1>\n }"]; Chequeing [label = "{<f0> Chequeing|}"]; Saving [label = "{<f0> Saving| }"]; Client [label = "{<f0> Client| }"]; Saving->IAccount [style="dashed,"dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Investment->IAccount [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Chequeing->IAccount [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] BankService [label = "{<f0> BankService(facade)| }"]; Client->BankService [dir="forward",arrowhead="normal",arrowtail="diamond",headlabel="",taillabel=" "] IAccount->BankService [dir="forward",arrowhead="diamond",arrowtail="diamond",headlabel="",taillabel="0..* "] } ``` Constructing Facade can devide into four steps: 1. Design the interface 2. Implement the interface 3. Create the facade class 4. Use the Facade class to access the subsystem #### Step1: Design the interface ```java= public interface IAccount{ public void deposit(BigDecimal _amount); public void withdraw(BigDecimal _amount); public void transfer(IAccount _toAccount, BigDecimal _amount); public int getAccountID(); } ``` #### Step2: Implement the interface ```java= public class Chequing implements IAccount{ ... }; public class Investment implements IAccount{ ... }; public class Saving implements IAccount{ ... }; ``` #### Step3: Create the facade class ```java= public class BankService{ private HashTable<int, IAccount> mBankAccounts; public BankService(){ this.mBankAccount = new HashTable<int, IAccount>(); } public int createNewAccount(string _type, BigDecimal _initAmount){ IAccount newAccount = null; switch(_type){ case "Saving":{ newAccount = new Saving(_initAmount); break; } case "Investment":{ newAccount = new Investment(_initAmount); break; } case "Chequing":{ newAccount = new Chequing(_initAmount); } default:{ System.out.printIn("Available type missing"); break; } } if(newAccount != null) { mBankAccounts.put(newAccount.getAccountID(), newAccount); return newAccount.getAccountID() } return -1; } public void transferMoney(int _fromID, int _toID, BigDecimal _amount) { IAccount fromAccount = mBankAccounts.get(_fromID); IAccount toAccount = mBankAccounts.get(_toID); fromAccount.transfer(toAccount, _amount); } ...//other method } ``` #### Step4: Use the Facade class to access the subsystem ```java= public void userFunction{ BankService bankService; int mySavingAccount = bankService.createNewAccount("Saving", new BigDecimal(500.00)); int myInvestmentAccount = bankService.createNewAccount("Investment", new BigDecimal(1000.00)); bankService.transferMoney(mySavingAccount, myInvestmentAccount, new BigDecimal(200.00)); } ``` The customer class does not need to worry about creating and managing its own accounts. The customer simply needs to know about the BankService and the set of behaviors the BankService is capable of performing. * What are the key design principles are used to implement the facade design pattern? > Encapsulation, information hiding, separation of concerns > While the facade pattern uses a number of different design principle, its purpose is to provide ease of accessing a complex subsystem. This is done by <span class="green">encapsulating</span> the subsystem classes into a Facade class, and then <span class="green">hiding</span> them from the client classes so that the clients do not know about the details of the subsystem. ## Structural Pattern2: Adapter Pattern: In program engineering, the output of one system may not conform the output of another system. The adapter design pattern will help facilitate communication between two existing systems by providing a compatible interface. ### General form of Adapter Pattern: ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> Target Interface}"]; Adapter [label = "{<f0> Adapter}"]; Adaptee [label = "{<f0> Adaptee| }"]; Client [label = "{<f0> Client| }"]; Adapter->Adaptee [style="normal,"dir="forward",arrowhead="vee",arrowtail="normal",headlabel=" ",taillabel=" "]; Adapter->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] Client->TargetInterface[style="normal,"dir="forward",arrowhead="vee",arrowtail="normal",headlabel=" ",taillabel=" "]; } ``` The client class is part of your system that want to use the third party library(or external system). The Adaptee is the interface of a third party library. The Adapter class which will implement the target interface that will be used by the Client class is between Client and Adaptee. The Adapter conforms what the Client is expected to see. The Client will send request to the Adapter through target interface and the adapter will then translate the request into a message that the adaptee will understand. ### Specific example of Adapter Pattern: ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> \<\<interface\>\>\nWebRequester|+request(Object):Object}"]; Adapter [label = "{<f0> WebAdapter|-service: WebService|+request(Object):Object\n +connect(WebService):void\n-toJson(Object):Json\n-toObject(Json):Object}"]; Adaptee [label = "{<f0> Adaptee| |+request(Json):Json}"]; Client [label = "{<f0> WebClient|-webRequester: WebRequester|-makeObject():Object\n+doWork():void }"]; Adapter->Adaptee [style="normal,"dir="forward",arrowhead="vee",arrowtail="normal",headlabel=" ",taillabel=" "]; Adapter->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] Client->TargetInterface[style="normal,"dir="forward",arrowhead="vee",arrowtail="normal",headlabel=" ",taillabel=" "]; } ``` In this example, there is a pre-existing web client that wants to communicate with a Web Server. The web client is expecting to send any form of Objects into request but the Web Sever only takes Json Object. We need to use an adapter to convert our object request into Json object. Constructing Adapter can devide into three steps: 1. Design the target interface 2. Implement the target interface with the Adapter class 3. Send the request from the Client to the Adapter using Target Interface. #### Step1: Design the target interface ```java= public interface WebRequester{ public Object request(Object _obj) } ``` #### Step2: Implement the target interface with the Adapter class ```java= public class WebAdapter implements WebRequester{ private WebService mService; private Json toJson(Object _obj){ ... //the transition code} private Object toObject(Json _json){ ... //the transition code} public Object request(Object _obj) { Json requestJson = toJson(_obj); Object responseObj = null; responseObj = toObject(mService.request(request)); if(responseObj != null) return responseObj; return null; } public void connect(WebService _service) { this.mService = _service; } } ``` #### Step3: Send the request from the Client to the Adapter using Target Interface. ```java= public class WebClient{ private WebRequester mWebRequester; private Object makeObject(){ ... }//where you generate the beSend message public WebClient(WebRequester _webRequester){ this.mWebRequester = _webRequester; } public void doWork(){ Object requestObject = makeObject(); Object responseObject = mWebRequester.request(requestObject); if(responseObject != null) System.out.printIn("result OK"); else System.out.printIn("result missed"); } } ``` * information hiding: The Adaptee is hidden from the client by the wrapping adapter class. * <span class = "red">Not a good idea: Change one or both of the system so that they could communicate</span> ### The adapter pattern is meant to: * Wrap the adaptee and expose a target interface to the client. * Indirectly change the adaptee's interface into one that the client is expecting to see by implementing a target interface. * Indirectly change the client's request into one that the adpatee is expecting * Reuse an existing adaptee with an compatible interface. ### Peer Graded Assignment: Ungraded Assignment – Adapter Pattern #### Review criteria You are working in an office with an old coffee machine that dispenses two different coffee flavours. However, the new boss wants to add a new coffee machine with a touchscreen that can also connect to the old coffee machine. Complete the provided code to add an adapter so that the new touchscreen will to work with the old coffee machine. Use the UML class diagram in the next section for a guide #### UML ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> \<\<interface\>\>\nCoffeeMachineInterface|+chooseFirstSelection():void\n+chooseSecondSelection():void}"]; Adapter [label = "{<f0> CoffeTouchscreenAdapter|-OldVendingMachine|+chooseFirstSelection():void\n +chooseSecondSelection():void\n}"]; Adaptee [label = "{<f0> OldCoffeeMachine| |+selectA():void\n+selectB():void}"]; Adapter->Adaptee [style="normal,"dir="forward",arrowhead="vee",arrowtail="normal",headlabel=" ",taillabel=" "]; Adapter->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] } ``` #### Answer: ```java= CoffeeMachineInterface.java public interface CoffeeMachineInterface { public void chooseFirstSelection(); public void chooseSecondSelection(); } OldCoffeeMachine.java public class OldCoffeeMachine { void selectA(); void selectB(); } CoffeeTouchscreenAdapter.java public class CoffeeTouchscreenAdapter implements CoffeeMachineInterface { private OldVendingMachine mOldVendingMachine; public CoffeeTouchscreenAdapter(OldVendingMachine _oldVendingMachine){ this.mOldVendingMachine = _oldVendingMachine; } public chooseFirstSelection(){ mOldVendingMachine.selectA(); } public chooseSecondSelection(){ mOldVendingMachine.selectB(); } } ``` ## Structural Pattern3: Composite Pattern Composite Pattern achieve two goals: 1. To compose nested structure of objects. Ex: Housing and room 2. To deal with classes for these objects uniformly. How? : **Polymorphism** ### UML for composite Pattern ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> \<\<interface\>\>\nIComponent|+operation()}"]; Leaf [label = "{<f0> Leaf||+operation()}"]; Composite [label = "{<f0> Composite| |+operation()}"]; Leaf->TargetInterface [style="dashed,"dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Composite->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] TargetInterface->Composite [style="normal",dir="forward",arrowhead="odiamond",arrowtail="normal",headlabel=" ",taillabel=" "] {rank = sink; TargetInterface;} {rank = same; Composite; Leaf;} } ``` The <span class = "red">composite class</span> is used to aggregate any class that implements the <span class = "red">component interface</span>. It can let you traverse through and manipulate the component objects that it contains. The <span class = "red">leaf class</span> which is not composed of other components represents a non-composed subtype. By having the <span class = "red">Composite class</span> and the <span class = "red">Leaf class</span> <span class = "green">implement the Component interface</span>, we unify them with a single type. **Note that a composite object can compose other composite object.** this is known as <span class = "red">Recusive Composition</span> Composite Pattern is used to address two issues: 1. How do wi use individual types of objects to build a tree-like structure? 2. How can we treat the individual types of objects uniformly without checking their types? ### Example of Housing and rooms ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> \<\<interface\>\>\nIHouseStructure|+enter():void\n+exit():void\n+location():void\n+getName():String\n}"]; Leaf [label = "{<f0> Room|-name:String|+enter():void\n+exit():void\n+location():void\n+getName():String\n}"]; Composite [label = "{<f0> Housing|-address: String\n-structure:ArrayList\<IStrucutre\> |+enter():void\n+exit():void\n+location():void\n+getName():String\n+addStructure(IStructure):int\n+getStructure(int):IStructure}"]; Leaf->TargetInterface [style="dashed,"dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Composite->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] TargetInterface->Composite [style="normal",dir="forward",arrowhead="odiamond",arrowtail="normal",headlabel=" ",taillabel=" "] {rank = sink; TargetInterface;} {rank = same; Composite; Leaf;} } ``` #### **Steps of implementing Composite Pattern** 1. Design the interface that defines the overall type 2. Implement the composite class 3. Implement the Leaf class 4. Use the pattern to construct the structure ### Practice Peer-graded Assignment: Ungraded Assessment – Composite Pattern #### Review criteria You have been asked to create a playlist application that will be used on Android devices (using the Java language). We will assume that each playlist can be composed of songs or other playlists, or a combination of both. Your project manager has told you that the composite pattern is best used in this situation. The following UML class diagram that communicates the application’s objects and relationships using the composite pattern. In this assignment you are required to complete the provided code. (Note: With the exception of the Playlist class, you do not need to actually implement the methods, just write filler comments (eg., // play song). With the Playlist class, write out the method to add songs to the playlist). #### UML ```graphviz digraph obj{ node[shape=record]; rankdir="BT"; TargetInterface [label = "{<f0> \<\<interface\>\>\nIComponent|+void play();\n+void setPlaybackSpeed(speed:float);\n+String getName();}"]; Leaf [label = "{<f0> Song|+String songName;\n+String artist;\n+float speed;|+play(): void\n+setPlaybackSpeed(speed:float): void\n+getName(): String\n+getArtist(): String}"]; Composite [label = "{<f0> Playlist|+String playlistName;\n+Arraylist\<IComponent\> playlist; |+play(): void\n+setPlaybackSpeed(speed:float): void\n+getName(): String\n+add(component: IComponent): void\n+remove(component: IComponent): void}"]; Leaf->TargetInterface [style="dashed,"dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "]; Composite->TargetInterface [style="dashed",dir="forward",arrowhead="onormal",arrowtail="normal",headlabel=" ",taillabel=" "] TargetInterface->Composite [style="normal",dir="forward",arrowhead="odiamond",arrowtail="normal",headlabel=" ",taillabel=" "] {rank = sink; TargetInterface;} {rank = same; Composite; Leaf;} } ``` #### Answer: ```java= -------------------- [Program.java] -------------------- public class Program { public static void main(String args[]) { // Make new empty "Study" playlist Playlist studyPlaylist = new Playlist("Study"); // Make "Synth Pop" playlist and add 2 songs to it. Playlist synthPopPlaylist = new Playlist("Synth Pop"); Song synthPopSong1 = new Song("Girl Like You", "Toro Y Moi" ); Song synthPopSong2 = new Song("Outside", "TOPS"); synthPopPlaylist.add(synthPopSong1); synthPopPlaylist.add(synthPopSong2); // Make "Experimental" playlist and add 3 songs to it, // then set playback speed of the playlist to 0.5x Playlist experimentalPlaylist = new Playlist("Experimental"); Song experimentalSong1 = new Song("About you", "XXYYXX"); Song experimentalSong2 = new Song("Motivation", "Clams Casino"); Song experimentalSong3 = new Song("Computer Vision", "Oneohtrix Point Never"); experimentalPlaylist.add(experimentalSong1); experimentalPlaylist.add(experimentalSong2); experimentalPlaylist.add(experimentalSong3); float slowSpeed = 0.5f; experimentalPlaylist.setPlaybackSpeed(slowSpeed); // Add the "Synth Pop" playlist to the "Experimental" playlist experimentalPlaylist.add(synthPopPlaylist); // Add the "Experimental" playlist to the "Study" playlist studyPlaylist.add(experimentalPlaylist); // Create a new song and set its playback speed to 1.25x, play this song, // get the name of glitchSong → "Textuell", then get the artist of this song → "Oval" Song glitchSong = new Song("Textuell", "Oval"); float fasterSpeed = 1.25f; glitchSong.setPlaybackSpeed(fasterSpeed); glitchSong.play(); String name = glitchSong.getName(); String artist = glitchSong.getArtist(); System.out.println ("The song name is " + name ); System.out.println ("The song artist is " + artist ); // Add glitchSong to the "Study" playlist studyPlaylist.add(glitchSong); // Play "Study" playlist. studyPlaylist.play(); // Get the playlist name of studyPlaylist → "Study" System.out.println ("The Playlist's name is " + studyPlaylist.getName() ); } } -------------------- [IComponent.java] -------------------- public interface IComponent { public void play(); public void setPlaybackSpeed(float _speed); public String getName(); } -------------------- [Playlist.java] -------------------- public class Playlist implements IComponent { public String playlistName; public ArrayList<IComponent> playlist = new ArrayList(); public Playlist(String playlistName) { this.playlistName = playlistName; } public void play(){ Iterator<IComponent> playlistIt = playlist.iterator(); while(playlistIt.hasNext()) { playlistIt.next().play(); } } public void setPlaybackSpeed(float _speed){ Iterator<IComponent> playlistIt = playlist.iterator(); while(playlistIt.hasNext()) { IComponent myComponent = playlistIt.next(); myComponent.setPlaybackSpeed(_speed); } } public String getName(){ return playlistName; } public void add(IComponent _component){ playlist.add(_component); } public void remove(IComponent _component){ int index = playlist.indexOf(_component); if(index != -1) { playlist.remove(index); } } } -------------------- [Song.java] -------------------- public class Song implements IComponent { public String songName; public String artist; public float speed = 1; // Default playback speed public Song(String songName, String artist ) { this.songName = songName; this.artist = artist; } public void play(){ //play } public void setPlaybackSpeed(float _speed){ speed = _speed; } public String getName(){ return songName; } public String getArtist(){ return artist; } } ```