├── .gitignore ├── LICENSE ├── README.md ├── bsconfig.json ├── package-lock.json ├── package.json ├── src ├── abstractFactory │ ├── abstractFactory.re │ └── abstractFactoryDemo.re ├── adapter │ └── adapter.re ├── bridge │ ├── README.md │ └── bridge.re ├── builder │ └── builder.re ├── factoryMethod │ ├── factoryMethod.re │ └── factoryMethodDemo.re ├── prototype │ └── prototype.re └── singleton │ ├── README.md │ ├── singleton.re │ ├── singletonCopy.re │ └── singletonDemo.re └── tasks.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/bs 22 | lib/js 23 | *.mlast 24 | *.mliast 25 | .vscode 26 | .merlin 27 | node_modules 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Charlie Greenman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoF Patterns Implemented Using Reason 2 | 3 | Enjoy! 4 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | // This is the configuration file used by BuckleScript's build system bsb. Its documentation lives here: http://bucklescript.github.io/bucklescript/docson/#build-schema.json 2 | // BuckleScript comes with its own parser for bsconfig.json, which is normal JSON, with the extra support of comments and trailing commas. 3 | { 4 | "name": "reason-oop-patterns", 5 | "version": "0.1.0", 6 | "bsc-flags": ["-bs-super-errors"], 7 | "refmt": 3, 8 | "sources": [{"dir": "src", "subdirs": true}], 9 | "bs-dependencies" : [ 10 | // add your dependencies here. You'd usually install them normally through `npm install my-dependency`. If my-dependency has a bsconfig.json too, then everything will work seamlessly. 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-oop-patterns", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "bs-platform": { 8 | "version": "2.0.0", 9 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-2.0.0.tgz", 10 | "integrity": "sha1-F+5gf6gp9nuLkgQ468pewd6rMnY=", 11 | "dev": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-oop-patterns", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "clean": "bsb -clean-world", 6 | "build": "bsb -make-world", 7 | "watch": "bsb -make-world -w" 8 | }, 9 | "keywords": [ 10 | "BuckleScript" 11 | ], 12 | "license": "MIT", 13 | "devDependencies": { 14 | "bs-platform": "^2.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/abstractFactory/abstractFactory.re: -------------------------------------------------------------------------------- 1 | type abstractProductA = {. methodA: string}; 2 | 3 | type abstractProductB = {. methodB: int}; 4 | 5 | class virtual abstractFactory = { 6 | pub virtual createProductA: abstractProductA; 7 | pub virtual createProductB: abstractProductB; 8 | }; 9 | 10 | class productA1 = { 11 | pub methodA = "This is methodA of ProductA1"; 12 | }; 13 | 14 | class productB1 = { 15 | pub methodB = 1; 16 | }; 17 | 18 | class productA2 = { 19 | pub methodA = "This is methodA of ProductA2"; 20 | }; 21 | 22 | class productB2 = { 23 | pub methodB = 2; 24 | }; 25 | 26 | class concreteFactory1 = { 27 | inherit class abstractFactory; 28 | pub createProductA = new productA1; 29 | pub createProductB = new productB1; 30 | }; 31 | 32 | class concreteFactory2 = { 33 | inherit class abstractFactory; 34 | pub createProductA = new productA2; 35 | pub createProductB = new productB2; 36 | }; 37 | 38 | class testFactory (factory: abstractFactory) = { 39 | as self; 40 | pub createProductA = factory#createProductA; 41 | pub createProductB = factory#createProductB; 42 | pub test = () => { 43 | Js.log(self#createProductA#methodA); 44 | Js.log(self#createProductB#methodB) 45 | }; 46 | }; 47 | -------------------------------------------------------------------------------- /src/abstractFactory/abstractFactoryDemo.re: -------------------------------------------------------------------------------- 1 | let show = () => { 2 | let factory1: AbstractFactory.abstractFactory = new AbstractFactory.concreteFactory1; 3 | let tester1: AbstractFactory.testFactory = (new AbstractFactory.testFactory)(factory1); 4 | tester1#test(); 5 | let factory2: AbstractFactory.abstractFactory = new AbstractFactory.concreteFactory2; 6 | let tester2: AbstractFactory.testFactory = (new AbstractFactory.testFactory)(factory2); 7 | tester2#test() 8 | }; 9 | 10 | show(); 11 | -------------------------------------------------------------------------------- /src/adapter/adapter.re: -------------------------------------------------------------------------------- 1 | class virtual screenSize = { 2 | as _; 3 | pub virtual windowWidth: unit => int; 4 | }; 5 | 6 | class macWindowSize (width:int) = { 7 | as _; 8 | inherit class screenSize; 9 | pub windowWidth = () => width; 10 | }; 11 | -------------------------------------------------------------------------------- /src/bridge/README.md: -------------------------------------------------------------------------------- 1 | # Bridge pattern 2 | "Decouple an abstraction from its implementation so that the two can vary independently". 3 | 4 | ##### Problem with the above description 5 | One note with the above description, is that it can be a bit hard to understand. 6 | I know that I personally had trouble understanding it. A couple of questions I had. 7 | 1. What is the relation between abstraction and implementation? 8 | 2. "... so that the two can vary", what two are we talking about? Are we just talking 9 | about the abstraction and the implementation? 10 | * If so what does the bridge pattern accomplish, and why even choose the name 11 | bridge in the first place? 12 | 13 | ##### Way of Explaining the bridge pattern in English. 14 | ###### Let's Imagine the following Scenario: 15 | Think of the bridge pattern as the item(Item A) being changed, and the 16 | item(Item B A.K.A Implementor) changing the item(Item A) being changed. 17 | 18 | ###### How to Simplify, Starting from the Top 19 | If we were to try and create a way so that we can create a base for both, and 20 | re-create the objects with differences here and there, how would we do it? 21 | 22 | The simple way would be to create an implementer class that would create both 23 | objects at the same time. It would be for instance a door implementer class. 24 | We would be able to pass a different sort of knob. One without a lock, one 25 | with a lock. One maybe with touchscreen capabilites. The only part of this door 26 | that would change would be the knob. The understanding with the door part of the 27 | implementer class, strictly within the bridge pattern is that it will stay, more 28 | or less the same. The refined abstractions will have the ability to constantly 29 | extend the object. 30 | 31 | ##### What problems can the Bridge design pattern solve? 32 | * An abstraction and its implementation should be defined and extended 33 | independently from each other. 34 | ###### Minor problem 35 | * A compile-time binding between an abstraction and its implementation 36 | should be avoided so that an implementation can be selected at run-time. 37 | 38 | ###### What solution does the Bridge design pattern describe? 39 | * Separate an abstraction (Abstraction) from its implementation (Implementor) 40 | by putting them in separate class hierarchies. 41 | 42 | * Implement the `Abstraction` in terms of (by delegating to) an `Implementor` object. 43 | -------------------------------------------------------------------------------- /src/bridge/bridge.re: -------------------------------------------------------------------------------- 1 | class virtual entertainmentDeviceVirtual = { 2 | as self; 3 | val virtual volumeLevel: int; 4 | val virtual deviceState: int; 5 | val virtual maxSetting: int; 6 | 7 | pub virtual buttonFivePressed: unit; 8 | pub virtual buttonSixPressed: unit; 9 | pub device_feedback = () => { 10 | if(self#deviceState > self#maxSetting || self#deviceState < 0) { 11 | Js.log("works"); 12 | } 13 | }; 14 | pub buttonSevenPressed = () => { 15 | self#volumeLevel + 1;1 16 | }; 17 | pub buttonEightPressed = () => { 18 | self#volumeLevel - 1; 19 | }; 20 | }; 21 | 22 | class entertainmentDevice = { 23 | inherit class entertainmentDeviceVirtual; 24 | pub tVDevice(newDeviceState: int, newMaxSetting: int) { 25 | val deviceState = newDeviceState; 26 | val maxSetting = newMaxSetting; 27 | }; 28 | pub buttonFivePressed() { 29 | Js.log("Channel Down"); 30 | deviceState + 1; 31 | }; 32 | 33 | pub buttonSixPressed() { 34 | Js.log("Channel Up"); 35 | deviceState - 1; 36 | }; 37 | /* pub tVDevice = (newDeviceState:int, newMaxSetting:int) => { 38 | self#deviceState = newDeviceState; 39 | self#maxSetting = newMaxSetting; 40 | }; */ 41 | }; 42 | -------------------------------------------------------------------------------- /src/builder/builder.re: -------------------------------------------------------------------------------- 1 | module type User = { 2 | type t; 3 | let name: string; 4 | let age: int; 5 | let phone: string; 6 | let address: string; 7 | 8 | let create : (~name:string, ~age:int, ~phone:string, ~address:string) => t; 9 | }; 10 | 11 | module type UserBuilder = { 12 | type t; 13 | type user; 14 | 15 | let setName: (t,string) => unit; 16 | let getName: t => string; 17 | let setAge: (t, int) => unit; 18 | let getAge: t => int; 19 | let setPhone: (t, string) => unit; 20 | let getPhone: t => string; 21 | let setAddress: (t, string) => unit; 22 | let getAddress: t => string; 23 | }; 24 | 25 | module BuilderPattern = (User: User) => { 26 | type t = { 27 | mutable name: string, 28 | mutable age: int, 29 | mutable phone: string, 30 | mutable address: string 31 | }; 32 | type user = User.t; 33 | 34 | let setName = (builder, name) => builder.name = name; 35 | let getName = (builder) => builder.name; 36 | 37 | let setAge = (builder, age) => builder.age = age; 38 | let getAge = (builder) => builder.age; 39 | 40 | let setPhone = (builder, phone) => builder.phone = phone; 41 | let getPhone = (builder) => builder.phone; 42 | 43 | let setAddress = (builder, address) => builder.address = address; 44 | let getAddress = (builder) => builder.address; 45 | 46 | let getResult = (builder) => 47 | User.create(~name=builder.name, ~age=builder.age, 48 | ~phone=builder.phone, ~address=builder.address) 49 | }; 50 | 51 | module Director = (User: User, Builder: UserBuilder with type user = User.t) => { 52 | let construct = (builder) => { 53 | Builder.setName(builder, "Charles"); 54 | Builder.setAge(builder, 120); 55 | Builder.setPhone(builder, "(123) 456-7890"); 56 | Builder.setAddress(builder, "123 Fake St."); 57 | 58 | Js.log(Builder.getName(builder)); 59 | Js.log(Builder.getAge(builder)); 60 | Js.log(Builder.getPhone(builder)); 61 | Js.log(Builder.getAddress(builder)); 62 | }; 63 | }; 64 | 65 | module ConcreteUser = { 66 | type t = { 67 | mutable name: string, 68 | mutable age: int, 69 | mutable phone: string, 70 | mutable address: string 71 | }; 72 | let name:string = "Charles"; 73 | let age:int = 120; 74 | let phone:string = "(123) 456-7890"; 75 | let address:string = "123 Fake St."; 76 | 77 | let create = (~name:string, ~age:int, ~phone:string, ~address:string) => { 78 | name: name, 79 | age: age, 80 | phone: phone, 81 | address: address 82 | }; 83 | 84 | }; 85 | 86 | module MyBuilder = BuilderPattern(ConcreteUser); 87 | 88 | module DisplayName = Director(ConcreteUser, MyBuilder); 89 | 90 | DisplayName.construct({ 91 | name: "Henry", 92 | age: 200, 93 | phone: "words words", 94 | address: "address address" 95 | }); 96 | -------------------------------------------------------------------------------- /src/factoryMethod/factoryMethod.re: -------------------------------------------------------------------------------- 1 | class virtual abstractProduct = { 2 | pub virtual method1: string; 3 | }; 4 | 5 | class concreteProductA = { 6 | inherit class abstractProduct; 7 | pub method1 = "This is method1 of ConcreteProductA"; 8 | }; 9 | 10 | class concreteProductB = { 11 | inherit class abstractProduct; 12 | pub method1 = "This is method of ConcreteProductB"; 13 | }; 14 | 15 | class productFactory (productType: char) = { 16 | let message = (productType) => 17 | switch productType { 18 | | 'A' => new concreteProductA 19 | | 'B' => new concreteProductB 20 | }; 21 | pub createProduct = message(productType); 22 | }; 23 | -------------------------------------------------------------------------------- /src/factoryMethod/factoryMethodDemo.re: -------------------------------------------------------------------------------- 1 | let show = () => { 2 | let a: FactoryMethod.abstractProduct = (new FactoryMethod.productFactory)('A')#createProduct; 3 | let b: FactoryMethod.abstractProduct = (new FactoryMethod.productFactory)('B')#createProduct; 4 | Js.log(a#method1); 5 | Js.log(b#method1); 6 | }; 7 | 8 | show (); 9 | -------------------------------------------------------------------------------- /src/prototype/prototype.re: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlieGreenman/reason-oop-patterns/3e14862919ef71fbbb33419f92a6c417b51c8636/src/prototype/prototype.re -------------------------------------------------------------------------------- /src/singleton/README.md: -------------------------------------------------------------------------------- 1 | "This pattern appears as a central concept in ML’s module system. Modules act 2 | as global singletons that can provide all kinds of functionality. Functor 3 | instantiation is another good example how singletons appear in ML." - Taken from http://people.csail.mit.edu/dnj/teaching/6898/projects/vicente-wagner.pdf 4 | 5 | I wanted to stay true to how modules act as a global singleton, so I have 6 | modules to signify singletons in Reason/OCaml. 7 | -------------------------------------------------------------------------------- /src/singleton/singleton.re: -------------------------------------------------------------------------------- 1 | let fivePlusFive: int = 5 + 5; 2 | -------------------------------------------------------------------------------- /src/singleton/singletonCopy.re: -------------------------------------------------------------------------------- 1 | let fivePlusFive: int = 5 + 5; 2 | -------------------------------------------------------------------------------- /src/singleton/singletonDemo.re: -------------------------------------------------------------------------------- 1 | let exampleOne = Singleton.fivePlusFive; 2 | 3 | let exampleTwo = SingletonCopy.fivePlusFive; 4 | 5 | Js.log(exampleOne); 6 | 7 | Js.log(exampleTwo); 8 | -------------------------------------------------------------------------------- /tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "npm", 4 | "options": { 5 | "cwd": "${workspaceRoot}" 6 | }, 7 | "isShellCommand": true, 8 | "args": [ 9 | "run", 10 | "watch" 11 | ], 12 | "showOutput": "always", 13 | "isBackground": true, 14 | "problemMatcher": { 15 | "fileLocation": "absolute", 16 | "owner": "ocaml", 17 | "watching": { 18 | "activeOnStart": false, 19 | "beginsPattern": ">>>> Start compiling", 20 | "endsPattern": ">>>> Finish compiling" 21 | }, 22 | "pattern": [ 23 | { 24 | "regexp": "^File \"(.*)\", line (\\d+)(?:, characters (\\d+)-(\\d+))?:$", 25 | "file": 1, 26 | "line": 2, 27 | "column": 3, 28 | "endColumn": 4 29 | }, 30 | { 31 | "regexp": "^(?:(?:Parse\\s+)?(Warning|[Ee]rror)(?:\\s+\\d+)?:)?\\s+(.*)$", 32 | "severity": 1, 33 | "message": 2, 34 | "loop": true 35 | } 36 | ] 37 | } 38 | } --------------------------------------------------------------------------------