Lecture 09 Strategy Pattern

Joseph Haugh

University of New Mexico

Free Recall

  • Get out a sheet of paper or open a text editor
  • For 2 minutes write down whatever comes to mind about the last class
    • This could be topics you learned
    • Questions you had
    • Connections you made

Example: Duck Simulator

  • Game has many duck species swimming and quacking
  • Initial design has Duck superclass extended by other types
  • Parent class has abstract display method implemented by child classes

Duck Class Hierarchy

Requirements Change

  • Let’s make ducks fly!
  • How hard can it be?
  • Let’s add a fly method to our Duck class and all the children will inherit it!

Duck Classes with Flying

Duck Classes with Flying

Just override fly for Rubber Duck?

Just override fly for Rubber Duck?

Just override fly for Rubber Duck?

Use an interface?

Use an interface?

Design Principles

  • Identify the aspects of your application that vary and separate them from what stays the same.
    • Encapsulate what varies
    • Program to an interface, not to an implementation
    • Favor composition over inheritance
  • For our example:
    • Pull the duck behavior out of the duck class
    • A Duck has a flying behavior
    • A Duck has a quacking behavior

Program to an Interface: Flying

Program to an Interface: Quacking

Extension and Reuse

  • Ducks delegate the flying and quacking behaviors
  • Now, other classes can use our quacking and flying behaviors since they’re not specific to ducks
  • We can easily add new quacking and flying styles without impacting our ducks!

Duck Classes again

Example Code

public class Duck {
    protected QuackBehavior quackBehavior;
        // ... more
    public void doQuack() {
        quackBehavior.quack();
    }
}
  • Instead of quacking on its own, a Duck delegates that behavior to the quackBehavior object
  • It doesn’t matter what kind of Duck it is
  • all it matters is a Duck knows how to quack

How to make ducks quack and fly

public class MallardDuck extends Duck {
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    public void display() {
        System.out.println("I’m a real Mallard duck!");
    }
}
  • This is not quite right yet because we’re still programming to the implementation (i.e., we have to know about the specific Quack behavior and FlyWithWings behavior).
  • We can fix this with another pattern… later…

Can Ducks learn to Quack and Fly?

  • How could you teach a Duck a new way to quack or a new way to fly?
  • Add new methods to the Duck class:
public void setFlyBehavior(FlyBehavior fb){
    flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb){
    quackBehavior = qb;
}

Favor Composition over Inheritance

  • Stated another way…
  • has-a is better than is-a
    • Ducks have quacking behaviors and flying behaviors instead of being Quackable and Flyable
  • Composition is good because:
    • It allows you to encapsulate a family of algorithms into a set of classes (the Strategy pattern)
    • It allows you to easily change the behavior at runtime

The Strategy Pattern

  • The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable
  • Strategy lets the algorithm vary independently from the clients that use it

Lambdas

  • Recall, that a lambda is a function without a name

  • For example, we could implement a function to add 1 to its argument and return the result like this:

    int add1(int n) {
        return n + 1;
    }
  • We could also do this using a lambda like this:

    x -> x + 1
  • This is often desirable for smaller functions

Higher Order Functions

  • java.util.function

  • We can write a function which takes another function as an argument as follows:

    public static int foo(Function<Integer, Integer> f, Integer x) {
        return f.apply(x);
    }
  • This is called a higher order function

  • Since this uses generics, the types must be reference types

Method References

  • It can be useful sometimes to pass a function to another function
  • This can be accomplished through lambdas as well as method references
  • When you want to pass a function without giving it any arguments you can do so as follows:
public class Main {
    public static int add1(int n) {
        return n + 1;
    }
    public static int foo(Function<Integer, Integer> f, Integer x) {
        return f.apply(x);
    }
    public static void main() {
        System.out.println(foo(x -> x + 1, 2)); // Lambda way
        System.out.println(foo(Main::add1, 2)); // Method reference
    }
}

Functional Interfaces

  • When you have an interface with one function such as:

    public interface FlyBehavior {
        void fly();
    }
  • It is can be useful to use the @FunctionalInterface annotation

  • The annotation only enforces that the interface can only have 1 function

  • Such an interface can be implement with a lambda or method reference

Functional Interfaces Three Ways

  • Given the FlyBehavior interface:

    public interface FlyBehavior {
        void fly();
    }
  • We could implement in 1 or three ways:

    • With a class:

      public class FlyWithWings implements FlyBehavior {
          @Override
          public void fly() {
              System.out.println("Winged Flying");
          }
      }

Functional Interfaces Three Ways

  • Given the FlyBehavior interface:

    public interface FlyBehavior {
        void fly();
    }
  • We could implement in 1 or three ways:

    • With a lambda:

      FlyBehavior flyBehavior = () -> System.out.println("Winged Flying");

Functional Interfaces Three Ways

  • Given the FlyBehavior interface:

    public interface FlyBehavior {
        void fly();
    }
  • We could implement in 1 or three ways:

    • With a method reference:

      public class Main {
          public static void fly() {
              System.out.println("Winged Flying"); 
          }
          public static void main() {
              FlyBehavior flyBehavior = Main::fly;
          } 
      }

Code Examples

  • All code examples from today can be found here