├── .gitignore ├── LICENSE ├── README.md ├── behavioral ├── chain-of-responsibility │ ├── README.md │ ├── etc │ │ ├── chain.mdj │ │ └── chain.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── chain │ │ │ ├── fp │ │ │ ├── App.scala │ │ │ ├── Request.scala │ │ │ ├── RequestHandler.scala │ │ │ ├── Response.scala │ │ │ └── SoftwareCompany.scala │ │ │ └── oo │ │ │ ├── App.scala │ │ │ ├── Request.scala │ │ │ ├── RequestHandler.scala │ │ │ └── SoftwareCompany.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── chain │ │ ├── fp │ │ └── FpChainSpec.scala │ │ └── oo │ │ └── OoChainSpec.scala ├── command │ ├── README.md │ ├── etc │ │ ├── command.mdj │ │ └── command.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── command │ │ │ ├── fp │ │ │ ├── App.scala │ │ │ ├── Command.scala │ │ │ ├── RemoteController.scala │ │ │ └── Television.scala │ │ │ └── oo │ │ │ ├── App.scala │ │ │ ├── Command.scala │ │ │ ├── RemoteController.scala │ │ │ └── Television.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── command │ │ ├── fp │ │ └── FpCommandSpec.scala │ │ └── oo │ │ └── OoCommandSpec.scala ├── loan │ ├── README.md │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── loan │ │ │ ├── App.scala │ │ │ └── using.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── loan │ │ └── LoanSpec.scala ├── mediator │ ├── README.md │ ├── etc │ │ ├── mediator.mdj │ │ └── mediator.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── mediator │ │ │ ├── App.scala │ │ │ ├── Country.scala │ │ │ └── UnitedNations.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── mediator │ │ └── MediatorSpec.scala ├── memento │ ├── README.md │ ├── etc │ │ ├── memento.mdj │ │ └── memento.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── memento │ │ │ ├── App.scala │ │ │ ├── Notebook.scala │ │ │ └── NotebookMemento.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── memento │ │ └── MementoSpec.scala ├── observer │ ├── README.md │ ├── etc │ │ ├── observer.mdj │ │ └── observer.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── observer │ │ │ ├── App.scala │ │ │ ├── Weather.scala │ │ │ ├── WeatherObserver.scala │ │ │ └── WeatherType.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── observer │ │ └── ObserverSpec.scala ├── state │ ├── README.md │ ├── etc │ │ ├── state.mdj │ │ └── state.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── state │ │ │ ├── App.scala │ │ │ ├── Keyboard.scala │ │ │ └── KeyboardState.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── state │ │ └── StateSpec.scala ├── strategy │ ├── README.md │ ├── etc │ │ ├── strategy.mdj │ │ └── strategy.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── strategy │ │ │ ├── fp │ │ │ ├── App.scala │ │ │ ├── SortStrategy.scala │ │ │ └── Sorter.scala │ │ │ └── oo │ │ │ ├── App.scala │ │ │ ├── SortStrategy.scala │ │ │ └── Sorter.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── strategy │ │ ├── fp │ │ └── FpStrategySpec.scala │ │ └── oo │ │ └── OoStrategySpec.scala ├── template-method │ ├── README.md │ ├── etc │ │ ├── template.mdj │ │ └── template.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── templatemethod │ │ │ ├── App.scala │ │ │ └── Customer.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── templatemethod │ │ └── TemplateSpec.scala └── visitor │ ├── README.md │ ├── etc │ ├── visitor.mdj │ └── visitor.png │ └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── visitor │ │ ├── fp │ │ ├── Animal.scala │ │ ├── AnimalOperation.scala │ │ ├── App.scala │ │ └── Zoo.scala │ │ └── oo │ │ ├── Animal.scala │ │ ├── AnimalOperation.scala │ │ ├── App.scala │ │ └── Zoo.scala │ └── test │ └── scala │ └── com │ └── gx │ └── visitor │ ├── fp │ └── FpVisitorSpec.scala │ └── oo │ └── OoVisitorSpec.scala ├── creational ├── abstract-factory │ ├── README.md │ ├── etc │ │ ├── abstract-factory.mdj │ │ └── abstract-factory.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── abstractfactory │ │ │ ├── App.scala │ │ │ ├── Car.scala │ │ │ ├── CarComponents.scala │ │ │ └── CarFactory.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── abstractfactory │ │ └── AbstractFactorySpec.scala ├── builder │ ├── README.md │ ├── etc │ │ ├── builder.mdj │ │ └── builder.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── builder │ │ │ ├── App.scala │ │ │ ├── Car.scala │ │ │ └── CarBuilder.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── builder │ │ └── BuilderSpec.scala ├── cake │ ├── README.md │ ├── etc │ │ ├── cake.mdj │ │ └── cake.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── cake │ │ │ ├── App.scala │ │ │ ├── Car.scala │ │ │ ├── CarComponentRegistry.scala │ │ │ └── CarComponents.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── cake │ │ └── CakeSpec.scala ├── factory-kit │ ├── README.md │ ├── etc │ │ ├── factory-kit.mdj │ │ └── factory-kit.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── factorykit │ │ │ ├── App.scala │ │ │ ├── Weapon.scala │ │ │ └── WeaponFactoryKit.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── factorykit │ │ └── FactoryKitSpec.scala ├── factory-method │ ├── README.md │ ├── etc │ │ ├── factory-method.mdj │ │ └── factory-method.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── factorymethod │ │ │ ├── App.scala │ │ │ ├── Chef.scala │ │ │ └── Noodle.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── factorymethod │ │ └── FactoryMethodSpec.scala ├── prototype │ ├── README.md │ ├── etc │ │ ├── prototype.mdj │ │ └── prototype.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── prototype │ │ │ ├── App.scala │ │ │ └── Prototype.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── prototype │ │ └── PrototypeSpec.scala ├── simple-factory │ ├── README.md │ ├── etc │ │ ├── simple-factory.mdj │ │ └── simple-factory.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── simplefactory │ │ │ ├── App.scala │ │ │ └── Operation.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── simplefactory │ │ └── SimpleFactorySpect.scala ├── singleton │ ├── README.md │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── singleton │ │ │ ├── App.scala │ │ │ └── Singleton.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── singleton │ │ └── SingletonSpec.scala └── value-object │ ├── README.md │ └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── valueobject │ │ ├── App.scala │ │ └── Point.scala │ └── test │ └── scala │ └── com │ └── gx │ └── valueobject │ └── ValueObjectSpec.scala ├── other ├── monad │ ├── README.md │ ├── etc │ │ ├── monad.mdj │ │ └── monad.png │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── gx │ │ │ └── monad │ │ │ ├── App.scala │ │ │ └── Validator.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── gx │ │ └── monad │ │ └── ValidatorSpec.scala └── selfless-trait │ ├── README.md │ ├── etc │ ├── selfless-trait.mdj │ └── selfless-trait.png │ └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── selflesstrait │ │ ├── App.scala │ │ ├── Friendly.scala │ │ └── Guy.scala │ └── test │ └── scala │ └── com │ └── gx │ └── selflesstrait │ └── SelflessTraitSpec.scala ├── persistence ├── data-access-object │ ├── README.md │ ├── etc │ │ ├── dao.mdj │ │ └── dao.png │ └── src │ │ └── main │ │ └── scala │ │ └── com │ │ └── gx │ │ └── dao │ │ ├── App.scala │ │ ├── Student.scala │ │ └── StudentDao.scala └── repository │ ├── README.md │ ├── etc │ ├── repository.mdj │ └── repository.png │ └── src │ └── main │ └── scala │ └── com │ └── gx │ └── repository │ ├── Account.scala │ ├── App.scala │ ├── Repository.scala │ └── Specification.scala ├── project ├── Builds.scala ├── Common.scala ├── Dependencies.scala ├── build.properties └── plugins.sbt └── structural ├── adapter ├── README.md ├── etc │ ├── adapter.mdj │ └── adapter.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── adapter │ │ ├── App.scala │ │ ├── Knife.scala │ │ ├── Soldier.scala │ │ ├── Weapon.scala │ │ └── package.scala │ └── test │ └── scala │ └── com │ └── gx │ └── adapter │ └── AdapterSepc.scala ├── bridge ├── README.md ├── etc │ ├── bridge.mdj │ └── bridge.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── bridge │ │ ├── App.scala │ │ ├── Theme.scala │ │ └── WebPage.scala │ └── test │ └── scala │ └── com │ └── gx │ └── bridge │ └── BridgeSpec.scala ├── composite ├── README.md ├── etc │ ├── composite.mdj │ └── composite.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── composite │ │ ├── Action.scala │ │ └── App.scala │ └── test │ └── scala │ └── com │ └── gx │ └── composite │ └── CompositeSpec.scala ├── decorator ├── README.md ├── etc │ ├── decorator.mdj │ └── decorator.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── decorator │ │ ├── App.scala │ │ └── Coffee.scala │ └── test │ └── scala │ └── com │ └── gx │ └── decorator │ └── DecoratorSpec.scala ├── facade ├── README.md ├── etc │ ├── facade.mdj │ └── facade.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── facade │ │ ├── App.scala │ │ ├── Computer.scala │ │ └── ComputerFacade.scala │ └── test │ └── scala │ └── com │ └── gx │ └── facade │ └── FacadeSpec.scala ├── flyweight ├── README.md ├── etc │ ├── flyweight.mdj │ └── flyweight.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── flyweight │ │ ├── App.scala │ │ ├── Tea.scala │ │ ├── TeaMaker.scala │ │ └── TeaShop.scala │ └── test │ └── scala │ └── com │ └── gx │ └── flyweight │ └── FlyweightSpec.scala ├── magnet ├── README.md ├── etc │ ├── magnet.mdj │ └── magnet.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── magnet │ │ ├── App.scala │ │ └── Doubling.scala │ └── test │ └── scala │ └── com │ └── gx │ └── magnet │ └── MagnetSpec.scala ├── proxy ├── README.md ├── etc │ ├── proxy.mdj │ └── proxy.png └── src │ ├── main │ └── scala │ │ └── com │ │ └── gx │ │ └── proxy │ │ ├── App.scala │ │ └── Computer.scala │ └── test │ └── scala │ └── com │ └── gx │ └── proxy │ └── ProxySpec.scala └── type-classes ├── README.md ├── etc ├── type-classes.mdj └── type-classes.png └── src ├── main └── scala │ └── com │ └── gx │ └── typeclasses │ ├── Animal.scala │ ├── App.scala │ ├── Human.scala │ └── Speakable.scala └── test └── scala └── com └── gx └── typeclasses └── TypeClassesSpec.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | dist/* 6 | target/ 7 | lib_managed/ 8 | src_managed/ 9 | project/boot/ 10 | project/plugins/project/ 11 | 12 | # Scala-IDE specific 13 | .scala_dependencies 14 | 15 | project/project/ 16 | project/target/ 17 | .idea* -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/etc/chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/chain-of-responsibility/etc/chain.png -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/fp/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * App is the client 22 | */ 23 | object App extends App { 24 | val company = new SoftwareCompany() 25 | 26 | company.handleRequest(FixBugRequest("bug")) 27 | company.handleRequest(FeatureRequest("feature")) 28 | company.handleRequest(ProductRequest("product")) 29 | company.handleRequest(FakeRequest("fake")) 30 | } 31 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/fp/Request.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | sealed abstract class Request 20 | 21 | case class FixBugRequest(desc: String) extends Request 22 | 23 | case class FeatureRequest(desc: String) extends Request 24 | 25 | case class ProductRequest(desc: String) extends Request 26 | 27 | case class FakeRequest(desc: String) extends Request 28 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/fp/RequestHandler.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object RequestHandler { 20 | // Handler Interface 21 | type Type = PartialFunction[Request, Response] 22 | 23 | // Concrete Handler 24 | val developer: RequestHandler.Type = { 25 | case req@FixBugRequest(desc) => 26 | println(s"I am a developer. I can fix this bug: $desc") 27 | Response(req, handled = true) 28 | } 29 | 30 | // Concrete Handler 31 | val architect: RequestHandler.Type = { 32 | case req@FeatureRequest(desc) => 33 | println(s"I am a architect. I can implement this feature: $desc") 34 | Response(req, handled = true) 35 | } 36 | 37 | // Concrete Handler 38 | val CTO: RequestHandler.Type = { 39 | case req@ProductRequest(desc) => 40 | println(s"I am a CTO. I can make this product: $desc") 41 | Response(req, handled = true) 42 | } 43 | 44 | // Concrete Handler 45 | val noOne: RequestHandler.Type = { 46 | case req: Request => 47 | println("No one is responsible for this request.") 48 | Response(req, handled = false) 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/fp/Response.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | case class Response(req: Request, handled: Boolean) 20 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/fp/SoftwareCompany.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * This is the client 22 | */ 23 | class SoftwareCompany { 24 | 25 | val chain: RequestHandler.Type = { 26 | RequestHandler.developer orElse RequestHandler.architect orElse RequestHandler.CTO orElse RequestHandler.noOne 27 | } 28 | 29 | def handleRequest(req: Request): Response = { 30 | chain(req) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/oo/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * App is the client 22 | */ 23 | object App extends App { 24 | 25 | val company = new SoftwareCompany() 26 | 27 | company.handleRequest(FixBugRequest("bug")) 28 | company.handleRequest(FeatureRequest("feature")) 29 | company.handleRequest(ProductRequest("product")) 30 | company.handleRequest(FakeRequest("fake")) 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/oo/Request.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | sealed trait Request { 21 | private var handled = false 22 | 23 | def markHandled(): Unit = { 24 | handled = true 25 | } 26 | 27 | def isHandled: Boolean = handled 28 | } 29 | 30 | case class FixBugRequest(desc: String) extends Request 31 | 32 | case class FeatureRequest(desc: String) extends Request 33 | 34 | case class ProductRequest(desc: String) extends Request 35 | 36 | case class FakeRequest(desc: String) extends Request 37 | 38 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/main/scala/com/gx/chain/oo/SoftwareCompany.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class SoftwareCompany { 20 | 21 | val chain: RequestHandler = { 22 | new Developer(new Architect(new CTO(null))) 23 | } 24 | 25 | def handleRequest(req: Request): Unit = { 26 | chain.handleRequest(req) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/test/scala/com/gx/chain/fp/FpChainSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.fp 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FpChainSpec extends FlatSpec with Matchers { 22 | 23 | it should "handle the requests" in { 24 | val company = new SoftwareCompany() 25 | 26 | val bug = FixBugRequest("a new bug") 27 | company.handleRequest(bug) should be(Response(bug, handled = true)) 28 | 29 | val feature = FeatureRequest("a new feature") 30 | company.handleRequest(feature) should be(Response(feature, handled = true)) 31 | 32 | } 33 | 34 | it should "not handle the request" in { 35 | val company = new SoftwareCompany() 36 | 37 | val fake = FakeRequest("just a kidding") 38 | company.handleRequest(fake) should be(Response(fake, handled = false)) 39 | 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility/src/test/scala/com/gx/chain/oo/OoChainSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.chain.oo 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class OoChainSpec extends FlatSpec with Matchers { 22 | 23 | it should "handle the requests" in { 24 | val company = new SoftwareCompany() 25 | 26 | val bug = FixBugRequest("a new bug") 27 | company.handleRequest(bug) 28 | bug.isHandled should be(true) 29 | 30 | val feature = FeatureRequest("a new feature") 31 | company.handleRequest(feature) 32 | feature.isHandled should be(true) 33 | 34 | } 35 | 36 | it should "not handle the request" in { 37 | val company = new SoftwareCompany() 38 | 39 | val fake = FakeRequest("just a kidding") 40 | company.handleRequest(fake) 41 | fake.isHandled should be(false) 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /behavioral/command/etc/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/command/etc/command.png -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/fp/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * This is an implementation of Command Pattern in functional programming style. The differences between 22 | * object oriented style and this are: 23 | * 1. Receiver(Television) is an invariable object. 24 | * 2. Command is actual a function: (Television) => Television. Every command has return value. It generates 25 | * a new 'Television' every time. 26 | * 3. Invoker(RemoteController) executes command in lazy mode. 27 | * 28 | */ 29 | 30 | 31 | /** 32 | * App is the client 33 | */ 34 | object App extends App { 35 | 36 | val remote = new RemoteController(Television()) 37 | remote.invoke(Command.setChannel(1)) 38 | remote.invoke(Command.setChannel(5)) 39 | remote.invoke(Command.setVolume(3)) 40 | remote.invoke(Command.setVolume(20)) 41 | println(remote.get()) 42 | remote.undo() 43 | remote.undo() 44 | remote.undo() 45 | remote.undo() 46 | remote.undo() 47 | remote.undo() 48 | println(remote.get()) 49 | remote.redo() 50 | remote.redo() 51 | remote.redo() 52 | println(remote.get()) 53 | 54 | } 55 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/fp/Command.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * All commands are here. 22 | * 23 | */ 24 | object Command { 25 | // In functional programing, the command is actually a function. 26 | type Type = (Television) => Television 27 | 28 | // a concrete command for setting channel 29 | def setChannel(channel: Int)(tv: Television): Television = { 30 | Television(channel, tv.volume) 31 | } 32 | 33 | // a concrete command for setting volume 34 | def setVolume(volume: Int)(tv: Television): Television = { 35 | Television(tv.channel, volume) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/fp/RemoteController.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.fp 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | /** 23 | * RemoteController is the invoker. 24 | * 25 | */ 26 | class RemoteController(tv: Television) { 27 | private val historyStack = mutable.ArrayStack[Command.Type]() 28 | private val redoStack = mutable.ArrayStack[Command.Type]() 29 | 30 | def invoke(cmd: Command.Type): Unit = { 31 | historyStack.push(cmd) 32 | } 33 | 34 | def undo(): Unit = { 35 | if (historyStack.nonEmpty) { 36 | val cmd = historyStack.pop() 37 | redoStack.push(cmd) 38 | } 39 | } 40 | 41 | def redo(): Unit = { 42 | if (redoStack.nonEmpty) { 43 | val cmd = redoStack.pop() 44 | historyStack.push(cmd) 45 | } 46 | } 47 | 48 | def get(): Television = { 49 | historyStack.foldRight(tv)((cmd, tv) => cmd(tv)) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/fp/Television.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * Television is the receiver 22 | * 23 | */ 24 | case class Television(channel: Int = 0, volume: Int = 0) 25 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/oo/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | 21 | /** 22 | * App is the client 23 | * 24 | */ 25 | object App extends App { 26 | val tv = new Television() 27 | val remote = new RemoteController() 28 | 29 | remote.invoke(new SetChannelCommand(1), tv) 30 | remote.invoke(new SetChannelCommand(5), tv) 31 | remote.invoke(new SetVolumeCommand(3), tv) 32 | remote.invoke(new SetVolumeCommand(20), tv) 33 | tv.show() 34 | 35 | remote.undo() 36 | remote.undo() 37 | remote.undo() 38 | remote.undo() 39 | remote.undo() 40 | remote.undo() 41 | tv.show() 42 | 43 | remote.redo() 44 | remote.redo() 45 | remote.redo() 46 | tv.show() 47 | 48 | } 49 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/oo/RemoteController.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.oo 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | /** 23 | * RemoteController is the invoker. 24 | * 25 | */ 26 | class RemoteController { 27 | private val undoStack = mutable.ArrayStack[Command]() 28 | private val redoStack = mutable.ArrayStack[Command]() 29 | 30 | def invoke(cmd: Command, tv: Television): Unit = { 31 | cmd.execute(tv) 32 | undoStack.push(cmd) 33 | redoStack.clear() 34 | } 35 | 36 | def undo(): Unit = { 37 | if (undoStack.nonEmpty) { 38 | val cmd = undoStack.pop() 39 | redoStack.push(cmd) 40 | cmd.undo() 41 | } 42 | } 43 | 44 | def redo(): Unit = { 45 | if (redoStack.nonEmpty) { 46 | val cmd = redoStack.pop() 47 | undoStack.push(cmd) 48 | cmd.redo() 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /behavioral/command/src/main/scala/com/gx/command/oo/Television.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * Television is the receiver 22 | * 23 | */ 24 | class Television { 25 | var channel: Int = 0 26 | var volume: Int = 0 27 | 28 | def show(): Unit = { 29 | println(s"tv channel = $channel, volume = $volume") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /behavioral/command/src/test/scala/com/gx/command/fp/FpCommandSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.command.fp 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FpCommandSpec extends FlatSpec with Matchers { 22 | 23 | it should "change channel and volume according to commands" in { 24 | val remoter = new RemoteController(Television()) 25 | 26 | remoter.invoke(Command.setChannel(5)) 27 | remoter.invoke(Command.setVolume(10)) 28 | remoter.get() should be(Television(5, 10)) 29 | } 30 | 31 | it should "undo previous commands" in { 32 | val remoter = new RemoteController(Television()) 33 | 34 | remoter.invoke(Command.setChannel(5)) 35 | remoter.invoke(Command.setVolume(10)) 36 | remoter.undo() 37 | remoter.get() should be(Television(5, 0)) 38 | remoter.undo() 39 | remoter.get() should be(Television(0, 0)) 40 | remoter.undo() 41 | remoter.get() should be(Television(0, 0)) 42 | } 43 | 44 | it should "redo previous commands" in { 45 | val remoter = new RemoteController(Television()) 46 | 47 | remoter.invoke(Command.setChannel(5)) 48 | remoter.invoke(Command.setVolume(10)) 49 | remoter.undo() 50 | remoter.undo() 51 | remoter.undo() 52 | remoter.redo() 53 | remoter.get() should be(Television(5, 0)) 54 | remoter.redo() 55 | remoter.get() should be(Television(5, 10)) 56 | remoter.redo() 57 | remoter.get() should be(Television(5, 10)) 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /behavioral/loan/README.md: -------------------------------------------------------------------------------- 1 | # Loan Pattern 2 | 3 | 4 | ## Intent 5 | Loan Pattern as the name suggests would loan a resource to your function. So if you break out the sentence. It would 6 | 7 | 1. Create a resource which you can use 8 | 2. Loan the resources to the function which would use it 9 | 3. This function would be passed by the caller 10 | 4. The resource would be destroyed 11 | 12 | 13 | ## Applicability 14 | As you would see, the advantages are multifold. 15 | * First, I am not constrained by the function which can use the loaned resource. I can pass any function that I desire. 16 | * Second, I am not concerned about the creation, destruction of the resource. The loan function takes care of it. 17 | 18 | 19 | ## Structure 20 | N/A 21 | 22 | 23 | ## Participants 24 | N/A 25 | 26 | 27 | ## Example 28 | In this example, the ```using``` loan a resource to a function and close the resource after the function finished using it. 29 | 30 | 31 | ## Scala Tips 32 | Scala offers a functionality known as **Structural Types** which allows to set a behaviour 33 | very similar to what dynamic languages allow to do when they support [Duck Typing](http://en.wikipedia.org/wiki/Duck_typing). 34 | 35 | 36 | ## Reference 37 | [Understanding Loan Pattern](https://blog.knoldus.com/2012/11/16/scalaknol-understanding-loan-pattern/) 38 | -------------------------------------------------------------------------------- /behavioral/loan/src/main/scala/com/gx/loan/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.loan 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | using(new FakeFile()) { file => 21 | println(file.content) 22 | } 23 | } 24 | 25 | class FakeFile { 26 | def close(): Unit = { 27 | println("closing fake file.") 28 | } 29 | 30 | def content: String = { 31 | "This is a Fake File." 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /behavioral/loan/src/main/scala/com/gx/loan/using.scala: -------------------------------------------------------------------------------- 1 | package com.gx.loan 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object using { 20 | def apply[R <: {def close() : Unit}, T](resource: => R)(f: R => T): T = { 21 | val source = Option(resource) 22 | try { 23 | f(source.get) 24 | } finally { 25 | for (s <- source) 26 | s.close() 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /behavioral/loan/src/test/scala/com/gx/loan/LoanSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.loan 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class LoanSpec extends FlatSpec with Matchers { 24 | 25 | it should "close the resource after using it" in { 26 | using(new ByteArrayOutputStream()) { buffer => 27 | 28 | Console.withOut(buffer) { 29 | using(new FakeFile()) { file => 30 | file.content should be("This is a Fake File.") 31 | } 32 | } 33 | 34 | buffer.toString should be("closing fake file.\r\n") 35 | } 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /behavioral/mediator/README.md: -------------------------------------------------------------------------------- 1 | # Mediator Pattern 2 | 3 | 4 | ## Intent 5 | Define an object that encapsulates how a set of objects interact.Mediator promotes 6 | loose coupling by keeping objects from referring toeach other explicitly, and 7 | it lets you vary their interaction independently. 8 | 9 | 10 | ## Applicability 11 | Use the Mediator pattern when 12 | * a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand. 13 | * reusing an object is difficult because it refers to and communicates with many other objects. 14 | * a behavior that's distributed between several classes should be customizable without a lot of subclassing. 15 | 16 | 17 | ## Structure 18 | ![mediator](./etc/mediator.png) 19 | 20 | 21 | ## Participants 22 | * **Mediator** 23 | - defines an interface for communicating with Colleague objects. 24 | * **ConcreteMediator** 25 | - implements cooperative behavior by coordinating Colleague objects. 26 | - knows and maintains its colleagues. 27 | * **Colleague** (optional) 28 | - defines an interface for services provided by colleagues objects. 29 | * **ConcreteColleague** 30 | - each Colleague class knows its Mediator object. 31 | - each colleague communicates with its mediator whenever it would have otherwise communicated with another colleague. 32 | 33 | 34 | ## Example 35 | Countries joined in the United Nations can declare and receive messages. The United Nations works as a mediator 36 | which implements the cooperative behavior by routing messages between the appropriate countries. 37 | 38 | Participants in this example: 39 | * Organization is the **Mediator**. 40 | * UnitedNations is the **ConcreteMediator**. 41 | * Country is the **Colleague**. 42 | * Canada/China/USA are the **ConcreteColleague** classes. 43 | 44 | 45 | ## Scala Tips 46 | N/A 47 | 48 | 49 | ## Reference 50 | * Design Patterns: Elements of Reusable Object-Oriented Software -------------------------------------------------------------------------------- /behavioral/mediator/etc/mediator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/mediator/etc/mediator.png -------------------------------------------------------------------------------- /behavioral/mediator/src/main/scala/com/gx/mediator/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.mediator 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val china = new China 22 | val usa = new USA 23 | val canada = new Canada 24 | 25 | val united = new UnitedNations 26 | united.addMember(china) 27 | united.addMember(usa) 28 | united.addMember(canada) 29 | 30 | usa.declare("Hello World") 31 | 32 | } 33 | -------------------------------------------------------------------------------- /behavioral/mediator/src/main/scala/com/gx/mediator/Country.scala: -------------------------------------------------------------------------------- 1 | package com.gx.mediator 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | abstract class Country(val name: String) { 21 | protected var organization: Organization = _ 22 | 23 | def join(org: Organization): Unit = { 24 | organization = org 25 | } 26 | 27 | def declare(msg: String): Unit = { 28 | organization.countryDeclare(this, msg) 29 | } 30 | 31 | def receive(msg: String): Unit = { 32 | println(s"$name received: '$msg'") 33 | } 34 | } 35 | 36 | class USA extends Country("USA") 37 | 38 | class China extends Country("China") 39 | 40 | class Canada extends Country("Canada") 41 | 42 | 43 | -------------------------------------------------------------------------------- /behavioral/mediator/src/main/scala/com/gx/mediator/UnitedNations.scala: -------------------------------------------------------------------------------- 1 | package com.gx.mediator 2 | 3 | import scala.collection.mutable.ListBuffer 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | trait Organization { 23 | def countryDeclare(country: Country, msg: String): Unit 24 | } 25 | 26 | 27 | class UnitedNations extends Organization { 28 | val countries = new ListBuffer[Country]() 29 | 30 | def addMember(country: Country): Unit = { 31 | country.join(this) 32 | countries.append(country) 33 | } 34 | 35 | override def countryDeclare(country: Country, msg: String): Unit = { 36 | println(s"${country.name} declared: '$msg'") 37 | for (c <- countries if c != country) { 38 | c.receive(msg) 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /behavioral/mediator/src/test/scala/com/gx/mediator/MediatorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.mediator 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class MediatorSpec extends FlatSpec with Matchers { 24 | 25 | it should "receive message from other countries" in { 26 | val china = new China 27 | val usa = new USA 28 | val canada = new Canada 29 | 30 | // united nations organization 31 | val united = new UnitedNations 32 | united.addMember(china) 33 | united.addMember(usa) 34 | united.addMember(canada) 35 | 36 | val buffer = new ByteArrayOutputStream() 37 | Console.withOut(buffer) { 38 | china.declare("Hello World") 39 | } 40 | 41 | val msg = buffer.toString.split("\r\n") 42 | msg(1) should be("USA received: 'Hello World'") 43 | msg(2) should be("Canada received: 'Hello World'") 44 | 45 | 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /behavioral/memento/README.md: -------------------------------------------------------------------------------- 1 | # Memento Pattern 2 | 3 | 4 | ## Intent 5 | Without violating encapsulation, capture and externalize an object's internal 6 | state so that the object can be restored to this state later. 7 | 8 | 9 | ## Applicability 10 | Use the Memento pattern when 11 | * a snapshot of (some portion of) an object's state must be saved so that it can be restored to that state later, and 12 | * a direct interface to obtaining the state would expose implementation details and break the object's encapsulation. 13 | 14 | 15 | ## Structure 16 | ![memento](./etc/memento.png) 17 | 18 | 19 | ## Participants 20 | * **Memento** 21 | - stores internal state of the Originator object. 22 | - protects against access by objects other than the originator. 23 | * **Originator** 24 | - creates a memento containing a snapshot of its current internal state. 25 | - uses the memento to restore its internal state. 26 | * **Caretaker** 27 | - is responsible for the memento's safekeeping. 28 | - never operates on or examines the contents of a memento. 29 | 30 | 31 | ## Example 32 | A NotebookMemento is a snapshot of a Notebook. Notebook can restore its state and provide undo operation according to the NotebookMemento. 33 | 34 | Participants in this example: 35 | * Notebook is the **Originator**. 36 | * NotebookMemento is the **Memento**, and NotebookMementoInternal is the **ConcreteMemento**. 37 | * App is the **Caretaker**. 38 | 39 | 40 | ## Scala Tips 41 | N/A 42 | 43 | 44 | ## Reference 45 | * Design Patterns: Elements of Reusable Object-Oriented Software 46 | -------------------------------------------------------------------------------- /behavioral/memento/etc/memento.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/memento/etc/memento.png -------------------------------------------------------------------------------- /behavioral/memento/src/main/scala/com/gx/memento/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.memento 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | object App extends App { 22 | 23 | val mementoStack = mutable.Stack[NotebookMemento]() 24 | val notebook = new Notebook 25 | 26 | notebook.write("I have a dream... ") 27 | mementoStack.push(notebook.getMemento) 28 | notebook.show() 29 | 30 | notebook.write("I have a dream that one day ") 31 | mementoStack.push(notebook.getMemento) 32 | notebook.show() 33 | 34 | notebook.write("black boys and black girls will be able to join hands ") 35 | mementoStack.push(notebook.getMemento) 36 | notebook.show() 37 | 38 | notebook.write("with white boys and white girls as sisters and brothers.") 39 | mementoStack.push(notebook.getMemento) 40 | notebook.show() 41 | 42 | notebook.setMemento(mementoStack.pop()) 43 | notebook.show() 44 | 45 | notebook.setMemento(mementoStack.pop()) 46 | notebook.show() 47 | 48 | notebook.setMemento(mementoStack.pop()) 49 | notebook.show() 50 | 51 | notebook.setMemento(mementoStack.pop()) 52 | notebook.show() 53 | 54 | } 55 | -------------------------------------------------------------------------------- /behavioral/memento/src/main/scala/com/gx/memento/Notebook.scala: -------------------------------------------------------------------------------- 1 | package com.gx.memento 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Notebook { 20 | private var content = new StringBuffer() 21 | 22 | def getMemento: NotebookMemento = { 23 | NotebookMementoInternal(content.toString) 24 | } 25 | 26 | def setMemento(memento: NotebookMemento): Unit = memento match { 27 | case NotebookMementoInternal(state) => content = new StringBuffer(state) 28 | case _ => throw new IllegalArgumentException() 29 | } 30 | 31 | def write(words: String): Unit = { 32 | content.append(words) 33 | } 34 | 35 | def show(): Unit = { 36 | println(content) 37 | } 38 | 39 | private case class NotebookMementoInternal(content: String) extends NotebookMemento 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /behavioral/memento/src/main/scala/com/gx/memento/NotebookMemento.scala: -------------------------------------------------------------------------------- 1 | package com.gx.memento 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait NotebookMemento 20 | -------------------------------------------------------------------------------- /behavioral/memento/src/test/scala/com/gx/memento/MementoSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.memento 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | import scala.collection.mutable 8 | 9 | /** 10 | * Copyright 2018 josephguan 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | * 24 | */ 25 | class MementoSpec extends FlatSpec with Matchers { 26 | 27 | it should "undo the last typing" in { 28 | val notebook = new Notebook 29 | notebook.write("Hello World!") 30 | 31 | val memento = notebook.getMemento 32 | notebook.write("I want to undo these words...") 33 | notebook.setMemento(memento) 34 | 35 | val buffer = new ByteArrayOutputStream() 36 | Console.withOut(buffer) { 37 | notebook.show() 38 | } 39 | buffer.toString() should be ("Hello World!\r\n") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /behavioral/observer/README.md: -------------------------------------------------------------------------------- 1 | # Observer Pattern 2 | 3 | 4 | ## Intent 5 | Define a one-to-many dependency between objects so that when one object changes 6 | state, all its dependents are notified and updated automatically. 7 | 8 | 9 | ## Applicability 10 | Use the Observer pattern in any of the following situations: 11 | * When an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently. 12 | * When a change to one object requires changing others, and you don't know how many objects need to be changed. 13 | * When an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled. 14 | 15 | 16 | ## Structure 17 | ![observer](./etc/observer.png) 18 | 19 | 20 | ## Participants 21 | * **Subject** 22 | - knows its observers. Any number of Observer objects may observe a subject. 23 | - provides an interface for attaching and detaching Observer objects. 24 | * **Observer** 25 | - defines an updating interface for objects that should be notified of changes in a subject. 26 | * **ConcreteSubject** 27 | - stores state of interest to ConcreteObserver objects. 28 | - sends a notification to its observers when its state changes. 29 | * **ConcreteObserver** 30 | - maintains a reference to a ConcreteSubject object. 31 | - stores state that should stay consistent with the subject's. 32 | - implements the Observer updating interface to keep its state consistent with the subject's. 33 | 34 | 35 | ## Example 36 | Imagine the weather is changing, what will the boys and girls do? 37 | 38 | Participants in this example: 39 | * Weather is the **Subject / ConcreteSubject**. 40 | * WeatherObserver is the **Observer**. 41 | * Boy/Girl is the **ConcreteObserver**. 42 | 43 | 44 | ## Scala Tips 45 | N/A 46 | 47 | 48 | ## Reference 49 | * Design Patterns: Elements of Reusable Object-Oriented Software 50 | 51 | -------------------------------------------------------------------------------- /behavioral/observer/etc/observer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/observer/etc/observer.png -------------------------------------------------------------------------------- /behavioral/observer/src/main/scala/com/gx/observer/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.observer 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App{ 20 | val weather = new Weather() 21 | weather.addObserver(new Boy) 22 | weather.addObserver(new Girl) 23 | weather.changing(WeatherType.COLD) 24 | weather.changing(WeatherType.RAINY) 25 | weather.changing(WeatherType.SUNNY) 26 | } 27 | -------------------------------------------------------------------------------- /behavioral/observer/src/main/scala/com/gx/observer/Weather.scala: -------------------------------------------------------------------------------- 1 | package com.gx.observer 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class Weather { 22 | val observers = mutable.ListBuffer[WeatherObserver]() 23 | var currentWeather: WeatherType = WeatherType.SUNNY 24 | 25 | def addObserver(ob: WeatherObserver): Unit = { 26 | observers.append(ob) 27 | } 28 | 29 | def removeObserver(ob: WeatherObserver): Unit = { 30 | val index = observers.indexOf(ob) 31 | if (index != -1) { 32 | observers.remove(index) 33 | } 34 | } 35 | 36 | def changing(weather: WeatherType): Unit = { 37 | currentWeather = weather 38 | notifyObservers() 39 | } 40 | 41 | def notifyObservers(): Unit = { 42 | observers.foreach { ob => 43 | ob.update(currentWeather) 44 | } 45 | } 46 | 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /behavioral/observer/src/main/scala/com/gx/observer/WeatherObserver.scala: -------------------------------------------------------------------------------- 1 | package com.gx.observer 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait WeatherObserver { 20 | def update(currentWeather: WeatherType) 21 | } 22 | 23 | class Boy extends WeatherObserver { 24 | override def update(currentWeather: WeatherType): Unit = currentWeather match { 25 | case WeatherType.SUNNY => println("It is sunny. I can go to play football.") 26 | case WeatherType.RAINY => println("It is rainy. I can play video games at home.") 27 | case WeatherType.COLD => println("It is cold. It is a nice for sleeping.") 28 | } 29 | } 30 | 31 | class Girl extends WeatherObserver { 32 | override def update(currentWeather: WeatherType): Unit = currentWeather match { 33 | case WeatherType.SUNNY => println("It is sunny. I can go shopping.") 34 | case WeatherType.RAINY => println("It is rainy. I should take an umbrella and go shopping.") 35 | case WeatherType.COLD => println("It is cold. I should go to buy some clothes.") 36 | } 37 | } -------------------------------------------------------------------------------- /behavioral/observer/src/main/scala/com/gx/observer/WeatherType.scala: -------------------------------------------------------------------------------- 1 | package com.gx.observer 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait WeatherType 20 | 21 | object WeatherType { 22 | 23 | case object SUNNY extends WeatherType 24 | 25 | case object RAINY extends WeatherType 26 | 27 | case object COLD extends WeatherType 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /behavioral/observer/src/test/scala/com/gx/observer/ObserverSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.observer 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class ObserverSpec extends FlatSpec with Matchers { 24 | 25 | it should "do something while sunny" in { 26 | val weather = new Weather() 27 | weather.addObserver(new Boy) 28 | weather.addObserver(new Girl) 29 | 30 | val buffer = new ByteArrayOutputStream() 31 | Console.withOut(buffer) { 32 | weather.changing(WeatherType.SUNNY) 33 | } 34 | 35 | val lines = buffer.toString().split("\r\n").toList 36 | lines.head should be ("It is sunny. I can go to play football.") 37 | lines.last should be ("It is sunny. I can go shopping.") 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /behavioral/state/README.md: -------------------------------------------------------------------------------- 1 | # State Pattern 2 | 3 | 4 | ## Intent 5 | Allow an object to alter its behavior when its internal state changes. 6 | The object will appear to change its class. 7 | 8 | 9 | ## Applicability 10 | Use the State pattern in either of the following cases: 11 | * An object's behavior depends on its state, and it must change its behaviorat run-time depending on that state. 12 | * Operations have large, multipart conditional statements that depend on the 13 | object's state. This state is usually represented by one or more enumerated 14 | constants. Often, several operations will contain this same conditional 15 | structure. The State pattern puts each branch of the conditional in a 16 | separate class. This lets you treat the object's state as an object in its 17 | own right that can vary independently from other objects. 18 | 19 | 20 | ## Structure 21 | ![state](./etc/state.png) 22 | 23 | 24 | ## Participants 25 | * **Context** 26 | - defines the interface of interest to clients. 27 | - maintains an instance of a ConcreteState subclass that defines the current state. 28 | * **State** 29 | - defines an interface for encapsulating the behavior associated with a particular state of the Context. 30 | * **ConcreteState** 31 | - each subclass implements a behavior associated with a state ofthe 32 | 33 | 34 | ## Example 35 | As you known, 'Caps Lock' button can change the behavior of keyboards. While it is turned on, 36 | all output characters are in upper case. In the other side, while it is turned off, all output 37 | characters are in lower case. Let's see how we model it! 38 | 39 | Participants in this example: 40 | * Keyboard is the **Context**. 41 | * KeyboardState is the **State**. 42 | * UpperCaseState/LowerCaseState is the **ConcreteState**. 43 | 44 | 45 | ## Scala Tips 46 | N/A 47 | 48 | 49 | ## Reference 50 | * Design Patterns: Elements of Reusable Object-Oriented Software 51 | 52 | -------------------------------------------------------------------------------- /behavioral/state/etc/state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/state/etc/state.png -------------------------------------------------------------------------------- /behavioral/state/src/main/scala/com/gx/state/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.state 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val keyboard = new Keyboard() 21 | keyboard.write("hello world") 22 | keyboard.pressCapsLock() 23 | keyboard.write("hello world") 24 | keyboard.pressCapsLock() 25 | keyboard.write("hello world") 26 | } 27 | -------------------------------------------------------------------------------- /behavioral/state/src/main/scala/com/gx/state/Keyboard.scala: -------------------------------------------------------------------------------- 1 | package com.gx.state 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Keyboard { 20 | var state: KeyboardState = new LowerCaseState() 21 | 22 | def write(word: String): Unit = { 23 | state.write(word) 24 | } 25 | 26 | def pressCapsLock(): Unit = state match { 27 | case lower: LowerCaseState => state = new UpperCaseState 28 | case upper: UpperCaseState => state = new LowerCaseState 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /behavioral/state/src/main/scala/com/gx/state/KeyboardState.scala: -------------------------------------------------------------------------------- 1 | package com.gx.state 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait KeyboardState { 20 | def write(word: String): Unit 21 | } 22 | 23 | class UpperCaseState extends KeyboardState { 24 | override def write(word: String): Unit = { 25 | println(word.toUpperCase) 26 | } 27 | } 28 | 29 | class LowerCaseState extends KeyboardState { 30 | override def write(word: String): Unit = { 31 | println(word.toLowerCase) 32 | } 33 | } -------------------------------------------------------------------------------- /behavioral/state/src/test/scala/com/gx/state/StateSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.state 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class StateSpec extends FlatSpec with Matchers { 24 | 25 | it should "output characters according to state" in { 26 | val keyboard = new Keyboard() 27 | 28 | val buffer = new ByteArrayOutputStream() 29 | Console.withOut(buffer) { 30 | keyboard.pressCapsLock() 31 | keyboard.write("hello world") 32 | keyboard.pressCapsLock() 33 | keyboard.write("hello world") 34 | } 35 | 36 | val lines = buffer.toString.split("\r\n") 37 | lines.head should be ("HELLO WORLD") 38 | lines.last should be ("hello world") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /behavioral/strategy/README.md: -------------------------------------------------------------------------------- 1 | # Strategy Pattern 2 | 3 | 4 | ## Intent 5 | Define a family of algorithms, encapsulate each one, and make them interchangeable. 6 | Strategy lets the algorithm vary independently from clients that use it. 7 | 8 | 9 | ## Applicability 10 | Use the Strategy pattern when 11 | * many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors. 12 | * you need different variants of an algorithm. 13 | * an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures. 14 | * a class defines many behaviors, and these appear as multiple conditional 15 | statements in its operations. Instead of many conditionals, move related 16 | conditional branches into their ownStrategy class. 17 | 18 | 19 | ## Structure 20 | ![strategy](./etc/strategy.png) 21 | 22 | 23 | ## Participants 24 | * **Strategy** 25 | - declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy. 26 | * **ConcreteStrategy** 27 | - implements the algorithm using the Strategy interface. 28 | * **Context** 29 | - is configured with a ConcreteStrategy object. 30 | - maintains a reference to a Strategy object. 31 | - may define an interface that lets Strategy access its data. 32 | 33 | 34 | ## Example 35 | This example demonstrates a Sorter that can sort an array of data and choose which specific sort algorithm (quick-sort or bubble-sort) is used. 36 | 37 | Participants in this example: 38 | * Sorter is the **Context**. 39 | * SortStrategy is the **Strategy**. 40 | * QuickSortStrategy/BubbleSortStrategy is the **ConcreteStrategy**. 41 | 42 | 43 | ## Scala Tips 44 | * You can implement strategy in functional programming style. 45 | 46 | 47 | ## Reference 48 | * Design Patterns: Elements of Reusable Object-Oriented Software 49 | 50 | -------------------------------------------------------------------------------- /behavioral/strategy/etc/strategy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/strategy/etc/strategy.png -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/fp/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val sorter = new Sorter(SortStrategy.bubbleSortStrategy[Int]) 22 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 23 | println(sortedList) 24 | } 25 | -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/fp/SortStrategy.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object SortStrategy { 20 | type Type[U] = List[U] => List[U] 21 | 22 | def bubbleSortStrategy[U](dataset: List[U])(implicit ord: Ordering[U]): List[U] = { 23 | println("sorting using bubble sort") 24 | dataset.sorted 25 | } 26 | 27 | def quickSortStrategy[U](dataset: List[U])(implicit ord: Ordering[U]): List[U] = { 28 | println("sorting using quick sort") 29 | dataset.sorted 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/fp/Sorter.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Sorter[T](strategy: SortStrategy.Type[T]) { 20 | def sort(dataset: List[T])(implicit ord: Ordering[T]): List[T] = { 21 | strategy(dataset) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/oo/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val sorter = new Sorter(new QuickSortStrategy) 22 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 23 | println(sortedList) 24 | 25 | // case class Box(i: Int) extends Ordered[Box] { 26 | // override def compare(that: Box): Int = i - that.i 27 | // } 28 | // println(sorter.sort(List(Box(1), Box(3), Box(2)))) 29 | } 30 | -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/oo/SortStrategy.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.oo 2 | 3 | import scala.math.Ordering 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | trait SortStrategy { 22 | def sort[T](dataset: List[T])(implicit ord: Ordering[T]): List[T] 23 | } 24 | 25 | class BubbleSortStrategy extends SortStrategy { 26 | override def sort[T](dataset: List[T])(implicit ord: Ordering[T]): List[T] = { 27 | println("sorting using bubble sort") 28 | dataset.sorted 29 | } 30 | } 31 | 32 | class QuickSortStrategy extends SortStrategy { 33 | override def sort[T](dataset: List[T])(implicit ord: Ordering[T]): List[T] = { 34 | println("sorting using quick sort") 35 | dataset.sorted 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /behavioral/strategy/src/main/scala/com/gx/strategy/oo/Sorter.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Sorter(strategy: SortStrategy) { 20 | def sort[T](dataset: List[T])(implicit ord: Ordering[T]): List[T] = { 21 | strategy.sort(dataset) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/strategy/src/test/scala/com/gx/strategy/fp/FpStrategySpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.fp 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2018 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FpStrategySpec extends FlatSpec with Matchers { 22 | 23 | it should "sort the array with quick sort algorithm " in { 24 | val sorter = new Sorter(SortStrategy.quickSortStrategy[Int]) 25 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 26 | sortedList should be(List(1, 2, 3, 4, 5)) 27 | } 28 | 29 | it should "sort the array with bubble sort algorithm " in { 30 | val sorter = new Sorter(SortStrategy.bubbleSortStrategy[Int]) 31 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 32 | sortedList should be(List(1, 2, 3, 4, 5)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /behavioral/strategy/src/test/scala/com/gx/strategy/oo/OoStrategySpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.strategy.oo 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2018 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class OoStrategySpec extends FlatSpec with Matchers { 22 | 23 | it should "sort the array with quick sort algorithm " in { 24 | val sorter = new Sorter(new QuickSortStrategy) 25 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 26 | sortedList should be(List(1, 2, 3, 4, 5)) 27 | } 28 | 29 | it should "sort the array with bubble sort algorithm " in { 30 | val sorter = new Sorter(new BubbleSortStrategy) 31 | val sortedList = sorter.sort(List(5, 4, 3, 2, 1)) 32 | sortedList should be(List(1, 2, 3, 4, 5)) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /behavioral/template-method/README.md: -------------------------------------------------------------------------------- 1 | # Template Method Pattern 2 | 3 | 4 | ## Intent 5 | Define the skeleton of an algorithm in an operation, deferring some steps to 6 | subclasses. Template Method lets subclasses redefinecertain steps of an algorithm 7 | without changing the algorithm's structure. 8 | 9 | 10 | ## Applicability 11 | The Template Method pattern should be used 12 | * to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary. 13 | * when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. 14 | * to control subclasses extensions. You can define a template method that calls 15 | "hook" operations at specific points,thereby permitting extensions only at those points. 16 | 17 | 18 | ## Structure 19 | ![template](./etc/template.png) 20 | 21 | 22 | ## Participants 23 | * **AbstractClass** 24 | - defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm. 25 | - implements a template method defining the skeleton of an algorithm.The template method calls primitive 26 | operations as well as operations defined in AbstractClass or those of other objects. 27 | * **ConcreteClass** 28 | - implements the primitive operations to carry out subclass-specific steps of the algorithm. 29 | 30 | 31 | ## Example 32 | Imagine a customer go to a restaurant, the common action of customers is ordering some food and then giving some comments. 33 | We can create a template for customers' action pattern, and let specific customer determine what specific operation they want to do. 34 | 35 | Participants in this example: 36 | * Customer is the **AbstractClass**. 37 | * Vegan/MeatLover is the **ConcreteClass**. 38 | 39 | 40 | ## Scala Tips 41 | N/A 42 | 43 | 44 | ## Reference 45 | * Design Patterns: Elements of Reusable Object-Oriented Software 46 | 47 | -------------------------------------------------------------------------------- /behavioral/template-method/etc/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/template-method/etc/template.png -------------------------------------------------------------------------------- /behavioral/template-method/src/main/scala/com/gx/templatemethod/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.templatemethod 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val vegan = new Vegan 22 | vegan.goToRestaurant() 23 | 24 | val meatLover = new MeatLover 25 | meatLover.goToRestaurant() 26 | 27 | } 28 | -------------------------------------------------------------------------------- /behavioral/template-method/src/main/scala/com/gx/templatemethod/Customer.scala: -------------------------------------------------------------------------------- 1 | package com.gx.templatemethod 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | abstract class Customer { 20 | 21 | def goToRestaurant(): Unit = { 22 | val orders = order() 23 | println(s"I am eating $orders.") 24 | println(s"The $orders is ${comments()}") 25 | } 26 | 27 | protected def order(): String 28 | 29 | protected def comments(): String 30 | 31 | } 32 | 33 | class Vegan extends Customer { 34 | override protected def order(): String = "vegetable" 35 | 36 | override protected def comments(): String = "yummy" 37 | } 38 | 39 | class MeatLover extends Customer { 40 | override protected def order(): String = "steak" 41 | 42 | override protected def comments(): String = "yummy" 43 | } -------------------------------------------------------------------------------- /behavioral/template-method/src/test/scala/com/gx/templatemethod/TemplateSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.templatemethod 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class TemplateSpec extends FlatSpec with Matchers { 24 | 25 | it should "order vegetable for vegan" in { 26 | val vegan = new Vegan 27 | val buffer = new ByteArrayOutputStream() 28 | Console.withOut(buffer) { 29 | vegan.goToRestaurant() 30 | } 31 | buffer.toString.split("\r\n").head should be ("I am eating vegetable.") 32 | } 33 | 34 | it should "order steak for meatlover" in { 35 | val meatLover = new MeatLover() 36 | val buffer = new ByteArrayOutputStream() 37 | Console.withOut(buffer) { 38 | meatLover.goToRestaurant() 39 | } 40 | buffer.toString.split("\r\n").head should be ("I am eating steak.") 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /behavioral/visitor/etc/visitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/behavioral/visitor/etc/visitor.png -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/fp/Animal.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | // Visitee 20 | trait Animal { 21 | def accept(operation: Animal.Operation) 22 | } 23 | 24 | class Monkey extends Animal { 25 | override def accept(operation: Animal.Operation): Unit = { 26 | operation(this) 27 | } 28 | } 29 | 30 | class Lion extends Animal { 31 | override def accept(operation: Animal.Operation): Unit = { 32 | operation(this) 33 | } 34 | } 35 | 36 | class Dolphin extends Animal { 37 | override def accept(operation: Animal.Operation): Unit = { 38 | operation(this) 39 | } 40 | } 41 | 42 | // Type of Visitor 43 | object Animal { 44 | type Operation = Animal => Unit 45 | } -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/fp/AnimalOperation.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.fp 2 | 3 | /** 4 | * Copyright 2018 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object AnimalOperation { 20 | def speak(animal: Animal): Unit = animal match { 21 | case monkey: Monkey => println("Ooh oo aa aa!") 22 | case lion: Lion => println("Roaaar!") 23 | case dolphin: Dolphin => println("Tuut tuttu tuutt!") 24 | case _ => // do nothing 25 | } 26 | 27 | def swim(animal: Animal): Unit = animal match { 28 | case dolphin: Dolphin => println("Dolphin swim fast!") 29 | case _ => // do nothing 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/fp/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 21 | zoo.accept(AnimalOperation.speak) 22 | zoo.accept(AnimalOperation.swim) 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/fp/Zoo.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.fp 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Zoo(animals: Animal*) { 20 | def accept(operation: Animal.Operation): Unit = { 21 | animals.foreach { 22 | _.accept(operation) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/oo/Animal.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Visitee 21 | trait Animal { 22 | def accept(operation: AnimalOperation) 23 | } 24 | 25 | class Monkey extends Animal { 26 | override def accept(operation: AnimalOperation): Unit = { 27 | operation.visitMonkey(this) 28 | } 29 | } 30 | 31 | class Lion extends Animal { 32 | override def accept(operation: AnimalOperation): Unit = { 33 | operation.visitLion(this) 34 | } 35 | } 36 | 37 | class Dolphin extends Animal { 38 | override def accept(operation: AnimalOperation): Unit = { 39 | operation.visitDolphin(this) 40 | } 41 | } -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/oo/AnimalOperation.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Visitor 21 | trait AnimalOperation { 22 | def visitMonkey(monkey: Monkey): Unit = { 23 | // do nothing 24 | } 25 | 26 | def visitLion(lion: Lion): Unit = { 27 | // do nothing 28 | } 29 | 30 | def visitDolphin(dolphin: Dolphin): Unit = { 31 | // do nothing 32 | } 33 | } 34 | 35 | class Speak extends AnimalOperation { 36 | override def visitMonkey(monkey: Monkey): Unit = println("Ooh oo aa aa!") 37 | 38 | override def visitLion(lion: Lion): Unit = println("Roaaar!") 39 | 40 | override def visitDolphin(dolphin: Dolphin): Unit = println("Tuut tuttu tuutt!") 41 | } 42 | 43 | 44 | class Swim extends AnimalOperation { 45 | override def visitDolphin(dolphin: Dolphin): Unit = println("Dolphin swim fast!") 46 | } -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/oo/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 21 | zoo.accept(new Speak) 22 | zoo.accept(new Swim) 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/visitor/src/main/scala/com/gx/visitor/oo/Zoo.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.oo 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Zoo(animals: Animal*) { 20 | def accept(operation: AnimalOperation): Unit = { 21 | animals.foreach { 22 | _.accept(operation) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/visitor/src/test/scala/com/gx/visitor/fp/FpVisitorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.fp 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2018 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class FpVisitorSpec extends FlatSpec with Matchers { 24 | 25 | it should "3 speakers" in { 26 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 27 | val buffer = new ByteArrayOutputStream() 28 | Console.withOut(buffer) { 29 | zoo.accept(AnimalOperation.speak) 30 | } 31 | val lines = buffer.toString.split("\r\n") 32 | lines.length should be(3) 33 | } 34 | 35 | it should "only 1 swimmer" in { 36 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 37 | val buffer = new ByteArrayOutputStream() 38 | Console.withOut(buffer) { 39 | zoo.accept(AnimalOperation.swim) 40 | } 41 | val lines = buffer.toString.split("\r\n") 42 | lines.length should be(1) 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /behavioral/visitor/src/test/scala/com/gx/visitor/oo/OoVisitorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.visitor.oo 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import com.gx.visitor.fp._ 6 | import org.scalatest.{FlatSpec, FunSuite, Matchers} 7 | 8 | /** 9 | * Copyright 2018 josephguan 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | * 23 | */ 24 | class OoVisitorSpec extends FlatSpec with Matchers { 25 | 26 | it should "3 speakers" in { 27 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 28 | val buffer = new ByteArrayOutputStream() 29 | Console.withOut(buffer) { 30 | zoo.accept(new Speak()) 31 | } 32 | val lines = buffer.toString.split("\r\n") 33 | lines.length should be(3) 34 | } 35 | 36 | it should "only 1 swimmer" in { 37 | val zoo = new Zoo(new Monkey, new Lion, new Dolphin) 38 | val buffer = new ByteArrayOutputStream() 39 | Console.withOut(buffer) { 40 | zoo.accept(new Swim()) 41 | } 42 | val lines = buffer.toString.split("\r\n") 43 | lines.length should be(1) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /creational/abstract-factory/etc/abstract-factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/abstract-factory/etc/abstract-factory.png -------------------------------------------------------------------------------- /creational/abstract-factory/src/main/scala/com/gx/abstractfactory/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.abstractfactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Refer to Cake Pattern 21 | object App extends App { 22 | 23 | val audi = new Car(AudiCarFactory) 24 | audi.drive() 25 | 26 | val bmw = new Car(BMWCarFactory) 27 | bmw.drive() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /creational/abstract-factory/src/main/scala/com/gx/abstractfactory/Car.scala: -------------------------------------------------------------------------------- 1 | package com.gx.abstractfactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | class Car(factory: CarFactory) { 21 | val engine = factory.createEngine() 22 | val wheel = factory.createWheel() 23 | val brand = factory.createBrand() 24 | 25 | def drive(): Unit = { 26 | brand.light() 27 | engine.start() 28 | wheel.rotate() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /creational/abstract-factory/src/main/scala/com/gx/abstractfactory/CarComponents.scala: -------------------------------------------------------------------------------- 1 | package com.gx.abstractfactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | //------------------- 21 | // Engines 22 | //------------------- 23 | trait Engine { 24 | def start(): Unit 25 | } 26 | 27 | class V6Engine extends Engine { 28 | override def start(): Unit = println("Vroom Vroom Vroom... V6 Engine started.") 29 | } 30 | 31 | class V8Engine extends Engine { 32 | override def start(): Unit = println("Vroom Vroom Vroom... V8 Engine started.") 33 | } 34 | 35 | //------------------- 36 | // Wheels 37 | //------------------- 38 | trait Wheel { 39 | def rotate(): Unit 40 | } 41 | 42 | class MichelinWheel extends Wheel { 43 | override def rotate(): Unit = println("Michelin wheel rotated.") 44 | } 45 | 46 | class DunlopWheel extends Wheel { 47 | override def rotate(): Unit = println("Dunlop wheel rotated.") 48 | } 49 | 50 | //------------------- 51 | // Engine 52 | //------------------- 53 | trait Brand { 54 | def light(): Unit 55 | } 56 | 57 | class AudiBrand extends Brand { 58 | override def light(): Unit = println("I am Audi.") 59 | } 60 | 61 | class BMWBrand extends Brand { 62 | override def light(): Unit = println("I am BMW.") 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /creational/abstract-factory/src/main/scala/com/gx/abstractfactory/CarFactory.scala: -------------------------------------------------------------------------------- 1 | package com.gx.abstractfactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait CarFactory { 20 | def createEngine(): Engine 21 | 22 | def createWheel(): Wheel 23 | 24 | def createBrand(): Brand 25 | } 26 | 27 | object AudiCarFactory extends CarFactory { 28 | override def createEngine(): Engine = new V6Engine() 29 | 30 | override def createWheel(): Wheel = new DunlopWheel() 31 | 32 | override def createBrand(): Brand = new AudiBrand 33 | } 34 | 35 | object BMWCarFactory extends CarFactory { 36 | override def createEngine(): Engine = new V8Engine() 37 | 38 | override def createWheel(): Wheel = new MichelinWheel() 39 | 40 | override def createBrand(): Brand = new BMWBrand 41 | } -------------------------------------------------------------------------------- /creational/abstract-factory/src/test/scala/com/gx/abstractfactory/AbstractFactorySpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.abstractfactory 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class AbstractFactorySpec extends FlatSpec with Matchers { 22 | 23 | it should "create Audi car" in { 24 | val car = new Car(AudiCarFactory) 25 | car.engine.isInstanceOf[V6Engine] should be (true) 26 | } 27 | 28 | it should "create BMW car" in { 29 | val car = new Car(BMWCarFactory) 30 | car.engine.isInstanceOf[V8Engine] should be (true) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /creational/builder/etc/builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/builder/etc/builder.png -------------------------------------------------------------------------------- /creational/builder/src/main/scala/com/gx/builder/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.builder 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App{ 20 | val car = new DIYCarBuilder().setEngine("V6").setWheels(4).setColor("Red").build() 21 | println(car) 22 | } 23 | -------------------------------------------------------------------------------- /creational/builder/src/main/scala/com/gx/builder/Car.scala: -------------------------------------------------------------------------------- 1 | package com.gx.builder 2 | 3 | 4 | /** 5 | * Copyright 2017 josephguan 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | case class Car(engine: String, wheels:Int, color:String) 21 | -------------------------------------------------------------------------------- /creational/builder/src/main/scala/com/gx/builder/CarBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.gx.builder 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Builder Interface 21 | trait CarBuilder { 22 | def setEngine(engine: String): CarBuilder 23 | 24 | def setWheels(wheels: Int): CarBuilder 25 | 26 | def setColor(color: String): CarBuilder 27 | 28 | def build(): Car 29 | } 30 | 31 | // Concrete Builder 32 | class DIYCarBuilder extends CarBuilder { 33 | private var engine = "" 34 | private var wheels = 0 35 | private var color = "" 36 | 37 | override def setEngine(engine: String): CarBuilder = { 38 | this.engine = engine 39 | this 40 | } 41 | 42 | override def setColor(color: String): CarBuilder = { 43 | this.color = color 44 | this 45 | } 46 | 47 | override def setWheels(wheels: Int): CarBuilder = { 48 | this.wheels = wheels 49 | this 50 | } 51 | 52 | override def build(): Car = { 53 | Car(engine, wheels, color) 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /creational/builder/src/test/scala/com/gx/builder/BuilderSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.builder 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class BuilderSpec extends FlatSpec with Matchers { 22 | 23 | it should "build car" in { 24 | val car = new DIYCarBuilder().setEngine("V6").setWheels(4).setColor("Red").build() 25 | car should be(Car("V6", 4, "Red")) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /creational/cake/etc/cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/cake/etc/cake.png -------------------------------------------------------------------------------- /creational/cake/src/main/scala/com/gx/cake/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.cake 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Refer to Abstract Factory Pattern 21 | object App extends App { 22 | 23 | val audi = new Car with AudiCarComponentRegistry 24 | audi.drive() 25 | 26 | val bmw = new Car with BMWCarComponentRegistry 27 | bmw.drive() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /creational/cake/src/main/scala/com/gx/cake/Car.scala: -------------------------------------------------------------------------------- 1 | package com.gx.cake 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | abstract class Car { 21 | self: EngineComponent with WheelComponent with BrandComponent => 22 | 23 | def drive(): Unit = { 24 | brand.light() 25 | engine.start() 26 | wheel.rotate() 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /creational/cake/src/main/scala/com/gx/cake/CarComponentRegistry.scala: -------------------------------------------------------------------------------- 1 | package com.gx.cake 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | trait AudiCarComponentRegistry extends EngineComponent with WheelComponent with BrandComponent { 21 | override val engine: Engine = new V6Engine() 22 | override val wheel: Wheel = new DunlopWheel() 23 | override val brand: Brand = new AudiBrand() 24 | } 25 | 26 | trait BMWCarComponentRegistry extends EngineComponent with WheelComponent with BrandComponent { 27 | override val engine: Engine = new V8Engine() 28 | override val wheel: Wheel = new MichelinWheel() 29 | override val brand: Brand = new BMWBrand() 30 | } -------------------------------------------------------------------------------- /creational/cake/src/test/scala/com/gx/cake/CakeSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.cake 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class CakeSpec extends FlatSpec with Matchers { 22 | 23 | it should "create Audi car" in { 24 | val car = new Car with AudiCarComponentRegistry 25 | car.engine.isInstanceOf[EngineComponent#V6Engine] should be(true) 26 | } 27 | 28 | it should "create BMW car" in { 29 | val car = new Car with BMWCarComponentRegistry 30 | car.engine.isInstanceOf[EngineComponent#V8Engine] should be(true) 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /creational/factory-kit/README.md: -------------------------------------------------------------------------------- 1 | # Factory Kit Pattern 2 | 3 | 4 | ## Intent 5 | Define a factory of immutable content with separated builder and factory interfaces. 6 | 7 | 8 | ## Applicability 9 | Use the Factory Kit pattern when 10 | 11 | * a class can't anticipate the class of objects it must create 12 | * you just want a new instance of a custom builder instead of the global one 13 | * you explicitly want to define types of objects, that factory can build 14 | * you want a separated builder and creator interface 15 | 16 | 17 | ## Structure 18 | ![factory-kit](./etc/factory-kit.png) 19 | 20 | 21 | ## Participants 22 | * **FactoryKit** 23 | - define an interface for operations that create product objects according to custom builder. 24 | * **AbstractProduct** 25 | - declares an interface for a type of product object. 26 | * **ConcreteProduct** 27 | - implements the AbstractProduct interface. 28 | * **Client** 29 | - define custom builder for factory kit. 30 | - use factory kit to create project objects. 31 | 32 | 33 | ## Example 34 | In this example, the client(App) creates one of the Weapon objects specified directly in the factory-kit instance. 35 | 36 | Participants in this example: 37 | * WeaponFactoryKit is the **FactoryKit**. 38 | * Weapon is the **AbstractProduct**. 39 | * Axe/Bow/Sword are the **ConcreteProduct**. 40 | * App is the **Client**. 41 | 42 | 43 | ## Scala Tips 44 | * use Map structure and lambda function to create define the custom builder. 45 | 46 | 47 | ## Reference 48 | * [Java design patterns](https://github.com/iluwatar/java-design-patterns/tree/master/factory-kit) -------------------------------------------------------------------------------- /creational/factory-kit/etc/factory-kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/factory-kit/etc/factory-kit.png -------------------------------------------------------------------------------- /creational/factory-kit/src/main/scala/com/gx/factorykit/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorykit 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val factory = WeaponFactoryKit.factory { builder => 22 | builder.put("axe", () => new Axe()) 23 | builder.put("bow", () => new Bow()) 24 | builder.put("sword", () => new Sword()) 25 | } 26 | 27 | val axe = factory.create("axe") 28 | println(axe) 29 | } 30 | -------------------------------------------------------------------------------- /creational/factory-kit/src/main/scala/com/gx/factorykit/Weapon.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorykit 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Weapon 20 | 21 | class Axe extends Weapon { 22 | override def toString: String = "Axe" 23 | } 24 | 25 | class Sword extends Weapon { 26 | override def toString: String = "Sword" 27 | } 28 | 29 | class Bow extends Weapon { 30 | override def toString: String = "Bow" 31 | } 32 | -------------------------------------------------------------------------------- /creational/factory-kit/src/main/scala/com/gx/factorykit/WeaponFactoryKit.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorykit 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class WeaponFactoryKit(dict: Map[String, () => Weapon]) { 22 | def create(key: String): Weapon = { 23 | dict.get(key).get() 24 | } 25 | } 26 | 27 | object WeaponFactoryKit { 28 | def factory(builder: mutable.Map[String, () => Weapon] => Unit) = { 29 | val dict = mutable.Map[String, () => Weapon]() 30 | builder(dict) 31 | new WeaponFactoryKit(dict.toMap) 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /creational/factory-kit/src/test/scala/com/gx/factorykit/FactoryKitSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorykit 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FactoryKitSpec extends FlatSpec with Matchers { 22 | 23 | it should "create objects using local factory kit" in { 24 | val factory = WeaponFactoryKit.factory { builder => 25 | builder.put("axe1", () => new Axe()) 26 | builder.put("axe2", () => new Axe()) 27 | builder.put("sword", () => new Sword()) 28 | } 29 | 30 | val axe1 = factory.create("axe1") 31 | val axe2 = factory.create("axe2") 32 | 33 | axe1.toString should be(axe2.toString) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /creational/factory-method/README.md: -------------------------------------------------------------------------------- 1 | # Factory Method Pattern 2 | 3 | ## Intent 4 | Define an interface for creating an object, but let subclasses decide which class 5 | to instantiate. Factory Method lets a class defer instantiation to subclasses. 6 | 7 | ## Applicability 8 | Use the Factory Method pattern when 9 | * a class can't anticipate the class of objects it must create. 10 | * a class wants its subclasses to specify the objects it creates. 11 | * classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. 12 | 13 | ## Structure 14 | ![factory-method](./etc/factory-method.png) 15 | 16 | ## Participants 17 | * **Product** 18 | - defines the interface of objects the factory method creates. 19 | * **ConcreteProduct** 20 | - implements the Product interface. 21 | * **Creator** 22 | - declares the factory method, which returns an object of type Product. Creator may also define a default implementation of the factory method that returns a default ConcreteProduct object. 23 | - may call the factory method to create a Product object. 24 | * **ConcreteCreator** 25 | - overrides the factory method to return an instance of a ConcreteProduct. 26 | 27 | ## Example 28 | In this example, it demonstrates a chef cook noodles. As you known, all chefs 29 | can cook, but different chef creates different flavors of noodle. 30 | 31 | Participants in this example: 32 | * Noodle is the **Product**. 33 | * PadThai/Spaghetti is the **ConcreteProduct**. 34 | * Chef is the **Creator**. 35 | * ItalianChef/ThaiChef is the **ConcreteCreator**. 36 | 37 | 38 | ## Scala Tips 39 | None 40 | 41 | 42 | ## Reference 43 | * Design Patterns: Elements of Reusable Object-Oriented Software -------------------------------------------------------------------------------- /creational/factory-method/etc/factory-method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/factory-method/etc/factory-method.png -------------------------------------------------------------------------------- /creational/factory-method/src/main/scala/com/gx/factorymethod/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorymethod 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val chef = new ItalianChef 21 | chef.cook() 22 | 23 | } 24 | -------------------------------------------------------------------------------- /creational/factory-method/src/main/scala/com/gx/factorymethod/Chef.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorymethod 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Client 21 | trait Chef { 22 | def makeNoodle(): Noodle 23 | 24 | def cook(): Unit = { 25 | val noodle = makeNoodle() 26 | println(s"The noodle is ${noodle.flavor()}. Yummy!") 27 | } 28 | } 29 | 30 | class ThaiChef extends Chef { 31 | override def makeNoodle(): Noodle = new PadThai() 32 | } 33 | 34 | class ItalianChef extends Chef { 35 | override def makeNoodle(): Noodle = new Spaghetti() 36 | } 37 | -------------------------------------------------------------------------------- /creational/factory-method/src/main/scala/com/gx/factorymethod/Noodle.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorymethod 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Product Interface 21 | trait Noodle { 22 | def flavor(): String 23 | } 24 | 25 | // Concrete Product 26 | class PadThai extends Noodle { 27 | override def flavor(): String = "Thai flavor" 28 | } 29 | 30 | // Concrete Product 31 | class Spaghetti extends Noodle { 32 | override def flavor(): String = "Italian flavor" 33 | } 34 | -------------------------------------------------------------------------------- /creational/factory-method/src/test/scala/com/gx/factorymethod/FactoryMethodSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.factorymethod 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FactoryMethodSpec extends FlatSpec with Matchers { 22 | 23 | it should "make Thai flavor noodle" in { 24 | val chef = new ThaiChef 25 | chef.makeNoodle().isInstanceOf[PadThai] should be (true) 26 | } 27 | 28 | it should "make Italian flavor noodle" in { 29 | val chef = new ItalianChef 30 | chef.makeNoodle().isInstanceOf[Spaghetti] should be (true) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /creational/prototype/README.md: -------------------------------------------------------------------------------- 1 | # Prototype Pattern 2 | 3 | ## Intent 4 | Specify the kinds of objects to create using a prototypical instance, and create 5 | new objects by copying this prototype. 6 | 7 | 8 | ## Applicability 9 | Use the Prototype pattern when a system should be independent of how its products 10 | are created, composed, and represented; and 11 | * when the classes to instantiate are specified at run-time, for example, by dynamic loading; or 12 | * to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or 13 | * when instances of a class can have one of only a few different combinations 14 | of state. It may be more convenient to install a corresponding number of 15 | prototypes and clone them rather than instantiating the class manually, 16 | each time with the appropriate state. 17 | 18 | 19 | ## Structure 20 | ![prototype](./etc/prototype.png) 21 | 22 | 23 | ## Participants 24 | * **Prototype** 25 | - declares an interface for cloning itself. 26 | * **ConcretePrototype** 27 | - implements an operation for cloning itself. 28 | * **Client** 29 | - creates a new object by asking a prototype to clone itself. 30 | 31 | 32 | ## Example 33 | In this example, it demonstrates Virus and Bacteria clone themselves. 34 | 35 | Participants in this example: 36 | * Prototype is the **Prototype**. 37 | * Bacteria/Virus is the **ConcretePrototype**. 38 | * App is the **Client**. 39 | 40 | 41 | ## Scala Tips 42 | * **case class** supports 'copy' operation built-in, which can clone itself, this is the easiest way to create a concrete prototype in scala. 43 | 44 | 45 | ## Reference 46 | * Design Patterns: Elements of Reusable Object-Oriented Software 47 | -------------------------------------------------------------------------------- /creational/prototype/etc/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/prototype/etc/prototype.png -------------------------------------------------------------------------------- /creational/prototype/src/main/scala/com/gx/prototype/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.prototype 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val v = new Virus("HIV", "BAD") 22 | println(v == v.clone()) 23 | 24 | val b = Bacteria("LAB", "GOOD") 25 | println(b == b.copy()) 26 | 27 | // val hero = new Hero("Joe") 28 | // hero.gender = Gender("male") 29 | // hero.weapon = new Weapon("gun") 30 | // 31 | // val hero2 = hero.clone() 32 | // 33 | // hero.weapon.name = "knife" 34 | // hero2.gender = Gender("female") 35 | // 36 | // println(hero.gender) 37 | // println(hero2.weapon.name) 38 | } 39 | -------------------------------------------------------------------------------- /creational/prototype/src/main/scala/com/gx/prototype/Prototype.scala: -------------------------------------------------------------------------------- 1 | package com.gx.prototype 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | trait Prototype extends Cloneable { 21 | override def clone(): AnyRef = super.clone() 22 | } 23 | 24 | class Virus(var name: String, var character: String) extends Prototype { 25 | override def clone(): Virus = new Virus(name, character) 26 | } 27 | 28 | // scala case class is a kind of prototype 29 | case class Bacteria(name:String, character: String) 30 | 31 | /* this is too complicated 32 | class Hero(name: String) extends Prototype { 33 | var gender: Gender = _ 34 | var weapon: Weapon = _ 35 | 36 | override def clone(): Hero = { 37 | val hero = new Hero(name) 38 | hero.gender = gender.copy() 39 | hero.weapon = weapon.clone() 40 | hero 41 | } 42 | } 43 | 44 | class Weapon(var name: String) extends Prototype { 45 | 46 | override def clone(): Weapon = { 47 | val weapon = new Weapon(name) 48 | weapon 49 | } 50 | } 51 | 52 | case class Gender(gender: String) 53 | 54 | */ -------------------------------------------------------------------------------- /creational/prototype/src/test/scala/com/gx/prototype/PrototypeSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.prototype 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class PrototypeSpec extends FlatSpec with Matchers { 22 | 23 | it should "clone itself" in { 24 | val virus = new Virus("HIV", "BAD") 25 | virus.name should be(virus.clone().name) 26 | 27 | val bacteria = Bacteria("LAB", "GOOD") 28 | bacteria should be(bacteria.copy()) 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /creational/simple-factory/README.md: -------------------------------------------------------------------------------- 1 | # Simple Factory Pattern 2 | 3 | 4 | ## Intent 5 | Creates objects without exposing the instantiation logic to the client. 6 | 7 | 8 | ## Applicability 9 | Use the Simple Factory pattern when 10 | * you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations. 11 | * a system should be independent of how its products are created, composed, and represented. 12 | 13 | 14 | ## Structure 15 | ![simple-factory](./etc/simple-factory.png) 16 | 17 | 18 | ## Participants 19 | * **SimpleFactory** 20 | - define a static factory method to create concrete product objects. 21 | * **Product** 22 | - defines the interface of objects the static factory method creates. 23 | * **ConcreteProduct** 24 | - implements the Product interface. 25 | * **Client** 26 | - creates a new object using simple factory. 27 | 28 | 29 | ## Example 30 | Imagine you are creating a calculator. You want to execute different operations according to operator(+,-,*,/), 31 | which means you need create different concrete operations according to different operator. 32 | This example demonstrates how to achieve it. 33 | 34 | Participants in this example: 35 | * Operation's companion object is the **SimpleFactory**, and the apply() function is the static factory method. 36 | * Operation is the **Prodcut**. 37 | * AddOperation/SubOperation/MulOperation/DivOperation is the **ConcreteProduct**. 38 | * App is the **Client**. 39 | 40 | 41 | ## Scala Tips 42 | * in scala, the **apply** function of **companion object** is the best place to implement the static factory method of simple factory. 43 | 44 | 45 | ## Reference 46 | -------------------------------------------------------------------------------- /creational/simple-factory/etc/simple-factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/creational/simple-factory/etc/simple-factory.png -------------------------------------------------------------------------------- /creational/simple-factory/src/main/scala/com/gx/simplefactory/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.simplefactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val op = Operation("*") 21 | val result = op.getResult(1, 2) 22 | println(result) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /creational/simple-factory/src/main/scala/com/gx/simplefactory/Operation.scala: -------------------------------------------------------------------------------- 1 | package com.gx.simplefactory 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Product Interface 21 | trait Operation { 22 | def getResult(a: Double, b: Double): Double 23 | } 24 | 25 | // Concrete Product 26 | class AddOperation extends Operation { 27 | override def getResult(a: Double, b: Double): Double = { 28 | a + b 29 | } 30 | } 31 | 32 | // Concrete Product 33 | class SubOperation extends Operation { 34 | override def getResult(a: Double, b: Double): Double = { 35 | a - b 36 | } 37 | } 38 | 39 | // Concrete Product 40 | class MulOperation extends Operation { 41 | override def getResult(a: Double, b: Double): Double = { 42 | a * b 43 | } 44 | } 45 | 46 | // Concrete Product 47 | class DivOperation extends Operation { 48 | override def getResult(a: Double, b: Double): Double = { 49 | if (b == 0) throw new Exception("b can not be zero") 50 | a / b 51 | } 52 | } 53 | 54 | // Simple Factory Method 55 | // also known as: companion object apply method 56 | object Operation { 57 | def apply(op: String) = op match { 58 | case "+" => new AddOperation() 59 | case "-" => new SubOperation() 60 | case "*" => new MulOperation() 61 | case "/" => new DivOperation(); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /creational/simple-factory/src/test/scala/com/gx/simplefactory/SimpleFactorySpect.scala: -------------------------------------------------------------------------------- 1 | package com.gx.simplefactory 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class SimpleFactorySpect extends FlatSpec with Matchers { 22 | 23 | it should "create AddOperation" in { 24 | val op = Operation("+") 25 | op.getResult(1, 2) should be(3) 26 | } 27 | 28 | it should "create SubOperation" in { 29 | val op = Operation("-") 30 | op.getResult(1, 2) should be(-1) 31 | } 32 | 33 | it should "create MulOperation" in { 34 | val op = Operation("*") 35 | op.getResult(1, 2) should be(2) 36 | } 37 | 38 | it should "create DivOperation" in { 39 | val op = Operation("/") 40 | op.getResult(1, 2) should be(0.5) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /creational/singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton Pattern 2 | 3 | 4 | ## Intent 5 | Ensure a class only has one instance, and provide a global point of access to it. 6 | 7 | 8 | ## Applicability 9 | Use the Singleton pattern when 10 | * there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point. 11 | * when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code. 12 | 13 | 14 | ## Structure 15 | N/A 16 | 17 | ## Participants 18 | N/A 19 | 20 | ## Example 21 | N/A 22 | 23 | ## Scala Tips 24 | * Scala provides concise direct realization of the singleton pattern in the language: **object** 25 | 26 | 27 | ## Reference 28 | * Design Patterns: Elements of Reusable Object-Oriented Software 29 | -------------------------------------------------------------------------------- /creational/singleton/src/main/scala/com/gx/singleton/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.singleton 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val s1 = Singleton 21 | val s2 = Singleton 22 | println(s1 == s2) 23 | } 24 | -------------------------------------------------------------------------------- /creational/singleton/src/main/scala/com/gx/singleton/Singleton.scala: -------------------------------------------------------------------------------- 1 | package com.gx.singleton 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Scala provides concise direct realization of the singleton pattern in the language: 21 | object Singleton { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /creational/singleton/src/test/scala/com/gx/singleton/SingletonSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.singleton 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class SingletonSpec extends FlatSpec with Matchers { 22 | 23 | it should "return the same object with multiple calls in same thread" in { 24 | val s1 = Singleton 25 | val s2 = Singleton 26 | s1 should be(s2) 27 | } 28 | 29 | it should "return the same object with multiple calls in different threads" in { 30 | val ss = (1 to 4).par.map(_ => Singleton) 31 | val expected = Singleton 32 | ss.foreach(s => s should be(expected)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /creational/value-object/README.md: -------------------------------------------------------------------------------- 1 | # Value Object Pattern 2 | 3 | ## Intent 4 | Provide objects which follow value semantics rather than reference semantics. 5 | This means value objects' equality are not based on identity. Two value objects are 6 | equal when they have the same value, not necessarily being the same object. 7 | 8 | 9 | ## Applicability 10 | Use the Value Object when 11 | * you need to measure the objects' equality based on the objects' value 12 | 13 | 14 | ## Structure 15 | N/A 16 | 17 | ## Participants 18 | N/A 19 | 20 | ## Example 21 | N/A 22 | 23 | ## Scala Tips 24 | * Scala offers complete support for value objects directly in the language. That's **case class**. 25 | 26 | 27 | ## Reference 28 | * [java design patterns](https://github.com/iluwatar/java-design-patterns/tree/master/value-object) -------------------------------------------------------------------------------- /creational/value-object/src/main/scala/com/gx/valueobject/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.valueobject 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val point1 = Point(1, 2) 21 | val point2 = Point(1, 2) 22 | println(point1) 23 | println(point1 == point2) 24 | } 25 | -------------------------------------------------------------------------------- /creational/value-object/src/main/scala/com/gx/valueobject/Point.scala: -------------------------------------------------------------------------------- 1 | package com.gx.valueobject 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | case class Point(x: Int, y: Int) 20 | -------------------------------------------------------------------------------- /creational/value-object/src/test/scala/com/gx/valueobject/ValueObjectSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.valueobject 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class ValueObjectSpec extends FlatSpec with Matchers { 22 | 23 | it should "be equal for two value objects with same value" in { 24 | Point(1, 2) should be(Point(1, 2)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /other/monad/README.md: -------------------------------------------------------------------------------- 1 | # Monad Pattern 2 | 3 | 4 | ## Intent 5 | In functional programming, a monad is a design pattern that represents the way of chaining operations together step by step. 6 | Binding functions can be described as passing one's output to another's input basing on the 'same type' contract. 7 | 8 | 9 | ## Applicability 10 | Use the Monad in any of the following situations 11 | 12 | * when you want to chain operations easily 13 | * when you want to apply each function regardless of the result of any of them 14 | 15 | 16 | ## Structure 17 | ![monad](./etc/monad.png) 18 | 19 | 20 | ## Participants 21 | * **Monad[T]** 22 | - create a function (e.g. apply) that takes plain type object and returns this object wrapped in a monadic value. 23 | - create a function (e.g. get) that return the plain type object in the monadic value. 24 | - create functions (e.g. flatMap, map etc) that takes a function from plain object to monadic value. 25 | 26 | 27 | ## Example 28 | In this example, we implements a Validator monad that can validate the plain object in it. 29 | 30 | Participants in this example: 31 | * Validator is an abstract **Monad**. 32 | * Valid/Illegal is the concrete class of Validator(**Monad**). 33 | 34 | 35 | ## Scala Tips 36 | * In scala, Option/Try/Either/List are all implementations of monad pattern. 37 | 38 | 39 | ## Reference 40 | * https://en.m.wikipedia.org/wiki/Monad_(functional_programming) -------------------------------------------------------------------------------- /other/monad/etc/monad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/other/monad/etc/monad.png -------------------------------------------------------------------------------- /other/monad/src/main/scala/com/gx/monad/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.monad 2 | 3 | /** 4 | * Copyright 2018 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | object App extends App { 21 | 22 | Validator("hello").validate(_.length > 6).flatMap(x => Valid(x.substring(6))) match { 23 | case Valid(s) => println(s) 24 | case Illegal => println("Illegal") 25 | } 26 | 27 | Validator("hello world").validate(_.length > 6).flatMap(x => Valid(x.substring(6).length)) match { 28 | case Valid(s) => println(s) 29 | case Illegal => println("Illegal") 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /other/monad/src/main/scala/com/gx/monad/Validator.scala: -------------------------------------------------------------------------------- 1 | package com.gx.monad 2 | 3 | import scala.collection.mutable.ListBuffer 4 | 5 | /** 6 | * Copyright 2018 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | // Monad 23 | abstract class Validator[+A] { 24 | 25 | protected val isValid: Boolean 26 | 27 | def get: A 28 | 29 | def validate(p: A => Boolean): Validator[A] = { 30 | if (!isValid) Illegal 31 | else if (!p(this.get)) Illegal 32 | else this 33 | } 34 | 35 | def flatMap[B](f: A => Validator[B]): Validator[B] = { 36 | if (isValid) f(this.get) else Illegal 37 | } 38 | 39 | } 40 | 41 | 42 | object Validator { 43 | def apply[A](testee: A): Validator[A] = new Valid[A](testee) 44 | } 45 | 46 | 47 | case class Valid[+A](testee: A) extends Validator[A] { 48 | override protected val isValid: Boolean = true 49 | override def get: A = testee 50 | } 51 | 52 | 53 | case object Illegal extends Validator[Nothing] { 54 | override protected val isValid: Boolean = false 55 | override def get = throw new IllegalStateException() 56 | } -------------------------------------------------------------------------------- /other/monad/src/test/scala/com/gx/monad/ValidatorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.monad 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2018 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | class ValidatorSpec extends FlatSpec with Matchers { 23 | 24 | it should "be valid" in { 25 | Validator("hello").validate(_.length > 1).get should be ("hello") 26 | } 27 | 28 | it should "be illegal" in { 29 | intercept[IllegalStateException] { 30 | Validator("hello").validate(_.length > 10).get 31 | } 32 | } 33 | 34 | it should "chain operations" in { 35 | Validator("Hell").validate(_.equals("Hello")).flatMap(x => Valid(x + " World")) should be (Illegal) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /other/selfless-trait/etc/selfless-trait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/other/selfless-trait/etc/selfless-trait.png -------------------------------------------------------------------------------- /other/selfless-trait/src/main/scala/com/gx/selflesstrait/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.selflesstrait 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | println(new Guy().greet()) 22 | println(new KindGuy().greet()) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /other/selfless-trait/src/main/scala/com/gx/selflesstrait/Friendly.scala: -------------------------------------------------------------------------------- 1 | package com.gx.selflesstrait 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Friendly { 20 | def greet(): String = "Hello!" 21 | } 22 | 23 | object Friendly extends Friendly -------------------------------------------------------------------------------- /other/selfless-trait/src/main/scala/com/gx/selflesstrait/Guy.scala: -------------------------------------------------------------------------------- 1 | package com.gx.selflesstrait 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | trait Human 21 | 22 | class Guy extends Human with Friendly { 23 | 24 | } 25 | 26 | 27 | class KindGuy extends Human { 28 | 29 | import Friendly.{greet => sayHi} 30 | 31 | def greet(): String = sayHi() + " Have a nice day!" 32 | 33 | } 34 | -------------------------------------------------------------------------------- /other/selfless-trait/src/test/scala/com/gx/selflesstrait/SelflessTraitSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.selflesstrait 2 | 3 | import org.scalatest.FlatSpec 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | class SelflessTraitSpec extends FlatSpec { 23 | 24 | // Matchers in scalatest is a good example of Selfless Trait Pattern!! 25 | import org.scalatest.Matchers._ 26 | 27 | it should "use Friendly by mixin" in { 28 | val guy = new Object with Friendly 29 | guy.greet() should be("Hello!") 30 | } 31 | 32 | it should "use Friendly by import" in { 33 | import Friendly._ 34 | greet() should be("Hello!") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /persistence/data-access-object/README.md: -------------------------------------------------------------------------------- 1 | # Data Access Object Pattern 2 | 3 | 4 | ## Intent 5 | Data Access Object Pattern or DAO pattern is used to separate low level data accessing API or operations from high level business services. 6 | It provides an abstract interface to some type of database or other persistence mechanism. 7 | 8 | 9 | ## Applicability 10 | Use the Data Access Object in any of the following situations 11 | 12 | * when you want to consolidate how the data layer is accessed 13 | * when you want to avoid writing multiple data retrieval/persistence layers 14 | 15 | 16 | ## Structure 17 | ![dao](./etc/dao.png) 18 | 19 | 20 | ## Participants 21 | * **DaoInterface** 22 | - This interface defines the standard operations to be performed on a model object(s). 23 | * **ConcreteDaoClass** 24 | - This class implements above interface. This class is responsible to get data from a data source which can be database / xml or any other storage mechanism. 25 | * **ModelObject** 26 | - This object is simple POJO/Cass Class containing get/set methods to store data retrieved using DAO class. 27 | 28 | 29 | ## Example 30 | In this example, we implements a data-access-object for save/query/update Student. 31 | 32 | Participants in this example: 33 | * StudentDao is the abstract **DaoInterface**. 34 | * InMemoryStudentDao is the **ConcreteDaoClass**. 35 | * Student is the **ModelObject**. 36 | 37 | 38 | ## Scala Tips 39 | None 40 | 41 | 42 | ## Reference 43 | https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm -------------------------------------------------------------------------------- /persistence/data-access-object/etc/dao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/persistence/data-access-object/etc/dao.png -------------------------------------------------------------------------------- /persistence/data-access-object/src/main/scala/com/gx/dao/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.dao 2 | 3 | object App extends App { 4 | 5 | val database = new InMemoryStudentDao() 6 | 7 | database.insert(Student(1, "abby", 11)) 8 | database.insert(Student(2, "bobby", 12)) 9 | 10 | println(database.findById(1).get) 11 | println(database.findById(2).get) 12 | database.update(Student(2, "bobby", 15)) 13 | println(database.findById(2).get) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /persistence/data-access-object/src/main/scala/com/gx/dao/Student.scala: -------------------------------------------------------------------------------- 1 | package com.gx.dao 2 | 3 | case class Student(id: Int, name: String, age: Int) 4 | -------------------------------------------------------------------------------- /persistence/data-access-object/src/main/scala/com/gx/dao/StudentDao.scala: -------------------------------------------------------------------------------- 1 | package com.gx.dao 2 | 3 | import scala.collection.mutable 4 | 5 | 6 | trait DaoBase[T] { 7 | def insert(obj: T): Unit 8 | 9 | def update(obj: T): Unit 10 | 11 | def findById(id: Int): Option[T] 12 | } 13 | 14 | 15 | trait StudentDao extends DaoBase[Student] 16 | 17 | 18 | class InMemoryStudentDao extends StudentDao { 19 | 20 | val students = mutable.HashMap[Int, Student]() 21 | 22 | override def insert(obj: Student): Unit = { 23 | if (students.get(obj.id).isEmpty) { 24 | students.put(obj.id, obj) 25 | } else { 26 | throw new Exception("Record exists!") 27 | } 28 | } 29 | 30 | override def update(obj: Student): Unit = { 31 | students.update(obj.id, obj) 32 | } 33 | 34 | override def findById(id: Int): Option[Student] = { 35 | students.get(id) 36 | } 37 | 38 | } 39 | 40 | // class MysqlStudentDao extends StudentDao 41 | // 42 | // class PostgreStudentDao extends StudentDao 43 | // 44 | // ...... -------------------------------------------------------------------------------- /persistence/repository/README.md: -------------------------------------------------------------------------------- 1 | # Repository Pattern 2 | 3 | 4 | ## Intent 5 | Repository layer is added between the domain and data mapping layers to isolate domain objects from details of 6 | the database access code and to minimize scattering and duplication of query code. 7 | 8 | Comparing with DAO pattern, the repository pattern uses a metaphor of a Collection. 9 | This metaphor gives the pattern a tight contract and make it easier to understand. 10 | 11 | 12 | ## Applicability 13 | Use the Repository pattern when 14 | 15 | * the number of domain objects is large 16 | * you want to avoid duplication of query code 17 | * you want to keep the database querying code in single place 18 | * you have multiple data sources 19 | 20 | 21 | ## Structure 22 | ![repository](./etc/repository.png) 23 | 24 | 25 | ## Participants 26 | * **Repository** 27 | - This interface defines the standard operations of a repository. 28 | * **ConcreteRepository** 29 | - This class implements above interface. This class is responsible to get data from a data source which can be database / memory or any other storage mechanism. 30 | * **Specification** 31 | - This interface defines operations that is able to tell if a candidate object matches some criteria. 32 | * **ConcreteSpecification** 33 | - It provides different kind of specifications. 34 | * **DomainObject** 35 | - It is the domain object which is managed by the repository. 36 | 37 | 38 | ## Example 39 | In this example, we implements a repository for save/find/delete Account. We can find the Account in different ways, 40 | e.g. "age between a and b" or "name = xxx". 41 | 42 | Participants in this example: 43 | * AccountRepository is the abstract **Repository**. 44 | * AccountRepositoryInMemory/AccountRepositoryInDatabase is the **ConcreteRepository**. 45 | * AccountSpecification is the abstract **Specification**. 46 | * AgeBetweenSpec/NameEqualSpec is the **ConcreteSpecification**. 47 | * Account is the **DomainObject**. 48 | 49 | 50 | ## Scala Tips 51 | None 52 | 53 | 54 | ## Reference 55 | https://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/ -------------------------------------------------------------------------------- /persistence/repository/etc/repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/persistence/repository/etc/repository.png -------------------------------------------------------------------------------- /persistence/repository/src/main/scala/com/gx/repository/Account.scala: -------------------------------------------------------------------------------- 1 | package com.gx.repository 2 | 3 | case class Account(id: Int, name: String, age: Int) 4 | -------------------------------------------------------------------------------- /persistence/repository/src/main/scala/com/gx/repository/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.repository 2 | 3 | import com.gx.repository 4 | 5 | object App extends App { 6 | 7 | val repository = new AccountRepositoryInMemory() 8 | 9 | val abby = Account(1, "abby", 10) 10 | val bobby = Account(2, "bobby", 11) 11 | val cathy = Account(3, "cathy", 12) 12 | 13 | // add 14 | repository.save(abby) 15 | repository.save(bobby) 16 | repository.save(cathy) 17 | println(repository.findAll()) 18 | 19 | // update 20 | val bobby2 = bobby.copy(age = 15) 21 | repository.save(bobby2) 22 | println(repository.findAll()) 23 | 24 | // delete 25 | repository.delete(cathy) 26 | repository.deleteById(bobby2.id) 27 | println(repository.findAll()) 28 | 29 | // restore 30 | repository.save(bobby) 31 | repository.save(cathy) 32 | 33 | // find by id 34 | println(repository.findById(2)) 35 | 36 | // find by age 37 | println(repository.findAll(new AccountSpecification.AgeBetweenSpec(5,11))) 38 | 39 | // find by name 40 | println(repository.findAll(new AccountSpecification.NameEqualSpec("Bobby"))) 41 | 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /persistence/repository/src/main/scala/com/gx/repository/Specification.scala: -------------------------------------------------------------------------------- 1 | package com.gx.repository 2 | 3 | trait Specification[T] { 4 | 5 | def specified(entity: T): Boolean 6 | 7 | def toSqlClauses(): String 8 | 9 | } 10 | 11 | 12 | trait AccountSpecification extends Specification[Account] 13 | 14 | object AccountSpecification { 15 | 16 | class AgeBetweenSpec(from: Int, to: Int) extends AccountSpecification { 17 | override def specified(entity: Account): Boolean = { 18 | entity.age >= from && entity.age <= to 19 | } 20 | 21 | override def toSqlClauses(): String = { 22 | s"""age >= $from and age <= $to""" 23 | } 24 | } 25 | 26 | class NameEqualSpec(name: String) extends AccountSpecification { 27 | override def specified(entity: Account): Boolean = { 28 | entity.name.equalsIgnoreCase(name) 29 | } 30 | 31 | override def toSqlClauses(): String = { 32 | s"""name = $name""" 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /project/Common.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import sbtassembly.AssemblyKeys._ 4 | 5 | object Common { 6 | val appVersion = "0.0.1" 7 | 8 | lazy val copyDependencies = TaskKey[Unit]("copy-dependencies") 9 | 10 | def copyDepTask = copyDependencies <<= (update, crossTarget, scalaVersion) map { 11 | (updateReport, out, scalaVer) => 12 | updateReport.allFiles foreach { 13 | srcPath => 14 | val destPath = out / "lib" / srcPath.getName 15 | IO.copyFile(srcPath, destPath, preserveLastModified = true) 16 | } 17 | } 18 | 19 | val settings: Seq[Def.Setting[_]] = Seq( 20 | version := appVersion, 21 | scalaVersion := "2.12.2", 22 | javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-encoding", "UTF-8"), 23 | scalacOptions ++= Seq("-deprecation", "-unchecked"), 24 | resolvers += Opts.resolver.mavenLocalFile, 25 | copyDepTask, 26 | assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false), 27 | assemblyJarName in assembly := s"${name.value}_${version.value}.jar" 28 | ) 29 | 30 | 31 | } -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Dependencies { 4 | 5 | val testDependencies: Seq[ModuleID] = Seq( 6 | "org.scalatest" %% "scalatest" % "3.0.1" % "test" 7 | ) 8 | 9 | } -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.17 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | //https://github.com/sbt/sbt-assembly 2 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.8") -------------------------------------------------------------------------------- /structural/adapter/README.md: -------------------------------------------------------------------------------- 1 | # Adapter Pattern 2 | 3 | ## Intent 4 | Convert the interface of a class into another interface clients expect. Adapter 5 | lets classes work together that couldn't otherwise because of incompatible interfaces. 6 | 7 | 8 | ## Applicability 9 | Use the Adapter pattern when 10 | * you want to use an existing class, and its interface does not match the one you need. 11 | * you want to create a reusable class that cooperates with unrelated or 12 | unforeseen classes, that is, classes that don't necessarily have compatible interfaces. 13 | * (object adapter only) you need to use several existing subclasses, but it's 14 | impractical to adapt their interface by subclassing every one. An object 15 | adapter can adapt the interface of its parent class. 16 | 17 | 18 | ## Structure 19 | ![adpater-pattern](./etc/adapter.png) 20 | 21 | 22 | ## Participants 23 | * Target 24 | - defines the domain-specific interface that Client uses. 25 | * Client 26 | - collaborates with objects conforming to the Target interface. 27 | * Adaptee 28 | - defines an existing interface that needs adapting. 29 | * Adapter 30 | - adapts the interface of Adaptee to the Target interface. 31 | 32 | 33 | ## Example 34 | Imagine soldiers can fight with weapons, but knife is not a sub-class of Weapon. 35 | What should we do? We can make a adapter for knife so that soldiers can use it as using a weapon. 36 | 37 | Participants in this example: 38 | * Weapon(Ak47) is the **Target**. 39 | * Knife is the **Adaptee**. 40 | * Soldier is the **Client**. 41 | * implicit Knife2WeaponAdapter is the **Adapter**. 42 | 43 | 44 | ## Scala Tips 45 | * In Scala, we have a built-in concept of interface adapters, expressed as **implicit classes**. 46 | 47 | 48 | ## Reference 49 | * Design Patterns: Elements of Reusable Object-Oriented Software -------------------------------------------------------------------------------- /structural/adapter/etc/adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/adapter/etc/adapter.png -------------------------------------------------------------------------------- /structural/adapter/src/main/scala/com/gx/adapter/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.adapter 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /** 21 | * In Scala, we have a built-in concept of interface adapters, expressed as implicit classes. 22 | */ 23 | object App extends App { 24 | val soldier = new Soldier() 25 | println(soldier.fightWith(new AK47())) 26 | println(soldier.fightWith(new Knife())) 27 | } 28 | -------------------------------------------------------------------------------- /structural/adapter/src/main/scala/com/gx/adapter/Knife.scala: -------------------------------------------------------------------------------- 1 | package com.gx.adapter 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Knife { 20 | def stab(): String = { 21 | "stab with knife" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /structural/adapter/src/main/scala/com/gx/adapter/Soldier.scala: -------------------------------------------------------------------------------- 1 | package com.gx.adapter 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Soldier { 20 | def fightWith(weapon: Weapon): String = { 21 | weapon.fire() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /structural/adapter/src/main/scala/com/gx/adapter/Weapon.scala: -------------------------------------------------------------------------------- 1 | package com.gx.adapter 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Weapon { 20 | def fire(): String 21 | } 22 | 23 | class AK47 extends Weapon { 24 | override def fire(): String = { 25 | "fire with AK47" 26 | } 27 | } -------------------------------------------------------------------------------- /structural/adapter/src/main/scala/com/gx/adapter/package.scala: -------------------------------------------------------------------------------- 1 | package com.gx 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | package object adapter { 20 | 21 | // Adapter 22 | implicit class Knife2WeaponAdapter(knife: Knife) extends Weapon { 23 | override def fire(): String = knife.stab() 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /structural/adapter/src/test/scala/com/gx/adapter/AdapterSepc.scala: -------------------------------------------------------------------------------- 1 | package com.gx.adapter 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class AdapterSepc extends FlatSpec with Matchers { 22 | 23 | it should "adapt Knife to Weapon" in { 24 | val soldier = new Soldier() 25 | soldier.fightWith(new AK47) should be ("fire with AK47") 26 | soldier.fightWith(new Knife) should be ("stab with knife") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /structural/bridge/etc/bridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/bridge/etc/bridge.png -------------------------------------------------------------------------------- /structural/bridge/src/main/scala/com/gx/bridge/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.bridge 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val about = new AboutPage(new DarkTheme) 22 | println(about.getContent) 23 | 24 | val help = new HelpPage(new LightTheme) 25 | println(help.getContent) 26 | 27 | help.changeTheme(new DarkTheme) 28 | println(help.getContent) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /structural/bridge/src/main/scala/com/gx/bridge/Theme.scala: -------------------------------------------------------------------------------- 1 | package com.gx.bridge 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Theme { 20 | def getColor: String 21 | } 22 | 23 | class DarkTheme extends Theme { 24 | override def getColor: String = "Dark Black" 25 | } 26 | 27 | class LightTheme extends Theme { 28 | override def getColor: String = "Off White" 29 | } -------------------------------------------------------------------------------- /structural/bridge/src/main/scala/com/gx/bridge/WebPage.scala: -------------------------------------------------------------------------------- 1 | package com.gx.bridge 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | abstract class WebPage(protected var theme: Theme) { 20 | def getContent: String 21 | 22 | def changeTheme(newTheme: Theme): Unit = { 23 | theme = newTheme 24 | } 25 | } 26 | 27 | class AboutPage(aboutTheme: Theme) extends WebPage(aboutTheme) { 28 | override def getContent: String = "About page in " + theme.getColor 29 | } 30 | 31 | class HelpPage(helpTheme: Theme) extends WebPage(helpTheme) { 32 | override def getContent: String = "Help page in " + theme.getColor 33 | } 34 | 35 | -------------------------------------------------------------------------------- /structural/bridge/src/test/scala/com/gx/bridge/BridgeSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.bridge 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class BridgeSpec extends FlatSpec with Matchers { 22 | 23 | it should "creates about-page with different theme" in { 24 | new AboutPage(new DarkTheme).getContent should be("About page in Dark Black") 25 | new AboutPage(new LightTheme).getContent should be("About page in Off White") 26 | } 27 | 28 | it should "change help-page's theme" in { 29 | val help = new HelpPage(new DarkTheme) 30 | help.getContent should be("Help page in Dark Black") 31 | help.changeTheme(new LightTheme) 32 | help.getContent should be("Help page in Off White") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /structural/composite/README.md: -------------------------------------------------------------------------------- 1 | # Composite Pattern 2 | 3 | 4 | ## Intent 5 | Compose objects into tree structures to represent part-whole hierarchies. 6 | Composite lets clients treat individual objects and compositions of objects uniformly. 7 | 8 | 9 | ## Applicability 10 | Use the Composite pattern when 11 | * you want to represent part-whole hierarchies of objects. 12 | * you want clients to be able to ignore the difference between compositions of objects and individual objects. 13 | Clients will treat all objects in the composite structure uniformly. 14 | 15 | 16 | ## Structure 17 | ![composite](./etc/composite.png) 18 | 19 | 20 | ## Participants 21 | * **Component** 22 | - declares the interface for objects in the composition. 23 | - implements default behavior for the interface common to all classes, as appropriate. 24 | - declares an interface for accessing and managing its child components. 25 | - (optional) defines an interface for accessing a component's parent in the recursive structure, and implements it if that's appropriate. 26 | * **Leaf** 27 | - represents leaf objects in the composition. A leaf has no children. 28 | - defines behavior for primitive objects in the composition. 29 | * **Composite** 30 | - defines behavior for components having children. 31 | - stores child components. 32 | - implements child-related operations in the Component interface. 33 | * **Client** 34 | - manipulates objects in the composition through the Component interface. 35 | 36 | 37 | ## Example 38 | We have several concrete action, like Forward, TurnLeft, TurnRight, etc. 39 | The composition of these actions is also a action. 40 | 41 | Participants in this example: 42 | * Action is the **Component**. 43 | * Forward/TurnLeft/TurnRight is the **Leaf**. 44 | * CompositeAction is the **Composite**. 45 | * App is the **Client**. 46 | 47 | 48 | ## Scala Tips 49 | * Scala allows you to pass variable length arguments to the function, for example: 50 | ```scala 51 | class CompositeAction(actions: Action*) 52 | ``` 53 | 54 | 55 | ## Reference 56 | * Design Patterns: Elements of Reusable Object-Oriented Software -------------------------------------------------------------------------------- /structural/composite/etc/composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/composite/etc/composite.png -------------------------------------------------------------------------------- /structural/composite/src/main/scala/com/gx/composite/Action.scala: -------------------------------------------------------------------------------- 1 | package com.gx.composite 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Action { 20 | def act(): Unit 21 | } 22 | 23 | class TurnRight extends Action { 24 | override def act(): Unit = println("turn right") 25 | } 26 | 27 | class TurnLeft extends Action { 28 | override def act(): Unit = println("turn left") 29 | } 30 | 31 | class Forward extends Action { 32 | override def act(): Unit = println("go forward") 33 | } 34 | 35 | class CompositeAction(actions: Action*) extends Action { 36 | override def act(): Unit = { 37 | actions.foreach(a => a.act()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /structural/composite/src/main/scala/com/gx/composite/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.composite 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val action = new CompositeAction(new Forward, new TurnLeft, new TurnRight) 22 | action.act() 23 | 24 | } 25 | -------------------------------------------------------------------------------- /structural/composite/src/test/scala/com/gx/composite/CompositeSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.composite 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2017 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class CompositeSpec extends FlatSpec with Matchers { 24 | 25 | it should "compose several actions into one action" in { 26 | val buffer = new ByteArrayOutputStream() 27 | Console.withOut(buffer) { 28 | val action = new CompositeAction(new Forward, new TurnLeft, new TurnRight) 29 | action.act() 30 | } 31 | buffer.toString should be("go forward\r\nturn left\r\nturn right\r\n") 32 | } 33 | 34 | 35 | it should "compose composite actions into one action" in { 36 | val buffer = new ByteArrayOutputStream() 37 | Console.withOut(buffer) { 38 | val action1 = new CompositeAction(new Forward, new TurnLeft, new TurnRight) 39 | val action2 = new CompositeAction(new Forward, action1, new TurnRight) 40 | action2.act() 41 | } 42 | buffer.toString should be("go forward\r\ngo forward\r\nturn left\r\nturn right\r\nturn right\r\n") 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /structural/decorator/etc/decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/decorator/etc/decorator.png -------------------------------------------------------------------------------- /structural/decorator/src/main/scala/com/gx/decorator/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.decorator 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | 21 | val coffee = new SimpleCoffee with MilkAdded with SugarAdded 22 | println(coffee.getCost) 23 | println(coffee.getDescription) 24 | } 25 | -------------------------------------------------------------------------------- /structural/decorator/src/main/scala/com/gx/decorator/Coffee.scala: -------------------------------------------------------------------------------- 1 | package com.gx.decorator 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Coffee { 20 | def getCost: Int 21 | 22 | def getDescription: String 23 | } 24 | 25 | class SimpleCoffee extends Coffee { 26 | override def getCost: Int = 10 27 | 28 | override def getDescription: String = "Simple coffee " 29 | } 30 | 31 | //Decorator 32 | trait MilkAdded extends Coffee { 33 | abstract override def getCost: Int = super.getCost + 5 34 | 35 | abstract override def getDescription: String = super.getDescription + "with milk " 36 | } 37 | 38 | //Decorator 39 | trait SugarAdded extends Coffee { 40 | abstract override def getCost: Int = super.getCost + 3 41 | 42 | abstract override def getDescription: String = super.getDescription + "with sugar " 43 | } 44 | -------------------------------------------------------------------------------- /structural/decorator/src/test/scala/com/gx/decorator/DecoratorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.decorator 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class DecoratorSpec extends FlatSpec with Matchers { 22 | 23 | it should "cost $18 for coffee with sugar and milk" in { 24 | val coffee = new SimpleCoffee with MilkAdded with SugarAdded 25 | coffee.getCost should be(18) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /structural/facade/etc/facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/facade/etc/facade.png -------------------------------------------------------------------------------- /structural/facade/src/main/scala/com/gx/facade/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.facade 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val computer = new ComputerFacade() 21 | computer.powerOn() 22 | } 23 | -------------------------------------------------------------------------------- /structural/facade/src/main/scala/com/gx/facade/Computer.scala: -------------------------------------------------------------------------------- 1 | package com.gx.facade 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class CPU { 20 | def powerOn(): Unit = println("CPU is powered on.") 21 | } 22 | 23 | class Fan { 24 | def run(): Unit = println("Fan is running.") 25 | } 26 | 27 | class Light { 28 | def flash(): Unit = println("Light is flashing.") 29 | } 30 | 31 | -------------------------------------------------------------------------------- /structural/facade/src/main/scala/com/gx/facade/ComputerFacade.scala: -------------------------------------------------------------------------------- 1 | package com.gx.facade 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class ComputerFacade { 20 | private val cpu = new CPU 21 | private val fan = new Fan 22 | private val light = new Light 23 | 24 | def powerOn():Unit = { 25 | cpu.powerOn() 26 | fan.run() 27 | light.flash() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /structural/facade/src/test/scala/com/gx/facade/FacadeSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.facade 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | import org.scalatest.{FlatSpec, Matchers} 6 | 7 | /** 8 | * Copyright 2017 josephguan 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | */ 23 | class FacadeSpec extends FlatSpec with Matchers { 24 | 25 | it should "simplify the power-on operation on computer system" in { 26 | val buffer = new ByteArrayOutputStream() 27 | Console.withOut(buffer) { 28 | val computer = new ComputerFacade() 29 | computer.powerOn() 30 | } 31 | buffer.toString() should be("CPU is powered on.\r\nFan is running.\r\nLight is flashing.\r\n") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /structural/flyweight/etc/flyweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/flyweight/etc/flyweight.png -------------------------------------------------------------------------------- /structural/flyweight/src/main/scala/com/gx/flyweight/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.flyweight 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val teaShop = new TeaShop() 21 | 22 | teaShop.takeOrder(1, Tea.GreenTea) 23 | teaShop.takeOrder(2, Tea.GreenTea) 24 | teaShop.takeOrder(3, Tea.UnsharedTea) 25 | teaShop.takeOrder(4, Tea.UnsharedTea) 26 | 27 | teaShop.serve() 28 | } 29 | -------------------------------------------------------------------------------- /structural/flyweight/src/main/scala/com/gx/flyweight/Tea.scala: -------------------------------------------------------------------------------- 1 | package com.gx.flyweight 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // Flyweight Interface 21 | trait Tea { 22 | // name is intrinsic state, shared state 23 | val name: String 24 | 25 | // table is extrinsic state, unshared state 26 | def serve(table: Int): Unit = { 27 | println(s"Serving $name to table# $table. hashCode: $hashCode") 28 | } 29 | } 30 | 31 | // concrete flyweight 32 | class GreenTea extends Tea { 33 | override val name: String = "Green Tea" 34 | } 35 | 36 | // unshared concrete flyweight 37 | class UnsharedTea extends Tea { 38 | override val name: String = "Unshared Tea" 39 | 40 | // unshared state 41 | val price = 10 42 | 43 | override def serve(table: Int): Unit = { 44 | println(s"Serving $name to table# $table. Price is $price. hashCode: $hashCode") 45 | } 46 | } 47 | 48 | // Tea type 49 | object Tea { 50 | 51 | trait Type 52 | 53 | case object GreenTea extends Type 54 | 55 | case object UnsharedTea extends Type 56 | 57 | } 58 | 59 | // This is what Tea will look like if it is not a Flyweight 60 | //trait Tea { 61 | // val name: String 62 | // val table: Int // create a object for each table 63 | // def serve(): Unit 64 | //} -------------------------------------------------------------------------------- /structural/flyweight/src/main/scala/com/gx/flyweight/TeaMaker.scala: -------------------------------------------------------------------------------- 1 | package com.gx.flyweight 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | // Flyweight Factory 23 | class TeaMaker { 24 | val teaPool = mutable.Map[Tea.Type, Tea]() 25 | 26 | def make(teaType: Tea.Type): Tea = { 27 | if (teaPool.isDefinedAt(teaType)) { 28 | teaPool.get(teaType).get 29 | } 30 | else { 31 | teaType match { 32 | case Tea.GreenTea => 33 | val tea = new GreenTea 34 | teaPool.put(teaType, tea) 35 | tea 36 | case Tea.UnsharedTea => 37 | new UnsharedTea() 38 | } 39 | } 40 | } 41 | 42 | def makeTeaInPool(teaType: Tea.Type, tea: Tea): Tea = { 43 | teaPool.put(teaType, tea) 44 | tea 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /structural/flyweight/src/main/scala/com/gx/flyweight/TeaShop.scala: -------------------------------------------------------------------------------- 1 | package com.gx.flyweight 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class TeaShop { 22 | private val orders = mutable.LinkedHashMap[Int, Tea]() 23 | private val maker = new TeaMaker() 24 | 25 | def takeOrder(table: Int, teaType: Tea.Type): Unit = { 26 | orders.put(table, maker.make(teaType)) 27 | } 28 | 29 | def serve(): Unit = { 30 | orders.foreach { 31 | case (table, tea) => tea.serve(table) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /structural/flyweight/src/test/scala/com/gx/flyweight/FlyweightSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.flyweight 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class FlyweightSpec extends FlatSpec with Matchers { 22 | 23 | it should "return same tea object" in { 24 | val maker = new TeaMaker() 25 | val tea1 = maker.make(Tea.GreenTea) 26 | val tea2 = maker.make(Tea.GreenTea) 27 | tea1 should be(tea2) 28 | } 29 | 30 | it should "return different tea object for unshared flyweight object" in { 31 | val maker = new TeaMaker() 32 | val tea1 = maker.make(Tea.UnsharedTea) 33 | val tea2 = maker.make(Tea.UnsharedTea) 34 | tea1 should not be tea2 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /structural/magnet/README.md: -------------------------------------------------------------------------------- 1 | # Magnet Pattern 2 | 3 | ## Intent 4 | The magnet pattern is an alternative approach to method overloading. 5 | 6 | 7 | ## Applicability 8 | method overloading in Scala comes with (at least) the following problems and inconveniences: 9 | 10 | 1. "Collisions" caused by type erasure 11 | 2. No lifting into a function (of all overloads at the same time) 12 | 3. Unavailability in package objects (before Scala 2.10) 13 | 4. Code duplication in case of many similar overloads 14 | 15 | The magnet pattern can solve these issues 16 | 17 | 18 | ## Structure 19 | ![magnet](./etc/magnet.png) 20 | 21 | 22 | ## Participants 23 | * **Magnet** 24 | - declare a magnet interface. 25 | - declare a abstract type for result. 26 | * **ConcreteMagnet** 27 | - implement Magnet interface. 28 | - declare it as implicit class 29 | * **Client** 30 | - define a function which take a magnet object as argument and return the type of magnet.Result 31 | 32 | 33 | ## Example 34 | Overload ```double``` function so that it can process Int, List[Int], List[String] and Tuple2[String, Int] 35 | 36 | Participants in this example: 37 | * DoubleMagnet is the **Magnet**. 38 | * fromInt/fromListInt/fromListString/fromStringIntTuple is the implicit class of **ConcreteMagnet**. 39 | * Doubling is the **Client**. 40 | 41 | 42 | ## Scala Tips 43 | * Magnet pattern is a way of using type-classes pattern for method overloading purpose. 44 | 45 | 46 | ## Reference 47 | http://spray.io/blog/2012-12-13-the-magnet-pattern/ -------------------------------------------------------------------------------- /structural/magnet/etc/magnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/magnet/etc/magnet.png -------------------------------------------------------------------------------- /structural/magnet/src/main/scala/com/gx/magnet/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.magnet 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val doubling = new Doubling() 21 | println(doubling.double(2)) 22 | println(doubling.double(List(1, 2, 3))) 23 | println(doubling.double(List("a", "b", "c"))) 24 | println(doubling.double("a", 5)) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /structural/magnet/src/main/scala/com/gx/magnet/Doubling.scala: -------------------------------------------------------------------------------- 1 | package com.gx.magnet 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | class Doubling { 20 | def double(magnet: DoubleMagnet): magnet.Result = magnet() 21 | 22 | // overloading collisions caused by type erasure 23 | // def notWork(ls: List[Int]): List[Int] = ls.map(_ * 2) 24 | // def notWork(ls: List[String]): List[String] = ls ++ ls 25 | } 26 | 27 | // Magnet Interface 28 | trait DoubleMagnet { 29 | type Result 30 | 31 | def apply(): Result 32 | } 33 | 34 | // Implicit Conversions 35 | object DoubleMagnet { 36 | 37 | implicit class fromInt(x: Int) extends DoubleMagnet { 38 | override type Result = Int 39 | 40 | override def apply(): Result = x * 2 41 | } 42 | 43 | implicit class fromListInt(ls: List[Int]) extends DoubleMagnet { 44 | override type Result = List[Int] 45 | 46 | override def apply(): Result = ls.map(_ * 2) 47 | } 48 | 49 | implicit class fromListString(ls: List[String]) extends DoubleMagnet { 50 | override type Result = List[String] 51 | 52 | override def apply(): Result = ls ++ ls 53 | } 54 | 55 | // overloading with different number of parameters 56 | implicit class fromStringIntTuple(para: Tuple2[String, Int]) extends DoubleMagnet { 57 | override type Result = String 58 | 59 | override def apply(): String = para._1 * para._2 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /structural/magnet/src/test/scala/com/gx/magnet/MagnetSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.magnet 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class MagnetSpec extends FlatSpec with Matchers { 22 | 23 | it should "double Int" in { 24 | val doubling = new Doubling() 25 | doubling.double(2) should be(4) 26 | println(doubling.double(List(1, 2, 3))) 27 | println(doubling.double(List("a", "b", "c"))) 28 | println(doubling.double("a", 5)) 29 | } 30 | 31 | it should "double List[Int]" in { 32 | val doubling = new Doubling() 33 | doubling.double(List(1, 2, 3)) should be(List(2, 4, 6)) 34 | } 35 | 36 | it should "double List[String]" in { 37 | val doubling = new Doubling() 38 | doubling.double(List("a", "b", "c")) should be(List("a", "b", "c", "a", "b", "c")) 39 | } 40 | 41 | it should "duplicate String with specific times" in { 42 | val doubling = new Doubling() 43 | doubling.double("a", 5) should be("aaaaa") 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /structural/proxy/etc/proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/proxy/etc/proxy.png -------------------------------------------------------------------------------- /structural/proxy/src/main/scala/com/gx/proxy/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.proxy 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val computer = new LinuxComputer() 21 | val ssh = new SecurityShell(computer) 22 | println(ssh.run("shutdown")) 23 | println(ssh.run("cd /root")) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /structural/proxy/src/main/scala/com/gx/proxy/Computer.scala: -------------------------------------------------------------------------------- 1 | package com.gx.proxy 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Computer { 20 | def run(command: String): String 21 | } 22 | 23 | // Real Subject 24 | class LinuxComputer extends Computer { 25 | override def run(command: String): String = s"running '$command' in linux" 26 | } 27 | 28 | // Proxy Subject 29 | class SecurityShell(realComputer: Computer) extends Computer { 30 | override def run(command: String): String = { 31 | if (command == "shutdown") { 32 | "shutdown is prohibited" 33 | } else { 34 | realComputer.run(command) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /structural/proxy/src/test/scala/com/gx/proxy/ProxySpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.proxy 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class ProxySpec extends FlatSpec with Matchers { 22 | 23 | it should "run command as at local" in { 24 | val computer = new LinuxComputer() 25 | val ssh = new SecurityShell(computer) 26 | ssh.run("pwd") should be("running 'pwd' in linux") 27 | } 28 | 29 | it should "prevent calling shutdown command in security shell" in { 30 | val computer = new LinuxComputer() 31 | val ssh = new SecurityShell(computer) 32 | ssh.run("shutdown") should be("shutdown is prohibited") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /structural/type-classes/README.md: -------------------------------------------------------------------------------- 1 | # Type Classes Pattern 2 | 3 | 4 | ## Intent 5 | In computer science, a type class is a type system construct that supports ad hoc polymorphism. 6 | The type classes pattern supports retroactive extension: the ability to extend existing software modules with new functionality without needing to touch or re-compile the original source. 7 | 8 | 9 | ## Applicability 10 | Use type-classes pattern when 11 | * you want to ad-hoc and retroactive polymorphism 12 | * avoiding using a lot of adapters 13 | 14 | 15 | ## Structure 16 | ![type-classes](./etc/type-classes.png) 17 | 18 | 19 | ## Participants 20 | * **TypeClass** 21 | - always takes one or more type parameters. 22 | - is usually designed to be stateless. 23 | * **ImplicitObject** 24 | - is the implicit value/object converting ```Target``` to ```TypeClass[Target]```. 25 | * **Target** 26 | - defines the real object that used in client's function as a parameter. 27 | * **Client** 28 | - defines functions which take parameter ```T``` and implicit parameter ```TypeClass[T]```. 29 | 30 | 31 | ## Example 32 | Imagine a human can say hello to some targets, but he has no idea what are concrete types of the targets. 33 | He only knows that the targets are Speakable. This example demonstrates how to make this happen. 34 | 35 | Participants in this example: 36 | * Speakable is the **TypeClass**. 37 | * SpeakableMonkey/SpeakableLion is the **ImplicitObject**. 38 | * Monkey/Lion is the **Target**. 39 | * Human is the **Client***. 40 | 41 | 42 | ## Scala Tips 43 | * As a shortcut for implicit parameters with only one type parameter, Scala provides so-called context bounds. For example: 44 | ```scala 45 | def sayHelloTo[A: Speakable[A]](target: A): String = { 46 | s"Human say hello, get reply ${implicitly[Speakable[A]].say()}" 47 | } 48 | ``` 49 | If you want to access that implicitly available value, you need to call the ```implicitly``` method. 50 | 51 | 52 | ## Reference 53 | http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html -------------------------------------------------------------------------------- /structural/type-classes/etc/type-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephguan/scala-design-patterns/0125124cb426dd2cbbad498ec87b3abd740adc6f/structural/type-classes/etc/type-classes.png -------------------------------------------------------------------------------- /structural/type-classes/src/main/scala/com/gx/typeclasses/Animal.scala: -------------------------------------------------------------------------------- 1 | package com.gx.typeclasses 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | trait Animal 20 | 21 | class Monkey extends Animal 22 | 23 | class Lion extends Animal 24 | 25 | object Animal { 26 | 27 | implicit object SpeakableMonkey extends Speakable[Monkey] { 28 | override def say(): String = "I'm monkey. Ooh oo aa aa!" 29 | } 30 | 31 | implicit object SpeakableLion extends Speakable[Lion] { 32 | override def say(): String = "I'm Lion. Roaaar!" 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /structural/type-classes/src/main/scala/com/gx/typeclasses/App.scala: -------------------------------------------------------------------------------- 1 | package com.gx.typeclasses 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | object App extends App { 20 | val human = new Human() 21 | val monkey = new Monkey() 22 | val lion = new Lion() 23 | println(human.sayHelloTo(monkey)) 24 | println(human.sayHelloTo(lion)) 25 | val x = List(1,2,3) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /structural/type-classes/src/main/scala/com/gx/typeclasses/Human.scala: -------------------------------------------------------------------------------- 1 | package com.gx.typeclasses 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | class Human { 21 | def sayHelloTo[A](target: A)(implicit s: Speakable[A]): String = { 22 | s"Human say hello, get reply: ${s.say()}" 23 | } 24 | 25 | // implementation using context-bounds 26 | // def sayHelloTo[A: Speakable[A]](target: A): String = { 27 | // s"Human say hello, get reply ${implicitly[Speakable[A]].say()}" 28 | // } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /structural/type-classes/src/main/scala/com/gx/typeclasses/Speakable.scala: -------------------------------------------------------------------------------- 1 | package com.gx.typeclasses 2 | 3 | /** 4 | * Copyright 2017 josephguan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | // type classes 21 | trait Speakable[T] { 22 | def say(): String 23 | } 24 | 25 | -------------------------------------------------------------------------------- /structural/type-classes/src/test/scala/com/gx/typeclasses/TypeClassesSpec.scala: -------------------------------------------------------------------------------- 1 | package com.gx.typeclasses 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Copyright 2017 josephguan 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | class TypeClassesSpec extends FlatSpec with Matchers { 22 | 23 | it should "say hello" in { 24 | val human = new Human() 25 | 26 | implicit val SpeakableInt = new Speakable[Int] { 27 | override def say(): String = "I'm a Number." 28 | } 29 | 30 | human.sayHelloTo(2) should be("Human say hello, get reply: I'm a Number.") 31 | 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------