Skip to content

Instantly share code, notes, and snippets.

@joseraya
Created June 8, 2012 12:33
Show Gist options
  • Save joseraya/2895389 to your computer and use it in GitHub Desktop.
Save joseraya/2895389 to your computer and use it in GitHub Desktop.
Encapsulation in clojure
(ns encapsulation.core-test
(:use midje.sweet)
(:require [clj-time.core :as time]))
;My point here is to show how we can achieve encapsulation in clojure
;I assume that we may need encapsulation for two purposes:
; - Hide the internal representation of data (so that it can change)
; - Ensure the consistency of the data (by applying integrity constraints)
;Let's say that we have a "class" Person with an age.
(def person1 {:age 20})
(defn how-old? [p] (:age p))
(fact "person1 is 20 years old"
(how-old? person1) => 20)
;We know, though, that it is better to store the birth-date instead
;of the age, right?
(def person2 {:birth-date (time/date-time 1992 1 1)})
(fact "person2 is not 20 years old"
(how-old? person2) =not=> 20)
;We have broken our how-old? function because it depended on the internal representation of data
;Protocols allow us to specify the valid ways to interact with an "object"
(defprotocol HasAge
(age [x] "The number of years since it was born"))
;From now on, we will use age instead of how-old?. The advantage of age is that, for every "object"
;that wants to "instantiate" the protocol, it has the responsibility of providing a suitable
;implementation of age
;We can reify the protocol with a very simple implementation
(def person3 (reify HasAge (age [_] 20)))
(fact "person3 is 20 years old"
(age person3) => 20)
;Or we could reify it using a hash as our internal representation (I will "re-use" person2)
(def person4
(reify HasAge
(age [_] (- (time/year (time/now)) (time/year (:birth-date person2))))))
(fact "person4 is 20 years old"
(age person4) => 20)
;We can also have the interpreter generate a more efficient internal representation for us with
;records and types:
(defrecord Person [the-age]
HasAge
(age [_] the-age))
(def person5 (Person. 20))
(fact "person5 is 20 years old"
(age person5) => 20)
;And, as a bonus, we have a place to add any validation logic about integrity constraints (the protocol)
;and some nice functioality like keyword accessors or associative suup
(defproject encapsulation "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:repositories {"stuart" "http://stuartsierra.com/maven2"}
:dependencies [[org.clojure/clojure "1.3.0"]
[clj-time "0.4.2"]
[midje "1.4.0"]
[com.stuartsierra/lazytest "1.2.3"]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment