Living without Objects (Thinking in Functions)

Post on 09-Aug-2015

168 views 1 download

Tags:

transcript

LIVING W/O OBJECTSTHINKING IN FUNCTIONS

Osvaldas Grigas | @ogrigas

OOP HANGOVER

DESIGN

DESIGNBefore After

MONOLITHIC DESIGN

OO DESIGNNoun-orientedVerb-orientedDomain-driven?

NOUNSCustomerDAOCustomerServiceCustomerController

VERBSRegisterCustomerPromoteCustomerToVIPRenderCustomerProfilePage

OO DESIGN PRINCIPLESSingle ResponsibilityInterface SegregationDependency Inversion

SINGLE RESPONSIBILITYpublic class ZipDownloadService

public List<File> downloadAndExtract(String location)

SINGLE RESPONSIBILITYpublic class FileDownloader

public List<File> downloadFiles(String location) ...

public class ZipExtractor

public File extractZip(File archive) ...

OR ... JUST FUNCTIONS(defn download­files [location] (...))

(defn extract­zip [archive] (...))

CLOJUREin a nutshell

( some-function arg1 arg2 arg3 )

INTERFACE SEGREGATIONpublic class ProductCatalog public ProductId Save(Product product) ...

public Product FindById(ProductId id) ...

INTERFACE SEGREGATIONpublic class ProductSaver public ProductId Save(Product product) ...

public class ProductFinder public Product FindById(ProductId id) ...

Somethin' ain't right

INTERFACE SEGREGATIONpublic class ProductRepository public ProductId Save(Product product) ...

public class ProductQuery public Product FindById(ProductId id) ...

Feelin' good now

OR ... JUST FUNCTIONS(defn save­product [product] (...))

(defn find­product­by­id [id] (...))

Applying OO design principleseventually leads to...

functional design

WHAT'S MISSINGCode organizationEncapsulationInheritance hierarchiesPolymorphism

NO CODE ORGANIZATION?

(ns my.product.repository)

(defn save [product] (...))

(defn find­by­id [id] (...))

(require '[my.product.repository :as product­repo])

(product­repo/find­by­id 42)

NO ENCAPSULATION?

Data is not an objectData is immutable

NO INHERITANCE HIERARCHIES?

Blimey! What a showstopper

NO POLYMORPHISM?

We'll get there

COMPOSITION

COMPOSITIONAvoiding hard-coded dependencies

OO COMPOSITIONpublic class ProfilePage

public String render(Repository repository, int customerId) return toHtml(repository.loadProfile(customerId));

Repository repository = new Repository();ProfilePage page = new ProfilePage();

String html = page.render(repository, customerId);

FP COMPOSITION(defn render­page [repository­fn customer­id] (to­html (repository­fn customer­id)))

(defn load­profile [customer­id] (...))

(render­page load­profile customer­id)

OO "DEPENDENCY INJECTION"ProfilePage pageInjected = new ProfilePage(new Repository());

pageInjected.render(customerId);

FP "DEPENDENCY INJECTION"(def render­injected (fn [customer­id] (render­page load­profile customer­id)))

(render­injected customer­id)

PARTIAL APPLICATION(def render­injected (partial render­page load­profile))

(render­injected customer­id)

"ADAPTER" PATTERN(defn parse­int [s] (Integer/parseInt s))

(render­page (comp load­profile parse­int) customer­id)

(defn to­view­model [profile] (...))

(render­page (comp to­view­model load­profile) customer­id)

"DECORATOR" PATTERN(defn with­logging [f] (fn [& args] (log/debug "Called with params" args) (def [result (apply f args)] (log/debug "Returned" result) result)))

(render­page (with­logging load­profile) customer­id)

POLYMORPHISM

POLYMORPHISMI don't know.

I don't want to know.

POLYMORPHISMSubtypeParametricAd-hoc

OO POLYMORPHISMInheritance hierarchyInterfacesDependency inversion

public interface JsonObj String toJson();

public class JsonString implements JsonObj private final String value;

public JsonString(String value) this.value = value;

public String toJson() return "\"" + value + "\"";

public class JsonList implements JsonObj private final List<? extends JsonObj> list;

public JsonString(List<? extends JsonObj> list) this.list = list;

public String toJson() return "[" + list.stream() .map(JsonObj::toJson) .collect(joining(",")) + "]";

JsonObj obj = new JsonList(asList( new JsonString("a"), new JsonList(asList( new JsonString("b"), new JsonString("c") )), new JsonString("d")));

System.out.println(obj.toJson());

// ["a",["b","c"],"d"]

LIMITATIONS

Need wrapper typesCannot extend existing types

Too constraining!

FP POLYMORPHISMFunction compositionDispatch on parameters

PROTOCOLSopen type system

(defprotocol Json (to­json [this]))

(extend­type String Json (to­json [this] (str "\"" this "\"")))

(extend­type List Json (to­json [this] (str "[" (­>> this (map to­json) (string/join ",")) "]")))

(extend­type nil Json (to­json [this] "null"))

(to­json ["a" ["b" "c"] nil "d"])

;;=> ["a",["b","c"],null,"d"]

Why stop there?

MULTIMETHODS

(defmulti greet :country)

(defmethod greet "LT" [person] (println "Labas," (:name person) ". Kaip sekasi?"))

(defmethod greet "FR" [person] (println "Bonjour," (:name person) "!"))

(defmethod greet :default [person] (println "Hi," (:name person)))

(greet :name "Jacques" :country "FR")

;;=> Bonjour, Jacques !

(defmulti say (fn [text n] (even? n)))

(defmethod say true [text n] (println text n "is even"))

(defmethod say false [text n] (println text n "is odd"))

(say "Guess what?" 5)

;;=> Guess what? 5 is odd

CONCLUSIONDesignCompositionPolymorphism

I'm probably over time already

(questions? "Osvaldas Grigas" @ogrigas)