Created
June 8, 2012 12:33
-
-
Save joseraya/2895389 to your computer and use it in GitHub Desktop.
Encapsulation in clojure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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