TP

Remaining Design Patterns

Review of Design Patterns

Already Covered Patterns:
  • Strategy:

    • Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable.

    • Allows algorithms to vary independently from clients using them.

  • Decorator:

    • Intent: Attach additional responsibilities to an object dynamically.

    • Offers a flexible alternative to subclassing for extending functionality.

  • Factory Method:

    • Intent: Define an interface for creating an object, but let subclasses decide which class to instantiate.

    • Lets a class defer instantiation to subclasses.

  • Template Method:

    • Intent: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.

    • Allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure.

  • Composite:

    • Intent: Compose objects into tree structures to represent part-whole hierarchies.

    • Lets clients treat individual objects and compositions of objects uniformly.

  • Iterator:

    • Intent: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Remaining Design Patterns
  • Adapter

  • Observer

  • Command

  • State

Why Design Patterns?
  • Definition: A general reusable solution to a commonly occurring problem in software design.

  • Facilitates communication of expertise from experts to novices.

  • Provides a common vocabulary for communication between experts.

  • Solutions often represent best practices, minimizing rewriting when software changes.

Abilities with Design Patterns
  • Explain the purpose of each design pattern.

  • Explain how each design pattern achieves its intent.

  • Explain the difference between two design patterns.

  • Recognize when a pattern might be needed in a given situation.

  • Apply a pattern in a given situation by:

    • Instantiating UML diagrams or writing Java code (or both).

    • Incorporating existing classes and operations.

  • Explain in detail the consequences of alternatives to the pattern like duplicated code or switch statements and the consequent cost of subsequent modification.

  • Explain why a program conforms to a design pattern (or why not).

  • Demonstrate how code that implements a design pattern can be extended.

Adapter Pattern
  • Intent: Change the interface of an object to match what a client expects.

Two Kinds of Adapter Pattern:
Object Adapter:
  • Associates with the Adaptee, a separate object.

  • Accessing Adaptee requires an indirection, which introduces overhead.

  • Operation specificRequest cannot be overridden directly; the Adapter would need to associate with a subclass of Adaptee.

  • Makes it possible to have many different Adaptee classes with different interfaces.

  • Allows many different Adapter classes with the same interface.

  • Makes it harder to override Adaptee behavior; requires subclassing Adaptee and making Adapter refer to the subclass.

Class Adapter:
  • Adapter inherits from Adaptee.

  • Operation specificRequest can be overridden.

  • Commits to a particular Adaptee class, so we can’t adapt a class and all of its subclasses (unlike Object Adapter).

Observer Pattern
  • Intent: Ensure that when one object changes state, all its dependents are notified and updated automatically.

GUI Programming Without Observer Pattern:
  • When a button is pressed, class TimeApp updates just one class.

  • actionPerformed method is simple.

  • If several display classes exist, each with different methods to call:

    • actionPerformed would be complex.

    • It would have to change whenever a new class was added or an old class changed.

  • Solution: actionPerformed only needs to update a list of display classes, all of which share the same interface.

    • These are Observer classes, all sharing the same interface.

    • There is also a Subject class, containing data to be displayed.

  • The coupling between the Observer and Subject is loose because Subject has to know very little about Observer.

Java API Support for Observer Pattern:
  • Interface Observer:

    • void update(Observable o, Object arg)

    • Here the new state is sent (push model) to the Observer, so it does not have to query the Observable (the pull model).

    • The new state must be constructed by the model.

  • Class Observable:

    • void addObserver(Observer o){}

    • void deleteObserver(Observer o){}

    • void notifyObservers(){}

    • protected void setChanged(){}

  • Major drawback: unnecessary updates.

    • This can be mitigated by having different sorts of update.

Example Code:
  • Observable:

 // This class extends Observable, allowing it to notify Observers of changes.
 public class WeatherData extends Observable {
 private float temperature, humidity, pressure;

 public WeatherData() {}

 // Call this method to signal that the measurements have changed.
 public void measurementsChanged() {
 setChanged(); // Mark the Observable object as changed
 notifyObservers(); // Notify all Observers
 }

 // Method to set the new measurements and then notify Observers.
 public void setMeasurements(float t, float h, float p){
 this.temperature = t;
 this.humidity = h;
 this.pressure = p;
 measurementsChanged(); // Notify observers that measurements have changed
 }
 }
  • Observer:

 // This class implements the Observer interface to receive updates from an Observable.
 public class CondsDisplay implements Observer, Display {
 private Observable observable;
 private float temperature, humidity;

 // Constructor to register the Observer with the Observable.
 public CondsDisplay(Observable observable){
 this.observable = observable;
 observable.addObserver(this); // Register as an Observer
 }

 // This method is called whenever the Observable's state changes.
 public void update(Observable obs, Object arg){
 // Check if the update is coming from a WeatherData object
 if (obs instanceof WeatherData) {
 WeatherData weatherData = (WeatherData) obs; // Cast to WeatherData
 this.temperature = weatherData.getTemperature(); // Get the temperature
 this.humidity = weatherData.getHumidity(); // Get the humidity
 display(); // Display the updated information
 }
 }
 }
Command Pattern
  • Intent: Represent method calls as objects.

  • Issue requests to objects without knowing the operation or the receiver.

  • Used in user interface toolkits (by MenuItem, Button, etc.).

  • The invoker only needs to know how to run the command, commands can be undone or stored in a "log"

  • Using polymorphism makes it easier to add new commands.

  • Note similarity with Factory Method pattern.
    *Compare with interface Runnable and method run() in the Java API

Command Pattern Examples:
  • Invoker = Button, Command = PasteCommand, Receiver = Document

  • Invoker = MenuItem, Command = OpenCommand, Receiver = Application

  • Commands can be undone or stored in a log (e.g., ArrayList) for redoing.

  • Composing with the Composite pattern allows commands to be macros:

    • (Command * Composite)[Component = Command ∧ Composite = ConcreteCommand ∧ operation = execute]

  • where ∗ denotes superposition and [ ] denotes restriction

Example Code:
  • Fan Example Code

 // This class implements the Command interface to encapsulate a fan's high speed setting.
 public class FanHighCommand implements Command {
 private Fan fan; // The receiver of the command
 private int prevSpeed; // Stores the previous speed of the fan for undo operation

 // Constructor to associate the command with a specific fan.
 public FanHighCommand(Fan fan){
 this.fan = fan;
 }

 // Executes the command by setting the fan to high speed.
 public void execute() {
 prevSpeed = fan.getSpeed(); // Save the current speed before changing it
 fan.high(); // Set the fan to high speed
 }

 // Undoes the command by reverting the fan to its previous speed.
 public void undo(){
 fan.setSpeed(prevSpeed); // Restore the fan to its previous speed
 }
 }
State Pattern
  • Intent: Make the behavior of an object dependent on its state.

  • The state of an object is the values of all of its attributes.

  • Suppose the number of states is small, e.g., traffic lights (red, red-amber, green, amber).

  • Procedural approach will lead to excessive switch statements.

  • Represent each state by a subclass.

  • Changing state involves switching from one subclass to another.

Code for Aeroplane Example:
  • State Pattern Code

 // This class represents the context whose behavior changes based on its state.
 public class Aeroplane {
 private State landedState;
 private State queueingState;//and others
 private State state = skyState; // Initial state

 // Constructor to initialize the different states.
 public Aeroplane(){
 landedState = new LandedState(this);
 queueingState = new QueueingState(this);//and others
 }

 // Method to initiate landing; delegates to the current state.
 public void land(){
 state.land();
 }

 // Method to set the current state of the aeroplane.
 public void setState(State state){
 this.state = state;
 }

 // Example of a state implementation.
 public class QueueingState implements State {
 private Aeroplane aeroplane;
 // Constructor to associate the state with an aeroplane.
 public QueueingState (Aeroplane aeroplane){
 this.aeroplane = aeroplane;
 }

 // Behavior when already in the queue.
 public void queue(){
 System.out.println("It is already in the queue!");
 }

 // Behavior to transition to the landed state.
 public void land(){
 // and others
 aeroplane.setState(aeroplane.getLandedState()); // Transition to landed state
 }
 }
 }
More on State Pattern:
  • Further advantages are that state-specific behavior is localized, and state transitions are made explicit.

  • It would be slightly simpler to make each transition operation return an object representing the new state instead of setting it.

  • Simpler too to create each state object when needed (but wasteful of memory).

  • Class diagram is the same as that of Strategy, but the intent and code are different.

Conclusion
  • Adapter: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

  • Observer: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

  • Command: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

  • State: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.