TWINS: OOP and FP - Warburton

75
OOP and FP Richard Warburton

description

Slides from Richard Warburton talk @ codemotion roma 2014

Transcript of TWINS: OOP and FP - Warburton

Page 1: TWINS: OOP and FP - Warburton

OOP and FPRichard Warburton

Page 2: TWINS: OOP and FP - Warburton

What on earth are you talking about?

SOLID Principles

Design Patterns

Anthropology

Page 3: TWINS: OOP and FP - Warburton
Page 4: TWINS: OOP and FP - Warburton

In Quotes ...

"OOP is to writing a program, what going through airport security is to flying"

- Richard Mansfield

"TDD replaces a type checker in Ruby in the same way that a strong drink replaces sorrows."

- byorgey

Page 5: TWINS: OOP and FP - Warburton

In Quotes ...

"Brain explosion is like a traditional pasttime in #haskell"

"Some people claim everything is lisp. One time I was eating some spaghetti and someone came by and said: 'Hey, nice lisp dialect you're hacking in there'"

Page 6: TWINS: OOP and FP - Warburton
Page 7: TWINS: OOP and FP - Warburton

Caveat: some unorthodox definitions may be provided

Page 8: TWINS: OOP and FP - Warburton

What on earth are you talking about?

SOLID Principles

Design Patterns

Anthropology

Page 9: TWINS: OOP and FP - Warburton

SOLID Principles

● Basic Object Oriented Programming Principles

● Make programs easier to maintain

● Guidelines to remove code smells

Page 10: TWINS: OOP and FP - Warburton

Single Responsibility Principle

● Each class/method should have single responsibility

● Responsibility means “reason to change”

● The responsibility should be encapsulated

Page 11: TWINS: OOP and FP - Warburton

int countPrimes(int upTo) {

int tally = 0;

for (int i = 1; i < upTo; i++) {

boolean isPrime = true;

for (int j = 2; j < i; j++) {

if (i % j == 0) {

isPrime = false;

}

}

if (isPrime) {

tally++;

}

}

return tally;

}

Page 12: TWINS: OOP and FP - Warburton
Page 13: TWINS: OOP and FP - Warburton

int countPrimes(int upTo) {

int tally = 0;

for (int i = 1; i < upTo; i++) {

if (isPrime(i)) {

tally++;

}

}

return tally;

}

boolean isPrime(int number) {

for (int i = 2; i < number; i++) {

if (number % i == 0) {

return false;

}

}

return true;

}

Page 14: TWINS: OOP and FP - Warburton

long countPrimes(int upTo) {

return IntStream.range(1, upTo)

.filter(this::isPrime)

.count();

}

boolean isPrime(int number) {

return IntStream.range(2, number)

.allMatch(x -> (number % x) != 0);

}

Page 15: TWINS: OOP and FP - Warburton

Higher Order Functions

● Hard to write single responsibility code in Java before 8

● Single responsibility requires ability to pass around behaviour

● Not just functions, Higher Order Functions

Page 16: TWINS: OOP and FP - Warburton

Open Closed Principle

Page 17: TWINS: OOP and FP - Warburton

"software entities should be open for extension, but closed for modification"

- Bertrand Meyer

Page 18: TWINS: OOP and FP - Warburton

Example: Graphing Metric Data

Page 19: TWINS: OOP and FP - Warburton

OCP as Polymorphism

● Graphing Metric Data○ CpuUsage○ ProcessDiskWrite○ MachineIO

● GraphDisplay depends upon a TimeSeries rather than each individually

● No need to change GraphDisplay to add SwapTime

Page 20: TWINS: OOP and FP - Warburton

// Example creation

ThreadLocal<DateFormat> formatter =

withInitial(() -> new SimpleDateFormat());

// Usage

DateFormat formatter = formatter.get();

// Or ...AtomicInteger threadId = new AtomicInteger();

ThreadLocal<Integer> formatter =

withInitial(() -> threadId.getAndIncrement());

OCP as High Order Function

Page 21: TWINS: OOP and FP - Warburton

OCP as Immutability

● Immutable Object cannot be modified after creation

● Safe to add additional behaviour

● New pure functions can’t break existing functionality because it can’t change state

Page 22: TWINS: OOP and FP - Warburton

Liskov Substitution Principle

Let q(x) be a property provable about objects

x of type T. Then q(y) should be true for

objects y of type S where S is a subtype of T.

* Excuse the informality

Page 23: TWINS: OOP and FP - Warburton
Page 24: TWINS: OOP and FP - Warburton

A subclass behaves like its parent.

* This is a conscious simplification

Page 25: TWINS: OOP and FP - Warburton

1. Where the parent worked the child should.

2. Where the parent caused an effect then the

child should.

3. Where parent always stuck by something

then the child should.

4. Don’t change things your parent didn’t.

Page 26: TWINS: OOP and FP - Warburton

Functional Perspective

● Inheritance isn’t key to FP

● Lesson: don’t inherit implementation and LSP isn’t an issue!

● Composite Reuse Principle already commonly accepted OOP principle

Page 27: TWINS: OOP and FP - Warburton

Interface Segregation Principle

"The dependency of one class to another one should depend on the smallest possible interface"

- Robert Martin

Page 28: TWINS: OOP and FP - Warburton

Factory Exampleinterface Worker {

public void goHome();

public void work();

}

AssemblyLine requires instances of Worker: AssemblyWorker and Manager

Page 29: TWINS: OOP and FP - Warburton

The factories start using robots...

… but a Robot doesn’t goHome()

Page 30: TWINS: OOP and FP - Warburton

Nominal Subtyping

● For Foo to extend Bar you need to see Foo extends Bar in your code.

● Relationship explicit between types based on the name of the type

● Common in Statically Typed, OO languages: Java, C++

Page 31: TWINS: OOP and FP - Warburton

class AssemblyWorker implements Worker

class Manager implements Worker

class Robot implements Worker

Page 32: TWINS: OOP and FP - Warburton

public void addWorker(Worker worker) { workers.add(worker);}

public static AssemblyLine newLine() { AssemblyLine line = new AssemblyLine(); line.addWorker(new Manager()); line.addWorker(new AssemblyWorker()); line.addWorker(new Robot()); return line;}

Page 33: TWINS: OOP and FP - Warburton

Structural Subtyping

● Relationship implicit between types based on the shape/structure of the type

● If you call obj.getFoo() then obj needs a getFoo method

● Common in wacky language: Ocaml, Go, C++ Templates, Ruby (quack quack)

Page 34: TWINS: OOP and FP - Warburton

class StructuralWorker {

def work(step:ProductionStep) { println( "I'm working on: " + step.getName) }

}

Page 35: TWINS: OOP and FP - Warburton

def addWorker(worker: {def work(step:ProductionStep)}) {

workers += worker

}

def newLine() = {

val line = new AssemblyLine

line.addWorker(new Manager())

line.addWorker(new StructuralWorker())

line.addWorker(new Robot())

line

}

Page 36: TWINS: OOP and FP - Warburton

Hypothetically …

def addWorker(worker) {

workers += worker

}

def newLine() = {

val line = new AssemblyLine

line.addWorker(new Manager())

line.addWorker(new StructuralWorker())

line.addWorker(new Robot())

line

}

Page 37: TWINS: OOP and FP - Warburton

Functional Interfaces

● An interface with a single abstract method

● By definition the minimal interface!

● Used as the inferred types for lambda expressions in Java 8

Page 38: TWINS: OOP and FP - Warburton

Thoughts on ISP

● Structural Subtyping removes the need for Interface Segregation Principle

● Functional Interfaces provide a nominal-structural bridge

● ISP != implementing 500 interfaces

Page 39: TWINS: OOP and FP - Warburton

Dependency Inversion Principle

Page 40: TWINS: OOP and FP - Warburton

● Abstractions should not depend on details, details should depend on abstractions

● Decouple glue code from business logic

● Inversion of Control/Dependency Injection is an implementation of DIP

Dependency Inversion Principle

Page 41: TWINS: OOP and FP - Warburton

Streams Library

album.getMusicians()

.filter(artist -> artist.name().contains(“The”))

.map(artist -> artist.getNationality())

.collect(toList());

Page 42: TWINS: OOP and FP - Warburton

Resource Handling & Logic

List<String> findHeadings() {

try (BufferedReader reader

= new BufferedReader(new FileReader(file))) {

return reader.lines()

.filter(isHeading)

.collect(toList());

} catch (IOException e) {

throw new HeadingLookupException(e);

}

}

Page 43: TWINS: OOP and FP - Warburton

Business Logic

private List<String> findHeadings() {

return withLinesOf(file,

lines -> lines.filter(isHeading)

.collect(toList()),

HeadingLookupException::new);

}

Page 44: TWINS: OOP and FP - Warburton

Resource Handling

<T> T withLinesOf(String file,

Function<Stream<String>, T> handler,

Function<IOException,

RuntimeException> error) {

try (BufferedReader reader =

new BufferedReader(new FileReader(file))) {

return handler.apply(reader.lines());

} catch (IOException e) {

throw error.apply(e);

}

}

Page 45: TWINS: OOP and FP - Warburton

DIP Summary

● Higher Order Functions also provide Inversion of Control

● Abstraction != interface

● Functional resource handling, eg withFile in haskell

Page 46: TWINS: OOP and FP - Warburton

All the solid patterns have a functional equivalent

Page 47: TWINS: OOP and FP - Warburton

The same idea expressed in different ways

Page 48: TWINS: OOP and FP - Warburton

What on earth are you talking about?

SOLID Principles

Design Patterns

Anthropology

Page 49: TWINS: OOP and FP - Warburton

Command Pattern

• Receiver - performs the actual work.

• Command - encapsulates all the information

required to call the receiver.

• Invoker - controls the sequencing and

execution of one or more commands.

• Client - creates concrete command instances

Page 50: TWINS: OOP and FP - Warburton

Macro: take something that’s long and make it short

Page 51: TWINS: OOP and FP - Warburton

public interface Editor {

public void save();

public void open();

public void close();

}

Page 52: TWINS: OOP and FP - Warburton

public interface Action {

public void perform();

}

Page 53: TWINS: OOP and FP - Warburton

public class Open implements Action {

private final Editor editor;

public Open(Editor editor) {

this.editor = editor;

}

public void perform() {

editor.open();

}

}

Page 54: TWINS: OOP and FP - Warburton

public class Macro {

private final List<Action> actions;

public void record(Action action) {

actions.add(action);

}

public void run() {

actions.forEach(Action::perform);

}

}

Page 55: TWINS: OOP and FP - Warburton

Macro macro = new Macro();macro.record(new Open(editor));macro.record(new Save(editor));macro.record(new Close(editor));macro.run();

Page 56: TWINS: OOP and FP - Warburton

The Command Object is a Function

Macro macro = new Macro();macro.record(() -> editor.open());macro.record(() -> editor.save());macro.record(() -> editor.close());macro.run();

Page 57: TWINS: OOP and FP - Warburton

Observer Pattern

Page 58: TWINS: OOP and FP - Warburton
Page 59: TWINS: OOP and FP - Warburton

Concrete Example: Profiler

public interface ProfileListener {

public void accept(Profile profile);

}

Page 60: TWINS: OOP and FP - Warburton

private final List<ProfileListener> listeners;

public void addListener(ProfileListener listener) {

listeners.add(listener);

}

private void accept(Profile profile) {

for (ProfileListener listener : listeners) {

listener.accept(profile)

}

}

Page 61: TWINS: OOP and FP - Warburton

Previously you needed to write this EVERY time.

Page 62: TWINS: OOP and FP - Warburton

Consumer<T> === () → TProfileListener === () → ProfileActionListener === () → Action

Page 63: TWINS: OOP and FP - Warburton

public class Listeners<T> implements Consumer<T> {

private final List<Consumer<T>> consumers;

public Listeners<T> add(Consumer<T> consumer) {

consumers.add(consumer);

return this;

}

@Override

public void accept(T value) {

consumers.forEach(consumer -> consumer.accept(value));

}

Page 64: TWINS: OOP and FP - Warburton

public ProfileListener provide(

FlatViewModel flatModel,

TreeViewModel treeModel) {

Listeners<Profile> listener = new

Listeners<Profile>()

.of(flatModel::accept)

.of(treeModel::accept);

return listener::accept;

}

Page 65: TWINS: OOP and FP - Warburton

Existing Design Patterns don’t need to be thrown away.

Page 66: TWINS: OOP and FP - Warburton

Existing Design Patterns can be improved.

Page 67: TWINS: OOP and FP - Warburton

What on earth are you talking about?

SOLID Principles

Design Patterns

Anthropology

Page 68: TWINS: OOP and FP - Warburton

Popular programming language evolution follows Arnie’s career.

Page 69: TWINS: OOP and FP - Warburton

The 1980s were great!

Page 70: TWINS: OOP and FP - Warburton

Programming 80s style

● Strongly multiparadigm languages○ Smalltalk 80 had lambda expressions○ Common Lisp Object System

● Polyglot Programmers

● Fertile Language Research

● Implementation Progress - GC, JITs, etc.

Page 71: TWINS: OOP and FP - Warburton

The 1990s ruined everything

Page 72: TWINS: OOP and FP - Warburton

90s and 2000s Market Convergence

● Huge Java popularity ramp○ Javaone in 2001 - 28,000 attendees○ Servlets, J2EE then Spring

● Virtual death of Smalltalk, LISP then Perl

● Object Oriented Dominance

Page 73: TWINS: OOP and FP - Warburton

Now everyone is friends

Page 74: TWINS: OOP and FP - Warburton

Increasingly Multiparadigm

● Established languages going multiparadigm○ Java 8 - Generics + Lambdas○ C++ - Templates, Lambdas

● Newer Languages are multi paradigm○ F#○ Ruby/Python/Groovy can be functional○ New JVM languages:

■ Scala■ Ceylon■ Kotlin

Page 75: TWINS: OOP and FP - Warburton

http://is.gd/javalambdas@richardwarburto

http://insightfullogic.com

Q & A