diff --git a/abstract-document/README.md b/abstract-document/README.md index 2adb3732b822..e9a2bed94fd8 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -12,6 +12,7 @@ tags: ## Intent Achieve flexibility of untyped languages and keep the type-safety +## Class diagram ![alt text](./etc/abstract-document.png "Abstract Document Traits and Domain") @@ -26,4 +27,4 @@ Use the Abstract Document Pattern when ## Credits * [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) -* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) \ No newline at end of file +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) diff --git a/abstract-document/etc/abstract-document.urm.puml b/abstract-document/etc/abstract-document.urm.puml new file mode 100644 index 000000000000..9940dc2cfd05 --- /dev/null +++ b/abstract-document/etc/abstract-document.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.abstractdocument.domain.enums { + enum Property { + + MODEL {static} + + PARTS {static} + + PRICE {static} + + TYPE {static} + + valueOf(name : String) : Property {static} + + values() : Property[] {static} + } +} +package com.iluwatar.abstractdocument.domain { + class Car { + + Car(properties : Map) + } + interface HasModel { + + getModel() : Optional + } + interface HasParts { + + getParts() : Stream + } + interface HasPrice { + + getPrice() : Optional + } + interface HasType { + + getType() : Optional + } + class Part { + + Part(properties : Map) + } +} +package com.iluwatar.abstractdocument { + abstract class AbstractDocument { + - properties : Map + # AbstractDocument(properties : Map) + + children(key : String, constructor : Function, T>) : Stream + + get(key : String) : Object + + put(key : String, value : Object) + + toString() : String + } + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + interface Document { + + children(String, Function, T>) : Stream {abstract} + + get(String) : Object {abstract} + + put(String, Object) {abstract} + } +} +AbstractDocument ..|> Document +Car ..|> HasModel +Car ..|> HasPrice +Car ..|> HasParts +Car --|> AbstractDocument +HasModel --|> Document +HasParts --|> Document +HasPrice --|> Document +HasType --|> Document +Part ..|> HasType +Part ..|> HasModel +Part ..|> HasPrice +Part --|> AbstractDocument +@enduml \ No newline at end of file diff --git a/abstract-factory/README.md b/abstract-factory/README.md index f6719af45ad6..a4fc5cc746da 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -157,6 +157,9 @@ public static void main(String[] args) { } ``` +## Class diagram +![alt text](./etc/abstract-factory.urm.png "Abstract Factory class diagram") + ## Applicability Use the Abstract Factory pattern when diff --git a/abstract-factory/etc/abstract-factory.urm.png b/abstract-factory/etc/abstract-factory.urm.png new file mode 100644 index 000000000000..836858a2c652 Binary files /dev/null and b/abstract-factory/etc/abstract-factory.urm.png differ diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml new file mode 100644 index 000000000000..999091ef54f6 --- /dev/null +++ b/abstract-factory/etc/abstract-factory.urm.puml @@ -0,0 +1,101 @@ +@startuml +package com.iluwatar.abstractfactory { + class App { + - LOGGER : Logger {static} + - army : Army + - castle : Castle + - king : King + + App() + + createKingdom(factory : KingdomFactory) + + getArmy() : Army + ~ getArmy(factory : KingdomFactory) : Army + + getCastle() : Castle + ~ getCastle(factory : KingdomFactory) : Castle + + getKing() : King + ~ getKing(factory : KingdomFactory) : King + + main(args : String[]) {static} + - setArmy(army : Army) + - setCastle(castle : Castle) + - setKing(king : King) + } + class FactoryMaker { + + FactoryMaker() + + makeFactory(type : KingdomType) : KingdomFactory {static} + } + enum KingdomType { + + ELF {static} + + ORC {static} + + valueOf(name : String) : KingdomType {static} + + values() : KingdomType[] {static} + } + interface Army { + + getDescription() : String {abstract} + } + interface Castle { + + getDescription() : String {abstract} + } + class ElfArmy { + ~ DESCRIPTION : String {static} + + ElfArmy() + + getDescription() : String + } + class ElfCastle { + ~ DESCRIPTION : String {static} + + ElfCastle() + + getDescription() : String + } + class ElfKing { + ~ DESCRIPTION : String {static} + + ElfKing() + + getDescription() : String + } + class ElfKingdomFactory { + + ElfKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } + interface King { + + getDescription() : String {abstract} + } + interface KingdomFactory { + + createArmy() : Army {abstract} + + createCastle() : Castle {abstract} + + createKing() : King {abstract} + } + class OrcArmy { + ~ DESCRIPTION : String {static} + + OrcArmy() + + getDescription() : String + } + class OrcCastle { + ~ DESCRIPTION : String {static} + + OrcCastle() + + getDescription() : String + } + class OrcKing { + ~ DESCRIPTION : String {static} + + OrcKing() + + getDescription() : String + } + class OrcKingdomFactory { + + OrcKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } +} +KingdomType ..+ FactoryMaker +App --> "-castle" Castle +FactoryMaker ..+ App +App --> "-king" King +App --> "-army" Army +ElfArmy ..|> Army +ElfCastle ..|> Castle +ElfKing ..|> King +ElfKingdomFactory ..|> KingdomFactory +OrcArmy ..|> Army +OrcCastle ..|> Castle +OrcKing ..|> King +OrcKingdomFactory ..|> KingdomFactory +@enduml \ No newline at end of file diff --git a/acyclic-visitor/README.md b/acyclic-visitor/README.md index 83b618601fb2..d855cfd73659 100644 --- a/acyclic-visitor/README.md +++ b/acyclic-visitor/README.md @@ -9,11 +9,12 @@ tags: - Difficulty-Intermediate --- -![alt text](./etc/acyclic-visitor.png "Acyclic Visitor") - ## Intent Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the troublesome dependency cycles that are inherent to the GOF VISITOR Pattern. +## Class diagram +![alt text](./etc/acyclic-visitor.png "Acyclic Visitor") + ## Applicability This pattern can be used: * When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy. diff --git a/acyclic-visitor/etc/acyclic-visitor.urm.puml b/acyclic-visitor/etc/acyclic-visitor.urm.puml new file mode 100644 index 000000000000..e67bbde4407c --- /dev/null +++ b/acyclic-visitor/etc/acyclic-visitor.urm.puml @@ -0,0 +1,53 @@ +@startuml +package com.iluwatar.acyclicvisitor { + interface AllModemVisitor { + } + class App { + + App() + + main(args : String[]) {static} + } + class ConfigureForDosVisitor { + - LOGGER : Logger {static} + + ConfigureForDosVisitor() + + visit(hayes : Hayes) + + visit(zoom : Zoom) + } + class ConfigureForUnixVisitor { + - LOGGER : Logger {static} + + ConfigureForUnixVisitor() + + visit(zoom : Zoom) + } + class Hayes { + - LOGGER : Logger {static} + + Hayes() + + accept(modemVisitor : ModemVisitor) + + toString() : String + } + interface HayesVisitor { + + visit(Hayes) {abstract} + } + abstract class Modem { + + Modem() + + accept(ModemVisitor) {abstract} + } + interface ModemVisitor { + } + class Zoom { + - LOGGER : Logger {static} + + Zoom() + + accept(modemVisitor : ModemVisitor) + + toString() : String + } + interface ZoomVisitor { + + visit(Zoom) {abstract} + } +} +AllModemVisitor --|> ZoomVisitor +AllModemVisitor --|> HayesVisitor +ConfigureForDosVisitor ..|> AllModemVisitor +ConfigureForUnixVisitor ..|> ZoomVisitor +Hayes --|> Modem +HayesVisitor --|> ModemVisitor +Zoom --|> Modem +ZoomVisitor --|> ModemVisitor +@enduml \ No newline at end of file diff --git a/adapter/README.md b/adapter/README.md index 0076e962c36c..b1ad775cf3e3 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -97,6 +97,9 @@ var captain = new Captain(new FishingBoatAdapter()); captain.row(); ``` +## Class diagram +![alt text](./etc/adapter.urm.png "Adapter class diagram") + ## Applicability Use the Adapter pattern when diff --git a/adapter/etc/adapter.urm.png b/adapter/etc/adapter.urm.png new file mode 100644 index 000000000000..341ad67699d9 Binary files /dev/null and b/adapter/etc/adapter.urm.png differ diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml new file mode 100644 index 000000000000..1277cbb87125 --- /dev/null +++ b/adapter/etc/adapter.urm.puml @@ -0,0 +1,31 @@ +@startuml +package com.iluwatar.adapter { + class App { + - App() + + main(args : String[]) {static} + } + class Captain { + - rowingBoat : RowingBoat + + Captain() + + Captain(boat : RowingBoat) + ~ row() + ~ setRowingBoat(boat : RowingBoat) + } + ~class FishingBoat { + - LOGGER : Logger {static} + ~ FishingBoat() + ~ sail() + } + class FishingBoatAdapter { + - boat : FishingBoat + + FishingBoatAdapter() + + row() + } + interface RowingBoat { + + row() {abstract} + } +} +FishingBoatAdapter --> "-boat" FishingBoat +Captain --> "-rowingBoat" RowingBoat +FishingBoatAdapter ..|> RowingBoat +@enduml \ No newline at end of file diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md index e65f26d9a38a..c3498d315ab0 100644 --- a/aggregator-microservices/README.md +++ b/aggregator-microservices/README.md @@ -18,6 +18,8 @@ More variations of the aggregator are: - Chained Microservice Design Pattern: In this case each microservice is dependent/ chained to a series of other microservices. +## Class diagram + ![alt text](./etc/aggregator-microservice.png "Aggregator Microservice") ## Applicability diff --git a/aggregator-microservices/aggregator-service/etc/aggregator-service.urm.puml b/aggregator-microservices/aggregator-service/etc/aggregator-service.urm.puml new file mode 100644 index 000000000000..32ab28920b87 --- /dev/null +++ b/aggregator-microservices/aggregator-service/etc/aggregator-service.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.aggregator.microservices { + class Aggregator { + - informationClient : ProductInformationClient + - inventoryClient : ProductInventoryClient + + Aggregator() + + getProduct() : Product + } + class App { + + App() + + main(args : String[]) {static} + } + class Product { + - productInventories : int + - title : String + + Product() + + getProductInventories() : int + + getTitle() : String + + setProductInventories(productInventories : int) + + setTitle(title : String) + } + interface ProductInformationClient { + + getProductTitle() : String {abstract} + } + class ProductInformationClientImpl { + - LOGGER : Logger {static} + + ProductInformationClientImpl() + + getProductTitle() : String + } + interface ProductInventoryClient { + + getProductInventories() : Integer {abstract} + } + class ProductInventoryClientImpl { + - LOGGER : Logger {static} + + ProductInventoryClientImpl() + + getProductInventories() : Integer + } +} +Aggregator --> "-informationClient" ProductInformationClient +Aggregator --> "-inventoryClient" ProductInventoryClient +ProductInformationClientImpl ..|> ProductInformationClient +ProductInventoryClientImpl ..|> ProductInventoryClient +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-microservices.urm.puml b/aggregator-microservices/etc/aggregator-microservices.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/aggregator-microservices/etc/aggregator-microservices.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/aggregator-microservices/information-microservice/etc/information-microservice.urm.puml b/aggregator-microservices/information-microservice/etc/information-microservice.urm.puml new file mode 100644 index 000000000000..e0a2ccb24fb2 --- /dev/null +++ b/aggregator-microservices/information-microservice/etc/information-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.information.microservice { + class InformationApplication { + + InformationApplication() + + main(args : String[]) {static} + } + class InformationController { + + InformationController() + + getProductTitle() : String + } +} +@enduml \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/etc/inventory-microservice.urm.puml b/aggregator-microservices/inventory-microservice/etc/inventory-microservice.urm.puml new file mode 100644 index 000000000000..90f327e07db8 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/etc/inventory-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.inventory.microservice { + class InventoryApplication { + + InventoryApplication() + + main(args : String[]) {static} + } + class InventoryController { + + InventoryController() + + getProductInventories() : int + } +} +@enduml \ No newline at end of file diff --git a/ambassador/README.md b/ambassador/README.md index 2d3edeafa0fd..862c579a0ec9 100644 --- a/ambassador/README.md +++ b/ambassador/README.md @@ -150,6 +150,9 @@ public class App { } ``` +## Class diagram +![alt text](./etc/ambassador.urm.png "Ambassador class diagram") + ## Applicability Ambassador is applicable when working with a legacy remote service that cannot be modified or would be extremely difficult to modify. Connectivity features can diff --git a/ambassador/etc/ambassador.urm.png b/ambassador/etc/ambassador.urm.png new file mode 100644 index 000000000000..9b50a02ad356 Binary files /dev/null and b/ambassador/etc/ambassador.urm.png differ diff --git a/ambassador/etc/ambassador.urm.puml b/ambassador/etc/ambassador.urm.puml new file mode 100644 index 000000000000..517b0bf51d07 --- /dev/null +++ b/ambassador/etc/ambassador.urm.puml @@ -0,0 +1,47 @@ +@startuml +package com.iluwatar.ambassador.util { + interface RandomProvider { + + random() : double {abstract} + } +} +package com.iluwatar.ambassador { + class App { + + App() + + main(args : String[]) {static} + } + class Client { + - LOGGER : Logger {static} + - serviceAmbassador : ServiceAmbassador + + Client() + ~ useService(value : int) : long + } + class RemoteService { + - LOGGER : Logger {static} + - THRESHOLD : int {static} + - randomProvider : RandomProvider + - service : RemoteService {static} + - RemoteService() + ~ RemoteService(randomProvider : RandomProvider) + + doRemoteFunction(value : int) : long + ~ getRemoteService() : RemoteService {static} + } + ~interface RemoteServiceInterface { + + FAILURE : int {static} + + doRemoteFunction(int) : long {abstract} + } + class ServiceAmbassador { + - DELAY_MS : int {static} + - LOGGER : Logger {static} + - RETRIES : int {static} + ~ ServiceAmbassador() + - checkLatency(value : int) : long + + doRemoteFunction(value : int) : long + - safeCall(value : int) : long + } +} +RemoteService --> "-service" RemoteService +Client --> "-serviceAmbassador" ServiceAmbassador +RemoteService --> "-randomProvider" RandomProvider +RemoteService ..|> RemoteServiceInterface +ServiceAmbassador ..|> RemoteServiceInterface +@enduml \ No newline at end of file diff --git a/api-gateway/README.md b/api-gateway/README.md index 23014ae0b8af..2fd04900e8a3 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -15,6 +15,7 @@ tags: Aggregate calls to microservices in a single location: the API Gateway. The user makes a single call to the API Gateway, and the API Gateway then calls each relevant microservice. +## Class diagram ![alt text](./etc/api-gateway.png "API Gateway") ## Applicability diff --git a/api-gateway/api-gateway-service/etc/api-gateway-service.urm.puml b/api-gateway/api-gateway-service/etc/api-gateway-service.urm.puml new file mode 100644 index 000000000000..5fabc6a0fab4 --- /dev/null +++ b/api-gateway/api-gateway-service/etc/api-gateway-service.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.api.gateway { + class ApiGateway { + - imageClient : ImageClient + - priceClient : PriceClient + + ApiGateway() + + getProductDesktop() : DesktopProduct + + getProductMobile() : MobileProduct + } + class App { + + App() + + main(args : String[]) {static} + } + class DesktopProduct { + - imagePath : String + - price : String + + DesktopProduct() + + getImagePath() : String + + getPrice() : String + + setImagePath(imagePath : String) + + setPrice(price : String) + } + interface ImageClient { + + getImagePath() : String {abstract} + } + class ImageClientImpl { + + ImageClientImpl() + + getImagePath() : String + } + class MobileProduct { + - price : String + + MobileProduct() + + getPrice() : String + + setPrice(price : String) + } + interface PriceClient { + + getPrice() : String {abstract} + } + class PriceClientImpl { + + PriceClientImpl() + + getPrice() : String + } +} +ApiGateway --> "-imageClient" ImageClient +ApiGateway --> "-priceClient" PriceClient +ImageClientImpl ..|> ImageClient +PriceClientImpl ..|> PriceClient +@enduml \ No newline at end of file diff --git a/api-gateway/etc/api-gateway.urm.puml b/api-gateway/etc/api-gateway.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/api-gateway/etc/api-gateway.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/api-gateway/image-microservice/etc/image-microservice.urm.puml b/api-gateway/image-microservice/etc/image-microservice.urm.puml new file mode 100644 index 000000000000..130dac9de383 --- /dev/null +++ b/api-gateway/image-microservice/etc/image-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.image.microservice { + class ImageApplication { + + ImageApplication() + + main(args : String[]) {static} + } + class ImageController { + + ImageController() + + getImagePath() : String + } +} +@enduml \ No newline at end of file diff --git a/api-gateway/price-microservice/etc/price-microservice.urm.puml b/api-gateway/price-microservice/etc/price-microservice.urm.puml new file mode 100644 index 000000000000..9893c9c601d1 --- /dev/null +++ b/api-gateway/price-microservice/etc/price-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.price.microservice { + class PriceApplication { + + PriceApplication() + + main(args : String[]) {static} + } + class PriceController { + + PriceController() + + getPrice() : String + } +} +@enduml \ No newline at end of file diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 2d99820c5d82..f1faa396d1ff 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -17,6 +17,7 @@ is not blocked while waiting results of tasks. The pattern provides parallel processing of multiple independent tasks and retrieving the results via callbacks or waiting until everything is done. +# Class diagram ![alt text](./etc/async-method-invocation.png "Async Method Invocation") ## Applicability diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml new file mode 100644 index 000000000000..6f5d0b27f9d4 --- /dev/null +++ b/async-method-invocation/etc/async-method-invocation.urm.puml @@ -0,0 +1,51 @@ +@startuml +package com.iluwatar.async.method.invocation { + class App { + - LOGGER : Logger {static} + + App() + - callback(name : String) : AsyncCallback {static} + - lazyval(value : T, delayMillis : long) : Callable {static} + - log(msg : String) {static} + + main(args : String[]) {static} + } + interface AsyncCallback { + + onComplete(T, Optional) {abstract} + } + interface AsyncExecutor { + + endProcess(AsyncResult) : T {abstract} + + startProcess(Callable) : AsyncResult {abstract} + + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} + } + interface AsyncResult { + + await() {abstract} + + getValue() : T {abstract} + + isCompleted() : boolean {abstract} + } + class ThreadAsyncExecutor { + - idx : AtomicInteger + + ThreadAsyncExecutor() + + endProcess(asyncResult : AsyncResult) : T + + startProcess(task : Callable) : AsyncResult + + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult + } + -class CompletableResult { + ~ COMPLETED : int {static} + ~ FAILED : int {static} + ~ RUNNING : int {static} + ~ callback : Optional> + ~ exception : Exception + ~ lock : Object + ~ state : int + ~ value : T + ~ CompletableResult(callback : AsyncCallback) + + await() + + getValue() : T + + isCompleted() : boolean + ~ setException(exception : Exception) + ~ setValue(value : T) + } +} +CompletableResult ..+ ThreadAsyncExecutor +ThreadAsyncExecutor ..|> AsyncExecutor +CompletableResult ..|> AsyncResult +@enduml \ No newline at end of file diff --git a/balking/README.md b/balking/README.md index fb5c83d5b412..36a3efcb9f69 100644 --- a/balking/README.md +++ b/balking/README.md @@ -13,6 +13,7 @@ tags: Balking Pattern is used to prevent an object from executing certain code if it is an incomplete or inappropriate state +## Class diagram ![alt text](./etc/balking.png "Balking") ## Applicability diff --git a/balking/etc/balking.urm.puml b/balking/etc/balking.urm.puml new file mode 100644 index 000000000000..191fd350bcd8 --- /dev/null +++ b/balking/etc/balking.urm.puml @@ -0,0 +1,30 @@ +@startuml +package com.iluwatar.balking { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + interface DelayProvider { + + executeAfterDelay(long, TimeUnit, Runnable) {abstract} + } + class WashingMachine { + - LOGGER : Logger {static} + - delayProvider : DelayProvider + - washingMachineState : WashingMachineState + + WashingMachine() + + WashingMachine(delayProvider : DelayProvider) + + endOfWashing() + + getWashingMachineState() : WashingMachineState + + wash() + } + enum WashingMachineState { + + ENABLED {static} + + WASHING {static} + + valueOf(name : String) : WashingMachineState {static} + + values() : WashingMachineState[] {static} + } +} +WashingMachine --> "-washingMachineState" WashingMachineState +WashingMachine --> "-delayProvider" DelayProvider +@enduml \ No newline at end of file diff --git a/bridge/README.md b/bridge/README.md index 8d13d1b72af5..c48e1025cab7 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -179,6 +179,9 @@ hammer.unwield(); // The item's glow fades. ``` +## Class diagram +![alt text](./etc/bridge.urm.png "Bridge class diagram") + ## Applicability Use the Bridge pattern when diff --git a/bridge/etc/bridge.urm.png b/bridge/etc/bridge.urm.png new file mode 100644 index 000000000000..785585bf8163 Binary files /dev/null and b/bridge/etc/bridge.urm.png differ diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml new file mode 100644 index 000000000000..d5d6a38a91d9 --- /dev/null +++ b/bridge/etc/bridge.urm.puml @@ -0,0 +1,58 @@ +@startuml +package com.iluwatar.bridge { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + interface Enchantment { + + apply() {abstract} + + onActivate() {abstract} + + onDeactivate() {abstract} + } + class FlyingEnchantment { + - LOGGER : Logger {static} + + FlyingEnchantment() + + apply() + + onActivate() + + onDeactivate() + } + class Hammer { + - LOGGER : Logger {static} + - enchantment : Enchantment + + Hammer(enchantment : Enchantment) + + getEnchantment() : Enchantment + + swing() + + unwield() + + wield() + } + class SoulEatingEnchantment { + - LOGGER : Logger {static} + + SoulEatingEnchantment() + + apply() + + onActivate() + + onDeactivate() + } + class Sword { + - LOGGER : Logger {static} + - enchantment : Enchantment + + Sword(enchantment : Enchantment) + + getEnchantment() : Enchantment + + swing() + + unwield() + + wield() + } + interface Weapon { + + getEnchantment() : Enchantment {abstract} + + swing() {abstract} + + unwield() {abstract} + + wield() {abstract} + } +} +Sword --> "-enchantment" Enchantment +Hammer --> "-enchantment" Enchantment +FlyingEnchantment ..|> Enchantment +Hammer ..|> Weapon +SoulEatingEnchantment ..|> Enchantment +Sword ..|> Weapon +@enduml \ No newline at end of file diff --git a/builder/README.md b/builder/README.md index a061f8afaf2d..c6190ef17a97 100644 --- a/builder/README.md +++ b/builder/README.md @@ -113,6 +113,9 @@ And then it can be used as: var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build(); ``` +## Class diagram +![alt text](./etc/builder.urm.png "Builder class diagram") + ## Applicability Use the Builder pattern when diff --git a/builder/etc/builder.urm.png b/builder/etc/builder.urm.png new file mode 100644 index 000000000000..d77808d36097 Binary files /dev/null and b/builder/etc/builder.urm.png differ diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml new file mode 100644 index 000000000000..43d595a176fc --- /dev/null +++ b/builder/etc/builder.urm.puml @@ -0,0 +1,100 @@ +@startuml +package com.iluwatar.builder { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + enum Armor { + + CHAIN_MAIL {static} + + CLOTHES {static} + + LEATHER {static} + + PLATE_MAIL {static} + - title : String + + toString() : String + + valueOf(name : String) : Armor {static} + + values() : Armor[] {static} + } + enum HairColor { + + BLACK {static} + + BLOND {static} + + BROWN {static} + + RED {static} + + WHITE {static} + + toString() : String + + valueOf(name : String) : HairColor {static} + + values() : HairColor[] {static} + } + enum HairType { + + BALD {static} + + CURLY {static} + + LONG_CURLY {static} + + LONG_STRAIGHT {static} + + SHORT {static} + - title : String + + toString() : String + + valueOf(name : String) : HairType {static} + + values() : HairType[] {static} + } + class Hero { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + - Hero(builder : Builder) + + getArmor() : Armor + + getHairColor() : HairColor + + getHairType() : HairType + + getName() : String + + getProfession() : Profession + + getWeapon() : Weapon + + toString() : String + } + class Builder { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + + Builder(profession : Profession, name : String) + + build() : Hero + + withArmor(armor : Armor) : Builder + + withHairColor(hairColor : HairColor) : Builder + + withHairType(hairType : HairType) : Builder + + withWeapon(weapon : Weapon) : Builder + } + enum Profession { + + MAGE {static} + + PRIEST {static} + + THIEF {static} + + WARRIOR {static} + + toString() : String + + valueOf(name : String) : Profession {static} + + values() : Profession[] {static} + } + enum Weapon { + + AXE {static} + + BOW {static} + + DAGGER {static} + + SWORD {static} + + WARHAMMER {static} + + toString() : String + + valueOf(name : String) : Weapon {static} + + values() : Weapon[] {static} + } +} +Hero --> "-profession" Profession +Builder ..+ Hero +Hero --> "-armor" Armor +Builder --> "-hairColor" HairColor +Builder --> "-weapon" Weapon +Builder --> "-hairType" HairType +Hero --> "-hairColor" HairColor +Builder --> "-profession" Profession +Hero --> "-weapon" Weapon +Hero --> "-hairType" HairType +Builder --> "-armor" Armor +@enduml \ No newline at end of file diff --git a/business-delegate/README.md b/business-delegate/README.md index e6e2491222e0..5debebbbad9f 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -15,6 +15,7 @@ presentation and business tiers. By using the pattern we gain loose coupling between the tiers and encapsulate knowledge about how to locate, connect to, and interact with the business objects that make up the application. +## Class diagram ![alt text](./etc/business-delegate.png "Business Delegate") ## Applicability diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml new file mode 100644 index 000000000000..40aa2d6f0997 --- /dev/null +++ b/business-delegate/etc/business-delegate.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.business.delegate { + class App { + + App() + + main(args : String[]) {static} + } + class BusinessDelegate { + - businessService : BusinessService + - lookupService : BusinessLookup + - serviceType : ServiceType + + BusinessDelegate() + + doTask() + + setLookupService(businessLookup : BusinessLookup) + + setServiceType(serviceType : ServiceType) + } + class BusinessLookup { + - ejbService : EjbService + - jmsService : JmsService + + BusinessLookup() + + getBusinessService(serviceType : ServiceType) : BusinessService + + setEjbService(ejbService : EjbService) + + setJmsService(jmsService : JmsService) + } + interface BusinessService { + + doProcessing() {abstract} + } + class Client { + - businessDelegate : BusinessDelegate + + Client(businessDelegate : BusinessDelegate) + + doTask() + } + class EjbService { + - LOGGER : Logger {static} + + EjbService() + + doProcessing() + } + class JmsService { + - LOGGER : Logger {static} + + JmsService() + + doProcessing() + } + enum ServiceType { + + EJB {static} + + JMS {static} + + valueOf(name : String) : ServiceType {static} + + values() : ServiceType[] {static} + } +} +BusinessLookup --> "-ejbService" EjbService +BusinessDelegate --> "-serviceType" ServiceType +Client --> "-businessDelegate" BusinessDelegate +BusinessDelegate --> "-businessService" BusinessService +BusinessDelegate --> "-lookupService" BusinessLookup +BusinessLookup --> "-jmsService" JmsService +EjbService ..|> BusinessService +JmsService ..|> BusinessService +@enduml \ No newline at end of file diff --git a/bytecode/README.md b/bytecode/README.md index d2fc45e1e2d1..13d8013d6d24 100644 --- a/bytecode/README.md +++ b/bytecode/README.md @@ -12,6 +12,9 @@ tags: ## Intent Allows to encode behaviour as instructions for virtual machine. +## Class diagram +![alt text](./etc/bytecode.urm.png "Bytecode class diagram") + ## Applicability Use the Bytecode pattern when you have a lot of behavior you need to define and your game’s implementation language isn’t a good fit because: diff --git a/bytecode/etc/bytecode.urm.png b/bytecode/etc/bytecode.urm.png new file mode 100644 index 000000000000..82036a78a9b1 Binary files /dev/null and b/bytecode/etc/bytecode.urm.png differ diff --git a/bytecode/etc/bytecode.urm.puml b/bytecode/etc/bytecode.urm.puml new file mode 100644 index 000000000000..d675ae398c6c --- /dev/null +++ b/bytecode/etc/bytecode.urm.puml @@ -0,0 +1,69 @@ +@startuml +package com.iluwatar.bytecode { + class App { + - LOGGER : Logger {static} + + App() + - interpretInstruction(instruction : String, vm : VirtualMachine) {static} + + main(args : String[]) {static} + } + enum Instruction { + + ADD {static} + + DIVIDE {static} + + GET_AGILITY {static} + + GET_HEALTH {static} + + GET_WISDOM {static} + + LITERAL {static} + + PLAY_SOUND {static} + + SET_AGILITY {static} + + SET_HEALTH {static} + + SET_WISDOM {static} + + SPAWN_PARTICLES {static} + - value : int + + getInstruction(value : int) : Instruction {static} + + getIntValue() : int + + valueOf(name : String) : Instruction {static} + + values() : Instruction[] {static} + } + class VirtualMachine { + - stack : Stack + - wizards : Wizard[] + + VirtualMachine() + + execute(bytecode : int[]) + + getAgility(wizard : int) : int + + getHealth(wizard : int) : int + + getStack() : Stack + + getWisdom(wizard : int) : int + + getWizards() : Wizard[] + + setAgility(wizard : int, amount : int) + + setHealth(wizard : int, amount : int) + + setWisdom(wizard : int, amount : int) + } + class Wizard { + - LOGGER : Logger {static} + - agility : int + - health : int + - numberOfPlayedSounds : int + - numberOfSpawnedParticles : int + - wisdom : int + + Wizard() + + getAgility() : int + + getHealth() : int + + getNumberOfPlayedSounds() : int + + getNumberOfSpawnedParticles() : int + + getWisdom() : int + + playSound() + + setAgility(agility : int) + + setHealth(health : int) + + setWisdom(wisdom : int) + + spawnParticles() + } +} +package com.iluwatar.bytecode.util { + class InstructionConverterUtil { + + InstructionConverterUtil() + + convertToByteCode(instructions : String) : int[] {static} + - isValidInstruction(instruction : String) : boolean {static} + - isValidInt(value : String) : boolean {static} + } +} +@enduml \ No newline at end of file diff --git a/caching/README.md b/caching/README.md index 7184d51d486d..34fe0222af4e 100644 --- a/caching/README.md +++ b/caching/README.md @@ -15,6 +15,7 @@ To avoid expensive re-acquisition of resources by not releasing the resources immediately after their use. The resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them again. +## Class diagram ![alt text](./etc/caching.png "Caching") ## Applicability diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml new file mode 100644 index 000000000000..a9dae801eb20 --- /dev/null +++ b/caching/etc/caching.urm.puml @@ -0,0 +1,119 @@ +@startuml +package com.iluwatar.caching.constants { + class CachingConstants { + + ADD_INFO : String {static} + + USER_ACCOUNT : String {static} + + USER_ID : String {static} + + USER_NAME : String {static} + + CachingConstants() + } +} +package com.iluwatar.caching { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + + useCacheAsideStategy() + + useReadAndWriteThroughStrategy() + + useReadThroughAndWriteAroundStrategy() + + useReadThroughAndWriteBehindStrategy() + } + class AppManager { + - cachingPolicy : CachingPolicy {static} + - AppManager() + + find(userId : String) : UserAccount {static} + - findAside(userId : String) : UserAccount {static} + + initCacheCapacity(capacity : int) {static} + + initCachingPolicy(policy : CachingPolicy) {static} + + initDb(useMongoDb : boolean) {static} + + printCacheContent() : String {static} + + save(userAccount : UserAccount) {static} + - saveAside(userAccount : UserAccount) {static} + } + class CacheStore { + - LOGGER : Logger {static} + - cache : LruCache {static} + - CacheStore() + + clearCache() {static} + + flushCache() {static} + + get(userId : String) : UserAccount {static} + + initCapacity(capacity : int) {static} + + invalidate(userId : String) {static} + + print() : String {static} + + readThrough(userId : String) : UserAccount {static} + + readThroughWithWriteBackPolicy(userId : String) : UserAccount {static} + + set(userId : String, userAccount : UserAccount) {static} + + writeAround(userAccount : UserAccount) {static} + + writeBehind(userAccount : UserAccount) {static} + + writeThrough(userAccount : UserAccount) {static} + } + enum CachingPolicy { + + AROUND {static} + + ASIDE {static} + + BEHIND {static} + + THROUGH {static} + - policy : String + + getPolicy() : String + + valueOf(name : String) : CachingPolicy {static} + + values() : CachingPolicy[] {static} + } + class DbManager { + - db : MongoDatabase {static} + - mongoClient : MongoClient {static} + - useMongoDB : boolean {static} + - virtualDB : Map {static} + - DbManager() + + connect() {static} + + createVirtualDb() {static} + + readFromDb(userId : String) : UserAccount {static} + + updateDb(userAccount : UserAccount) {static} + + upsertDb(userAccount : UserAccount) {static} + + writeToDb(userAccount : UserAccount) {static} + } + class LruCache { + - LOGGER : Logger {static} + ~ cache : Map + ~ capacity : int + ~ end : Node + ~ head : Node + + LruCache(capacity : int) + + clear() + + contains(userId : String) : boolean + + get(userId : String) : UserAccount + + getCacheDataInListForm() : List + + getLruData() : UserAccount + + invalidate(userId : String) + + isFull() : boolean + + remove(node : Node) + + set(userId : String, userAccount : UserAccount) + + setCapacity(newCapacity : int) + + setHead(node : Node) + } + ~class Node { + ~ next : Node + ~ previous : Node + ~ userAccount : UserAccount + ~ userId : String + + Node(this$0 : String, userId : UserAccount) + } + class UserAccount { + - additionalInfo : String + - userId : String + - userName : String + + UserAccount(userId : String, userName : String, additionalInfo : String) + + getAdditionalInfo() : String + + getUserId() : String + + getUserName() : String + + setAdditionalInfo(additionalInfo : String) + + setUserId(userId : String) + + setUserName(userName : String) + + toString() : String + } +} +Node --+ LruCache +LruCache --> "-head" Node +Node --> "-previous" Node +AppManager --> "-cachingPolicy" CachingPolicy +Node --> "-userAccount" UserAccount +CacheStore --> "-cache" LruCache +@enduml \ No newline at end of file diff --git a/callback/README.md b/callback/README.md index a408fd5e0bd5..27af6337cacc 100644 --- a/callback/README.md +++ b/callback/README.md @@ -16,6 +16,7 @@ Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. +## Class diagram ![alt text](./etc/callback.png "Callback") ## Applicability diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml new file mode 100644 index 000000000000..a666a4fdb25a --- /dev/null +++ b/callback/etc/callback.urm.puml @@ -0,0 +1,28 @@ +@startuml +package com.iluwatar.callback { + class App { + - LOGGER : Logger {static} + - App() + + main(args : String[]) {static} + } + interface Callback { + + call() {abstract} + } + class LambdasApp { + - LOGGER : Logger {static} + - LambdasApp() + + main(args : String[]) {static} + } + class SimpleTask { + - LOGGER : Logger {static} + + SimpleTask() + + execute() + } + abstract class Task { + + Task() + + execute() {abstract} + ~ executeWith(callback : Callback) + } +} +SimpleTask --|> Task +@enduml \ No newline at end of file diff --git a/chain/README.md b/chain/README.md index db1ee182582b..7fdc4243b3ac 100644 --- a/chain/README.md +++ b/chain/README.md @@ -141,6 +141,9 @@ king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax" ``` +## Class diagram +![alt text](./etc/chain.urm.png "Chain of Responsibility class diagram") + ## Applicability Use Chain of Responsibility when diff --git a/chain/etc/chain.urm.png b/chain/etc/chain.urm.png new file mode 100644 index 000000000000..c3a4c80ba322 Binary files /dev/null and b/chain/etc/chain.urm.png differ diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml new file mode 100644 index 000000000000..43c78a04223e --- /dev/null +++ b/chain/etc/chain.urm.puml @@ -0,0 +1,61 @@ +@startuml +package com.iluwatar.chain { + class App { + + App() + + main(args : String[]) {static} + } + class OrcCommander { + + OrcCommander(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class OrcKing { + - chain : RequestHandler + + OrcKing() + - buildChain() + + makeRequest(req : Request) + } + class OrcOfficer { + + OrcOfficer(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class OrcSoldier { + + OrcSoldier(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class Request { + - handled : boolean + - requestDescription : String + - requestType : RequestType + + Request(requestType : RequestType, requestDescription : String) + + getRequestDescription() : String + + getRequestType() : RequestType + + isHandled() : boolean + + markHandled() + + toString() : String + } + abstract class RequestHandler { + - LOGGER : Logger {static} + - next : RequestHandler + + RequestHandler(next : RequestHandler) + + handleRequest(req : Request) + # printHandling(req : Request) + + toString() : String {abstract} + } + enum RequestType { + + COLLECT_TAX {static} + + DEFEND_CASTLE {static} + + TORTURE_PRISONER {static} + + valueOf(name : String) : RequestType {static} + + values() : RequestType[] {static} + } +} +OrcKing --> "-chain" RequestHandler +RequestHandler --> "-next" RequestHandler +Request --> "-requestType" RequestType +OrcCommander --|> RequestHandler +OrcOfficer --|> RequestHandler +OrcSoldier --|> RequestHandler +@enduml \ No newline at end of file diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md index 6a1c631c7cf7..8e840ce1efb3 100644 --- a/circuit-breaker/README.md +++ b/circuit-breaker/README.md @@ -165,6 +165,8 @@ How does the above pattern prevent failures? Let's understand via this finite st - If the number of failures cross a certain threshold, we move to the **open** state, which acts just like an open circuit and prevents remote service calls from being made, thus saving resources. (Here, we return the response called ```stale response from API```) - Once we exceed the retry timeout period, we move to the **half-open** state and make another call to the remote service again to check if the service is working so that we can serve fresh content. A *failure* sets it back to **open** state and another attempt is made after retry timeout period, while a *success* sets it to **closed** state so that everything starts working normally again. +## Class diagram +![alt text](./etc/circuit-breaker.urm.png "Circuit Breaker class diagram") ## Applicability Use the Circuit Breaker pattern when diff --git a/circuit-breaker/etc/circuit-breaker.urm.png b/circuit-breaker/etc/circuit-breaker.urm.png new file mode 100644 index 000000000000..9278ce216665 Binary files /dev/null and b/circuit-breaker/etc/circuit-breaker.urm.png differ diff --git a/circuit-breaker/etc/circuit-breaker.urm.puml b/circuit-breaker/etc/circuit-breaker.urm.puml new file mode 100644 index 000000000000..21471900283a --- /dev/null +++ b/circuit-breaker/etc/circuit-breaker.urm.puml @@ -0,0 +1,44 @@ +@startuml +package com.iluwatar.circuitbreaker { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class CircuitBreaker { + ~ failureCount : int + - failureThreshold : int + - futureTime : long + ~ lastFailureTime : long + - retryTimePeriod : long + - state : State + - timeout : long + ~ CircuitBreaker(timeout : long, failureThreshold : int, retryTimePeriod : long) + + call(serviceToCall : String, serverStartTime : long) : String + + getState() : String + - recordFailure() + - reset() + # setState() + + setStateForBypass(state : State) + } + class DelayedService { + - delay : int + + DelayedService() + + DelayedService(delay : int) + + response(serverStartTime : long) : String + } + class MonitoringService { + + MonitoringService() + + localResourceResponse() : String + + remoteResourceResponse(circuitBreaker : CircuitBreaker, serverStartTime : long) : String + } + enum State { + + CLOSED {static} + + HALF_OPEN {static} + + OPEN {static} + + valueOf(name : String) : State {static} + + values() : State[] {static} + } +} +CircuitBreaker --> "-state" State +@enduml \ No newline at end of file diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md index 9d2f2693b910..3f223dd07c65 100644 --- a/collection-pipeline/README.md +++ b/collection-pipeline/README.md @@ -14,6 +14,7 @@ tags: Collection Pipeline introduces Function Composition and Collection Pipeline, two functional-style patterns that you can combine to iterate collections in your code. In functional programming, it's common to sequence complex operations through a series of smaller modular functions or operations. The series is called a composition of functions, or a function composition. When a collection of data flows through a function composition, it becomes a collection pipeline. Function Composition and Collection Pipeline are two design patterns frequently used in functional-style programming. +## Class diagram ![alt text](./etc/collection-pipeline.png "Collection Pipeline") ## Applicability diff --git a/collection-pipeline/etc/collection-pipeline.urm.puml b/collection-pipeline/etc/collection-pipeline.urm.puml new file mode 100644 index 000000000000..8cd51c44656b --- /dev/null +++ b/collection-pipeline/etc/collection-pipeline.urm.puml @@ -0,0 +1,52 @@ +@startuml +package com.iluwatar.collectionpipeline { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Car { + - category : Category + - make : String + - model : String + - year : int + + Car(make : String, model : String, yearOfMake : int, category : Category) + + equals(obj : Object) : boolean + + getCategory() : Category + + getMake() : String + + getModel() : String + + getYear() : int + + hashCode() : int + } + class CarFactory { + - CarFactory() + + createCars() : List {static} + } + enum Category { + + CONVERTIBLE {static} + + JEEP {static} + + SEDAN {static} + + valueOf(name : String) : Category {static} + + values() : Category[] {static} + } + class FunctionalProgramming { + - FunctionalProgramming() + + getGroupingOfCarsByCategory(cars : List) : Map> {static} + + getModelsAfter2000(cars : List) : List {static} + + getSedanCarsOwnedSortedByDate(persons : List) : List {static} + } + class ImperativeProgramming { + - ImperativeProgramming() + + getGroupingOfCarsByCategory(cars : List) : Map> {static} + + getModelsAfter2000(cars : List) : List {static} + + getSedanCarsOwnedSortedByDate(persons : List) : List {static} + } + class Person { + - cars : List + + Person(cars : List) + + getCars() : List + } +} +Person --> "-cars" Car +Car --> "-category" Category +@enduml \ No newline at end of file diff --git a/combinator/README.md b/combinator/README.md index e2245032121e..98d353050dbf 100644 --- a/combinator/README.md +++ b/combinator/README.md @@ -18,6 +18,8 @@ The functional pattern representing a style of organizing libraries centered aro Putting it simply, there is some type T, some functions for constructing "primitive" values of type T, and some "combinators" which can combine values of type T in various ways to build up more complex values of type T. +## Class diagram +![alt text](./etc/combinator.urm.png "Combinator class diagram") ## Applicability Use the combinator pattern when: diff --git a/combinator/etc/combinator.urm.png b/combinator/etc/combinator.urm.png new file mode 100644 index 000000000000..80fdd36340bb Binary files /dev/null and b/combinator/etc/combinator.urm.png differ diff --git a/combinator/etc/combinator.urm.puml b/combinator/etc/combinator.urm.puml new file mode 100644 index 000000000000..83feec1ae782 --- /dev/null +++ b/combinator/etc/combinator.urm.puml @@ -0,0 +1,26 @@ +@startuml +package com.iluwatar.combinator { + class CombinatorApp { + - LOGGER : Logger {static} + + CombinatorApp() + + main(args : String[]) {static} + - text() : String {static} + } + interface Finder { + + and(andFinder : Finder) : Finder + + contains(word : String) : Finder {static} + + find(String) : List {abstract} + + not(notFinder : Finder) : Finder + + or(orFinder : Finder) : Finder + } + class Finders { + - Finders() + + advancedFinder(query : String, orQuery : String, notQuery : String) : Finder {static} + + expandedFinder(queries : String[]) : Finder {static} + + filteredFinder(query : String, excludeQueries : String[]) : Finder {static} + - identMult() : Finder {static} + - identSum() : Finder {static} + + specializedFinder(queries : String[]) : Finder {static} + } +} +@enduml \ No newline at end of file diff --git a/command/README.md b/command/README.md index 41e8b78e1aaa..bbe149df099a 100644 --- a/command/README.md +++ b/command/README.md @@ -19,6 +19,7 @@ Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. +## Class diagram ![alt text](./etc/command.png "Command") ## Applicability diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml new file mode 100644 index 000000000000..a8b773418ee3 --- /dev/null +++ b/command/etc/command.urm.puml @@ -0,0 +1,83 @@ +@startuml +package com.iluwatar.command { + class App { + + App() + + main(args : String[]) {static} + } + abstract class Command { + + Command() + + execute(Target) {abstract} + + redo() {abstract} + + toString() : String {abstract} + + undo() {abstract} + } + class Goblin { + + Goblin() + + toString() : String + } + class InvisibilitySpell { + - target : Target + + InvisibilitySpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class ShrinkSpell { + - oldSize : Size + - target : Target + + ShrinkSpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + enum Size { + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } + abstract class Target { + - LOGGER : Logger {static} + - size : Size + - visibility : Visibility + + Target() + + getSize() : Size + + getVisibility() : Visibility + + printStatus() + + setSize(size : Size) + + setVisibility(visibility : Visibility) + + toString() : String {abstract} + } + enum Visibility { + + INVISIBLE {static} + + VISIBLE {static} + - title : String + + toString() : String + + valueOf(name : String) : Visibility {static} + + values() : Visibility[] {static} + } + class Wizard { + - LOGGER : Logger {static} + - redoStack : Deque + - undoStack : Deque + + Wizard() + + castSpell(command : Command, target : Target) + + redoLastSpell() + + toString() : String + + undoLastSpell() + } +} +Target --> "-size" Size +Wizard --> "-undoStack" Command +ShrinkSpell --> "-oldSize" Size +InvisibilitySpell --> "-target" Target +ShrinkSpell --> "-target" Target +Target --> "-visibility" Visibility +Goblin --|> Target +InvisibilitySpell --|> Command +ShrinkSpell --|> Command +@enduml \ No newline at end of file diff --git a/commander/README.md b/commander/README.md index 71ef7fe9c303..0cee5dc77ee6 100644 --- a/commander/README.md +++ b/commander/README.md @@ -13,6 +13,9 @@ tags: > Used to handle all problems that can be encountered when doing distributed transactions. +## Class diagram +![alt text](./etc/commander.urm.png "Commander class diagram") + ## Applicability This pattern can be used when we need to make commits into 2 (or more) databases to complete transaction, which cannot be done atomically and can thereby create problems. diff --git a/commander/etc/commander.urm.png b/commander/etc/commander.urm.png new file mode 100644 index 000000000000..6b5ebba75bd6 Binary files /dev/null and b/commander/etc/commander.urm.png differ diff --git a/commander/etc/commander.urm.puml b/commander/etc/commander.urm.puml new file mode 100644 index 000000000000..959fbfadcf88 --- /dev/null +++ b/commander/etc/commander.urm.puml @@ -0,0 +1,329 @@ +@startuml +package com.iluwatar.commander.queue { + class Queue { + - front : Queue.Node + - rear : Queue.Node + - size : int + ~ Queue() + ~ dequeue() : T + ~ enqueue(obj : T) + ~ isEmpty() : boolean + ~ peek() : T + } + ~class Node { + ~ next : Queue.Node + ~ value : V + ~ Node(obj : V, b : Queue.Node) + } + class QueueDatabase { + - data : Queue + + exceptionsList : List + + QueueDatabase(exc : Exception[]) + + add(t : QueueTask) : QueueTask + + dequeue() : QueueTask + + get(taskId : String) : QueueTask + + peek() : QueueTask + } + class QueueTask { + + firstAttemptTime : long + + messageType : int + + order : Order + + taskType : TaskType + + QueueTask(o : Order, t : TaskType, messageType : int) + + getType() : String + } + enum TaskType { + + EmployeeDb {static} + + Messaging {static} + + Payment {static} + + valueOf(name : String) : TaskType {static} + + values() : TaskType[] {static} + } +} +package com.iluwatar.commander.messagingservice { + class MessagingDatabase { + - data : Hashtable + + MessagingDatabase() + + add(r : MessageRequest) : MessageRequest + + get(requestId : String) : MessageRequest + } + class MessagingService { + - LOGGER : Logger {static} + + MessagingService(db : MessagingDatabase, exc : Exception[]) + + receiveRequest(parameters : Object[]) : String + ~ sendMessage(m : MessageToSend) : String + # updateDb(parameters : Object[]) : String + } + ~class MessageRequest { + ~ msg : MessageToSend + ~ reqId : String + ~ MessageRequest(this$0 : String, reqId : MessageToSend) + } + ~enum MessageToSend { + + PaymentFail {static} + + PaymentSuccessful {static} + + PaymentTrying {static} + + valueOf(name : String) : MessageToSend {static} + + values() : MessageToSend[] {static} + } +} +package com.iluwatar.commander { + class AppEmployeeDbFailCases { + - employeeTime : long + - messageTime : long + - numOfRetries : int + - paymentTime : long + - queueTaskTime : long + - queueTime : long + - retryDuration : long + + AppEmployeeDbFailCases() + ~ employeeDatabaseUnavailableCase() + ~ employeeDbSuccessCase() + + main(args : String[]) {static} + } + class AppMessagingFailCases { + - employeeTime : long + - messageTime : long + - numOfRetries : int + - paymentTime : long + - queueTaskTime : long + - queueTime : long + - retryDuration : long + + AppMessagingFailCases() + + main(args : String[]) {static} + ~ messagingDatabaseUnavailableCasePaymentError() + ~ messagingDatabaseUnavailableCasePaymentFailure() + ~ messagingDatabaseUnavailableCasePaymentSuccess() + ~ messagingSuccessCase() + } + class AppPaymentFailCases { + - employeeTime : long + - messageTime : long + - numOfRetries : int + - paymentTime : long + - queueTaskTime : long + - queueTime : long + - retryDuration : long + + AppPaymentFailCases() + + main(args : String[]) {static} + ~ paymentDatabaseUnavailableCase() + ~ paymentNotPossibleCase() + ~ paymentSuccessCase() + } + class AppQueueFailCases { + - employeeTime : long + - messageTime : long + - numOfRetries : int + - paymentTime : long + - queueTaskTime : long + - queueTime : long + - retryDuration : long + + AppQueueFailCases() + + main(args : String[]) {static} + ~ queueEmployeeDbTaskDatabaseUnavailableCase() + ~ queueMessageTaskDatabaseUnavailableCase() + ~ queuePaymentTaskDatabaseUnavailableCase() + ~ queueSuccessCase() + } + class AppShippingFailCases { + - employeeTime : long + - messageTime : long + - numOfRetries : int + - paymentTime : long + - queueTaskTime : long + - queueTime : long + - retryDuration : long + + AppShippingFailCases() + ~ itemUnavailableCase() + + main(args : String[]) {static} + ~ shippingDatabaseUnavailableCase() + ~ shippingNotPossibleCase() + ~ shippingSuccessCase() + } + class Commander { + - LOG : Logger {static} + - employeeDb : EmployeeHandle + - employeeTime : long + - finalSiteMsgShown : boolean + - messageTime : long + - messagingService : MessagingService + - numOfRetries : int + - paymentService : PaymentService + - paymentTime : long + - queue : QueueDatabase + - queueItems : int + - queueTaskTime : long + - queueTime : long + - retryDuration : long + - shippingService : ShippingService + ~ Commander(empDb : EmployeeHandle, paymentService : PaymentService, shippingService : ShippingService, messagingService : MessagingService, qdb : QueueDatabase, numOfRetries : int, retryDuration : long, queueTime : long, queueTaskTime : long, paymentTime : long, messageTime : long, employeeTime : long) + - doTasksInQueue() + - employeeHandleIssue(order : Order) + ~ placeOrder(order : Order) + - sendPaymentFailureMessage(order : Order) + - sendPaymentPossibleErrorMsg(order : Order) + - sendPaymentRequest(order : Order) + - sendShippingRequest(order : Order) + - sendSuccessMessage(order : Order) + - tryDequeue() + - tryDoingTasksInQueue() + - updateQueue(qt : QueueTask) + } + abstract class Database { + + Database() + + add(T) : T {abstract} + + get(String) : T {abstract} + } + class Order { + - ALL_CHARS : String {static} + - RANDOM : Random {static} + - USED_IDS : Hashtable {static} + ~ addedToEmployeeHandle : boolean + ~ createdTime : long + + id : String + ~ item : String + ~ messageSent : MessageSent + ~ paid : PaymentStatus + ~ price : float + ~ user : User + ~ Order(user : User, item : String, price : float) + - createUniqueId() : String + } + ~enum MessageSent { + + NoneSent {static} + + PaymentFail {static} + + PaymentSuccessful {static} + + PaymentTrying {static} + + valueOf(name : String) : MessageSent {static} + + values() : MessageSent[] {static} + } + ~enum PaymentStatus { + + Done {static} + + NotDone {static} + + Trying {static} + + valueOf(name : String) : PaymentStatus {static} + + values() : PaymentStatus[] {static} + } + class Retry { + - RANDOM : Random {static} + - attempts : AtomicInteger + - errors : List + - handleError : Retry.HandleErrorIssue + - maxAttempts : int + - maxDelay : long + - op : Operation + - test : Predicate + ~ Retry(op : Operation, handleError : Retry.HandleErrorIssue, maxAttempts : int, maxDelay : long, ignoreTests : Predicate[]) + + perform(list : List, obj : T) + } + interface HandleErrorIssue { + + handleIssue(T, Exception) {abstract} + } + interface Operation { + + operation(List) {abstract} + } + abstract class Service { + - ALL_CHARS : String {static} + - RANDOM : Random {static} + - USED_IDS : Hashtable {static} + # database : Database + + exceptionsList : ArrayList + # Service(db : Database, exc : Exception[]) + # generateId() : String + + receiveRequest(Object[]) : String {abstract} + # updateDb(Object[]) : String {abstract} + } + class User { + ~ address : String + ~ name : String + ~ User(name : String, address : String) + } +} +package com.iluwatar.commander.shippingservice { + class ShippingDatabase { + - data : Hashtable + + ShippingDatabase() + + add(r : ShippingRequest) : ShippingRequest + + get(trasnactionId : String) : ShippingRequest + } + class ShippingService { + + ShippingService(db : ShippingDatabase, exc : Exception[]) + + receiveRequest(parameters : Object[]) : String + # updateDb(parameters : Object[]) : String + } + ~class ShippingRequest { + ~ address : String + ~ item : String + ~ transactionId : String + ~ ShippingRequest(transactionId : String, item : String, address : String) + } +} +package com.iluwatar.commander.paymentservice { + class PaymentDatabase { + - data : Hashtable + + PaymentDatabase() + + add(r : PaymentRequest) : PaymentRequest + + get(requestId : String) : PaymentRequest + } + class PaymentService { + + PaymentService(db : PaymentDatabase, exc : Exception[]) + + receiveRequest(parameters : Object[]) : String + # updateDb(parameters : Object[]) : String + } + ~class PaymentRequest { + ~ paid : boolean + ~ payment : float + ~ transactionId : String + ~ PaymentRequest(this$0 : String, transactionId : float) + } +} +package com.iluwatar.commander.employeehandle { + class EmployeeDatabase { + - data : Hashtable + + EmployeeDatabase() + + add(o : Order) : Order + + get(orderId : String) : Order + } + class EmployeeHandle { + + EmployeeHandle(db : EmployeeDatabase, exc : Exception[]) + + receiveRequest(parameters : Object[]) : String + # updateDb(parameters : Object[]) : String + } +} +Order --> "-messageSent" MessageSent +MessageSent ..+ Order +MessageToSend ..+ MessagingService +Retry --> "-op" Operation +Operation ..+ Retry +Service --> "-database" Database +Node --> "-next" Node +PaymentRequest --+ PaymentService +Commander --> "-messagingService" MessagingService +ShippingRequest ..+ ShippingService +Commander --> "-shippingService" ShippingService +Commander --> "-paymentService" PaymentService +MessageRequest --+ MessagingService +Commander --> "-employeeDb" EmployeeHandle +HandleErrorIssue ..+ Retry +Retry --> "-handleError" HandleErrorIssue +QueueTask --> "-taskType" TaskType +TaskType ..+ QueueTask +Order --> "-user" User +MessageRequest --> "-msg" MessageToSend +QueueTask --> "-order" Order +Commander --> "-queue" QueueDatabase +QueueDatabase --> "-data" Queue +Queue --> "-front" Node +Node ..+ Queue +Order --> "-paid" PaymentStatus +PaymentStatus ..+ Order +EmployeeDatabase --|> Database +EmployeeHandle --|> Service +MessagingDatabase --|> Database +MessagingService --|> Service +PaymentDatabase --|> Database +PaymentService --|> Service +QueueDatabase --|> Database +ShippingDatabase --|> Database +ShippingService --|> Service +@enduml \ No newline at end of file diff --git a/composite/README.md b/composite/README.md index 017a5079c47a..ba939b8035a6 100644 --- a/composite/README.md +++ b/composite/README.md @@ -154,6 +154,9 @@ var elfMessage = new Messenger().messageFromElves(); elfMessage.print(); // Much wind pours from your mouth. ``` +## Class diagram +![alt text](./etc/composite.urm.png "Composite class diagram") + ## Applicability Use the Composite pattern when diff --git a/composite/etc/composite.urm.png b/composite/etc/composite.urm.png new file mode 100644 index 000000000000..93c160f6450a Binary files /dev/null and b/composite/etc/composite.urm.png differ diff --git a/composite/etc/composite.urm.puml b/composite/etc/composite.urm.puml new file mode 100644 index 000000000000..6ff774711bcd --- /dev/null +++ b/composite/etc/composite.urm.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.composite { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Letter { + - character : char + + Letter(c : char) + # printThisBefore() + } + abstract class LetterComposite { + - children : List + + LetterComposite() + + add(letter : LetterComposite) + + count() : int + + print() + # printThisAfter() + # printThisBefore() + } + class Messenger { + + Messenger() + ~ messageFromElves() : LetterComposite + ~ messageFromOrcs() : LetterComposite + } + class Sentence { + + Sentence(words : List) + # printThisAfter() + } + class Word { + + Word(letters : List) + + Word(letters : char[]) + # printThisBefore() + } +} +LetterComposite --> "-children" LetterComposite +Letter --|> LetterComposite +Sentence --|> LetterComposite +Word --|> LetterComposite +@enduml \ No newline at end of file diff --git a/converter/README.md b/converter/README.md index 5cde646eedbc..824d47e3813a 100644 --- a/converter/README.md +++ b/converter/README.md @@ -15,8 +15,6 @@ conversion between corresponding types, allowing a clean implementation in which need to be aware of each other. Moreover, the Converter Pattern introduces bidirectional collection mapping, reducing a boilerplate code to minimum. -![alt text](./etc/converter.png "Converter Pattern") - ## Explanation Real world example @@ -82,6 +80,9 @@ UserDto dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com"); User user = userConverter.convertFromDto(dtoUser); ``` +## Class diagram +![alt text](./etc/converter.png "Converter Pattern") + ## Applicability Use the Converter Pattern in the following situations: diff --git a/converter/etc/converter.urm.puml b/converter/etc/converter.urm.puml new file mode 100644 index 000000000000..275af84e6bc8 --- /dev/null +++ b/converter/etc/converter.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.converter { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Converter { + - fromDto : Function + - fromEntity : Function + + Converter(fromDto : Function, fromEntity : Function) + + convertFromDto(dto : T) : U + + convertFromEntity(entity : U) : T + + createFromDtos(dtos : Collection) : List + + createFromEntities(entities : Collection) : List + } + class User { + - firstName : String + - isActive : boolean + - lastName : String + - userId : String + + User(firstName : String, lastName : String, isActive : boolean, userId : String) + + equals(o : Object) : boolean + + getFirstName() : String + + getLastName() : String + + getUserId() : String + + hashCode() : int + + isActive() : boolean + + toString() : String + } + class UserConverter { + + UserConverter() + } + class UserDto { + - email : String + - firstName : String + - isActive : boolean + - lastName : String + + UserDto(firstName : String, lastName : String, isActive : boolean, email : String) + + equals(o : Object) : boolean + + getEmail() : String + + getFirstName() : String + + getLastName() : String + + hashCode() : int + + isActive() : boolean + + toString() : String + } +} +UserConverter --|> Converter +@enduml \ No newline at end of file diff --git a/cqrs/README.md b/cqrs/README.md index 111c1ccd45ec..b9cbeacd8775 100644 --- a/cqrs/README.md +++ b/cqrs/README.md @@ -12,6 +12,7 @@ tags: ## Intent CQRS Command Query Responsibility Segregation - Separate the query side from the command side. +## Class diagram ![alt text](./etc/cqrs.png "CQRS") ## Applicability diff --git a/cqrs/etc/cqrs.urm.puml b/cqrs/etc/cqrs.urm.puml new file mode 100644 index 000000000000..2b06980983c5 --- /dev/null +++ b/cqrs/etc/cqrs.urm.puml @@ -0,0 +1,134 @@ +@startuml +package com.iluwatar.cqrs.util { + class HibernateUtil { + - LOGGER : Logger {static} + - SESSIONFACTORY : SessionFactory {static} + + HibernateUtil() + - buildSessionFactory() : SessionFactory {static} + + getSessionFactory() : SessionFactory {static} + } +} +package com.iluwatar.cqrs.app { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.cqrs.dto { + class Author { + - email : String + - name : String + - username : String + + Author() + + Author(name : String, email : String, username : String) + + equals(obj : Object) : boolean + + getEmail() : String + + getName() : String + + getUsername() : String + + hashCode() : int + + toString() : String + } + class Book { + - price : double + - title : String + + Book() + + Book(title : String, price : double) + + equals(obj : Object) : boolean + + getPrice() : double + + getTitle() : String + + hashCode() : int + + toString() : String + } +} +package com.iluwatar.cqrs.commandes { + class CommandServiceImpl { + - sessionFactory : SessionFactory + + CommandServiceImpl() + + authorCreated(username : String, name : String, email : String) + + authorEmailUpdated(username : String, email : String) + + authorNameUpdated(username : String, name : String) + + authorUsernameUpdated(oldUsername : String, newUsername : String) + + bookAddedToAuthor(title : String, price : double, username : String) + + bookPriceUpdated(title : String, price : double) + + bookTitleUpdated(oldTitle : String, newTitle : String) + - getAuthorByUsername(username : String) : Author + - getBookByTitle(title : String) : Book + } + interface ICommandService { + + authorCreated(String, String, String) {abstract} + + authorEmailUpdated(String, String) {abstract} + + authorNameUpdated(String, String) {abstract} + + authorUsernameUpdated(String, String) {abstract} + + bookAddedToAuthor(String, double, String) {abstract} + + bookPriceUpdated(String, double) {abstract} + + bookTitleUpdated(String, String) {abstract} + } +} +package com.iluwatar.cqrs.queries { + interface IQueryService { + + getAuthorBooks(String) : List {abstract} + + getAuthorBooksCount(String) : BigInteger {abstract} + + getAuthorByUsername(String) : Author {abstract} + + getAuthorsCount() : BigInteger {abstract} + + getBook(String) : Book {abstract} + } + class QueryServiceImpl { + - sessionFactory : SessionFactory + + QueryServiceImpl() + + getAuthorBooks(username : String) : List + + getAuthorBooksCount(username : String) : BigInteger + + getAuthorByUsername(username : String) : Author + + getAuthorsCount() : BigInteger + + getBook(title : String) : Book + } +} +package com.iluwatar.cqrs.constants { + class AppConstants { + + E_EVANS : String {static} + + J_BLOCH : String {static} + + M_FOWLER : String {static} + + USER_NAME : String {static} + + AppConstants() + } +} +package com.iluwatar.cqrs.domain.model { + class Author { + - email : String + - id : long + - name : String + - username : String + # Author() + + Author(username : String, name : String, email : String) + + getEmail() : String + + getId() : long + + getName() : String + + getUsername() : String + + setEmail(email : String) + + setId(id : long) + + setName(name : String) + + setUsername(username : String) + + toString() : String + } + class Book { + - author : Author + - id : long + - price : double + - title : String + # Book() + + Book(title : String, price : double, author : Author) + + getAuthor() : Author + + getId() : long + + getPrice() : double + + getTitle() : String + + setAuthor(author : Author) + + setId(id : long) + + setPrice(price : double) + + setTitle(title : String) + + toString() : String + } +} +Book --> "-author" Author +CommandServiceImpl ..|> ICommandService +QueryServiceImpl ..|> IQueryService +@enduml \ No newline at end of file diff --git a/dao/README.md b/dao/README.md index 785a1c362a58..e1120fbf2af4 100644 --- a/dao/README.md +++ b/dao/README.md @@ -13,6 +13,7 @@ tags: Object provides an abstract interface to some type of database or other persistence mechanism. +## Class diagram ![alt text](./etc/dao.png "Data Access Object") ## Applicability diff --git a/dao/etc/dao.urm.puml b/dao/etc/dao.urm.puml new file mode 100644 index 000000000000..12dfd5e51215 --- /dev/null +++ b/dao/etc/dao.urm.puml @@ -0,0 +1,68 @@ +@startuml +package com.iluwatar.dao { + class App { + - ALL_CUSTOMERS : String {static} + - DB_URL : String {static} + - log : Logger {static} + + App() + - addCustomers(customerDao : CustomerDao) {static} + - createDataSource() : DataSource {static} + - createSchema(dataSource : DataSource) {static} + - deleteSchema(dataSource : DataSource) {static} + + generateSampleCustomers() : List {static} + + main(args : String[]) {static} + - performOperationsUsing(customerDao : CustomerDao) {static} + } + class Customer { + - firstName : String + - id : int + - lastName : String + + Customer(id : int, firstName : String, lastName : String) + + equals(that : Object) : boolean + + getFirstName() : String + + getId() : int + + getLastName() : String + + hashCode() : int + + setFirstName(firstName : String) + + setId(id : int) + + setLastName(lastName : String) + + toString() : String + } + interface CustomerDao { + + add(Customer) : boolean {abstract} + + delete(Customer) : boolean {abstract} + + getAll() : Stream {abstract} + + getById(int) : Optional {abstract} + + update(Customer) : boolean {abstract} + } + class CustomerSchemaSql { + + CREATE_SCHEMA_SQL : String {static} + + DELETE_SCHEMA_SQL : String {static} + - CustomerSchemaSql() + } + class DbCustomerDao { + - LOGGER : Logger {static} + - dataSource : DataSource + + DbCustomerDao(dataSource : DataSource) + + add(customer : Customer) : boolean + - createCustomer(resultSet : ResultSet) : Customer + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + - getConnection() : Connection + - mutedClose(connection : Connection, statement : PreparedStatement, resultSet : ResultSet) + + update(customer : Customer) : boolean + } + class InMemoryCustomerDao { + - idToCustomer : Map + + InMemoryCustomerDao() + + add(customer : Customer) : boolean + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + + update(customer : Customer) : boolean + } +} +DbCustomerDao ..|> CustomerDao +InMemoryCustomerDao ..|> CustomerDao +@enduml \ No newline at end of file diff --git a/data-bus/README.md b/data-bus/README.md index 257192e25234..428c4db96f5b 100644 --- a/data-bus/README.md +++ b/data-bus/README.md @@ -16,6 +16,7 @@ Allows send of messages/events between components of an application without them needing to know about each other. They only need to know about the type of the message/event being sent. +## Class diagram ![data bus pattern uml diagram](./etc/data-bus.urm.png "Data Bus pattern") ## Applicability diff --git a/data-bus/etc/data-bus.urm.puml b/data-bus/etc/data-bus.urm.puml new file mode 100644 index 000000000000..8ca7f85efb11 --- /dev/null +++ b/data-bus/etc/data-bus.urm.puml @@ -0,0 +1,82 @@ +@startuml +package com.iluwatar.databus { + class AbstractDataType { + - dataBus : DataBus + + AbstractDataType() + + getDataBus() : DataBus + + setDataBus(dataBus : DataBus) + } + ~class App { + ~ App() + + main(args : String[]) {static} + } + class DataBus { + - INSTANCE : DataBus {static} + - listeners : Set + + DataBus() + + getInstance() : DataBus {static} + + publish(event : DataType) + + subscribe(member : Member) + + unsubscribe(member : Member) + } + interface DataType { + + getDataBus() : DataBus {abstract} + + setDataBus(DataBus) {abstract} + } + interface Member { + + accept(DataType) {abstract} + } +} +package com.iluwatar.databus.data { + class MessageData { + - message : String + + MessageData(message : String) + + getMessage() : String + + of(message : String) : DataType {static} + } + class StartingData { + - when : LocalDateTime + + StartingData(when : LocalDateTime) + + getWhen() : LocalDateTime + + of(when : LocalDateTime) : DataType {static} + } + class StoppingData { + - when : LocalDateTime + + StoppingData(when : LocalDateTime) + + getWhen() : LocalDateTime + + of(when : LocalDateTime) : DataType {static} + } +} +package com.iluwatar.databus.members { + class MessageCollectorMember { + - LOGGER : Logger {static} + - messages : List + - name : String + + MessageCollectorMember(name : String) + + accept(data : DataType) + + getMessages() : List + - handleEvent(data : MessageData) + } + class StatusMember { + - LOGGER : Logger {static} + - id : int + - started : LocalDateTime + - stopped : LocalDateTime + + StatusMember(id : int) + + accept(data : DataType) + + getStarted() : LocalDateTime + + getStopped() : LocalDateTime + - handleEvent(data : StartingData) + - handleEvent(data : StoppingData) + } +} +AbstractDataType --> "-dataBus" DataBus +DataBus --> "-INSTANCE" DataBus +DataBus --> "-listeners" Member +AbstractDataType ..|> DataType +MessageData --|> AbstractDataType +StartingData --|> AbstractDataType +StoppingData --|> AbstractDataType +MessageCollectorMember ..|> Member +StatusMember ..|> Member +@enduml \ No newline at end of file diff --git a/data-locality/README.md b/data-locality/README.md index 6a72a45194b3..8811e59acd7b 100644 --- a/data-locality/README.md +++ b/data-locality/README.md @@ -15,6 +15,8 @@ Accelerate memory access by arranging data to take advantage of CPU caching. Modern CPUs have caches to speed up memory access. These can access memory adjacent to recently accessed memory much quicker. Take advantage of that to improve performance by increasing data locality keeping data in contiguous memory in the order that you process it. +## Class diagram +![alt text](./etc/data-locality.urm.png "Data Locality pattern class diagram") ## Applicability diff --git a/data-locality/etc/data-locality.urm.png b/data-locality/etc/data-locality.urm.png new file mode 100644 index 000000000000..d19873739551 Binary files /dev/null and b/data-locality/etc/data-locality.urm.png differ diff --git a/data-locality/etc/data-locality.urm.puml b/data-locality/etc/data-locality.urm.puml new file mode 100644 index 000000000000..1e2d3e6793cb --- /dev/null +++ b/data-locality/etc/data-locality.urm.puml @@ -0,0 +1,80 @@ +@startuml +package com.iluwatar.data.locality.game.component.manager { + class AiComponentManager { + - AI_COMPONENTS : Component[] {static} + - LOGGER : Logger {static} + - MAX_ENTITIES : int {static} + - numEntities : int + + AiComponentManager(numEntities : int) + + start() + + update() + } + class PhysicsComponentManager { + - LOGGER : Logger {static} + - MAX_ENTITIES : int {static} + - PHYSICS_COMPONENTS : Component[] {static} + - numEntities : int + + PhysicsComponentManager(numEntities : int) + + start() + + update() + } + class RenderComponentManager { + - LOGGER : Logger {static} + - MAX_ENTITIES : int {static} + - RENDER_COMPONENTS : Component[] {static} + - numEntities : int + + RenderComponentManager(numEntities : int) + + render() + + start() + } +} +package com.iluwatar.data.locality { + class Application { + - LOGGER : Logger {static} + - NUM_ENTITIES : int {static} + + Application() + + main(args : String[]) {static} + } +} +package com.iluwatar.data.locality.game { + class GameEntity { + - LOGGER : Logger {static} + - aiComponentManager : AiComponentManager + - physicsComponentManager : PhysicsComponentManager + - renderComponentManager : RenderComponentManager + + GameEntity(numEntities : int) + + start() + + update() + } +} +package com.iluwatar.data.locality.game.component { + class AiComponent { + - LOGGER : Logger {static} + + AiComponent() + + render() + + update() + } + interface Component { + + render() {abstract} + + update() {abstract} + } + class PhysicsComponent { + - LOGGER : Logger {static} + + PhysicsComponent() + + render() + + update() + } + class RenderComponent { + - LOGGER : Logger {static} + + RenderComponent() + + render() + + update() + } +} +GameEntity --> "-physicsComponentManager" PhysicsComponentManager +GameEntity --> "-aiComponentManager" AiComponentManager +GameEntity --> "-renderComponentManager" RenderComponentManager +AiComponent ..|> Component +PhysicsComponent ..|> Component +RenderComponent ..|> Component +@enduml \ No newline at end of file diff --git a/data-mapper/README.md b/data-mapper/README.md index 075e8eecef15..f23d5a62ac8e 100644 --- a/data-mapper/README.md +++ b/data-mapper/README.md @@ -12,6 +12,7 @@ tags: ## Intent A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself +## Class diagram ![alt text](./etc/data-mapper.png "Data Mapper") ## Applicability diff --git a/data-mapper/etc/data-mapper.urm.puml b/data-mapper/etc/data-mapper.urm.puml new file mode 100644 index 000000000000..3b32411d11c9 --- /dev/null +++ b/data-mapper/etc/data-mapper.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.datamapper { + class App { + - STUDENT_STRING : String {static} + - log : Logger {static} + - App() + + main(args : String[]) {static} + } + class Student { + - grade : char + - name : String + - serialVersionUID : long {static} + - studentId : int + + Student(studentId : int, name : String, grade : char) + + equals(inputObject : Object) : boolean + + getGrade() : char + + getName() : String + + getStudentId() : int + + hashCode() : int + + setGrade(grade : char) + + setName(name : String) + + setStudentId(studentId : int) + + toString() : String + } + interface StudentDataMapper { + + delete(Student) {abstract} + + find(int) : Optional {abstract} + + insert(Student) {abstract} + + update(Student) {abstract} + } + class StudentDataMapperImpl { + - students : List + + StudentDataMapperImpl() + + delete(studentToBeDeleted : Student) + + find(studentId : int) : Optional + + getStudents() : List + + insert(studentToBeInserted : Student) + + update(studentToBeUpdated : Student) + } +} +StudentDataMapperImpl --> "-students" Student +StudentDataMapperImpl ..|> StudentDataMapper +@enduml \ No newline at end of file diff --git a/data-transfer-object/README.md b/data-transfer-object/README.md index ad9b9f4e2198..c023795273cc 100644 --- a/data-transfer-object/README.md +++ b/data-transfer-object/README.md @@ -15,6 +15,7 @@ tags: Pass data with multiple attributes in one shot from client to server, to avoid multiple calls to remote server. +## Class diagram ![alt text](./etc/data-transfer-object.urm.png "data-transfer-object") ## Applicability diff --git a/data-transfer-object/etc/data-transfer-object.urm.puml b/data-transfer-object/etc/data-transfer-object.urm.puml new file mode 100644 index 000000000000..ee96c37ca967 --- /dev/null +++ b/data-transfer-object/etc/data-transfer-object.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.datatransfer { + class CustomerClientApp { + - LOGGER : Logger {static} + + CustomerClientApp() + + main(args : String[]) {static} + - printCustomerDetails(allCustomers : List) {static} + } + class CustomerDto { + - firstName : String + - id : String + - lastName : String + + CustomerDto(id : String, firstName : String, lastName : String) + + getFirstName() : String + + getId() : String + + getLastName() : String + } + class CustomerResource { + - customers : List + + CustomerResource(customers : List) + + delete(customerId : String) + + getAllCustomers() : List + + save(customer : CustomerDto) + } +} +CustomerResource --> "-customers" CustomerDto +@enduml \ No newline at end of file diff --git a/decorator/README.md b/decorator/README.md index c003325fddca..6adb5d057639 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -109,6 +109,9 @@ clubbedTroll.attack(); // The troll tries to grab you! The troll swings at you w clubbedTroll.fleeBattle(); // The troll shrieks in horror and runs away! ``` +## Class diagram +![alt text](./etc/decorator.urm.png "Decorator pattern class diagram") + ## Applicability Use Decorator diff --git a/decorator/etc/decorator.urm.png b/decorator/etc/decorator.urm.png new file mode 100644 index 000000000000..141c0563f0c6 Binary files /dev/null and b/decorator/etc/decorator.urm.png differ diff --git a/decorator/etc/decorator.urm.puml b/decorator/etc/decorator.urm.puml new file mode 100644 index 000000000000..edfd927600f5 --- /dev/null +++ b/decorator/etc/decorator.urm.puml @@ -0,0 +1,32 @@ +@startuml +package com.iluwatar.decorator { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class ClubbedTroll { + - LOGGER : Logger {static} + - decorated : Troll + + ClubbedTroll(decorated : Troll) + + attack() + + fleeBattle() + + getAttackPower() : int + } + class SimpleTroll { + - LOGGER : Logger {static} + + SimpleTroll() + + attack() + + fleeBattle() + + getAttackPower() : int + } + interface Troll { + + attack() {abstract} + + fleeBattle() {abstract} + + getAttackPower() : int {abstract} + } +} +ClubbedTroll --> "-decorated" Troll +ClubbedTroll ..|> Troll +SimpleTroll ..|> Troll +@enduml \ No newline at end of file diff --git a/delegation/README.md b/delegation/README.md index e5c0c63760c4..b18c6f7f3470 100644 --- a/delegation/README.md +++ b/delegation/README.md @@ -16,6 +16,7 @@ Proxy Pattern It is a technique where an object expresses certain behavior to the outside but in reality delegates responsibility for implementing that behaviour to an associated object. +## Class diagram ![alt text](./etc/delegation.png "Delegate") ## Applicability diff --git a/delegation/etc/delegation.urm.puml b/delegation/etc/delegation.urm.puml new file mode 100644 index 000000000000..169f7103f061 --- /dev/null +++ b/delegation/etc/delegation.urm.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.delegation.simple.printers { + class CanonPrinter { + - LOGGER : Logger {static} + + CanonPrinter() + + print(message : String) + } + class EpsonPrinter { + - LOGGER : Logger {static} + + EpsonPrinter() + + print(message : String) + } + class HpPrinter { + - LOGGER : Logger {static} + + HpPrinter() + + print(message : String) + } +} +package com.iluwatar.delegation.simple { + class App { + - MESSAGE_TO_PRINT : String {static} + + App() + + main(args : String[]) {static} + } + interface Printer { + + print(String) {abstract} + } + class PrinterController { + - printer : Printer + + PrinterController(printer : Printer) + + print(message : String) + } +} +PrinterController --> "-printer" Printer +PrinterController ..|> Printer +CanonPrinter ..|> Printer +EpsonPrinter ..|> Printer +HpPrinter ..|> Printer +@enduml \ No newline at end of file diff --git a/dependency-injection/README.md b/dependency-injection/README.md index 735f589b1555..ba81dc7034c5 100644 --- a/dependency-injection/README.md +++ b/dependency-injection/README.md @@ -17,6 +17,7 @@ pattern separates the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and to follow the inversion of control and single responsibility principles. +## Class diagram ![alt text](./etc/dependency-injection.png "Dependency Injection") ## Applicability diff --git a/dependency-injection/etc/dependency-injection.urm.puml b/dependency-injection/etc/dependency-injection.urm.puml new file mode 100644 index 000000000000..84ed3c6dd321 --- /dev/null +++ b/dependency-injection/etc/dependency-injection.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.dependency.injection { + class AdvancedSorceress { + - tobacco : Tobacco + + AdvancedSorceress() + + setTobacco(tobacco : Tobacco) + + smoke() + } + class AdvancedWizard { + - tobacco : Tobacco + + AdvancedWizard(tobacco : Tobacco) + + smoke() + } + class App { + + App() + + main(args : String[]) {static} + } + class GuiceWizard { + - tobacco : Tobacco + + GuiceWizard(tobacco : Tobacco) + + smoke() + } + class OldTobyTobacco { + + OldTobyTobacco() + } + class RivendellTobacco { + + RivendellTobacco() + } + class SecondBreakfastTobacco { + + SecondBreakfastTobacco() + } + class SimpleWizard { + - tobacco : OldTobyTobacco + + SimpleWizard() + + smoke() + } + abstract class Tobacco { + - LOGGER : Logger {static} + + Tobacco() + + smoke(wizard : Wizard) + } + interface Wizard { + + smoke() {abstract} + } +} +AdvancedSorceress --> "-tobacco" Tobacco +SimpleWizard --> "-tobacco" OldTobyTobacco +AdvancedWizard --> "-tobacco" Tobacco +GuiceWizard --> "-tobacco" Tobacco +AdvancedSorceress ..|> Wizard +AdvancedWizard ..|> Wizard +GuiceWizard ..|> Wizard +OldTobyTobacco --|> Tobacco +RivendellTobacco --|> Tobacco +SecondBreakfastTobacco --|> Tobacco +SimpleWizard ..|> Wizard +@enduml \ No newline at end of file diff --git a/dirty-flag/README.md b/dirty-flag/README.md index bea4d324bb69..885c7f03b5f5 100644 --- a/dirty-flag/README.md +++ b/dirty-flag/README.md @@ -14,6 +14,7 @@ tags: To avoid expensive re-acquisition of resources. The resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them again. +## Class diagram ![alt text](./etc/dirty-flag.png "Dirty Flag") ## Applicability diff --git a/dirty-flag/etc/dirty-flag.urm.puml b/dirty-flag/etc/dirty-flag.urm.puml new file mode 100644 index 000000000000..9685d3fdfbe4 --- /dev/null +++ b/dirty-flag/etc/dirty-flag.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.dirtyflag { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + + run() + } + class DataFetcher { + - LOGGER : Logger {static} + - filename : String + - lastFetched : long + + DataFetcher() + + fetch() : List + - isDirty(fileLastModified : long) : boolean + } + class World { + - countries : List + - df : DataFetcher + + World() + + fetch() : List + } +} +World --> "-df" DataFetcher +@enduml \ No newline at end of file diff --git a/double-buffer/README.md b/double-buffer/README.md index 9cd5ebfa8420..d187ee2e2a98 100644 --- a/double-buffer/README.md +++ b/double-buffer/README.md @@ -12,6 +12,9 @@ tags: ## Intent Double buffering is a term used to describe a device that has two buffers. The usage of multiple buffers increases the overall throughput of a device and helps prevents bottlenecks. This example shows using double buffer pattern on graphics. It is used to show one image or frame while a separate frame is being buffered to be shown next. This method makes animations and games look more realistic than the same done in a single buffer mode. +## Class diagram +![alt text](./etc/double-buffer.urm.png "Double Buffer pattern class diagram") + ## Applicability This pattern is one of those ones where you’ll know when you need it. If you have a system that lacks double buffering, it will probably look visibly wrong (tearing, etc.) or will behave incorrectly. But saying, “you’ll know when you need it” doesn’t give you much to go on. More specifically, this pattern is appropriate when all of these are true: @@ -25,4 +28,4 @@ This pattern is one of those ones where you’ll know when you need it. If you h ## Credits -* [Game Programming Patterns - Double Buffer]([http://gameprogrammingpatterns.com/double-buffer.html](http://gameprogrammingpatterns.com/double-buffer.html)) \ No newline at end of file +* [Game Programming Patterns - Double Buffer]([http://gameprogrammingpatterns.com/double-buffer.html](http://gameprogrammingpatterns.com/double-buffer.html)) diff --git a/double-buffer/etc/double-buffer.urm.png b/double-buffer/etc/double-buffer.urm.png new file mode 100644 index 000000000000..072ec4dad8be Binary files /dev/null and b/double-buffer/etc/double-buffer.urm.png differ diff --git a/double-buffer/etc/double-buffer.urm.puml b/double-buffer/etc/double-buffer.urm.puml new file mode 100644 index 000000000000..7555c972b5ed --- /dev/null +++ b/double-buffer/etc/double-buffer.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.doublebuffer { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + - printBlackPixelCoordinate(buffer : Buffer) {static} + } + interface Buffer { + + clear(int, int) {abstract} + + clearAll() {abstract} + + draw(int, int) {abstract} + + getPixels() : Pixel[] {abstract} + } + class FrameBuffer { + + HEIGHT : int {static} + + WIDTH : int {static} + - pixels : Pixel[] + + FrameBuffer() + + clear(x : int, y : int) + + clearAll() + + draw(x : int, y : int) + - getIndex(x : int, y : int) : int + + getPixels() : Pixel[] + } + enum Pixel { + + BLACK {static} + + WHITE {static} + - color : int + + valueOf(name : String) : Pixel {static} + + values() : Pixel[] {static} + } + class Scene { + - LOGGER : Logger {static} + - current : int + - frameBuffers : Buffer[] + - next : int + + Scene() + + draw(coordinateList : List>) + + getBuffer() : Buffer + - swap() + } +} +FrameBuffer ..|> Buffer +@enduml \ No newline at end of file diff --git a/double-checked-locking/README.md b/double-checked-locking/README.md index da1fdd1a2ee7..d1077951a09d 100644 --- a/double-checked-locking/README.md +++ b/double-checked-locking/README.md @@ -16,6 +16,7 @@ locking criterion (the "lock hint") without actually acquiring the lock. Only if the locking criterion check indicates that locking is required does the actual locking logic proceed. +## Class diagram ![alt text](./etc/double_checked_locking_1.png "Double Checked Locking") ## Applicability diff --git a/double-checked-locking/etc/double-checked-locking.urm.puml b/double-checked-locking/etc/double-checked-locking.urm.puml new file mode 100644 index 000000000000..242519fce77c --- /dev/null +++ b/double-checked-locking/etc/double-checked-locking.urm.puml @@ -0,0 +1,22 @@ +@startuml +package com.iluwatar.doublechecked.locking { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Inventory { + - LOGGER : Logger {static} + - inventorySize : int + - items : List + - lock : Lock + + Inventory(inventorySize : int) + + addItem(item : Item) : boolean + + getItems() : List + } + class Item { + + Item() + } +} +Inventory --> "-items" Item +@enduml \ No newline at end of file diff --git a/double-dispatch/README.md b/double-dispatch/README.md index ae87208a2639..dfc86a8270c3 100644 --- a/double-dispatch/README.md +++ b/double-dispatch/README.md @@ -14,6 +14,7 @@ tags: Double Dispatch pattern is a way to create maintainable dynamic behavior based on receiver and parameter types. +## Class diagram ![alt text](./etc/double-dispatch.png "Double Dispatch") ## Applicability diff --git a/double-dispatch/etc/double-dispatch.urm.puml b/double-dispatch/etc/double-dispatch.urm.puml new file mode 100644 index 000000000000..17bcb1431bd6 --- /dev/null +++ b/double-dispatch/etc/double-dispatch.urm.puml @@ -0,0 +1,74 @@ +@startuml +package com.iluwatar.doubledispatch.constants { + class AppConstants { + + HITS : String {static} + + AppConstants() + } +} +package com.iluwatar.doubledispatch { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class FlamingAsteroid { + + FlamingAsteroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + abstract class GameObject { + - damaged : boolean + - onFire : boolean + + GameObject(left : int, top : int, right : int, bottom : int) + + collision(GameObject) {abstract} + + collisionResolve(FlamingAsteroid) {abstract} + + collisionResolve(Meteoroid) {abstract} + + collisionResolve(SpaceStationIss) {abstract} + + collisionResolve(SpaceStationMir) {abstract} + + isDamaged() : boolean + + isOnFire() : boolean + + setDamaged(damaged : boolean) + + setOnFire(onFire : boolean) + + toString() : String + } + class Meteoroid { + - LOGGER : Logger {static} + + Meteoroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } + class Rectangle { + - bottom : int + - left : int + - right : int + - top : int + + Rectangle(left : int, top : int, right : int, bottom : int) + + getBottom() : int + + getLeft() : int + + getRight() : int + + getTop() : int + ~ intersectsWith(r : Rectangle) : boolean + + toString() : String + } + class SpaceStationIss { + + SpaceStationIss(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + class SpaceStationMir { + - LOGGER : Logger {static} + + SpaceStationMir(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } +} +FlamingAsteroid --|> Meteoroid +GameObject --|> Rectangle +Meteoroid --|> GameObject +SpaceStationIss --|> SpaceStationMir +SpaceStationMir --|> GameObject +@enduml \ No newline at end of file diff --git a/eip-aggregator/README.md b/eip-aggregator/README.md index a1dc18ea8fb8..2b55b4e09b4b 100644 --- a/eip-aggregator/README.md +++ b/eip-aggregator/README.md @@ -17,7 +17,8 @@ the best parameters. Aggregator allows you to merge messages based on defined criteria and parameters. It gathers original messages, applies aggregation strategy and upon fulfilling given criteria, releasing merged messages. - + +## Diagram ![alt text](./etc/aggregator.gif "Splitter") ## Applicability @@ -30,4 +31,3 @@ Use the Aggregator pattern when * [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Aggregator.html) * [Apache Camel - Documentation](http://camel.apache.org/aggregator2.html) - diff --git a/eip-aggregator/etc/eip-aggregator.urm.puml b/eip-aggregator/etc/eip-aggregator.urm.puml new file mode 100644 index 000000000000..4d8661e210c2 --- /dev/null +++ b/eip-aggregator/etc/eip-aggregator.urm.puml @@ -0,0 +1,14 @@ +@startuml +package com.iluwatar.eip.aggregator { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.eip.aggregator.routes { + class MessageAggregationStrategy { + + MessageAggregationStrategy() + + aggregate(oldExchange : Exchange, newExchange : Exchange) : Exchange + } +} +@enduml \ No newline at end of file diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java index 491ddaf0b339..34fa6e3077ba 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java @@ -27,7 +27,6 @@ import org.apache.camel.builder.RouteBuilder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; /** * Sometimes in enterprise systems there is a need to group incoming data in order to process it as @@ -49,19 +48,17 @@ public class App { */ public static void main(String[] args) throws Exception { // Run Spring Boot application and obtain ApplicationContext - ConfigurableApplicationContext context = SpringApplication.run(App.class, args); + var context = SpringApplication.run(App.class, args); // Get CamelContext from ApplicationContext - CamelContext camelContext = (CamelContext) context.getBean("camelContext"); + var camelContext = (CamelContext) context.getBean("camelContext"); // Add a new routes that will handle endpoints form SplitterRoute class. camelContext.addRoutes(new RouteBuilder() { - @Override - public void configure() throws Exception { + public void configure() { from("{{endpoint}}").log("ENDPOINT: ${body}"); } - }); // Add producer that will send test message to an entry point in WireTapRoute diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java index 60499e1b8183..2465fd960aaa 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java @@ -48,8 +48,6 @@ public class AggregatorRoute extends RouteBuilder { /** * Configures the route. - * - * @throws Exception in case of exception during configuration */ @Override public void configure() { diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java index d38923ecc939..9fd4c933acab 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java @@ -40,8 +40,8 @@ public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { return newExchange; } - String in1 = (String) oldExchange.getIn().getBody(); - String in2 = (String) newExchange.getIn().getBody(); + var in1 = (String) oldExchange.getIn().getBody(); + var in2 = (String) newExchange.getIn().getBody(); oldExchange.getIn().setBody(in1 + ";" + in2); diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java index 1dc0311a0c71..ed604e8c2060 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java @@ -32,7 +32,6 @@ public class AppTest { @Test public void testMain() throws Exception { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java index ff83321964f9..e1c6f5b2bc62 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java @@ -23,6 +23,8 @@ package com.iluwatar.eip.aggregator.routes; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.apache.camel.EndpointInject; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.mock.MockEndpoint; @@ -35,13 +37,11 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test class for AggregatorRoute. *

- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need to substitute - * original endpoint names to mocks. + * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need + * to substitute original endpoint names to mocks. *

*/ @ExtendWith(SpringExtension.class) @@ -59,6 +59,7 @@ public class AggregatorRouteTest { /** * Test if endpoint receives three separate messages. + * * @throws Exception in case of en exception during the test */ @Test @@ -76,10 +77,10 @@ public void testSplitter() throws Exception { endpoint.expectedMessageCount(2); endpoint.assertIsSatisfied(); - String body = (String) endpoint.getReceivedExchanges().get(0).getIn().getBody(); + var body = (String) endpoint.getReceivedExchanges().get(0).getIn().getBody(); assertEquals(3, body.split(";").length); - String body2 = (String) endpoint.getReceivedExchanges().get(1).getIn().getBody(); + var body2 = (String) endpoint.getReceivedExchanges().get(1).getIn().getBody(); assertEquals(2, body2.split(";").length); } } diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java index 69862faf1fe9..8325e725521b 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java @@ -23,13 +23,12 @@ package com.iluwatar.eip.aggregator.routes; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.apache.camel.CamelContext; -import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Tests MessageAggregationStrategy */ @@ -37,27 +36,27 @@ public class MessageAggregationStrategyTest { @Test public void testAggregate() { - MessageAggregationStrategy mas = new MessageAggregationStrategy(); - Exchange oldExchange = new DefaultExchange((CamelContext) null); + var mas = new MessageAggregationStrategy(); + var oldExchange = new DefaultExchange((CamelContext) null); oldExchange.getIn().setBody("TEST1"); - Exchange newExchange = new DefaultExchange((CamelContext) null); + var newExchange = new DefaultExchange((CamelContext) null); newExchange.getIn().setBody("TEST2"); - Exchange output = mas.aggregate(oldExchange, newExchange); - String outputBody = (String) output.getIn().getBody(); + var output = mas.aggregate(oldExchange, newExchange); + var outputBody = (String) output.getIn().getBody(); assertEquals("TEST1;TEST2", outputBody); } @Test public void testAggregateOldNull() { - MessageAggregationStrategy mas = new MessageAggregationStrategy(); + var mas = new MessageAggregationStrategy(); - Exchange newExchange = new DefaultExchange((CamelContext) null); + var newExchange = new DefaultExchange((CamelContext) null); newExchange.getIn().setBody("TEST2"); - Exchange output = mas.aggregate(null, newExchange); - String outputBody = (String) output.getIn().getBody(); + var output = mas.aggregate(null, newExchange); + var outputBody = (String) output.getIn().getBody(); assertEquals(newExchange, output); assertEquals("TEST2", outputBody); diff --git a/eip-message-channel/README.md b/eip-message-channel/README.md index 606619619e8f..f43072f52928 100644 --- a/eip-message-channel/README.md +++ b/eip-message-channel/README.md @@ -14,6 +14,7 @@ tags: When two applications communicate using a messaging system they do it by using logical addresses of the system, so called Message Channels. +## Class diagram ![alt text](./etc/message-channel.png "Message Channel") ## Applicability diff --git a/eip-message-channel/etc/eip-message-channel.urm.puml b/eip-message-channel/etc/eip-message-channel.urm.puml new file mode 100644 index 000000000000..38e2369ce562 --- /dev/null +++ b/eip-message-channel/etc/eip-message-channel.urm.puml @@ -0,0 +1,9 @@ +@startuml +package com.iluwatar.eip.message.channel { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java b/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java index 78a23bdd34a0..fefac5e0d8b0 100644 --- a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java +++ b/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java @@ -23,7 +23,6 @@ package com.iluwatar.eip.message.channel; -import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; import org.slf4j.Logger; @@ -57,7 +56,7 @@ public class App { * Program entry point. */ public static void main(String[] args) throws Exception { - CamelContext context = new DefaultCamelContext(); + var context = new DefaultCamelContext(); context.addRoutes(new RouteBuilder() { diff --git a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java b/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java index c518a7cad16a..9f11c0209561 100644 --- a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java +++ b/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java @@ -26,15 +26,12 @@ import org.junit.jupiter.api.Test; /** - * * Application test - * */ public class AppTest { @Test public void test() throws Exception { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/eip-publish-subscribe/README.md b/eip-publish-subscribe/README.md index 90ac02c9b462..f9ec20fdfd24 100644 --- a/eip-publish-subscribe/README.md +++ b/eip-publish-subscribe/README.md @@ -13,6 +13,7 @@ tags: ## Intent Broadcast messages from sender to all the interested receivers. +## Class diagram ![alt text](./etc/publish-subscribe.png "Publish Subscribe Channel") ## Applicability diff --git a/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml b/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml new file mode 100644 index 000000000000..a201f59bba67 --- /dev/null +++ b/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml @@ -0,0 +1,9 @@ +@startuml +package com.iluwatar.eip.publish.subscribe { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java b/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java index c8a311374734..a726432481a2 100644 --- a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java +++ b/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java @@ -23,8 +23,6 @@ package com.iluwatar.eip.publish.subscribe; -import org.apache.camel.CamelContext; -import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; import org.slf4j.Logger; @@ -55,14 +53,14 @@ public class App { * Program entry point. */ public static void main(String[] args) throws Exception { - CamelContext context = new DefaultCamelContext(); + var context = new DefaultCamelContext(); context.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:origin").multicast().to("mock:foo", "mock:bar", "stream:out"); } }); - ProducerTemplate template = context.createProducerTemplate(); + var template = context.createProducerTemplate(); context.start(); context.getRoutes().forEach(r -> LOGGER.info(r.toString())); template.sendBody("direct:origin", "Hello from origin"); diff --git a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java b/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java index f39b8d0ada1a..107e954edfa5 100644 --- a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java +++ b/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java @@ -26,15 +26,12 @@ import org.junit.jupiter.api.Test; /** - * * Application test - * */ public class AppTest { @Test public void test() throws Exception { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/eip-splitter/README.md b/eip-splitter/README.md index 51a917badd4b..a2eedbb38ea1 100644 --- a/eip-splitter/README.md +++ b/eip-splitter/README.md @@ -17,6 +17,7 @@ service/sold goods, price etc.). Such bundled messages may not be accepted by ot pattern comes in handy. It will take the whole document, split it based on given criteria and send individual items to the endpoint. +## Diagram ![alt text](./etc/sequencer.gif "Splitter") ## Applicability @@ -29,4 +30,3 @@ Use the Splitter pattern when * [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html) * [Apache Camel - Documentation](http://camel.apache.org/splitter.html) - diff --git a/eip-splitter/etc/eip-splitter.urm.puml b/eip-splitter/etc/eip-splitter.urm.puml new file mode 100644 index 000000000000..ad063b709c3d --- /dev/null +++ b/eip-splitter/etc/eip-splitter.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.eip.splitter { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java index b0e338f27a51..2792e0be4c1c 100644 --- a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java +++ b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java @@ -27,7 +27,6 @@ import org.apache.camel.builder.RouteBuilder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; /** * It is very common in integration systems that incoming messages consists of many items bundled @@ -53,10 +52,10 @@ public class App { */ public static void main(String[] args) throws Exception { // Run Spring Boot application and obtain ApplicationContext - ConfigurableApplicationContext context = SpringApplication.run(App.class, args); + var context = SpringApplication.run(App.class, args); // Get CamelContext from ApplicationContext - CamelContext camelContext = (CamelContext) context.getBean("camelContext"); + var camelContext = (CamelContext) context.getBean("camelContext"); // Add a new routes that will handle endpoints form SplitterRoute class. camelContext.addRoutes(new RouteBuilder() { diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java index 4f8088c6d3a8..1a7dfcb0a45d 100644 --- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java +++ b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java @@ -32,7 +32,6 @@ public class AppTest { @Test public void testMain() throws Exception { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java index 3c8bdac91f0d..037954609e2d 100644 --- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java +++ b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java @@ -38,8 +38,8 @@ /** * Test class for SplitterRoute. *

- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need to substitute - * original endpoint names to mocks. + * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need + * to substitute original endpoint names to mocks. *

*/ @ExtendWith(SpringExtension.class) @@ -57,6 +57,7 @@ public class SplitterRouteTest { /** * Test if endpoint receives three separate messages. + * * @throws Exception in case of en exception during the test */ @Test @@ -64,7 +65,7 @@ public class SplitterRouteTest { public void testSplitter() throws Exception { // Three items in one entry message - entry.sendBody(new String[] {"TEST1", "TEST2", "TEST3"}); + entry.sendBody(new String[]{"TEST1", "TEST2", "TEST3"}); // Endpoint should have three different messages in the end order of the messages is not important endpoint.expectedMessageCount(3); diff --git a/eip-wire-tap/README.md b/eip-wire-tap/README.md index 31cbd9123603..b4609975ebba 100644 --- a/eip-wire-tap/README.md +++ b/eip-wire-tap/README.md @@ -15,6 +15,7 @@ In most integration cases there is a need to monitor the messages flowing throug by intercepting the message and redirecting it to a different location like console, filesystem or the database. It is important that such functionality should not modify the original message and influence the processing path. +## Diagram ![alt text](./etc/wiretap.gif "Wire Tap") ## Applicability diff --git a/eip-wire-tap/etc/eip-wire-tap.urm.puml b/eip-wire-tap/etc/eip-wire-tap.urm.puml new file mode 100644 index 000000000000..51ee99723cc8 --- /dev/null +++ b/eip-wire-tap/etc/eip-wire-tap.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.eip.wiretap { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java index 36407fbf715a..f3b5c11c1541 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java @@ -27,7 +27,6 @@ import org.apache.camel.builder.RouteBuilder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; /** * In most integration cases there is a need to monitor the messages flowing through the system. It @@ -52,10 +51,10 @@ public class App { */ public static void main(String[] args) throws Exception { // Run Spring Boot application and obtain ApplicationContext - ConfigurableApplicationContext context = SpringApplication.run(App.class, args); + var context = SpringApplication.run(App.class, args); // Get CamelContext from ApplicationContext - CamelContext camelContext = (CamelContext) context.getBean("camelContext"); + var camelContext = (CamelContext) context.getBean("camelContext"); // Add a new routes that will handle endpoints form WireTapRoute class. camelContext.addRoutes(new RouteBuilder() { diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java index fe2a01acbd5f..31154043c31a 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java @@ -32,7 +32,6 @@ public class AppTest { @Test public void testMain() throws Exception { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java index d134c53e5e16..0d13374dc5f8 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java @@ -23,8 +23,9 @@ package com.iluwatar.eip.wiretap.routes; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.apache.camel.EndpointInject; -import org.apache.camel.Message; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.mock.MockEndpoint; import org.junit.jupiter.api.Test; @@ -36,13 +37,11 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test class for WireTapRoute. *

- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need to substitute - * original endpoint names to mocks. + * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need + * to substitute original endpoint names to mocks. *

*/ @ExtendWith(SpringExtension.class) @@ -63,6 +62,7 @@ public class WireTapRouteTest { /** * Test if both endpoints receive exactly one message containing the same, unchanged body. + * * @throws Exception in case of en exception during the test */ @Test @@ -76,8 +76,8 @@ public void testWireTap() throws Exception { endpoint.assertIsSatisfied(); wireTapEndpoint.assertIsSatisfied(); - Message endpointIn = endpoint.getExchanges().get(0).getIn(); - Message wireTapEndpointIn = wireTapEndpoint.getExchanges().get(0).getIn(); + var endpointIn = endpoint.getExchanges().get(0).getIn(); + var wireTapEndpointIn = wireTapEndpoint.getExchanges().get(0).getIn(); assertEquals("TEST", endpointIn.getBody()); assertEquals("TEST", wireTapEndpointIn.getBody()); diff --git a/etc/java-design-patterns.urm.puml b/etc/java-design-patterns.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/etc/java-design-patterns.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/event-aggregator/README.md b/event-aggregator/README.md index ac07869e7a89..79965bce1d7a 100644 --- a/event-aggregator/README.md +++ b/event-aggregator/README.md @@ -18,6 +18,7 @@ requires a separate subscription. An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator. +## Class diagram ![alt text](./etc/classes.png "Event Aggregator") ## Applicability diff --git a/event-aggregator/etc/event-aggregator.urm.puml b/event-aggregator/etc/event-aggregator.urm.puml new file mode 100644 index 000000000000..341d7d65e02c --- /dev/null +++ b/event-aggregator/etc/event-aggregator.urm.puml @@ -0,0 +1,74 @@ +@startuml +package com.iluwatar.event.aggregator { + class App { + + App() + + main(args : String[]) {static} + } + enum Event { + + STARK_SIGHTED {static} + + TRAITOR_DETECTED {static} + + WARSHIPS_APPROACHING {static} + - description : String + + toString() : String + + valueOf(name : String) : Event {static} + + values() : Event[] {static} + } + abstract class EventEmitter { + - observers : List + + EventEmitter() + + EventEmitter(obs : EventObserver) + # notifyObservers(e : Event) + + registerObserver(obs : EventObserver) + + timePasses(Weekday) {abstract} + } + interface EventObserver { + + onEvent(Event) {abstract} + } + class KingJoffrey { + - LOGGER : Logger {static} + + KingJoffrey() + + onEvent(e : Event) + } + class KingsHand { + + KingsHand() + + KingsHand(obs : EventObserver) + + onEvent(e : Event) + + timePasses(day : Weekday) + } + class LordBaelish { + + LordBaelish() + + LordBaelish(obs : EventObserver) + + timePasses(day : Weekday) + } + class LordVarys { + + LordVarys() + + LordVarys(obs : EventObserver) + + timePasses(day : Weekday) + } + class Scout { + + Scout() + + Scout(obs : EventObserver) + + timePasses(day : Weekday) + } + enum Weekday { + + FRIDAY {static} + + MONDAY {static} + + SATURDAY {static} + + SUNDAY {static} + + THURSDAY {static} + + TUESDAY {static} + + WEDNESDAY {static} + - description : String + + toString() : String + + valueOf(name : String) : Weekday {static} + + values() : Weekday[] {static} + } +} +EventEmitter --> "-observers" EventObserver +KingJoffrey ..|> EventObserver +KingsHand ..|> EventObserver +KingsHand --|> EventEmitter +LordBaelish --|> EventEmitter +LordVarys --|> EventEmitter +Scout --|> EventEmitter +@enduml \ No newline at end of file diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java index 9e459c7a9ff5..4ddcb9d2b82e 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java @@ -23,8 +23,9 @@ package com.iluwatar.event.aggregator; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; /** * A system with lots of objects can lead to complexities when a client wants to subscribe to @@ -47,19 +48,18 @@ public class App { */ public static void main(String[] args) { - KingJoffrey kingJoffrey = new KingJoffrey(); - KingsHand kingsHand = new KingsHand(kingJoffrey); + var kingJoffrey = new KingJoffrey(); + var kingsHand = new KingsHand(kingJoffrey); - List emitters = new ArrayList<>(); - emitters.add(kingsHand); - emitters.add(new LordBaelish(kingsHand)); - emitters.add(new LordVarys(kingsHand)); - emitters.add(new Scout(kingsHand)); + var emitters = List.of( + kingsHand, + new LordBaelish(kingsHand), + new LordVarys(kingsHand), + new Scout(kingsHand) + ); - for (Weekday day : Weekday.values()) { - for (EventEmitter emitter : emitters) { - emitter.timePasses(day); - } - } + Arrays.stream(Weekday.values()) + .>map(day -> emitter -> emitter.timePasses(day)) + .forEachOrdered(emitters::forEach); } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java index eef64af1a740..9985cee606d6 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java @@ -47,9 +47,7 @@ public final void registerObserver(EventObserver obs) { } protected void notifyObservers(Event e) { - for (EventObserver obs : observers) { - obs.onEvent(e); - } + observers.forEach(obs -> obs.onEvent(e)); } public abstract void timePasses(Weekday day); diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java index dcc01c7df93f..4b0f4c898b34 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java @@ -37,7 +37,7 @@ public LordBaelish(EventObserver obs) { @Override public void timePasses(Weekday day) { - if (day.equals(Weekday.FRIDAY)) { + if (day == Weekday.FRIDAY) { notifyObservers(Event.STARK_SIGHTED); } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java index c39080fa5810..0e92580eae11 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java @@ -37,7 +37,7 @@ public LordVarys(EventObserver obs) { @Override public void timePasses(Weekday day) { - if (day.equals(Weekday.SATURDAY)) { + if (day == Weekday.SATURDAY) { notifyObservers(Event.TRAITOR_DETECTED); } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java index 24d6f232886d..00b4a7a49dcb 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java @@ -37,7 +37,7 @@ public Scout(EventObserver obs) { @Override public void timePasses(Weekday day) { - if (day.equals(Weekday.TUESDAY)) { + if (day == Weekday.TUESDAY) { notifyObservers(Event.WARSHIPS_APPROACHING); } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java index 730977bdb42f..9ec61339c7df 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java @@ -28,8 +28,13 @@ */ public enum Weekday { - MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY( - "Friday"), SATURDAY("Saturday"), SUNDAY("Sunday"); + MONDAY("Monday"), + TUESDAY("Tuesday"), + WEDNESDAY("Wednesday"), + THURSDAY("Thursday"), + FRIDAY("Friday"), + SATURDAY("Saturday"), + SUNDAY("Sunday"); private String description; diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java index 23c961a3147c..a65424023f99 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java @@ -26,15 +26,12 @@ import org.junit.jupiter.api.Test; /** - * * Application test - * */ public class AppTest { @Test public void test() { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } } diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java index 3cf7cef9ee0f..f0de726f39bd 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java @@ -23,8 +23,6 @@ package com.iluwatar.event.aggregator; -import org.junit.jupiter.api.Test; - import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -35,10 +33,11 @@ import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; +import org.junit.jupiter.api.Test; /** - * Date: 12/12/15 - 10:58 PM - * Tests for Event Emitter + * Date: 12/12/15 - 10:58 PM Tests for Event Emitter + * * @param Type of Event Emitter * @author Jeroen Meulemeester */ @@ -99,13 +98,13 @@ public void testAllDays() { private void testAllDays(final Weekday specialDay, final Event event, final E emitter, final EventObserver... observers) { - for (final Weekday weekday : Weekday.values()) { + for (final var weekday : Weekday.values()) { // Pass each week of the day, day by day to the event emitter emitter.timePasses(weekday); if (weekday == specialDay) { // On a special day, every observer should have received the event - for (final EventObserver observer : observers) { + for (final var observer : observers) { verify(observer, times(1)).onEvent(eq(event)); } } else { @@ -126,10 +125,10 @@ private void testAllDays(final Weekday specialDay, final Event event, final E em * @param event The expected event emitted by the test object */ private void testAllDaysWithoutDefaultObserver(final Weekday specialDay, final Event event) { - final EventObserver observer1 = mock(EventObserver.class); - final EventObserver observer2 = mock(EventObserver.class); + final var observer1 = mock(EventObserver.class); + final var observer2 = mock(EventObserver.class); - final E emitter = this.factoryWithoutDefaultObserver.get(); + final var emitter = this.factoryWithoutDefaultObserver.get(); emitter.registerObserver(observer1); emitter.registerObserver(observer2); @@ -143,11 +142,11 @@ private void testAllDaysWithoutDefaultObserver(final Weekday specialDay, final E * @param event The expected event emitted by the test object */ private void testAllDaysWithDefaultObserver(final Weekday specialDay, final Event event) { - final EventObserver defaultObserver = mock(EventObserver.class); - final EventObserver observer1 = mock(EventObserver.class); - final EventObserver observer2 = mock(EventObserver.class); + final var defaultObserver = mock(EventObserver.class); + final var observer1 = mock(EventObserver.class); + final var observer2 = mock(EventObserver.class); - final E emitter = this.factoryWithDefaultObserver.apply(defaultObserver); + final var emitter = this.factoryWithDefaultObserver.apply(defaultObserver); emitter.registerObserver(observer1); emitter.registerObserver(observer2); diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java index d767c63e81d1..59034366aa8c 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java @@ -23,11 +23,12 @@ package com.iluwatar.event.aggregator; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + /** * Date: 12/12/15 - 2:52 PM * @@ -40,11 +41,10 @@ public class EventTest { */ @Test public void testToString() { - for (final Event event : Event.values()) { - final String toString = event.toString(); + Arrays.stream(Event.values()).map(Event::toString).forEach(toString -> { assertNotNull(toString); assertFalse(toString.trim().isEmpty()); - } + }); } } \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java index bc291de34fd0..a8bb6cbaa310 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java @@ -23,19 +23,19 @@ package com.iluwatar.event.aggregator; +import static org.junit.jupiter.api.Assertions.assertEquals; + import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.IntStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; -import java.util.LinkedList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Date: 12/12/15 - 3:04 PM * @@ -60,17 +60,16 @@ public void tearDown() { */ @Test public void testOnEvent() { - final KingJoffrey kingJoffrey = new KingJoffrey(); + final var kingJoffrey = new KingJoffrey(); - for (int i = 0; i < Event.values().length; ++i) { + IntStream.range(0, Event.values().length).forEach(i -> { assertEquals(i, appender.getLogSize()); - Event event = Event.values()[i]; + var event = Event.values()[i]; kingJoffrey.onEvent(event); - - final String expectedMessage = "Received event from the King's Hand: " + event.toString(); + final var expectedMessage = "Received event from the King's Hand: " + event.toString(); assertEquals(expectedMessage, appender.getLastMessage()); assertEquals(i + 1, appender.getLogSize()); - } + }); } diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java index 6e951f3761d2..08af63f9a30e 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java @@ -23,14 +23,15 @@ package com.iluwatar.event.aggregator; -import org.junit.jupiter.api.Test; - import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; /** * Date: 12/12/15 - 10:57 AM @@ -47,24 +48,24 @@ public KingsHandTest() { } /** - * The {@link KingsHand} is both an {@link EventEmitter} as an {@link EventObserver} so verify if every - * event received is passed up to it's superior, in most cases {@link KingJoffrey} but now just a - * mocked observer. + * The {@link KingsHand} is both an {@link EventEmitter} as an {@link EventObserver} so verify if + * every event received is passed up to it's superior, in most cases {@link KingJoffrey} but now + * just a mocked observer. */ @Test public void testPassThrough() throws Exception { - final EventObserver observer = mock(EventObserver.class); - final KingsHand kingsHand = new KingsHand(observer); + final var observer = mock(EventObserver.class); + final var kingsHand = new KingsHand(observer); // The kings hand should not pass any events before he received one verifyZeroInteractions(observer); // Verify if each event is passed on to the observer, nothing less, nothing more. - for (final Event event : Event.values()) { + Arrays.stream(Event.values()).forEach(event -> { kingsHand.onEvent(event); verify(observer, times(1)).onEvent(eq(event)); verifyNoMoreInteractions(observer); - } + }); } diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java index 342b16cfb684..411bb9bc3fdd 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java @@ -23,11 +23,12 @@ package com.iluwatar.event.aggregator; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + /** * Date: 12/12/15 - 2:12 PM * @@ -37,11 +38,11 @@ public class WeekdayTest { @Test public void testToString() { - for (final Weekday weekday : Weekday.values()) { + Arrays.stream(Weekday.values()).forEach(weekday -> { final String toString = weekday.toString(); assertNotNull(toString); assertEquals(weekday.name(), toString.toUpperCase()); - } + }); } } \ No newline at end of file diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index 4c26e0a3caa6..68684e6ed87a 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -19,6 +19,7 @@ of the complex issues inherent in multithreaded design. Using a class that suppo 3. Wait for resources to become available without stopping ("hanging") your application. 4. Communicate with pending asynchronous operations using the familiar events-and-delegates model. +## Class diagram ![alt text](./etc/event-asynchronous.png "Event-based Asynchronous") ## Applicability diff --git a/event-asynchronous/etc/event-asynchronous.urm.puml b/event-asynchronous/etc/event-asynchronous.urm.puml new file mode 100644 index 000000000000..3a896c32f59f --- /dev/null +++ b/event-asynchronous/etc/event-asynchronous.urm.puml @@ -0,0 +1,70 @@ +@startuml +package com.iluwatar.event.asynchronous { + class App { + - LOGGER : Logger {static} + + PROP_FILE_NAME : String {static} + ~ interactiveMode : boolean + + App() + + main(args : String[]) {static} + - processOption1(eventManager : EventManager, s : Scanner) + - processOption2(eventManager : EventManager, s : Scanner) + - processOption3(eventManager : EventManager, s : Scanner) + + quickRun() + + run() + + runInteractiveMode() + + setUp() + } + class Event { + - LOGGER : Logger {static} + - eventId : int + - eventListener : ThreadCompleteListener + - eventTime : int + - isComplete : boolean + - isSynchronous : boolean + - thread : Thread + + Event(eventId : int, eventTime : int, isSynchronous : boolean) + + addListener(listener : ThreadCompleteListener) + - completed() + + isSynchronous() : boolean + + removeListener(listener : ThreadCompleteListener) + + run() + + start() + + status() + + stop() + } + class EventManager { + - DOES_NOT_EXIST : String {static} + + MAX_EVENT_TIME : int {static} + + MAX_ID : int {static} + + MAX_RUNNING_EVENTS : int {static} + + MIN_ID : int {static} + - currentlyRunningSyncEvent : int + - eventPool : Map + - rand : Random + + EventManager() + + cancel(eventId : int) + + completedEventHandler(eventId : int) + + create(eventTime : int) : int + + createAsync(eventTime : int) : int + - createEvent(eventTime : int, isSynchronous : boolean) : int + - generateId() : int + + getEventPool() : Map + + numOfCurrentlyRunningSyncEvent() : int + + shutdown() + + start(eventId : int) + + status(eventId : int) + + statusOfAllEvents() + } + interface IEvent { + + start() {abstract} + + status() {abstract} + + stop() {abstract} + } + interface ThreadCompleteListener { + + completedEventHandler(int) {abstract} + } +} +Event --> "-eventListener" ThreadCompleteListener +Event ..|> IEvent +EventManager ..|> ThreadCompleteListener +@enduml \ No newline at end of file diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 42b7b3391fb2..341a47c8ed21 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -24,7 +24,6 @@ package com.iluwatar.event.asynchronous; import java.io.IOException; -import java.io.InputStream; import java.util.Properties; import java.util.Scanner; import org.slf4j.Logger; @@ -70,8 +69,7 @@ public class App { * @param args command line args */ public static void main(String[] args) { - App app = new App(); - + var app = new App(); app.setUp(); app.run(); } @@ -82,9 +80,9 @@ public static void main(String[] args) { * operations. */ public void setUp() { - Properties prop = new Properties(); + var prop = new Properties(); - InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); + var inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); if (inputStream != null) { try { @@ -92,7 +90,7 @@ public void setUp() { } catch (IOException e) { LOGGER.error("{} was not found. Defaulting to non-interactive mode.", PROP_FILE_NAME, e); } - String property = prop.getProperty("INTERACTIVE_MODE"); + var property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { interactiveMode = true; } @@ -114,17 +112,17 @@ public void run() { * Run program in non-interactive mode. */ public void quickRun() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { // Create an Asynchronous event. - int asyncEventId = eventManager.createAsync(60); + var asyncEventId = eventManager.createAsync(60); LOGGER.info("Async Event [{}] has been created.", asyncEventId); eventManager.start(asyncEventId); LOGGER.info("Async Event [{}] has been started.", asyncEventId); // Create a Synchronous event. - int syncEventId = eventManager.create(60); + var syncEventId = eventManager.create(60); LOGGER.info("Sync Event [{}] has been created.", syncEventId); eventManager.start(syncEventId); LOGGER.info("Sync Event [{}] has been started.", syncEventId); @@ -147,10 +145,10 @@ public void quickRun() { * Run program in interactive mode. */ public void runInteractiveMode() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); - Scanner s = new Scanner(System.in); - int option = -1; + var s = new Scanner(System.in); + var option = -1; while (option != 4) { LOGGER.info("Hello. Would you like to boil some eggs?"); LOGGER.info("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); @@ -174,7 +172,7 @@ public void runInteractiveMode() { private void processOption3(EventManager eventManager, Scanner s) { s.nextLine(); LOGGER.info("Just one egg (O) OR all of them (A) ?: "); - String eggChoice = s.nextLine(); + var eggChoice = s.nextLine(); if (eggChoice.equalsIgnoreCase("O")) { LOGGER.info("Which egg?: "); @@ -191,7 +189,7 @@ private void processOption3(EventManager eventManager, Scanner s) { private void processOption2(EventManager eventManager, Scanner s) { LOGGER.info("Which egg?: "); - int eventId = s.nextInt(); + var eventId = s.nextInt(); try { eventManager.cancel(eventId); LOGGER.info("Egg [{}] is removed from boiler.", eventId); @@ -203,12 +201,12 @@ private void processOption2(EventManager eventManager, Scanner s) { private void processOption1(EventManager eventManager, Scanner s) { s.nextLine(); LOGGER.info("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); - String eventType = s.nextLine(); + var eventType = s.nextLine(); LOGGER.info("How long should this egg be boiled for (in seconds)?: "); - int eventTime = s.nextInt(); + var eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventId = eventManager.createAsync(eventTime); + var eventId = eventManager.createAsync(eventTime); eventManager.start(eventId); LOGGER.info("Egg [{}] is being boiled.", eventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException @@ -217,7 +215,7 @@ private void processOption1(EventManager eventManager, Scanner s) { } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventId = eventManager.create(eventTime); + var eventId = eventManager.create(eventTime); eventManager.start(eventId); LOGGER.info("Egg [{}] is being boiled.", eventId); } catch (MaxNumOfEventsAllowedException | InvalidOperationException diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index b275b16a2325..6925a2ffd1ad 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -82,8 +82,8 @@ public void status() { @Override public void run() { - long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime * 1000); + var currentTime = System.currentTimeMillis(); + var endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { Thread.sleep(1000); // Sleep for 1 second. diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index 2201394d9e23..14d28860bb22 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -23,7 +23,6 @@ package com.iluwatar.event.asynchronous; -import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; @@ -75,7 +74,7 @@ public int create(int eventTime) + " running. Please wait until it finishes and try again."); } - int eventId = createEvent(eventTime, true); + var eventId = createEvent(eventTime, true); currentlyRunningSyncEvent = eventId; return eventId; @@ -106,9 +105,9 @@ private int createEvent(int eventTime, boolean isSynchronous) "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again."); } - int newEventId = generateId(); + var newEventId = generateId(); - Event newEvent = new Event(newEventId, eventTime, isSynchronous); + var newEvent = new Event(newEventId, eventTime, isSynchronous); newEvent.addListener(this); eventPool.put(newEventId, newEvent); @@ -167,11 +166,7 @@ public void status(int eventId) throws EventDoesNotExistException { */ @SuppressWarnings("rawtypes") public void statusOfAllEvents() { - Iterator it = eventPool.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry) it.next(); - ((Event) pair.getValue()).status(); - } + eventPool.entrySet().forEach(entry -> ((Event) ((Map.Entry) entry).getValue()).status()); } /** @@ -179,11 +174,7 @@ public void statusOfAllEvents() { */ @SuppressWarnings("rawtypes") public void shutdown() { - Iterator it = eventPool.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry) it.next(); - ((Event) pair.getValue()).stop(); - } + eventPool.entrySet().forEach(entry -> ((Event) ((Map.Entry) entry).getValue()).stop()); } /** @@ -194,7 +185,7 @@ public void shutdown() { private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; + var randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; while (eventPool.containsKey(randomNum)) { randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java index cdf8566eb73c..76554d6b1800 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java @@ -25,15 +25,12 @@ import org.junit.jupiter.api.Test; -import java.io.IOException; - /** * Tests that EventAsynchronous example runs without errors. */ public class AppTest { @Test - public void test() throws IOException { - String[] args = {}; - App.main(args); + public void test() { + App.main(new String[]{}); } } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index fc9f030fd445..26253f6ccbad 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -23,28 +23,26 @@ package com.iluwatar.event.asynchronous; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * * Application test - * */ public class EventAsynchronousTest { private static final Logger LOGGER = LoggerFactory.getLogger(EventAsynchronousTest.class); @Test public void testAsynchronousEvent() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { - int aEventId = eventManager.createAsync(60); + var aEventId = eventManager.createAsync(60); eventManager.start(aEventId); assertEquals(1, eventManager.getEventPool().size()); assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); @@ -58,9 +56,9 @@ public void testAsynchronousEvent() { @Test public void testSynchronousEvent() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { - int sEventId = eventManager.create(60); + var sEventId = eventManager.create(60); eventManager.start(sEventId); assertEquals(1, eventManager.getEventPool().size()); assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); @@ -76,9 +74,9 @@ public void testSynchronousEvent() { @Test public void testUnsuccessfulSynchronousEvent() { assertThrows(InvalidOperationException.class, () -> { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { - int sEventId = eventManager.create(60); + var sEventId = eventManager.create(60); eventManager.start(sEventId); sEventId = eventManager.create(60); eventManager.start(sEventId); @@ -90,20 +88,18 @@ public void testUnsuccessfulSynchronousEvent() { @Test public void testFullSynchronousEvent() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { - int eventTime = 1; + var eventTime = 1; - int sEventId = eventManager.create(eventTime); + var sEventId = eventManager.create(eventTime); assertEquals(1, eventManager.getEventPool().size()); eventManager.start(sEventId); - long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to - // complete - // properly. - while (System.currentTimeMillis() < endTime) { - } + var currentTime = System.currentTimeMillis(); + // +2 to give a bit of buffer time for event to complete properly. + var endTime = currentTime + (eventTime + 2 * 1000); + while (System.currentTimeMillis() < endTime) ; assertTrue(eventManager.getEventPool().isEmpty()); @@ -115,24 +111,23 @@ public void testFullSynchronousEvent() { @Test public void testFullAsynchronousEvent() { - EventManager eventManager = new EventManager(); + var eventManager = new EventManager(); try { - int eventTime = 1; + var eventTime = 1; - int aEventId1 = eventManager.createAsync(eventTime); - int aEventId2 = eventManager.createAsync(eventTime); - int aEventId3 = eventManager.createAsync(eventTime); + var aEventId1 = eventManager.createAsync(eventTime); + var aEventId2 = eventManager.createAsync(eventTime); + var aEventId3 = eventManager.createAsync(eventTime); assertEquals(3, eventManager.getEventPool().size()); eventManager.start(aEventId1); eventManager.start(aEventId2); eventManager.start(aEventId3); - long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete - // properly. - while (System.currentTimeMillis() < endTime) { - } + var currentTime = System.currentTimeMillis(); + // +2 to give a bit of buffer time for event to complete properly. + var endTime = currentTime + (eventTime + 2 * 1000); + while (System.currentTimeMillis() < endTime) ; assertTrue(eventManager.getEventPool().isEmpty()); diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index e2f06cf5aff5..5fcce357f76b 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -13,6 +13,7 @@ tags: ## Intent Send and notify state changes of your objects to other applications using an Event-driven Architecture. +## Class diagram ![alt text](./etc/eda.png "Event Driven Architecture") ## Applicability diff --git a/event-driven-architecture/etc/event-driven-architecture.urm.puml b/event-driven-architecture/etc/event-driven-architecture.urm.puml new file mode 100644 index 000000000000..6b67f0a0e569 --- /dev/null +++ b/event-driven-architecture/etc/event-driven-architecture.urm.puml @@ -0,0 +1,64 @@ +@startuml +package com.iluwatar.eda.handler { + class UserCreatedEventHandler { + - LOGGER : Logger {static} + + UserCreatedEventHandler() + + onEvent(event : UserCreatedEvent) + } + class UserUpdatedEventHandler { + - LOGGER : Logger {static} + + UserUpdatedEventHandler() + + onEvent(event : UserUpdatedEvent) + } +} +package com.iluwatar.eda.event { + abstract class AbstractEvent { + + AbstractEvent() + + getType() : Class + } + class UserCreatedEvent { + - user : User + + UserCreatedEvent(user : User) + + getUser() : User + } + class UserUpdatedEvent { + - user : User + + UserUpdatedEvent(user : User) + + getUser() : User + } +} +package com.iluwatar.eda.framework { + interface Event { + + getType() : Class {abstract} + } + class EventDispatcher { + - handlers : Map, Handler> + + EventDispatcher() + + dispatch(event : E extends Event) + + registerHandler(eventType : Class, handler : Handler) + } + interface Handler { + + onEvent(E extends Event) {abstract} + } +} +package com.iluwatar.eda.model { + class User { + - username : String + + User(username : String) + + getUsername() : String + } +} +package com.iluwatar.eda { + class App { + + App() + + main(args : String[]) {static} + } +} +UserCreatedEvent --> "-user" User +UserUpdatedEvent --> "-user" User +AbstractEvent ..|> Event +UserCreatedEvent --|> AbstractEvent +UserUpdatedEvent --|> AbstractEvent +UserCreatedEventHandler ..|> Handler +UserUpdatedEventHandler ..|> Handler +@enduml \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java index 6e328e04032f..33374a58d814 100644 --- a/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java @@ -52,11 +52,11 @@ public class App { */ public static void main(String[] args) { - EventDispatcher dispatcher = new EventDispatcher(); + var dispatcher = new EventDispatcher(); dispatcher.registerHandler(UserCreatedEvent.class, new UserCreatedEventHandler()); dispatcher.registerHandler(UserUpdatedEvent.class, new UserUpdatedEventHandler()); - User user = new User("iluwatar"); + var user = new User("iluwatar"); dispatcher.dispatch(new UserCreatedEvent(user)); dispatcher.dispatch(new UserUpdatedEvent(user)); } diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java index a78bd87cf6c8..dd72c1e93beb 100644 --- a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java @@ -44,8 +44,10 @@ public EventDispatcher() { * @param eventType The {@link Event} to be registered * @param handler The {@link Handler} that will be handling the {@link Event} */ - public void registerHandler(Class eventType, - Handler handler) { + public void registerHandler( + Class eventType, + Handler handler + ) { handlers.put(eventType, handler); } @@ -56,7 +58,7 @@ public void registerHandler(Class eventType, */ @SuppressWarnings("unchecked") public void dispatch(E event) { - Handler handler = (Handler) handlers.get(event.getClass()); + var handler = (Handler) handlers.get(event.getClass()); if (handler != null) { handler.onEvent(event); } diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java index da2f65d67805..0f1720363b2d 100644 --- a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java @@ -25,15 +25,12 @@ import org.junit.jupiter.api.Test; -import java.io.IOException; - /** * Tests that Event Driven Architecture example runs without errors. */ public class AppTest { @Test - public void test() throws IOException { - String[] args = {}; - App.main(args); + public void test() { + App.main(new String[]{}); } } diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java index da170438c33e..bbd74fcdb056 100644 --- a/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java @@ -23,11 +23,11 @@ package com.iluwatar.eda.event; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.iluwatar.eda.model.User; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * {@link UserCreatedEventTest} tests and verifies {@link AbstractEvent} behaviour. */ @@ -39,8 +39,8 @@ public class UserCreatedEventTest { */ @Test public void testGetEventType() { - User user = new User("iluwatar"); - UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); + var user = new User("iluwatar"); + var userCreatedEvent = new UserCreatedEvent(user); assertEquals(UserCreatedEvent.class, userCreatedEvent.getType()); } } diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java index c37e12a84502..f45eca703781 100644 --- a/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java @@ -23,6 +23,9 @@ package com.iluwatar.eda.framework; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import com.iluwatar.eda.event.UserCreatedEvent; import com.iluwatar.eda.event.UserUpdatedEvent; import com.iluwatar.eda.handler.UserCreatedEventHandler; @@ -30,9 +33,6 @@ import com.iluwatar.eda.model.User; import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - /** * Event Dispatcher unit tests to assert and verify correct event dispatcher behaviour */ @@ -45,16 +45,16 @@ public class EventDispatcherTest { @Test public void testEventDriverPattern() { - EventDispatcher dispatcher = spy(new EventDispatcher()); - UserCreatedEventHandler userCreatedEventHandler = spy(new UserCreatedEventHandler()); - UserUpdatedEventHandler userUpdatedEventHandler = spy(new UserUpdatedEventHandler()); + var dispatcher = spy(new EventDispatcher()); + var userCreatedEventHandler = spy(new UserCreatedEventHandler()); + var userUpdatedEventHandler = spy(new UserUpdatedEventHandler()); dispatcher.registerHandler(UserCreatedEvent.class, userCreatedEventHandler); dispatcher.registerHandler(UserUpdatedEvent.class, userUpdatedEventHandler); - User user = new User("iluwatar"); + var user = new User("iluwatar"); - UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); - UserUpdatedEvent userUpdatedEvent = new UserUpdatedEvent(user); + var userCreatedEvent = new UserCreatedEvent(user); + var userUpdatedEvent = new UserUpdatedEvent(user); //fire a userCreatedEvent and verify that userCreatedEventHandler has been invoked. dispatcher.dispatch(userCreatedEvent); diff --git a/event-queue/README.md b/event-queue/README.md index 081c0144970c..b4d7a071fefe 100644 --- a/event-queue/README.md +++ b/event-queue/README.md @@ -17,6 +17,7 @@ It puts all the requests in a queue and process them asynchronously. Gives the resource for the event when it is the next in the queue and in same time removes it from the queue. +## Class diagram ![alt text](./etc/model.png "Event Queue") ## Applicability diff --git a/event-queue/etc/event-queue.urm.puml b/event-queue/etc/event-queue.urm.puml new file mode 100644 index 000000000000..de4390e4e599 --- /dev/null +++ b/event-queue/etc/event-queue.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.event.queue { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Audio { + - INSTANCE : Audio {static} + - LOGGER : Logger {static} + - MAX_PENDING : int {static} + - headIndex : int + - pendingAudio : PlayMessage[] + - tailIndex : int + - updateThread : Thread + ~ Audio() + + getAudioStream(filePath : String) : AudioInputStream + + getInstance() : Audio {static} + + getPendingAudio() : PlayMessage[] + + init() + + isServiceRunning() : boolean + + playSound(stream : AudioInputStream, volume : float) + - startThread() + + stopService() + - update() + } + class PlayMessage { + - stream : AudioInputStream + - volume : float + + PlayMessage(stream : AudioInputStream, volume : float) + + getStream() : AudioInputStream + + getVolume() : float + - setStream(stream : AudioInputStream) + + setVolume(volume : float) + } +} +Audio --> "-INSTANCE" Audio +@enduml \ No newline at end of file diff --git a/event-queue/src/main/java/com/iluwatar/event/queue/App.java b/event-queue/src/main/java/com/iluwatar/event/queue/App.java index 788dd81353d9..368eb1ccbd2b 100644 --- a/event-queue/src/main/java/com/iluwatar/event/queue/App.java +++ b/event-queue/src/main/java/com/iluwatar/event/queue/App.java @@ -53,12 +53,12 @@ public class App { */ public static void main(String[] args) throws UnsupportedAudioFileException, IOException, InterruptedException { - Audio audio = Audio.getInstance(); + var audio = Audio.getInstance(); audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f); audio.playSound(audio.getAudioStream("./etc/Closed-Hi-Hat-1.wav"), -8.0f); LOGGER.info("Press Enter key to stop the program..."); - try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { + try (var br = new BufferedReader(new InputStreamReader(System.in))) { br.read(); } audio.stopService(); diff --git a/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java b/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java index de31caabd50c..4286a5ed0930 100644 --- a/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java +++ b/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java @@ -27,7 +27,6 @@ import java.io.IOException; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Clip; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import org.slf4j.Logger; @@ -116,10 +115,11 @@ private synchronized void startThread() { public void playSound(AudioInputStream stream, float volume) { init(); // Walk the pending requests. - for (int i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) { - if (getPendingAudio()[i].getStream() == stream) { + for (var i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) { + var playMessage = getPendingAudio()[i]; + if (playMessage.getStream() == stream) { // Use the larger of the two volumes. - getPendingAudio()[i].setVolume(Math.max(volume, getPendingAudio()[i].getVolume())); + playMessage.setVolume(Math.max(volume, playMessage.getVolume())); // Don't need to enqueue. return; @@ -137,11 +137,10 @@ private void update() { if (headIndex == tailIndex) { return; } - Clip clip = null; try { - AudioInputStream audioStream = getPendingAudio()[headIndex].getStream(); + var audioStream = getPendingAudio()[headIndex].getStream(); headIndex++; - clip = AudioSystem.getClip(); + var clip = AudioSystem.getClip(); clip.open(audioStream); clip.start(); } catch (LineUnavailableException e) { diff --git a/event-sourcing/README.md b/event-sourcing/README.md index 1079405fa885..9e577af6c9fe 100644 --- a/event-sourcing/README.md +++ b/event-sourcing/README.md @@ -13,6 +13,7 @@ tags: ## Intent Instead of storing just the current state of the data in a domain, use an append-only store to record the full series of actions taken on that data. The store acts as the system of record and can be used to materialize the domain objects. This can simplify tasks in complex domains, by avoiding the need to synchronize the data model and the business domain, while improving performance, scalability, and responsiveness. It can also provide consistency for transactional data, and maintain full audit trails and history that can enable compensating actions. +## Class diagram ![alt text](./etc/event-sourcing.png "Event Sourcing") ## Applicability diff --git a/event-sourcing/etc/event-sourcing.urm.puml b/event-sourcing/etc/event-sourcing.urm.puml new file mode 100644 index 000000000000..363af9967012 --- /dev/null +++ b/event-sourcing/etc/event-sourcing.urm.puml @@ -0,0 +1,107 @@ +@startuml +package com.iluwatar.event.sourcing.processor { + class DomainEventProcessor { + - processorJournal : JsonFileJournal + + DomainEventProcessor() + + process(domainEvent : DomainEvent) + + recover() + + reset() + } + class JsonFileJournal { + - events : List + - file : File + - index : int + + JsonFileJournal() + + readNext() : DomainEvent + + reset() + + write(domainEvent : DomainEvent) + } +} +package com.iluwatar.event.sourcing.event { + class AccountCreateEvent { + - accountNo : int + - owner : String + + AccountCreateEvent(sequenceId : long, createdTime : long, accountNo : int, owner : String) + + getAccountNo() : int + + getOwner() : String + + process() + } + abstract class DomainEvent { + - createdTime : long + - eventClassName : String + - realTime : boolean + - sequenceId : long + + DomainEvent(sequenceId : long, createdTime : long, eventClassName : String) + + getCreatedTime() : long + + getEventClassName() : String + + getSequenceId() : long + + isRealTime() : boolean + + process() {abstract} + + setRealTime(realTime : boolean) + } + class MoneyDepositEvent { + - accountNo : int + - money : BigDecimal + + MoneyDepositEvent(sequenceId : long, createdTime : long, accountNo : int, money : BigDecimal) + + getAccountNo() : int + + getMoney() : BigDecimal + + process() + } + class MoneyTransferEvent { + - accountNoFrom : int + - accountNoTo : int + - money : BigDecimal + + MoneyTransferEvent(sequenceId : long, createdTime : long, money : BigDecimal, accountNoFrom : int, accountNoTo : int) + + getAccountNoFrom() : int + + getAccountNoTo() : int + + getMoney() : BigDecimal + + process() + } +} +package com.iluwatar.event.sourcing.app { + class App { + + ACCOUNT_OF_DAENERYS : int {static} + + ACCOUNT_OF_JON : int {static} + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.event.sourcing.state { + class AccountAggregate { + - accounts : Map {static} + - AccountAggregate() + + getAccount(accountNo : int) : Account {static} + + putAccount(account : Account) {static} + + resetState() {static} + } +} +package com.iluwatar.event.sourcing.domain { + class Account { + - LOGGER : Logger {static} + - MSG : String {static} + - accountNo : int + - money : BigDecimal + - owner : String + + Account(accountNo : int, owner : String) + + copy() : Account + - depositMoney(money : BigDecimal) + + getAccountNo() : int + + getMoney() : BigDecimal + + getOwner() : String + - handleDeposit(money : BigDecimal, realTime : boolean) + + handleEvent(accountCreateEvent : AccountCreateEvent) + + handleEvent(moneyDepositEvent : MoneyDepositEvent) + + handleTransferFromEvent(moneyTransferEvent : MoneyTransferEvent) + + handleTransferToEvent(moneyTransferEvent : MoneyTransferEvent) + - handleWithdrawal(money : BigDecimal, realTime : boolean) + + setMoney(money : BigDecimal) + + toString() : String + - withdrawMoney(money : BigDecimal) + } +} +DomainEventProcessor --> "-processorJournal" JsonFileJournal +AccountCreateEvent --|> DomainEvent +MoneyDepositEvent --|> DomainEvent +MoneyTransferEvent --|> DomainEvent +@enduml \ No newline at end of file diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java index d3d6d7ee2d25..8cebeea385e5 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java @@ -69,7 +69,7 @@ public class App { */ public static void main(String[] args) { - DomainEventProcessor eventProcessor = new DomainEventProcessor(); + var eventProcessor = new DomainEventProcessor(); LOGGER.info("Running the system first time............"); diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java index c037add359a8..34ca628f24e9 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java @@ -104,7 +104,7 @@ public void setMoney(BigDecimal money) { * @return the account */ public Account copy() { - Account account = new Account(accountNo, owner); + var account = new Account(accountNo, owner); account.setMoney(money); return account; } @@ -135,7 +135,7 @@ private void handleDeposit(BigDecimal money, boolean realTime) { } private void handleWithdrawal(BigDecimal money, boolean realTime) { - if (this.money.compareTo(money) == -1) { + if (this.money.compareTo(money) < 0) { throw new RuntimeException("Insufficient Account Balance"); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java index 45751ffffe74..0077b214bbb0 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java @@ -72,7 +72,7 @@ public String getOwner() { @Override public void process() { - Account account = AccountAggregate.getAccount(accountNo); + var account = AccountAggregate.getAccount(accountNo); if (account != null) { throw new RuntimeException("Account already exists"); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java index 3e465c75d08c..86b3f0c0e8af 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java @@ -23,9 +23,9 @@ package com.iluwatar.event.sourcing.event; -import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; +import java.util.Optional; /** * This is the class that implements money deposit event. Holds the necessary info for a money @@ -73,10 +73,8 @@ public int getAccountNo() { @Override public void process() { - Account account = AccountAggregate.getAccount(accountNo); - if (account == null) { - throw new RuntimeException("Account not found"); - } + var account = Optional.ofNullable(AccountAggregate.getAccount(accountNo)) + .orElseThrow(() -> new RuntimeException("Account not found")); account.handleEvent(this); } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java index ccc1ac3674b9..c72dd9b59093 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java @@ -23,9 +23,9 @@ package com.iluwatar.event.sourcing.event; -import com.iluwatar.event.sourcing.domain.Account; import com.iluwatar.event.sourcing.state.AccountAggregate; import java.math.BigDecimal; +import java.util.Optional; /** * This is the class that implements money transfer event. Holds the necessary info for a money @@ -86,15 +86,10 @@ public int getAccountNoTo() { @Override public void process() { - Account accountFrom = AccountAggregate.getAccount(accountNoFrom); - if (accountFrom == null) { - throw new RuntimeException("Account not found " + accountNoFrom); - } - Account accountTo = AccountAggregate.getAccount(accountNoTo); - if (accountTo == null) { - throw new RuntimeException("Account not found " + accountNoTo); - } - + var accountFrom = Optional.ofNullable(AccountAggregate.getAccount(accountNoFrom)) + .orElseThrow(() -> new RuntimeException("Account not found " + accountNoFrom)); + var accountTo = Optional.ofNullable(AccountAggregate.getAccount(accountNoTo)) + .orElseThrow(() -> new RuntimeException("Account not found " + accountNoTo)); accountFrom.handleTransferFromEvent(this); accountTo.handleTransferToEvent(this); } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java index bf84f8369213..bc37a88725f9 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java @@ -57,13 +57,8 @@ public void reset() { */ public void recover() { DomainEvent domainEvent; - while (true) { - domainEvent = processorJournal.readNext(); - if (domainEvent == null) { - break; - } else { - domainEvent.process(); - } + while ((domainEvent = processorJournal.readNext()) != null) { + domainEvent.process(); } } } diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java index 15105dce5272..4fb10821f6b8 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java @@ -38,7 +38,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; -import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -60,8 +60,8 @@ public class JsonFileJournal { public JsonFileJournal() { file = new File("Journal.json"); if (file.exists()) { - try (BufferedReader input = new BufferedReader( - new InputStreamReader(new FileInputStream(file), "UTF-8"))) { + try (var input = new BufferedReader( + new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { String line; while ((line = input.readLine()) != null) { events.add(line); @@ -81,7 +81,7 @@ public JsonFileJournal() { * @param domainEvent the domain event */ public void write(DomainEvent domainEvent) { - Gson gson = new Gson(); + var gson = new Gson(); JsonElement jsonElement; if (domainEvent instanceof AccountCreateEvent) { jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class); @@ -93,9 +93,9 @@ public void write(DomainEvent domainEvent) { throw new RuntimeException("Journal Event not recegnized"); } - try (Writer output = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8"))) { - String eventString = jsonElement.toString(); + try (var output = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8))) { + var eventString = jsonElement.toString(); output.write(eventString + "\r\n"); } catch (IOException e) { throw new RuntimeException(e); @@ -120,13 +120,13 @@ public DomainEvent readNext() { if (index >= events.size()) { return null; } - String event = events.get(index); + var event = events.get(index); index++; - JsonParser parser = new JsonParser(); - JsonElement jsonElement = parser.parse(event); - String eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString(); - Gson gson = new Gson(); + var parser = new JsonParser(); + var jsonElement = parser.parse(event); + var eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString(); + var gson = new Gson(); DomainEvent domainEvent; if (eventClassName.equals("AccountCreateEvent")) { domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class); diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java index 398da5c6ab03..d040acc04c27 100644 --- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java +++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java @@ -26,6 +26,7 @@ import com.iluwatar.event.sourcing.domain.Account; import java.util.HashMap; import java.util.Map; +import java.util.Optional; /** * This is the static accounts map holder class. This class holds the state of the accounts. @@ -55,11 +56,10 @@ public static void putAccount(Account account) { * @return the copy of the account or null if not found */ public static Account getAccount(int accountNo) { - Account account = accounts.get(accountNo); - if (account == null) { - return null; - } - return account.copy(); + return Optional.of(accountNo) + .map(accounts::get) + .map(Account::copy) + .orElse(null); } /** diff --git a/event-sourcing/src/test/java/IntegrationTest.java b/event-sourcing/src/test/java/IntegrationTest.java index 88bcc5c5a155..e3bac5c8fff2 100644 --- a/event-sourcing/src/test/java/IntegrationTest.java +++ b/event-sourcing/src/test/java/IntegrationTest.java @@ -21,25 +21,23 @@ * THE SOFTWARE. */ -import com.iluwatar.event.sourcing.domain.Account; +import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_DAENERYS; +import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.iluwatar.event.sourcing.event.AccountCreateEvent; import com.iluwatar.event.sourcing.event.MoneyDepositEvent; import com.iluwatar.event.sourcing.event.MoneyTransferEvent; import com.iluwatar.event.sourcing.processor.DomainEventProcessor; import com.iluwatar.event.sourcing.state.AccountAggregate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.math.BigDecimal; import java.util.Date; - -import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_DAENERYS; -import static com.iluwatar.event.sourcing.app.App.ACCOUNT_OF_JON; -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Intergartion Test for Event Sourcing state recovery - * + *

* Created by Serdar Hamzaogullari on 19.08.2017. */ public class IntegrationTest { @@ -71,25 +69,25 @@ public void testStateRecovery() { 1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow")); eventProcessor.process(new MoneyDepositEvent( - 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000"))); + 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000"))); eventProcessor.process(new MoneyDepositEvent( - 3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100"))); + 3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100"))); eventProcessor.process(new MoneyTransferEvent( 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON)); - Account accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); - Account accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); + var accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); + var accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); AccountAggregate.resetState(); eventProcessor = new DomainEventProcessor(); eventProcessor.recover(); - Account accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); - Account accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); + var accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS); + var accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON); assertEquals(accountOfDaenerysBeforeShotDown.getMoney(), accountOfDaenerysAfterShotDown.getMoney()); diff --git a/execute-around/README.md b/execute-around/README.md index f669f18ff0f3..9af46812ebaf 100644 --- a/execute-around/README.md +++ b/execute-around/README.md @@ -16,6 +16,7 @@ should always be executed before and after the business method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with the resource. +## Class diagram ![alt text](./etc/execute-around.png "Execute Around") ## Applicability diff --git a/execute-around/etc/execute-around.urm.puml b/execute-around/etc/execute-around.urm.puml new file mode 100644 index 000000000000..fd01a5fe1fe9 --- /dev/null +++ b/execute-around/etc/execute-around.urm.puml @@ -0,0 +1,14 @@ +@startuml +package com.iluwatar.execute.around { + class App { + + App() + + main(args : String[]) {static} + } + interface FileWriterAction { + + writeFile(FileWriter) {abstract} + } + class SimpleFileWriter { + + SimpleFileWriter(filename : String, action : FileWriterAction) + } +} +@enduml \ No newline at end of file diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java index d51ffa60f7f9..4c019035b339 100644 --- a/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java +++ b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java @@ -36,7 +36,7 @@ public class SimpleFileWriter { * Constructor. */ public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { - try (FileWriter writer = new FileWriter(filename)) { + try (var writer = new FileWriter(filename)) { action.writeFile(writer); } } diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java index ab15e08ab256..5512ba8d2be4 100644 --- a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java +++ b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java @@ -23,30 +23,26 @@ package com.iluwatar.execute.around; +import java.io.File; +import java.io.IOException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.File; -import java.io.IOException; - /** - * * Tests execute-around example. - * */ public class AppTest { @Test public void test() throws IOException { - String[] args = {}; - App.main(args); + App.main(new String[]{}); } @BeforeEach @AfterEach public void cleanup() { - File file = new File("testfile.txt"); + var file = new File("testfile.txt"); file.delete(); } } diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java index 0cf473ca3823..9bbd89963e73 100644 --- a/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java +++ b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java @@ -23,20 +23,19 @@ package com.iluwatar.execute.around; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import org.junit.Rule; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; import org.junit.rules.TemporaryFolder; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Date: 12/12/15 - 3:21 PM * @@ -50,13 +49,13 @@ public class SimpleFileWriterTest { @Test public void testWriterNotNull() throws Exception { - final File temporaryFile = this.testFolder.newFile(); + final var temporaryFile = this.testFolder.newFile(); new SimpleFileWriter(temporaryFile.getPath(), Assertions::assertNotNull); } @Test public void testCreatesNonExistentFile() throws Exception { - final File nonExistingFile = new File(this.testFolder.getRoot(), "non-existing-file"); + final var nonExistingFile = new File(this.testFolder.getRoot(), "non-existing-file"); assertFalse(nonExistingFile.exists()); new SimpleFileWriter(nonExistingFile.getPath(), Assertions::assertNotNull); @@ -65,9 +64,9 @@ public void testCreatesNonExistentFile() throws Exception { @Test public void testContentsAreWrittenToFile() throws Exception { - final String testMessage = "Test message"; + final var testMessage = "Test message"; - final File temporaryFile = this.testFolder.newFile(); + final var temporaryFile = this.testFolder.newFile(); assertTrue(temporaryFile.exists()); new SimpleFileWriter(temporaryFile.getPath(), writer -> writer.write(testMessage)); @@ -76,9 +75,9 @@ public void testContentsAreWrittenToFile() throws Exception { @Test public void testRipplesIoExceptionOccurredWhileWriting() { - String message = "Some error"; + var message = "Some error"; assertThrows(IOException.class, () -> { - final File temporaryFile = this.testFolder.newFile(); + final var temporaryFile = this.testFolder.newFile(); new SimpleFileWriter(temporaryFile.getPath(), writer -> { throw new IOException(message); }); diff --git a/extension-objects/README.md b/extension-objects/README.md index 9b5c93a2200d..5b0da5cf4b0f 100644 --- a/extension-objects/README.md +++ b/extension-objects/README.md @@ -13,6 +13,7 @@ tags: Anticipate that an object’s interface needs to be extended in the future. Additional interfaces are defined by extension objects. +## Class diagram ![Extension_objects](./etc/extension_obj.png "Extension objects") ## Applicability diff --git a/extension-objects/etc/extension-objects.urm.puml b/extension-objects/etc/extension-objects.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/extension-objects/etc/extension-objects.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/extension-objects/src/main/java/App.java b/extension-objects/src/main/java/App.java index 9ee53ee26424..89b17b85b369 100644 --- a/extension-objects/src/main/java/App.java +++ b/extension-objects/src/main/java/App.java @@ -24,7 +24,8 @@ import abstractextensions.CommanderExtension; import abstractextensions.SergeantExtension; import abstractextensions.SoldierExtension; -import org.slf4j.Logger; +import java.util.Optional; +import java.util.function.Function; import org.slf4j.LoggerFactory; import units.CommanderUnit; import units.SergeantUnit; @@ -45,9 +46,9 @@ public class App { public static void main(String[] args) { //Create 3 different units - Unit soldierUnit = new SoldierUnit("SoldierUnit1"); - Unit sergeantUnit = new SergeantUnit("SergeantUnit1"); - Unit commanderUnit = new CommanderUnit("CommanderUnit1"); + var soldierUnit = new SoldierUnit("SoldierUnit1"); + var sergeantUnit = new SergeantUnit("SergeantUnit1"); + var commanderUnit = new CommanderUnit("CommanderUnit1"); //check for each unit to have an extension checkExtensionsForUnit(soldierUnit); @@ -57,32 +58,24 @@ public static void main(String[] args) { } private static void checkExtensionsForUnit(Unit unit) { - final Logger logger = LoggerFactory.getLogger(App.class); + final var logger = LoggerFactory.getLogger(App.class); - SoldierExtension soldierExtension = - (SoldierExtension) unit.getUnitExtension("SoldierExtension"); - SergeantExtension sergeantExtension = - (SergeantExtension) unit.getUnitExtension("SergeantExtension"); - CommanderExtension commanderExtension = - (CommanderExtension) unit.getUnitExtension("CommanderExtension"); + var name = unit.getName(); + Function func = (e) -> () -> logger.info(name + " without " + e); - //if unit have extension call the method - if (soldierExtension != null) { - soldierExtension.soldierReady(); - } else { - logger.info(unit.getName() + " without SoldierExtension"); - } + var extension = "SoldierExtension"; + Optional.ofNullable(unit.getUnitExtension(extension)) + .map(e -> (SoldierExtension) e) + .ifPresentOrElse(SoldierExtension::soldierReady, func.apply(extension)); - if (sergeantExtension != null) { - sergeantExtension.sergeantReady(); - } else { - logger.info(unit.getName() + " without SergeantExtension"); - } + extension = "SergeantExtension"; + Optional.ofNullable(unit.getUnitExtension(extension)) + .map(e -> (SergeantExtension) e) + .ifPresentOrElse(SergeantExtension::sergeantReady, func.apply(extension)); - if (commanderExtension != null) { - commanderExtension.commanderReady(); - } else { - logger.info(unit.getName() + " without CommanderExtension"); - } + extension = "CommanderExtension"; + Optional.ofNullable(unit.getUnitExtension(extension)) + .map(e -> (CommanderExtension) e) + .ifPresentOrElse(CommanderExtension::commanderReady, func.apply(extension)); } } diff --git a/extension-objects/src/main/java/units/CommanderUnit.java b/extension-objects/src/main/java/units/CommanderUnit.java index 32b7bbb78e45..3d8a5bc2fafc 100644 --- a/extension-objects/src/main/java/units/CommanderUnit.java +++ b/extension-objects/src/main/java/units/CommanderUnit.java @@ -25,6 +25,7 @@ import abstractextensions.UnitExtension; import concreteextensions.Commander; +import java.util.Optional; /** * Class defining CommanderUnit. @@ -39,10 +40,7 @@ public CommanderUnit(String name) { public UnitExtension getUnitExtension(String extensionName) { if (extensionName.equals("CommanderExtension")) { - if (unitExtension == null) { - unitExtension = new Commander(this); - } - return unitExtension; + return Optional.ofNullable(unitExtension).orElseGet(() -> new Commander(this)); } return super.getUnitExtension(extensionName); diff --git a/extension-objects/src/main/java/units/SergeantUnit.java b/extension-objects/src/main/java/units/SergeantUnit.java index 38c6598fa703..b1e7397c8f61 100644 --- a/extension-objects/src/main/java/units/SergeantUnit.java +++ b/extension-objects/src/main/java/units/SergeantUnit.java @@ -25,6 +25,7 @@ import abstractextensions.UnitExtension; import concreteextensions.Sergeant; +import java.util.Optional; /** * Class defining SergeantUnit. @@ -39,10 +40,7 @@ public SergeantUnit(String name) { public UnitExtension getUnitExtension(String extensionName) { if (extensionName.equals("SergeantExtension")) { - if (unitExtension == null) { - unitExtension = new Sergeant(this); - } - return unitExtension; + return Optional.ofNullable(unitExtension).orElseGet(() -> new Sergeant(this)); } return super.getUnitExtension(extensionName); diff --git a/extension-objects/src/main/java/units/SoldierUnit.java b/extension-objects/src/main/java/units/SoldierUnit.java index 9f3901cf989b..721c51effe27 100644 --- a/extension-objects/src/main/java/units/SoldierUnit.java +++ b/extension-objects/src/main/java/units/SoldierUnit.java @@ -25,6 +25,7 @@ import abstractextensions.UnitExtension; import concreteextensions.Soldier; +import java.util.Optional; /** * Class defining SoldierUnit. @@ -39,12 +40,9 @@ public SoldierUnit(String name) { public UnitExtension getUnitExtension(String extensionName) { if (extensionName.equals("SoldierExtension")) { - if (unitExtension == null) { - unitExtension = new Soldier(this); - } - - return unitExtension; + return Optional.ofNullable(unitExtension).orElseGet(() -> new Soldier(this)); } + return super.getUnitExtension(extensionName); } } diff --git a/extension-objects/src/test/java/AppTest.java b/extension-objects/src/test/java/AppTest.java index 001c14a91c68..2af33e506718 100644 --- a/extension-objects/src/test/java/AppTest.java +++ b/extension-objects/src/test/java/AppTest.java @@ -28,10 +28,8 @@ */ public class AppTest { @Test - public void main() throws Exception { - - String[] args = {}; - App.main(args); + public void main() { + App.main(new String[]{}); } } \ No newline at end of file diff --git a/extension-objects/src/test/java/concreteextensions/CommanderTest.java b/extension-objects/src/test/java/concreteextensions/CommanderTest.java index 0bf29bb06abb..60ff614e4c86 100644 --- a/extension-objects/src/test/java/concreteextensions/CommanderTest.java +++ b/extension-objects/src/test/java/concreteextensions/CommanderTest.java @@ -32,8 +32,7 @@ public class CommanderTest { @Test public void commanderReady() { - final Commander commander = new Commander(new CommanderUnit("CommanderUnitTest")); - + final var commander = new Commander(new CommanderUnit("CommanderUnitTest")); commander.commanderReady(); } diff --git a/extension-objects/src/test/java/concreteextensions/SergeantTest.java b/extension-objects/src/test/java/concreteextensions/SergeantTest.java index 4bf25b46bb37..a5a60d9146c0 100644 --- a/extension-objects/src/test/java/concreteextensions/SergeantTest.java +++ b/extension-objects/src/test/java/concreteextensions/SergeantTest.java @@ -31,9 +31,8 @@ */ public class SergeantTest { @Test - public void sergeantReady() throws Exception { - final Sergeant sergeant = new Sergeant(new SergeantUnit("SergeantUnitTest")); - + public void sergeantReady() { + final var sergeant = new Sergeant(new SergeantUnit("SergeantUnitTest")); sergeant.sergeantReady(); } diff --git a/extension-objects/src/test/java/concreteextensions/SoldierTest.java b/extension-objects/src/test/java/concreteextensions/SoldierTest.java index 3853b2982e69..89c8c2d917f2 100644 --- a/extension-objects/src/test/java/concreteextensions/SoldierTest.java +++ b/extension-objects/src/test/java/concreteextensions/SoldierTest.java @@ -31,9 +31,8 @@ */ public class SoldierTest { @Test - public void soldierReady() throws Exception { - final Soldier soldier = new Soldier(new SoldierUnit("SoldierUnitTest")); - + public void soldierReady() { + final var soldier = new Soldier(new SoldierUnit("SoldierUnitTest")); soldier.soldierReady(); } diff --git a/extension-objects/src/test/java/units/CommanderUnitTest.java b/extension-objects/src/test/java/units/CommanderUnitTest.java index b0e4d87cfb73..0b35fbe9aaa1 100644 --- a/extension-objects/src/test/java/units/CommanderUnitTest.java +++ b/extension-objects/src/test/java/units/CommanderUnitTest.java @@ -23,20 +23,18 @@ package units; -import abstractextensions.CommanderExtension; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + /** * Created by Srdjan on 03-May-17. */ public class CommanderUnitTest { @Test public void getUnitExtension() { - - final Unit unit = new CommanderUnit("CommanderUnitName"); + final var unit = new CommanderUnit("CommanderUnitName"); assertNull(unit.getUnitExtension("SoldierExtension")); assertNull(unit.getUnitExtension("SergeantExtension")); diff --git a/extension-objects/src/test/java/units/SergeantUnitTest.java b/extension-objects/src/test/java/units/SergeantUnitTest.java index 6b89e8f99446..52edadf999a9 100644 --- a/extension-objects/src/test/java/units/SergeantUnitTest.java +++ b/extension-objects/src/test/java/units/SergeantUnitTest.java @@ -23,20 +23,18 @@ package units; -import abstractextensions.SergeantExtension; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + /** * Created by Srdjan on 03-May-17. */ public class SergeantUnitTest { @Test public void getUnitExtension() { - - final Unit unit = new SergeantUnit("SergeantUnitName"); + final var unit = new SergeantUnit("SergeantUnitName"); assertNull(unit.getUnitExtension("SoldierExtension")); assertNotNull(unit.getUnitExtension("SergeantExtension")); diff --git a/extension-objects/src/test/java/units/SoldierUnitTest.java b/extension-objects/src/test/java/units/SoldierUnitTest.java index 8056d2cfa847..a384011244c4 100644 --- a/extension-objects/src/test/java/units/SoldierUnitTest.java +++ b/extension-objects/src/test/java/units/SoldierUnitTest.java @@ -23,26 +23,22 @@ package units; -import abstractextensions.SoldierExtension; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + /** * Created by Srdjan on 03-May-17. */ public class SoldierUnitTest { @Test public void getUnitExtension() { - - final Unit unit = new SoldierUnit("SoldierUnitName"); + final var unit = new SoldierUnit("SoldierUnitName"); assertNotNull(unit.getUnitExtension("SoldierExtension")); assertNull(unit.getUnitExtension("SergeantExtension")); assertNull(unit.getUnitExtension("CommanderExtension")); - - } } \ No newline at end of file diff --git a/extension-objects/src/test/java/units/UnitTest.java b/extension-objects/src/test/java/units/UnitTest.java index 1072666b5b3b..f65ed3f0f9c8 100644 --- a/extension-objects/src/test/java/units/UnitTest.java +++ b/extension-objects/src/test/java/units/UnitTest.java @@ -23,11 +23,11 @@ package units; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + /** * Created by Srdjan on 03-May-17. */ @@ -35,11 +35,11 @@ public class UnitTest { @Test public void testConstGetSet() throws Exception { - final String name = "testName"; - final Unit unit = new Unit(name); + final var name = "testName"; + final var unit = new Unit(name); assertEquals(name, unit.getName()); - final String newName = "newName"; + final var newName = "newName"; unit.setName(newName); assertEquals(newName, unit.getName()); diff --git a/facade/README.md b/facade/README.md index d6c64500c3f8..89a6a32e9a1c 100644 --- a/facade/README.md +++ b/facade/README.md @@ -197,6 +197,9 @@ facade.endDay(); // Dwarven tunnel digger goes to sleep. ``` +## Class diagram +![alt text](./etc/facade.urm.png "Facade pattern class diagram") + ## Applicability Use the Facade pattern when diff --git a/facade/etc/facade.urm.png b/facade/etc/facade.urm.png new file mode 100644 index 000000000000..8e3ec7aca45e Binary files /dev/null and b/facade/etc/facade.urm.png differ diff --git a/facade/etc/facade.urm.puml b/facade/etc/facade.urm.puml new file mode 100644 index 000000000000..477f9eee7e47 --- /dev/null +++ b/facade/etc/facade.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.facade { + class App { + + App() + + main(args : String[]) {static} + } + class DwarvenCartOperator { + - LOGGER : Logger {static} + + DwarvenCartOperator() + + name() : String + + work() + } + class DwarvenGoldDigger { + - LOGGER : Logger {static} + + DwarvenGoldDigger() + + name() : String + + work() + } + class DwarvenGoldmineFacade { + - workers : List + + DwarvenGoldmineFacade() + + digOutGold() + + endDay() + - makeActions(workers : Collection, actions : Action[]) {static} + + startNewDay() + } + abstract class DwarvenMineWorker { + - LOGGER : Logger {static} + + DwarvenMineWorker() + - action(action : Action) + + action(actions : Action[]) + + goHome() + + goToMine() + + goToSleep() + + name() : String {abstract} + + wakeUp() + + work() {abstract} + } + ~enum Action { + + GO_HOME {static} + + GO_TO_MINE {static} + + GO_TO_SLEEP {static} + + WAKE_UP {static} + + WORK {static} + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } + class DwarvenTunnelDigger { + - LOGGER : Logger {static} + + DwarvenTunnelDigger() + + name() : String + + work() + } +} +DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker +Action ..+ DwarvenMineWorker +DwarvenCartOperator --|> DwarvenMineWorker +DwarvenGoldDigger --|> DwarvenMineWorker +DwarvenTunnelDigger --|> DwarvenMineWorker +@enduml \ No newline at end of file diff --git a/factory-kit/README.md b/factory-kit/README.md index c257010479bd..f374886d4496 100644 --- a/factory-kit/README.md +++ b/factory-kit/README.md @@ -13,6 +13,7 @@ tags: ## Intent Define a factory of immutable content with separated builder and factory interfaces. +## Class diagram ![alt text](./etc/factory-kit.png "Factory Kit") ## Applicability diff --git a/factory-kit/etc/factory-kit.urm.puml b/factory-kit/etc/factory-kit.urm.puml new file mode 100644 index 000000000000..23cf658f467e --- /dev/null +++ b/factory-kit/etc/factory-kit.urm.puml @@ -0,0 +1,46 @@ +@startuml +package com.iluwatar.factorykit { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Axe { + + Axe() + + toString() : String + } + class Bow { + + Bow() + + toString() : String + } + interface Builder { + + add(WeaponType, Supplier) {abstract} + } + class Spear { + + Spear() + + toString() : String + } + class Sword { + + Sword() + + toString() : String + } + interface Weapon { + } + interface WeaponFactory { + + create(WeaponType) : Weapon {abstract} + + factory(consumer : Consumer) : WeaponFactory {static} + } + enum WeaponType { + + AXE {static} + + BOW {static} + + SPEAR {static} + + SWORD {static} + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +Axe ..|> Weapon +Bow ..|> Weapon +Spear ..|> Weapon +Sword ..|> Weapon +@enduml \ No newline at end of file diff --git a/factory-method/README.md b/factory-method/README.md index be5ead3e7329..37ab743933da 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -62,6 +62,9 @@ blacksmith.manufactureWeapon(WeaponType.AXE); // Elvish weapons are created ``` +## Class diagram +![alt text](./etc/factory-method.urm.png "Factory Method pattern class diagram") + ## Applicability Use the Factory Method pattern when diff --git a/factory-method/etc/factory-method.urm.png b/factory-method/etc/factory-method.urm.png new file mode 100644 index 000000000000..7c97aff91ee6 Binary files /dev/null and b/factory-method/etc/factory-method.urm.png differ diff --git a/factory-method/etc/factory-method.urm.puml b/factory-method/etc/factory-method.urm.puml new file mode 100644 index 000000000000..a5d8d4e22f7c --- /dev/null +++ b/factory-method/etc/factory-method.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.factory.method { + class App { + - LOGGER : Logger {static} + - blacksmith : Blacksmith + + App(blacksmith : Blacksmith) + + main(args : String[]) {static} + - manufactureWeapons() + } + interface Blacksmith { + + manufactureWeapon(WeaponType) : Weapon {abstract} + } + class ElfBlacksmith { + - ELFARSENAL : Map {static} + + ElfBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class ElfWeapon { + - weaponType : WeaponType + + ElfWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + class OrcBlacksmith { + - ORCARSENAL : Map {static} + + OrcBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class OrcWeapon { + - weaponType : WeaponType + + OrcWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + interface Weapon { + + getWeaponType() : WeaponType {abstract} + } + enum WeaponType { + + AXE {static} + + SHORT_SWORD {static} + + SPEAR {static} + + UNDEFINED {static} + - title : String + + toString() : String + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +ElfWeapon --> "-weaponType" WeaponType +OrcWeapon --> "-weaponType" WeaponType +App --> "-blacksmith" Blacksmith +ElfBlacksmith ..|> Blacksmith +ElfWeapon ..|> Weapon +OrcBlacksmith ..|> Blacksmith +OrcWeapon ..|> Weapon +@enduml \ No newline at end of file diff --git a/feature-toggle/README.md b/feature-toggle/README.md index c466b8a9454b..b4ba16cb4e00 100644 --- a/feature-toggle/README.md +++ b/feature-toggle/README.md @@ -18,6 +18,7 @@ and rolled out. Allowing switching back to the older feature quickly if needed. can easily introduce code complexity. There is also cause for concern that the old feature that the toggle is eventually going to phase out is never removed, causing redundant code smells and increased maintainability. +## Class diagram ![alt text](./etc/feature-toggle.png "Feature Toggle") ## Applicability @@ -29,4 +30,4 @@ Use the Feature Toggle pattern when ## Credits -* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html) \ No newline at end of file +* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html) diff --git a/feature-toggle/etc/feature-toggle.urm.puml b/feature-toggle/etc/feature-toggle.urm.puml new file mode 100644 index 000000000000..0febb26319d1 --- /dev/null +++ b/feature-toggle/etc/feature-toggle.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.featuretoggle.pattern { + interface Service { + + getWelcomeMessage(User) : String {abstract} + + isEnhanced() : boolean {abstract} + } +} +package com.iluwatar.featuretoggle.user { + class User { + - name : String + + User(name : String) + + toString() : String + } + class UserGroup { + - freeGroup : List {static} + - paidGroup : List {static} + + UserGroup() + + addUserToFreeGroup(user : User) {static} + + addUserToPaidGroup(user : User) {static} + + isPaid(user : User) : boolean {static} + } +} +package com.iluwatar.featuretoggle.pattern.tieredversion { + class TieredFeatureToggleVersion { + + TieredFeatureToggleVersion() + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle.pattern.propertiesversion { + class PropertiesFeatureToggleVersion { + - isEnhanced : boolean + + PropertiesFeatureToggleVersion(properties : Properties) + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +UserGroup --> "-freeGroup" User +PropertiesFeatureToggleVersion ..|> Service +TieredFeatureToggleVersion ..|> Service +@enduml \ No newline at end of file diff --git a/fluentinterface/README.md b/fluentinterface/README.md index 767792da79ab..08e1275e9155 100644 --- a/fluentinterface/README.md +++ b/fluentinterface/README.md @@ -21,6 +21,7 @@ A fluent interface can be implemented using any of * Static Factory Methods and Imports * Named parameters - can be simulated in Java using static factory methods. +## Class diagram ![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") ## Applicability diff --git a/fluentinterface/etc/fluentinterface.urm.puml b/fluentinterface/etc/fluentinterface.urm.puml new file mode 100644 index 000000000000..ef71a0f4bace --- /dev/null +++ b/fluentinterface/etc/fluentinterface.urm.puml @@ -0,0 +1,72 @@ +@startuml +package com.iluwatar.fluentinterface.fluentiterable.simple { + class SimpleFluentIterable { + - iterable : Iterable + # SimpleFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + forEach(action : Consumer) + + from(iterable : Iterable) : FluentIterable {static} + + fromCopyOf(iterable : Iterable) : FluentIterable {static} + + getRemainingElementsCount() : int + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + + spliterator() : Spliterator + + toList(iterator : Iterator) : List {static} + } +} +package com.iluwatar.fluentinterface.app { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + - negatives() : Predicate {static} + - positives() : Predicate {static} + - prettyPrint(delimiter : String, prefix : String, iterable : Iterable) {static} + - prettyPrint(prefix : String, iterable : Iterable) {static} + - transformToString() : Function {static} + } +} +package com.iluwatar.fluentinterface.fluentiterable.lazy { + abstract class DecoratingIterator { + # fromIterator : Iterator + - next : E + + DecoratingIterator(fromIterator : Iterator) + + computeNext() : E {abstract} + + hasNext() : boolean + + next() : E + } + class LazyFluentIterable { + - iterable : Iterable + # LazyFluentIterable() + # LazyFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + from(iterable : Iterable) : FluentIterable {static} + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + } +} +package com.iluwatar.fluentinterface.fluentiterable { + interface FluentIterable { + + asList() : List {abstract} + + copyToList(iterable : Iterable) : List {static} + + filter(Predicate) : FluentIterable {abstract} + + first() : Optional {abstract} + + first(int) : FluentIterable {abstract} + + last() : Optional {abstract} + + last(int) : FluentIterable {abstract} + + map(Function) : FluentIterable {abstract} + } +} +LazyFluentIterable ..|> FluentIterable +SimpleFluentIterable ..|> FluentIterable +@enduml \ No newline at end of file diff --git a/flux/README.md b/flux/README.md index 7ac312c443d2..43e791509d73 100644 --- a/flux/README.md +++ b/flux/README.md @@ -15,6 +15,7 @@ user interacts with a view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. +## Class diagram ![alt text](./etc/flux.png "Flux") ## Applicability diff --git a/flux/etc/flux.urm.puml b/flux/etc/flux.urm.puml new file mode 100644 index 000000000000..300f179764cb --- /dev/null +++ b/flux/etc/flux.urm.puml @@ -0,0 +1,117 @@ +@startuml +package com.iluwatar.flux.view { + class ContentView { + - LOGGER : Logger {static} + - content : Content + + ContentView() + + render() + + storeChanged(store : Store) + } + class MenuView { + - LOGGER : Logger {static} + - selected : MenuItem + + MenuView() + + itemClicked(item : MenuItem) + + render() + + storeChanged(store : Store) + } + interface View { + + render() {abstract} + + storeChanged(Store) {abstract} + } +} +package com.iluwatar.flux.action { + abstract class Action { + - type : ActionType + + Action(type : ActionType) + + getType() : ActionType + } + enum ActionType { + + CONTENT_CHANGED {static} + + MENU_ITEM_SELECTED {static} + + valueOf(name : String) : ActionType {static} + + values() : ActionType[] {static} + } + enum Content { + + COMPANY {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : Content {static} + + values() : Content[] {static} + } + class ContentAction { + - content : Content + + ContentAction(content : Content) + + getContent() : Content + } + class MenuAction { + - menuItem : MenuItem + + MenuAction(menuItem : MenuItem) + + getMenuItem() : MenuItem + } + enum MenuItem { + + COMPANY {static} + + HOME {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : MenuItem {static} + + values() : MenuItem[] {static} + } +} +package com.iluwatar.flux.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.flux.store { + class ContentStore { + - content : Content + + ContentStore() + + getContent() : Content + + onAction(action : Action) + } + class MenuStore { + - selected : MenuItem + + MenuStore() + + getSelected() : MenuItem + + onAction(action : Action) + } + abstract class Store { + - views : List + + Store() + # notifyChange() + + onAction(Action) {abstract} + + registerView(view : View) + } +} +package com.iluwatar.flux.dispatcher { + class Dispatcher { + - instance : Dispatcher {static} + - stores : List + - Dispatcher() + - dispatchAction(action : Action) + + getInstance() : Dispatcher {static} + + menuItemSelected(menuItem : MenuItem) + + registerStore(store : Store) + } +} +MenuAction --> "-menuItem" MenuItem +Action --> "-type" ActionType +MenuStore --> "-selected" MenuItem +Dispatcher --> "-instance" Dispatcher +ContentView --> "-content" Content +Dispatcher --> "-stores" Store +MenuView --> "-selected" MenuItem +Store --> "-views" View +ContentStore --> "-content" Content +ContentAction --> "-content" Content +ContentAction --|> Action +MenuAction --|> Action +ContentStore --|> Store +MenuStore --|> Store +ContentView ..|> View +MenuView ..|> View +@enduml \ No newline at end of file diff --git a/flyweight/README.md b/flyweight/README.md index e008fc3465a4..ff2ddcfe693b 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -110,6 +110,9 @@ factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potio factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364) ``` +## Class diagram +![alt text](./etc/flyweight.urm.png "Flyweight pattern class diagram") + ## Applicability The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the Flyweight pattern when all of the following are diff --git a/flyweight/etc/flyweight.urm.png b/flyweight/etc/flyweight.urm.png new file mode 100644 index 000000000000..299cdb7bdf86 Binary files /dev/null and b/flyweight/etc/flyweight.urm.png differ diff --git a/flyweight/etc/flyweight.urm.puml b/flyweight/etc/flyweight.urm.puml new file mode 100644 index 000000000000..61f2aa696eaf --- /dev/null +++ b/flyweight/etc/flyweight.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.flyweight { + class AlchemistShop { + - LOGGER : Logger {static} + - bottomShelf : List + - topShelf : List + + AlchemistShop() + + enumerate() + + getBottomShelf() : List + + getTopShelf() : List + } + class App { + + App() + + main(args : String[]) {static} + } + class HealingPotion { + - LOGGER : Logger {static} + + HealingPotion() + + drink() + } + class HolyWaterPotion { + - LOGGER : Logger {static} + + HolyWaterPotion() + + drink() + } + class InvisibilityPotion { + - LOGGER : Logger {static} + + InvisibilityPotion() + + drink() + } + class PoisonPotion { + - LOGGER : Logger {static} + + PoisonPotion() + + drink() + } + interface Potion { + + drink() {abstract} + } + class PotionFactory { + - potions : Map + + PotionFactory() + ~ createPotion(type : PotionType) : Potion + } + enum PotionType { + + HEALING {static} + + HOLY_WATER {static} + + INVISIBILITY {static} + + POISON {static} + + STRENGTH {static} + + valueOf(name : String) : PotionType {static} + + values() : PotionType[] {static} + } + class StrengthPotion { + - LOGGER : Logger {static} + + StrengthPotion() + + drink() + } +} +AlchemistShop --> "-topShelf" Potion +HealingPotion ..|> Potion +HolyWaterPotion ..|> Potion +InvisibilityPotion ..|> Potion +PoisonPotion ..|> Potion +StrengthPotion ..|> Potion +@enduml \ No newline at end of file diff --git a/front-controller/README.md b/front-controller/README.md index a462a08e0ab6..3126e5e24ae7 100644 --- a/front-controller/README.md +++ b/front-controller/README.md @@ -14,6 +14,7 @@ Introduce a common handler for all requests for a web site. This way we can encapsulate common functionality such as security, internationalization, routing and logging in a single place. +## Class diagram ![alt text](./etc/front-controller.png "Front Controller") ## Applicability diff --git a/front-controller/etc/front-controller.urm.puml b/front-controller/etc/front-controller.urm.puml new file mode 100644 index 000000000000..d2c21fc015a0 --- /dev/null +++ b/front-controller/etc/front-controller.urm.puml @@ -0,0 +1,53 @@ +@startuml +package com.iluwatar.front.controller { + class App { + + App() + + main(args : String[]) {static} + } + class ArcherCommand { + + ArcherCommand() + + process() + } + class ArcherView { + - LOGGER : Logger {static} + + ArcherView() + + display() + } + class CatapultCommand { + + CatapultCommand() + + process() + } + class CatapultView { + - LOGGER : Logger {static} + + CatapultView() + + display() + } + interface Command { + + process() {abstract} + } + class ErrorView { + - LOGGER : Logger {static} + + ErrorView() + + display() + } + class FrontController { + + FrontController() + - getCommand(request : String) : Command + - getCommandClass(request : String) : Class {static} + + handleRequest(request : String) + } + class UnknownCommand { + + UnknownCommand() + + process() + } + interface View { + + display() {abstract} + } +} +ArcherCommand ..|> Command +ArcherView ..|> View +CatapultCommand ..|> Command +CatapultView ..|> View +ErrorView ..|> View +UnknownCommand ..|> Command +@enduml \ No newline at end of file diff --git a/game-loop/README.md b/game-loop/README.md index 5df86e045866..566ea37f35b3 100644 --- a/game-loop/README.md +++ b/game-loop/README.md @@ -34,7 +34,9 @@ The variable-step game loop chooses a time step to advance based on how much rea For fixed-step game loop, a certain amount of real time has elapsed since the last turn of the game loop. This is how much game time need to be simulated for the game’s “now” to catch up with the player’s. +## Class diagram +![alt text](./etc/game-loop.urm.png "Game Loop pattern class diagram") ## Credits -* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html) \ No newline at end of file +* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html) diff --git a/game-loop/etc/game-loop.urm.png b/game-loop/etc/game-loop.urm.png new file mode 100644 index 000000000000..b7ffc11d2659 Binary files /dev/null and b/game-loop/etc/game-loop.urm.png differ diff --git a/game-loop/etc/game-loop.urm.puml b/game-loop/etc/game-loop.urm.puml new file mode 100644 index 000000000000..d0704096f11e --- /dev/null +++ b/game-loop/etc/game-loop.urm.puml @@ -0,0 +1,63 @@ +@startuml +package com.iluwatar.gameloop { + class App { + - GAME_LOOP_DURATION_TIME : int {static} + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Bullet { + - position : float + + Bullet() + + getPosition() : float + + setPosition(position : float) + } + class FixedStepGameLoop { + - MS_PER_FRAME : long {static} + + FixedStepGameLoop() + # processGameLoop() + # update() + } + class FrameBasedGameLoop { + + FrameBasedGameLoop() + # processGameLoop() + # update() + } + class GameController { + # bullet : Bullet + + GameController() + + getBulletPosition() : float + + moveBullet(offset : float) + } + abstract class GameLoop { + # controller : GameController + - gameThread : Thread + # logger : Logger + # status : GameStatus + + GameLoop() + + isGameRunning() : boolean + # processGameLoop() {abstract} + # processInput() + # render() + + run() + + stop() + } + enum GameStatus { + + RUNNING {static} + + STOPPED {static} + + valueOf(name : String) : GameStatus {static} + + values() : GameStatus[] {static} + } + class VariableStepGameLoop { + + VariableStepGameLoop() + # processGameLoop() + # update(elapsedTime : Long) + } +} +GameLoop --> "-status" GameStatus +GameController --> "-bullet" Bullet +GameLoop --> "-controller" GameController +FixedStepGameLoop --|> GameLoop +FrameBasedGameLoop --|> GameLoop +VariableStepGameLoop --|> GameLoop +@enduml \ No newline at end of file diff --git a/guarded-suspension/README.md b/guarded-suspension/README.md index 35044f9b2491..28ffa9d9874c 100644 --- a/guarded-suspension/README.md +++ b/guarded-suspension/README.md @@ -12,6 +12,7 @@ tags: ## Intent Use Guarded suspension pattern to handle a situation when you want to execute a method on object which is not in a proper state. +## Class diagram ![Guarded Suspension diagram](./etc/guarded-suspension.png) ## Applicability diff --git a/guarded-suspension/etc/guarded-suspension.urm.puml b/guarded-suspension/etc/guarded-suspension.urm.puml new file mode 100644 index 000000000000..45a7d37904cb --- /dev/null +++ b/guarded-suspension/etc/guarded-suspension.urm.puml @@ -0,0 +1,15 @@ +@startuml +package com.iluwatar.guarded.suspension { + class App { + + App() + + main(args : String[]) {static} + } + class GuardedQueue { + - LOGGER : Logger {static} + - sourceList : Queue + + GuardedQueue() + + get() : Integer + + put(e : Integer) + } +} +@enduml \ No newline at end of file diff --git a/half-sync-half-async/README.md b/half-sync-half-async/README.md index ef463edadc6f..4ad350d59cd9 100644 --- a/half-sync-half-async/README.md +++ b/half-sync-half-async/README.md @@ -14,6 +14,7 @@ The Half-Sync/Half-Async pattern decouples synchronous I/O from asynchronous I/O in a system to simplify concurrent programming effort without degrading execution efficiency. +## Class diagram ![Half-Sync/Half-Async class diagram](./etc/half-sync-half-async.png) ## Applicability diff --git a/half-sync-half-async/etc/half-sync-half-async.urm.puml b/half-sync-half-async/etc/half-sync-half-async.urm.puml new file mode 100644 index 000000000000..64d8a5f8d87d --- /dev/null +++ b/half-sync-half-async/etc/half-sync-half-async.urm.puml @@ -0,0 +1,33 @@ +@startuml +package com.iluwatar.halfsynchalfasync { + class App { + - LOGGER : Logger {static} + + App() + - ap(i : long) : long {static} + + main(args : String[]) {static} + } + ~class ArithmeticSumTask { + - numberOfElements : long + + ArithmeticSumTask(numberOfElements : long) + + call() : Long + + onError(throwable : Throwable) + + onPostCall(result : Long) + + onPreCall() + } + interface AsyncTask { + + call() : O {abstract} + + onError(Throwable) {abstract} + + onPostCall(O) {abstract} + + onPreCall() {abstract} + } + class AsynchronousService { + - LOGGER : Logger {static} + - service : ExecutorService + + AsynchronousService(workQueue : BlockingQueue) + + close() + + execute(task : AsyncTask) + } +} +ArithmeticSumTask ..+ App +ArithmeticSumTask ..|> AsyncTask +@enduml \ No newline at end of file diff --git a/hexagonal/README.md b/hexagonal/README.md index 1e6f897d0297..eb5ef0987e31 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -18,6 +18,7 @@ tags: ## Intent Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. +## Class diagram ![Hexagonal Architecture class diagram](./etc/hexagonal.png) ## Applicability diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml new file mode 100644 index 000000000000..10fcc7f6d71e --- /dev/null +++ b/hexagonal/etc/hexagonal.urm.puml @@ -0,0 +1,305 @@ +@startuml +package com.iluwatar.hexagonal.sampledata { + class SampleData { + - PLAYERS : List {static} + - RANDOM : Random {static} + + SampleData() + - getRandomPlayerDetails() : PlayerDetails {static} + + submitTickets(lotteryService : LotteryService, numTickets : int) {static} + } +} +package com.iluwatar.hexagonal.service { + class ConsoleLottery { + - LOGGER : Logger {static} + + ConsoleLottery() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} + } + interface LotteryConsoleService { + + addFundsToLotteryAccount(WireTransfers, Scanner) {abstract} + + checkTicket(LotteryService, Scanner) {abstract} + + queryLotteryAccountFunds(WireTransfers, Scanner) {abstract} + + submitTicket(LotteryService, Scanner) {abstract} + } + class LotteryConsoleServiceImpl { + - logger : Logger + + LotteryConsoleServiceImpl(logger : Logger) + + addFundsToLotteryAccount(bank : WireTransfers, scanner : Scanner) + + checkTicket(service : LotteryService, scanner : Scanner) + + queryLotteryAccountFunds(bank : WireTransfers, scanner : Scanner) + - readString(scanner : Scanner) : String + + submitTicket(service : LotteryService, scanner : Scanner) + } +} +package com.iluwatar.hexagonal.mongo { + class MongoConnectionPropertiesLoader { + - DEFAULT_HOST : String {static} + - DEFAULT_PORT : int {static} + + MongoConnectionPropertiesLoader() + + load() {static} + } +} +package com.iluwatar.hexagonal.domain { + class LotteryAdministration { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryAdministration(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + getAllSubmittedTickets() : Map + + performLottery() : LotteryNumbers + + resetLottery() + } + class LotteryConstants { + + PLAYER_MAX_BALANCE : int {static} + + PRIZE_AMOUNT : int {static} + + SERVICE_BANK_ACCOUNT : String {static} + + SERVICE_BANK_ACCOUNT_BALANCE : int {static} + + TICKET_PRIZE : int {static} + - LotteryConstants() + } + class LotteryNumbers { + + MAX_NUMBER : int {static} + + MIN_NUMBER : int {static} + + NUM_NUMBERS : int {static} + - numbers : Set + - LotteryNumbers() + - LotteryNumbers(givenNumbers : Set) + + create(givenNumbers : Set) : LotteryNumbers {static} + + createRandom() : LotteryNumbers {static} + + equals(obj : Object) : boolean + - generateRandomNumbers() + + getNumbers() : Set + + getNumbersAsString() : String + + hashCode() : int + + toString() : String + } + -class RandomNumberGenerator { + - randomIterator : OfInt + + RandomNumberGenerator(min : int, max : int) + + nextInt() : int + } + class LotteryService { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryService(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult + + submitTicket(ticket : LotteryTicket) : Optional + } + class LotteryTicket { + - id : LotteryTicketId + - lotteryNumbers : LotteryNumbers + - playerDetails : PlayerDetails + + LotteryTicket(id : LotteryTicketId, details : PlayerDetails, numbers : LotteryNumbers) + + equals(obj : Object) : boolean + + getId() : LotteryTicketId + + getNumbers() : LotteryNumbers + + getPlayerDetails() : PlayerDetails + + hashCode() : int + + setId(id : LotteryTicketId) + + toString() : String + } + class LotteryTicketCheckResult { + - checkResult : CheckResult + - prizeAmount : int + + LotteryTicketCheckResult(result : CheckResult) + + LotteryTicketCheckResult(result : CheckResult, amount : int) + + equals(obj : Object) : boolean + + getPrizeAmount() : int + + getResult() : CheckResult + + hashCode() : int + } + enum CheckResult { + + NO_PRIZE {static} + + TICKET_NOT_SUBMITTED {static} + + WIN_PRIZE {static} + + valueOf(name : String) : CheckResult {static} + + values() : CheckResult[] {static} + } + class LotteryTicketId { + - id : int + - numAllocated : AtomicInteger {static} + + LotteryTicketId() + + LotteryTicketId(id : int) + + equals(o : Object) : boolean + + getId() : int + + hashCode() : int + + toString() : String + } + class LotteryUtils { + - LotteryUtils() + + checkTicketForPrize(repository : LotteryTicketRepository, id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult {static} + } + class PlayerDetails { + - bankAccountNumber : String + - emailAddress : String + - phoneNumber : String + + PlayerDetails(email : String, bankAccount : String, phone : String) + + equals(obj : Object) : boolean + + getBankAccount() : String + + getEmail() : String + + getPhoneNumber() : String + + hashCode() : int + + toString() : String + } +} +package com.iluwatar.hexagonal.banking { + class InMemoryBank { + - accounts : Map {static} + + InMemoryBank() + + getFunds(bankAccount : String) : int + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceAccount : String, destinationAccount : String) : boolean + } + class MongoBank { + - DEFAULT_ACCOUNTS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - accountsCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + + MongoBank() + + MongoBank(dbName : String, accountsCollectionName : String) + + connect() + + connect(dbName : String, accountsCollectionName : String) + + getAccountsCollection() : MongoCollection + + getFunds(bankAccount : String) : int + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceAccount : String, destinationAccount : String) : boolean + } + interface WireTransfers { + + getFunds(String) : int {abstract} + + setFunds(String, int) {abstract} + + transferFunds(int, String, String) : boolean {abstract} + } +} +package com.iluwatar.hexagonal.database { + class InMemoryTicketRepository { + - tickets : Map {static} + + InMemoryTicketRepository() + + deleteAll() + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + save(ticket : LotteryTicket) : Optional + } + interface LotteryTicketRepository { + + deleteAll() {abstract} + + findAll() : Map {abstract} + + findById(LotteryTicketId) : Optional {abstract} + + save(LotteryTicket) : Optional {abstract} + } + class MongoTicketRepository { + - DEFAULT_COUNTERS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - DEFAULT_TICKETS_COLLECTION : String {static} + - countersCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + - ticketsCollection : MongoCollection + + MongoTicketRepository() + + MongoTicketRepository(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + connect() + + connect(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + deleteAll() + - docToTicket(doc : Document) : LotteryTicket + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + getCountersCollection() : MongoCollection + + getNextId() : int + + getTicketsCollection() : MongoCollection + - initCounters() + + save(ticket : LotteryTicket) : Optional + } +} +package com.iluwatar.hexagonal { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.hexagonal.administration { + class ConsoleAdministration { + - LOGGER : Logger {static} + + ConsoleAdministration() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} + } + interface ConsoleAdministrationSrv { + + getAllSubmittedTickets() {abstract} + + performLottery() {abstract} + + resetLottery() {abstract} + } + class ConsoleAdministrationSrvImpl { + - administration : LotteryAdministration + - logger : Logger + + ConsoleAdministrationSrvImpl(administration : LotteryAdministration, logger : Logger) + + getAllSubmittedTickets() + + performLottery() + + resetLottery() + } +} +package com.iluwatar.hexagonal.eventlog { + interface LotteryEventLog { + + prizeError(PlayerDetails, int) {abstract} + + ticketDidNotWin(PlayerDetails) {abstract} + + ticketSubmitError(PlayerDetails) {abstract} + + ticketSubmitted(PlayerDetails) {abstract} + + ticketWon(PlayerDetails, int) {abstract} + } + class MongoEventLog { + - DEFAULT_DB : String {static} + - DEFAULT_EVENTS_COLLECTION : String {static} + - database : MongoDatabase + - eventsCollection : MongoCollection + - mongoClient : MongoClient + - stdOutEventLog : StdOutEventLog + + MongoEventLog() + + MongoEventLog(dbName : String, eventsCollectionName : String) + + connect() + + connect(dbName : String, eventsCollectionName : String) + + getEventsCollection() : MongoCollection + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) + } + class StdOutEventLog { + - LOGGER : Logger {static} + + StdOutEventLog() + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) + } +} +LotteryTicket --> "-playerDetails" PlayerDetails +MongoEventLog --> "-stdOutEventLog" StdOutEventLog +LotteryService --> "-wireTransfers" WireTransfers +LotteryAdministration --> "-notifications" LotteryEventLog +LotteryAdministration --> "-wireTransfers" WireTransfers +LotteryTicket --> "-id" LotteryTicketId +LotteryAdministration --> "-repository" LotteryTicketRepository +LotteryService --> "-notifications" LotteryEventLog +LotteryTicket --> "-lotteryNumbers" LotteryNumbers +SampleData --> "-PLAYERS" PlayerDetails +ConsoleAdministrationSrvImpl --> "-administration" LotteryAdministration +RandomNumberGenerator ..+ LotteryNumbers +LotteryService --> "-repository" LotteryTicketRepository +CheckResult ..+ LotteryTicketCheckResult +LotteryTicketCheckResult --> "-checkResult" CheckResult +ConsoleAdministrationSrvImpl ..|> ConsoleAdministrationSrv +InMemoryBank ..|> WireTransfers +MongoBank ..|> WireTransfers +InMemoryTicketRepository ..|> LotteryTicketRepository +MongoTicketRepository ..|> LotteryTicketRepository +MongoEventLog ..|> LotteryEventLog +StdOutEventLog ..|> LotteryEventLog +LotteryConsoleServiceImpl ..|> LotteryConsoleService +@enduml \ No newline at end of file diff --git a/intercepting-filter/README.md b/intercepting-filter/README.md index c287138953eb..660d67e3d916 100644 --- a/intercepting-filter/README.md +++ b/intercepting-filter/README.md @@ -12,10 +12,9 @@ tags: ## Intent Provide pluggable filters to conduct necessary pre-processing and post-processing to requests from a client to a target - -![alt text](./etc/intercepting-filter.png "Intercepting Filter") - +## Class diagram +![alt text](./etc/intercepting-filter.png "Intercepting Filter") ## Applicability Use the Intercepting Filter pattern when diff --git a/intercepting-filter/etc/intercepting-filter.urm.puml b/intercepting-filter/etc/intercepting-filter.urm.puml new file mode 100644 index 000000000000..6e565fd668c9 --- /dev/null +++ b/intercepting-filter/etc/intercepting-filter.urm.puml @@ -0,0 +1,88 @@ +@startuml +package com.iluwatar.intercepting.filter { + abstract class AbstractFilter { + - next : Filter + + AbstractFilter() + + AbstractFilter(next : Filter) + + execute(order : Order) : String + + getLast() : Filter + + getNext() : Filter + + setNext(filter : Filter) + } + class AddressFilter { + + AddressFilter() + + execute(order : Order) : String + } + class App { + + App() + + main(args : String[]) {static} + } + class ContactFilter { + + ContactFilter() + + execute(order : Order) : String + } + class DepositFilter { + + DepositFilter() + + execute(order : Order) : String + } + interface Filter { + + execute(Order) : String {abstract} + + getLast() : Filter {abstract} + + getNext() : Filter {abstract} + + setNext(Filter) {abstract} + } + class FilterChain { + - chain : Filter + + FilterChain() + + addFilter(filter : Filter) + + execute(order : Order) : String + } + class FilterManager { + - filterChain : FilterChain + + FilterManager() + + addFilter(filter : Filter) + + filterRequest(order : Order) : String + } + class NameFilter { + + NameFilter() + + execute(order : Order) : String + } + class Order { + - address : String + - contactNumber : String + - depositNumber : String + - name : String + - orderItem : String + + Order() + + Order(name : String, contactNumber : String, address : String, depositNumber : String, order : String) + + getAddress() : String + + getContactNumber() : String + + getDepositNumber() : String + + getName() : String + + getOrderItem() : String + + setAddress(address : String) + + setContactNumber(contactNumber : String) + + setDepositNumber(depositNumber : String) + + setName(name : String) + + setOrderItem(order : String) + } + class OrderFilter { + + OrderFilter() + + execute(order : Order) : String + } + ~class DListener { + ~ DListener() + + actionPerformed(e : ActionEvent) + } +} +AbstractFilter --> "-next" Filter +DListener --+ Target +FilterManager --> "-filterChain" FilterChain +FilterChain --> "-chain" Filter +AbstractFilter ..|> Filter +AddressFilter --|> AbstractFilter +ContactFilter --|> AbstractFilter +DepositFilter --|> AbstractFilter +NameFilter --|> AbstractFilter +OrderFilter --|> AbstractFilter +@enduml \ No newline at end of file diff --git a/interpreter/README.md b/interpreter/README.md index be65179627c9..6349450051a2 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -15,6 +15,7 @@ Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. +## Class diagram ![alt text](./etc/interpreter_1.png "Interpreter") ## Applicability diff --git a/interpreter/etc/interpreter.urm.puml b/interpreter/etc/interpreter.urm.puml new file mode 100644 index 000000000000..e79ad1989588 --- /dev/null +++ b/interpreter/etc/interpreter.urm.puml @@ -0,0 +1,51 @@ +@startuml +package com.iluwatar.interpreter { + class App { + - LOGGER : Logger {static} + + App() + + getOperatorInstance(s : String, left : Expression, right : Expression) : Expression {static} + + isOperator(s : String) : boolean {static} + + main(args : String[]) {static} + } + abstract class Expression { + + Expression() + + interpret() : int {abstract} + + toString() : String {abstract} + } + class MinusExpression { + - leftExpression : Expression + - rightExpression : Expression + + MinusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class MultiplyExpression { + - leftExpression : Expression + - rightExpression : Expression + + MultiplyExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class NumberExpression { + - number : int + + NumberExpression(number : int) + + NumberExpression(s : String) + + interpret() : int + + toString() : String + } + class PlusExpression { + - leftExpression : Expression + - rightExpression : Expression + + PlusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } +} +MultiplyExpression --> "-leftExpression" Expression +MinusExpression --> "-leftExpression" Expression +PlusExpression --> "-leftExpression" Expression +MinusExpression --|> Expression +MultiplyExpression --|> Expression +NumberExpression --|> Expression +PlusExpression --|> Expression +@enduml \ No newline at end of file diff --git a/iterator/README.md b/iterator/README.md index 723e7f03cd20..3dfcb10858ba 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -17,6 +17,7 @@ Cursor Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. +## Class diagram ![alt text](./etc/iterator_1.png "Iterator") ## Applicability diff --git a/iterator/etc/iterator.urm.puml b/iterator/etc/iterator.urm.puml new file mode 100644 index 000000000000..59cb93be26b6 --- /dev/null +++ b/iterator/etc/iterator.urm.puml @@ -0,0 +1,85 @@ +@startuml +package com.iluwatar.iterator { + class App { + - LOGGER : Logger {static} + - TREASURE_CHEST : TreasureChest {static} + + App() + - buildIntegerBst() : TreeNode {static} + - demonstrateBstIterator() {static} + - demonstrateTreasureChestIteratorForType(itemType : ItemType) {static} + + main(args : String[]) {static} + } + interface Iterator { + + hasNext() : boolean {abstract} + + next() : T {abstract} + } +} +package com.iluwatar.iterator.bst { + class BstIterator> { + - pathStack : ArrayDeque>> + + BstIterator>(root : TreeNode>) + + hasNext() : boolean + + next() : TreeNode> + - pushPathToNextSmallest(node : TreeNode>) + } + class TreeNode> { + - left : TreeNode> + - right : TreeNode> + - val : T extends Comparable + + TreeNode>(val : T extends Comparable) + + getLeft() : TreeNode> + - getParentNodeOfValueToBeInserted(valToInsert : T extends Comparable) : TreeNode> + + getRight() : TreeNode> + + getVal() : T extends Comparable + + insert(valToInsert : T extends Comparable) + - insertNewChild(valToInsert : T extends Comparable) + - isGreaterThan(val : T extends Comparable) : boolean + - isLessThanOrEqualTo(val : T extends Comparable) : boolean + - setLeft(left : TreeNode>) + - setRight(right : TreeNode>) + + toString() : String + - traverseOneLevelDown(value : T extends Comparable) : TreeNode> + } +} +package com.iluwatar.iterator.list { + class Item { + - name : String + - type : ItemType + + Item(type : ItemType, name : String) + + getType() : ItemType + + setType(type : ItemType) + + toString() : String + } + enum ItemType { + + ANY {static} + + POTION {static} + + RING {static} + + WEAPON {static} + + valueOf(name : String) : ItemType {static} + + values() : ItemType[] {static} + } + class TreasureChest { + - items : List + + TreasureChest() + + getItems() : List + + iterator(itemType : ItemType) : Iterator + } + class TreasureChestItemIterator { + - chest : TreasureChest + - idx : int + - type : ItemType + + TreasureChestItemIterator(chest : TreasureChest, type : ItemType) + - findNextIdx() : int + + hasNext() : boolean + + next() : Item + } +} +TreasureChestItemIterator --> "-type" ItemType +TreeNode --> "-left" TreeNode +TreasureChestItemIterator --> "-chest" TreasureChest +TreasureChest --> "-items" Item +Item --> "-type" ItemType +App --> "-TREASURE_CHEST" TreasureChest +BstIterator ..|> Iterator +TreasureChestItemIterator ..|> Iterator +@enduml \ No newline at end of file diff --git a/layers/README.md b/layers/README.md index 3214a9e61756..66a5c9c321af 100644 --- a/layers/README.md +++ b/layers/README.md @@ -15,15 +15,6 @@ tags: Layers is an architectural pattern where software responsibilities are divided among the different layers of the application. -![alt text](./etc/layers.png "Layers") - -## Applicability -Use the Layers architecture when - -* you want clearly divide software responsibilities into different parts of the program -* you want to prevent a change from propagating throughout the application -* you want to make your application more maintainable and testable - ## Explanation Real world example @@ -102,6 +93,16 @@ public class CakeViewImpl implements View { } ``` +## Class diagram +![alt text](./etc/layers.png "Layers") + +## Applicability +Use the Layers architecture when + +* you want clearly divide software responsibilities into different parts of the program +* you want to prevent a change from propagating throughout the application +* you want to make your application more maintainable and testable + ## Credits * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/layers/etc/layers.urm.puml b/layers/etc/layers.urm.puml new file mode 100644 index 000000000000..f73b7964a0b4 --- /dev/null +++ b/layers/etc/layers.urm.puml @@ -0,0 +1,136 @@ +@startuml +package com.iluwatar.layers.dto { + class CakeInfo { + + cakeLayerInfos : List + + cakeToppingInfo : CakeToppingInfo + + id : Optional + + CakeInfo(cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + CakeInfo(id : Long, cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + calculateTotalCalories() : int + + toString() : String + } + class CakeLayerInfo { + + calories : int + + id : Optional + + name : String + + CakeLayerInfo(id : Long, name : String, calories : int) + + CakeLayerInfo(name : String, calories : int) + + toString() : String + } + class CakeToppingInfo { + + calories : int + + id : Optional + + name : String + + CakeToppingInfo(id : Long, name : String, calories : int) + + CakeToppingInfo(name : String, calories : int) + + toString() : String + } +} +package com.iluwatar.layers.entity { + class Cake { + - id : Long + - layers : Set + - topping : CakeTopping + + Cake() + + addLayer(layer : CakeLayer) + + getId() : Long + + getLayers() : Set + + getTopping() : CakeTopping + + setId(id : Long) + + setLayers(layers : Set) + + setTopping(topping : CakeTopping) + + toString() : String + } + class CakeLayer { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeLayer() + + CakeLayer(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } + class CakeTopping { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeTopping() + + CakeTopping(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } +} +package com.iluwatar.layers.view { + class CakeViewImpl { + - LOGGER : Logger {static} + - cakeBakingService : CakeBakingService + + CakeViewImpl(cakeBakingService : CakeBakingService) + + render() + } + interface View { + + render() {abstract} + } +} +package com.iluwatar.layers.app { + class App { + - cakeBakingService : CakeBakingService {static} + + App() + - initializeData(cakeBakingService : CakeBakingService) {static} + + main(args : String[]) {static} + } +} +package com.iluwatar.layers.dao { + interface CakeDao { + } + interface CakeLayerDao { + } + interface CakeToppingDao { + } +} +package com.iluwatar.layers.service { + interface CakeBakingService { + + bakeNewCake(CakeInfo) {abstract} + + getAllCakes() : List {abstract} + + getAvailableLayers() : List {abstract} + + getAvailableToppings() : List {abstract} + + saveNewLayer(CakeLayerInfo) {abstract} + + saveNewTopping(CakeToppingInfo) {abstract} + } + class CakeBakingServiceImpl { + - context : AbstractApplicationContext + + CakeBakingServiceImpl() + + bakeNewCake(cakeInfo : CakeInfo) + + getAllCakes() : List + - getAvailableLayerEntities() : List + + getAvailableLayers() : List + - getAvailableToppingEntities() : List + + getAvailableToppings() : List + + saveNewLayer(layerInfo : CakeLayerInfo) + + saveNewTopping(toppingInfo : CakeToppingInfo) + } +} +CakeInfo --> "-cakeLayerInfos" CakeLayerInfo +CakeInfo --> "-cakeToppingInfo" CakeToppingInfo +CakeViewImpl --> "-cakeBakingService" CakeBakingService +App --> "-cakeBakingService" CakeBakingService +Cake --> "-topping" CakeTopping +CakeLayer --> "-cake" Cake +CakeBakingServiceImpl ..|> CakeBakingService +CakeViewImpl ..|> View +@enduml \ No newline at end of file diff --git a/lazy-loading/README.md b/lazy-loading/README.md index d40061293531..8156b3524897 100644 --- a/lazy-loading/README.md +++ b/lazy-loading/README.md @@ -17,6 +17,7 @@ initialization of an object until the point at which it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used. +## Class diagram ![alt text](./etc/lazy-loading.png "Lazy Loading") ## Applicability diff --git a/lazy-loading/etc/lazy-loading.urm.puml b/lazy-loading/etc/lazy-loading.urm.puml new file mode 100644 index 000000000000..aaf49f1df941 --- /dev/null +++ b/lazy-loading/etc/lazy-loading.urm.puml @@ -0,0 +1,40 @@ +@startuml +package com.iluwatar.lazy.loading { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Heavy { + - LOGGER : Logger {static} + + Heavy() + } + class HolderNaive { + - LOGGER : Logger {static} + - heavy : Heavy + + HolderNaive() + + getHeavy() : Heavy + } + class HolderThreadSafe { + - LOGGER : Logger {static} + - heavy : Heavy + + HolderThreadSafe() + + getHeavy() : Heavy + } + class Java8Holder { + - LOGGER : Logger {static} + - heavy : Supplier + + Java8Holder() + - createAndCacheHeavy() : Heavy + + getHeavy() : Heavy + } + ~class HeavyFactory { + - heavyInstance : Heavy + ~ HeavyFactory() + + get() : Heavy + } +} +HolderThreadSafe --> "-heavy" Heavy +HolderNaive --> "-heavy" Heavy +HeavyFactory --> "-heavyInstance" Heavy +@enduml \ No newline at end of file diff --git a/leader-election/README.md b/leader-election/README.md index 36c2670dffa0..15f2f93415e7 100644 --- a/leader-election/README.md +++ b/leader-election/README.md @@ -12,6 +12,9 @@ tags: ## Intent Leader Election pattern is commonly used in cloud system design. It can help to ensure that task instances selec the leader instance correctly and do not conflict with each other, cause contention for shared resources, or inadvertently interfere with the work that other task instances are performing. +## Class diagram +![alt text](./etc/leader-election.urm.png "Leader Election pattern class diagram") + ## Applicability Use this pattern when diff --git a/leader-election/etc/leader-election.urm.png b/leader-election/etc/leader-election.urm.png new file mode 100644 index 000000000000..85a90b75b636 Binary files /dev/null and b/leader-election/etc/leader-election.urm.png differ diff --git a/leader-election/etc/leader-election.urm.puml b/leader-election/etc/leader-election.urm.puml new file mode 100644 index 000000000000..c28066f31566 --- /dev/null +++ b/leader-election/etc/leader-election.urm.puml @@ -0,0 +1,120 @@ +@startuml +package com.iluwatar.leaderelection.ring { + class RingApp { + + RingApp() + + main(args : String[]) {static} + } + class RingInstance { + - LOGGER : Logger {static} + + RingInstance(messageManager : MessageManager, localId : int, leaderId : int) + # handleElectionInvokeMessage() + # handleElectionMessage(message : Message) + # handleHeartbeatInvokeMessage() + # handleHeartbeatMessage(message : Message) + # handleLeaderInvokeMessage() + # handleLeaderMessage(message : Message) + } + class RingMessageManager { + + RingMessageManager(instanceMap : Map) + + sendElectionMessage(currentId : int, content : String) : boolean + + sendHeartbeatInvokeMessage(currentId : int) + + sendHeartbeatMessage(leaderId : int) : boolean + + sendLeaderMessage(currentId : int, leaderId : int) : boolean + } +} +package com.iluwatar.leaderelection { + abstract class AbstractInstance { + # HEARTBEAT_INTERVAL : int {static} + - LOGGER : Logger {static} + # alive : boolean + # leaderId : int + # localId : int + # messageManager : MessageManager + # messageQueue : Queue + + AbstractInstance(messageManager : MessageManager, localId : int, leaderId : int) + # handleElectionInvokeMessage() {abstract} + # handleElectionMessage(Message) {abstract} + # handleHeartbeatInvokeMessage() {abstract} + # handleHeartbeatMessage(Message) {abstract} + # handleLeaderInvokeMessage() {abstract} + # handleLeaderMessage(Message) {abstract} + + isAlive() : boolean + + onMessage(message : Message) + - processMessage(message : Message) + + run() + + setAlive(alive : boolean) + } + abstract class AbstractMessageManager { + # instanceMap : Map + + AbstractMessageManager(instanceMap : Map) + # findNextInstance(currentId : int) : Instance + } + interface Instance { + + isAlive() : boolean {abstract} + + onMessage(Message) {abstract} + + setAlive(boolean) {abstract} + } + class Message { + - content : String + - type : MessageType + + Message() + + Message(type : MessageType, content : String) + + equals(o : Object) : boolean + + getContent() : String + + getType() : MessageType + + hashCode() : int + + setContent(content : String) + + setType(type : MessageType) + } + interface MessageManager { + + sendElectionMessage(int, String) : boolean {abstract} + + sendHeartbeatInvokeMessage(int) {abstract} + + sendHeartbeatMessage(int) : boolean {abstract} + + sendLeaderMessage(int, int) : boolean {abstract} + } + enum MessageType { + + ELECTION {static} + + ELECTION_INVOKE {static} + + HEARTBEAT {static} + + HEARTBEAT_INVOKE {static} + + LEADER {static} + + LEADER_INVOKE {static} + + valueOf(name : String) : MessageType {static} + + values() : MessageType[] {static} + } +} +package com.iluwatar.leaderelection.bully { + class BullyApp { + + BullyApp() + + main(args : String[]) {static} + } + class BullyInstance { + - LOGGER : Logger {static} + + BullyInstance(messageManager : MessageManager, localId : int, leaderId : int) + # handleElectionInvokeMessage() + # handleElectionMessage(message : Message) + # handleHeartbeatInvokeMessage() + # handleHeartbeatMessage(message : Message) + # handleLeaderInvokeMessage() + # handleLeaderMessage(message : Message) + - isLeader() : boolean + } + class BullyMessageManager { + + BullyMessageManager(instanceMap : Map) + - findElectionCandidateInstanceList(currentId : int) : List + + sendElectionMessage(currentId : int, content : String) : boolean + + sendHeartbeatInvokeMessage(currentId : int) + + sendHeartbeatMessage(leaderId : int) : boolean + + sendLeaderMessage(currentId : int, leaderId : int) : boolean + } +} +AbstractInstance --> "-messageQueue" Message +Message --> "-type" MessageType +AbstractInstance --> "-messageManager" MessageManager +AbstractInstance ..|> Instance +AbstractMessageManager ..|> MessageManager +BullyInstance --|> AbstractInstance +BullyMessageManager --|> AbstractMessageManager +RingInstance --|> AbstractInstance +RingMessageManager --|> AbstractMessageManager +@enduml \ No newline at end of file diff --git a/marker/README.md b/marker/README.md index 6f5fc1e91c7a..70e92480edab 100644 --- a/marker/README.md +++ b/marker/README.md @@ -12,6 +12,7 @@ tags: ## Intent Using empty interfaces as markers to distinguish special treated objects. +## Class diagram ![alt text](./etc/MarkerDiagram.png "Marker Interface") ## Applicability diff --git a/marker/etc/marker.urm.puml b/marker/etc/marker.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/marker/etc/marker.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/master-worker-pattern/README.md b/master-worker-pattern/README.md index f8b5509369ff..239b83fde274 100644 --- a/master-worker-pattern/README.md +++ b/master-worker-pattern/README.md @@ -17,6 +17,9 @@ tags: > Used for centralised parallel processing. +## Class diagram +![alt text](./etc/master-worker-pattern.urm.png "Master-Worker pattern class diagram") + ## Applicability This pattern can be used when data can be divided into multiple parts, all of which need to go through the same computation to give a result, which need to be aggregated to get the final result. diff --git a/master-worker-pattern/etc/master-worker-pattern.urm.png b/master-worker-pattern/etc/master-worker-pattern.urm.png new file mode 100644 index 000000000000..a4c7a16d3ac9 Binary files /dev/null and b/master-worker-pattern/etc/master-worker-pattern.urm.png differ diff --git a/master-worker-pattern/etc/master-worker-pattern.urm.puml b/master-worker-pattern/etc/master-worker-pattern.urm.puml new file mode 100644 index 000000000000..b3b5add30671 --- /dev/null +++ b/master-worker-pattern/etc/master-worker-pattern.urm.puml @@ -0,0 +1,78 @@ +@startuml +package com.iluwatar.masterworker.system.systemmaster { + class ArrayTransposeMaster { + + ArrayTransposeMaster(numOfWorkers : int) + ~ aggregateData() : ArrayResult + ~ setWorkers(num : int) : ArrayList + } + abstract class Master { + - allResultData : Hashtable> + - expectedNumResults : int + - finalResult : Result + - numOfWorkers : int + - workers : ArrayList + ~ Master(numOfWorkers : int) + ~ aggregateData() : Result {abstract} + - collectResult(data : Result, workerId : int) + - divideWork(input : Input) + + doWork(input : Input) + ~ getAllResultData() : Hashtable> + ~ getExpectedNumResults() : int + + getFinalResult() : Result + ~ getWorkers() : ArrayList + + receiveData(data : Result, w : Worker) + ~ setWorkers(int) : ArrayList {abstract} + } +} +package com.iluwatar.masterworker.system { + class ArrayTransposeMasterWorker { + + ArrayTransposeMasterWorker() + ~ setMaster(numOfWorkers : int) : Master + } + abstract class MasterWorker { + - master : Master + + MasterWorker(numOfWorkers : int) + + getResult(input : Input) : Result + ~ setMaster(int) : Master {abstract} + } +} +package com.iluwatar.masterworker { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class ArrayInput { + + ArrayInput(data : int[][]) + + divideData(num : int) : ArrayList> + ~ makeDivisions(data : int[][], num : int) : int[] {static} + } + class ArrayResult { + + ArrayResult(data : int[][]) + } + class ArrayUtilityMethods { + - LOGGER : Logger {static} + - RANDOM : Random {static} + + ArrayUtilityMethods() + + arraysSame(a1 : int[], a2 : int[]) : boolean {static} + + createRandomIntMatrix(rows : int, columns : int) : int[][] {static} + + matricesSame(m1 : int[][], m2 : int[][]) : boolean {static} + + printMatrix(matrix : int[][]) {static} + } + abstract class Input { + + data : T + + Input(data : T) + + divideData(int) : ArrayList> {abstract} + } + abstract class Result { + + data : T + + Result(data : T) + } +} +Master --> "-finalResult" Result +MasterWorker --> "-master" Master +ArrayInput --|> Input +ArrayResult --|> Result +ArrayTransposeMasterWorker --|> MasterWorker +ArrayTransposeMaster --|> Master +@enduml \ No newline at end of file diff --git a/mediator/README.md b/mediator/README.md index 3452082ef8e6..4e38d833c35f 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -15,6 +15,7 @@ Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. +## Class diagram ![alt text](./etc/mediator_1.png "Mediator") ## Applicability diff --git a/mediator/etc/mediator.urm.puml b/mediator/etc/mediator.urm.puml new file mode 100644 index 000000000000..04e75d2b8841 --- /dev/null +++ b/mediator/etc/mediator.urm.puml @@ -0,0 +1,69 @@ +@startuml +package com.iluwatar.mediator { + enum Action { + + ENEMY {static} + + GOLD {static} + + HUNT {static} + + NONE {static} + + TALE {static} + - description : String + - title : String + + getDescription() : String + + toString() : String + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } + class App { + + App() + + main(args : String[]) {static} + } + class Hobbit { + + Hobbit() + + toString() : String + } + class Hunter { + + Hunter() + + toString() : String + } + interface Party { + + act(PartyMember, Action) {abstract} + + addMember(PartyMember) {abstract} + } + class PartyImpl { + - members : List + + PartyImpl() + + act(actor : PartyMember, action : Action) + + addMember(member : PartyMember) + } + interface PartyMember { + + act(Action) {abstract} + + joinedParty(Party) {abstract} + + partyAction(Action) {abstract} + } + abstract class PartyMemberBase { + - LOGGER : Logger {static} + # party : Party + + PartyMemberBase() + + act(action : Action) + + joinedParty(party : Party) + + partyAction(action : Action) + + toString() : String {abstract} + } + class Rogue { + + Rogue() + + toString() : String + } + class Wizard { + + Wizard() + + toString() : String + } +} +PartyImpl --> "-members" PartyMember +PartyMemberBase --> "-party" Party +Hobbit --|> PartyMemberBase +Hunter --|> PartyMemberBase +PartyImpl ..|> Party +PartyMemberBase ..|> PartyMember +Rogue --|> PartyMemberBase +Wizard --|> PartyMemberBase +@enduml \ No newline at end of file diff --git a/memento/README.md b/memento/README.md index 463b5fec0836..34555f550ce3 100644 --- a/memento/README.md +++ b/memento/README.md @@ -17,6 +17,7 @@ Token Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. +## Class diagram ![alt text](./etc/memento.png "Memento") ## Applicability diff --git a/memento/etc/memento.urm.puml b/memento/etc/memento.urm.puml new file mode 100644 index 000000000000..319227ad242c --- /dev/null +++ b/memento/etc/memento.urm.puml @@ -0,0 +1,49 @@ +@startuml +package com.iluwatar.memento { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Star { + - ageYears : int + - massTons : int + - type : StarType + + Star(startType : StarType, startAge : int, startMass : int) + ~ getMemento() : StarMemento + ~ setMemento(memento : StarMemento) + + timePasses() + + toString() : String + } + -class StarMementoInternal { + - ageYears : int + - massTons : int + - type : StarType + - StarMementoInternal() + + getAgeYears() : int + + getMassTons() : int + + getType() : StarType + + setAgeYears(ageYears : int) + + setMassTons(massTons : int) + + setType(type : StarType) + } + interface StarMemento { + } + enum StarType { + + DEAD {static} + + RED_GIANT {static} + + SUN {static} + + SUPERNOVA {static} + + UNDEFINED {static} + + WHITE_DWARF {static} + - title : String + + toString() : String + + valueOf(name : String) : StarType {static} + + values() : StarType[] {static} + } +} +StarMementoInternal --> "-type" StarType +Star --> "-type" StarType +StarMementoInternal ..+ Star +StarMementoInternal ..|> StarMemento +@enduml \ No newline at end of file diff --git a/model-view-controller/README.md b/model-view-controller/README.md index bc96f7ab1e01..acc3c07a96b2 100644 --- a/model-view-controller/README.md +++ b/model-view-controller/README.md @@ -15,6 +15,7 @@ the model, the view and the controller. Let the model manage the data, the view display the data and the controller mediate updating the data and redrawing the display. +## Class diagram ![alt text](./etc/model-view-controller.png "Model-View-Controller") ## Applicability diff --git a/model-view-controller/etc/model-view-controller.urm.puml b/model-view-controller/etc/model-view-controller.urm.puml new file mode 100644 index 000000000000..92ec1e949d83 --- /dev/null +++ b/model-view-controller/etc/model-view-controller.urm.puml @@ -0,0 +1,70 @@ +@startuml +package com.iluwatar.model.view.controller { + class App { + + App() + + main(args : String[]) {static} + } + enum Fatigue { + + ALERT {static} + + SLEEPING {static} + + TIRED {static} + - title : String + + toString() : String + + valueOf(name : String) : Fatigue {static} + + values() : Fatigue[] {static} + } + class GiantController { + - giant : GiantModel + - view : GiantView + + GiantController(giant : GiantModel, view : GiantView) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + updateView() + } + class GiantModel { + - fatigue : Fatigue + - health : Health + - nourishment : Nourishment + ~ GiantModel(health : Health, fatigue : Fatigue, nourishment : Nourishment) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + toString() : String + } + class GiantView { + - LOGGER : Logger {static} + + GiantView() + + displayGiant(giant : GiantModel) + } + enum Health { + + DEAD {static} + + HEALTHY {static} + + WOUNDED {static} + - title : String + + toString() : String + + valueOf(name : String) : Health {static} + + values() : Health[] {static} + } + enum Nourishment { + + HUNGRY {static} + + SATURATED {static} + + STARVING {static} + - title : String + + toString() : String + + valueOf(name : String) : Nourishment {static} + + values() : Nourishment[] {static} + } +} +GiantModel --> "-nourishment" Nourishment +GiantController --> "-giant" GiantModel +GiantModel --> "-fatigue" Fatigue +GiantModel --> "-health" Health +GiantController --> "-view" GiantView +@enduml \ No newline at end of file diff --git a/model-view-presenter/README.md b/model-view-presenter/README.md index a3b921ce4f04..ede8e3baeb20 100644 --- a/model-view-presenter/README.md +++ b/model-view-presenter/README.md @@ -13,6 +13,7 @@ tags: Apply a "Separation of Concerns" principle in a way that allows developers to build and test user interfaces. +## Class diagram ![alt text](./etc/model-view-presenter_1.png "Model-View-Presenter") ## Applicability diff --git a/model-view-presenter/etc/model-view-presenter.urm.puml b/model-view-presenter/etc/model-view-presenter.urm.puml new file mode 100644 index 000000000000..bedb232cb5a7 --- /dev/null +++ b/model-view-presenter/etc/model-view-presenter.urm.puml @@ -0,0 +1,90 @@ +@startuml +package com.iluwatar.model.view.presenter { + class App { + + App() + + main(args : String[]) {static} + } + class FileLoader { + - LOGGER : Logger {static} + - fileName : String + - loaded : boolean + - serialVersionUID : long {static} + + FileLoader() + + fileExists() : boolean + + getFileName() : String + + isLoaded() : boolean + + loadData() : String + + setFileName(fileName : String) + } + class FileSelectorJFrame { + - area : JTextArea + - cancel : JButton + - contents : JLabel + - fileName : String + - info : JLabel + - input : JTextField + - ok : JButton + - panel : JPanel + - presenter : FileSelectorPresenter + - serialVersionUID : long {static} + + FileSelectorJFrame() + + actionPerformed(e : ActionEvent) + + close() + + displayData(data : String) + + getFileName() : String + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + class FileSelectorPresenter { + - loader : FileLoader + - serialVersionUID : long {static} + - view : FileSelectorView + + FileSelectorPresenter(view : FileSelectorView) + + cancelled() + + confirmed() + + fileNameChanged() + + setLoader(loader : FileLoader) + + start() + } + class FileSelectorStub { + - dataDisplayed : boolean + - name : String + - numOfMessageSent : int + - opened : boolean + - presenter : FileSelectorPresenter + + FileSelectorStub() + + close() + + dataDisplayed() : boolean + + displayData(data : String) + + getFileName() : String + + getMessagesSent() : int + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + interface FileSelectorView { + + close() {abstract} + + displayData(String) {abstract} + + getFileName() : String {abstract} + + getPresenter() : FileSelectorPresenter {abstract} + + isOpened() : boolean {abstract} + + open() {abstract} + + setFileName(String) {abstract} + + setPresenter(FileSelectorPresenter) {abstract} + + showMessage(String) {abstract} + } +} +FileSelectorJFrame --> "-presenter" FileSelectorPresenter +FileSelectorStub --> "-presenter" FileSelectorPresenter +FileSelectorPresenter --> "-view" FileSelectorView +FileSelectorPresenter --> "-loader" FileLoader +FileSelectorJFrame ..|> FileSelectorView +FileSelectorStub ..|> FileSelectorView +@enduml \ No newline at end of file diff --git a/module/README.md b/module/README.md index 39f4dbffd7e1..316458e0b5b0 100644 --- a/module/README.md +++ b/module/README.md @@ -12,6 +12,7 @@ tags: ## Intent Module pattern is used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept. +## Class diagram ![alt text](./etc/module.png "Module") ## Applicability diff --git a/module/etc/module.urm.puml b/module/etc/module.urm.puml new file mode 100644 index 000000000000..b92446ca14e7 --- /dev/null +++ b/module/etc/module.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.module { + class App { + + consoleLoggerModule : ConsoleLoggerModule {static} + + fileLoggerModule : FileLoggerModule {static} + + App() + + execute(args : String[]) {static} + + main(args : String[]) {static} + + prepare() {static} + + unprepare() {static} + } + class ConsoleLoggerModule { + - LOGGER : Logger {static} + + error : PrintStream + + output : PrintStream + - singleton : ConsoleLoggerModule {static} + - ConsoleLoggerModule() + + getSingleton() : ConsoleLoggerModule {static} + + prepare() : ConsoleLoggerModule + + printErrorString(value : String) + + printString(value : String) + + unprepare() + } + class FileLoggerModule { + - ERROR_FILE : String {static} + - LOGGER : Logger {static} + - OUTPUT_FILE : String {static} + + error : PrintStream + + output : PrintStream + - singleton : FileLoggerModule {static} + - FileLoggerModule() + + getSingleton() : FileLoggerModule {static} + + prepare() : FileLoggerModule + + printErrorString(value : String) + + printString(value : String) + + unprepare() + } +} +FileLoggerModule --> "-singleton" FileLoggerModule +App --> "-consoleLoggerModule" ConsoleLoggerModule +ConsoleLoggerModule --> "-singleton" ConsoleLoggerModule +App --> "-fileLoggerModule" FileLoggerModule +@enduml \ No newline at end of file diff --git a/monad/README.md b/monad/README.md index 41edd3d92b4f..cf751929c200 100644 --- a/monad/README.md +++ b/monad/README.md @@ -19,6 +19,7 @@ operations: bind - that takes monadic object and a function from plain object to monadic value and returns monadic value return - that takes plain type object and returns this object wrapped in a monadic value. +## Class diagram ![alt text](./etc/monad.png "Monad") ## Applicability @@ -32,4 +33,4 @@ Use the Monad in any of the following situations * [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) * [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) -* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming)) \ No newline at end of file +* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming)) diff --git a/monad/etc/monad.urm.puml b/monad/etc/monad.urm.puml new file mode 100644 index 000000000000..709ef269f66a --- /dev/null +++ b/monad/etc/monad.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.monad { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + enum Sex { + + FEMALE {static} + + MALE {static} + + valueOf(name : String) : Sex {static} + + values() : Sex[] {static} + } + class User { + - age : int + - email : String + - name : String + - sex : Sex + + User(name : String, age : int, sex : Sex, email : String) + + getAge() : int + + getEmail() : String + + getName() : String + + getSex() : Sex + } + class Validator { + - exceptions : List + - obj : T + - Validator(obj : T) + + get() : T + + of(t : T) : Validator {static} + + validate(projection : Function, validation : Predicate, message : String) : Validator + + validate(validation : Predicate, message : String) : Validator + } +} +User --> "-sex" Sex +@enduml \ No newline at end of file diff --git a/monostate/README.md b/monostate/README.md index 3576dc659313..93cf6a45bfa7 100644 --- a/monostate/README.md +++ b/monostate/README.md @@ -15,6 +15,7 @@ Borg ## Intent Enforces a behaviour like sharing the same state amongst all instances. +## Class diagram ![alt text](./etc/monostate.png "MonoState") ## Applicability diff --git a/monostate/etc/monostate.urm.puml b/monostate/etc/monostate.urm.puml new file mode 100644 index 000000000000..c078fb7e712c --- /dev/null +++ b/monostate/etc/monostate.urm.puml @@ -0,0 +1,32 @@ +@startuml +package com.iluwatar.monostate { + class App { + + App() + + main(args : String[]) {static} + } + class LoadBalancer { + - SERVERS : List {static} + - lastServedId : int {static} + + LoadBalancer() + + addServer(server : Server) + + getLastServedId() : int + + getNoOfServers() : int + + serverRequest(request : Request) + } + class Request { + + value : String + + Request(value : String) + } + class Server { + - LOGGER : Logger {static} + + host : String + + id : int + + port : int + + Server(host : String, port : int, id : int) + + getHost() : String + + getPort() : int + + serve(request : Request) + } +} +LoadBalancer --> "-SERVERS" Server +@enduml \ No newline at end of file diff --git a/multiton/README.md b/multiton/README.md index 0462ff0ecc48..105dd9d91944 100644 --- a/multiton/README.md +++ b/multiton/README.md @@ -16,6 +16,7 @@ Registry Ensure a class only has limited number of instances, and provide a global point of access to them. +## Class diagram ![alt text](./etc/multiton.png "Multiton") ## Applicability diff --git a/multiton/etc/multiton.urm.puml b/multiton/etc/multiton.urm.puml new file mode 100644 index 000000000000..5380d36c3146 --- /dev/null +++ b/multiton/etc/multiton.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.multiton { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Nazgul { + - name : NazgulName + - nazguls : Map {static} + - Nazgul(name : NazgulName) + + getInstance(name : NazgulName) : Nazgul {static} + + getName() : NazgulName + } + enum NazgulEnum { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulEnum {static} + + values() : NazgulEnum[] {static} + } + enum NazgulName { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulName {static} + + values() : NazgulName[] {static} + } +} +Nazgul --> "-name" NazgulName +@enduml \ No newline at end of file diff --git a/mute-idiom/README.md b/mute-idiom/README.md index bb674b648b48..7df0ae1106b1 100644 --- a/mute-idiom/README.md +++ b/mute-idiom/README.md @@ -14,7 +14,7 @@ tags: Provide a template to suppress any exceptions that either are declared but cannot occur or should only be logged; while executing some business logic. The template removes the need to write repeated `try-catch` blocks. - +## Class diagram ![alt text](./etc/mute-idiom.png "Mute Idiom") ## Applicability diff --git a/mute-idiom/etc/mute-idiom.urm.puml b/mute-idiom/etc/mute-idiom.urm.puml new file mode 100644 index 000000000000..32d036bc8144 --- /dev/null +++ b/mute-idiom/etc/mute-idiom.urm.puml @@ -0,0 +1,24 @@ +@startuml +package com.iluwatar.mute { + class App { + - LOGGER : Logger {static} + + App() + - acquireResource() : Resource {static} + - closeResource(resource : Resource) {static} + + main(args : String[]) {static} + - useOfLoggedMute() {static} + - useOfMute() {static} + - utilizeResource(resource : Resource) {static} + } + interface CheckedRunnable { + + run() {abstract} + } + class Mute { + - Mute() + + loggedMute(runnable : CheckedRunnable) {static} + + mute(runnable : CheckedRunnable) {static} + } + interface Resource { + } +} +@enduml \ No newline at end of file diff --git a/mutex/README.md b/mutex/README.md index 84755872f56f..14b465a60622 100644 --- a/mutex/README.md +++ b/mutex/README.md @@ -16,6 +16,7 @@ Binary Semaphore ## Intent Create a lock which only allows a single thread to access a resource at any one instant. +## Class diagram ![alt text](./etc/mutex.png "Mutex") ## Applicability diff --git a/mutex/etc/mutex.urm.puml b/mutex/etc/mutex.urm.puml new file mode 100644 index 000000000000..08cf43b90795 --- /dev/null +++ b/mutex/etc/mutex.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.mutex { + class App { + + App() + + main(args : String[]) {static} + } + class Jar { + - beans : int + - lock : Lock + + Jar(beans : int, lock : Lock) + + takeBean() : boolean + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class Mutex { + - owner : Object + + Mutex() + + acquire() + + getOwner() : Object + + release() + } +} +Jar --> "-lock" Lock +Mutex ..|> Lock +@enduml \ No newline at end of file diff --git a/naked-objects/README.md b/naked-objects/README.md index eb1c083b14e2..3d3cdaab981d 100644 --- a/naked-objects/README.md +++ b/naked-objects/README.md @@ -14,6 +14,7 @@ The Naked Objects architectural pattern is well suited for rapid prototyping. Using the pattern, you only need to write the domain objects, everything else is autogenerated by the framework. +## Class diagram ![alt text](./etc/naked-objects.png "Naked Objects") ## Applicability diff --git a/naked-objects/dom/etc/naked-objects-dom.urm.puml b/naked-objects/dom/etc/naked-objects-dom.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/naked-objects/dom/etc/naked-objects-dom.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects.urm.puml b/naked-objects/etc/naked-objects.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/naked-objects/etc/naked-objects.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/naked-objects/fixture/etc/naked-objects-fixture.urm.puml b/naked-objects/fixture/etc/naked-objects-fixture.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/naked-objects/fixture/etc/naked-objects-fixture.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/naked-objects/integtests/etc/naked-objects-integtests.urm.puml b/naked-objects/integtests/etc/naked-objects-integtests.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/naked-objects/integtests/etc/naked-objects-integtests.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/naked-objects/webapp/etc/naked-objects-webapp.urm.puml b/naked-objects/webapp/etc/naked-objects-webapp.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/naked-objects/webapp/etc/naked-objects-webapp.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/null-object/README.md b/null-object/README.md index 0ed28a0af33c..1be72a1b747b 100644 --- a/null-object/README.md +++ b/null-object/README.md @@ -19,6 +19,7 @@ implements the expected interface, but whose method body is empty. The advantage of this approach over a working default implementation is that a Null Object is very predictable and has no side effects: it does nothing. +## Class diagram ![alt text](./etc/null-object.png "Null Object") ## Applicability diff --git a/null-object/etc/null-object.urm.puml b/null-object/etc/null-object.urm.puml new file mode 100644 index 000000000000..a937a2b178e2 --- /dev/null +++ b/null-object/etc/null-object.urm.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.nullobject { + class App { + + App() + + main(args : String[]) {static} + } + interface Node { + + getLeft() : Node {abstract} + + getName() : String {abstract} + + getRight() : Node {abstract} + + getTreeSize() : int {abstract} + + walk() {abstract} + } + class NodeImpl { + - LOGGER : Logger {static} + - left : Node + - name : String + - right : Node + + NodeImpl(name : String, left : Node, right : Node) + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } + class NullNode { + - instance : NullNode {static} + - NullNode() + + getInstance() : NullNode {static} + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } +} +NullNode --> "-instance" NullNode +NodeImpl --> "-left" Node +NodeImpl ..|> Node +NullNode ..|> Node +@enduml \ No newline at end of file diff --git a/object-mother/README.md b/object-mother/README.md index 125ecdfe599e..bd4c14e1ce40 100644 --- a/object-mother/README.md +++ b/object-mother/README.md @@ -12,6 +12,7 @@ tags: ## Object Mother Define a factory of immutable content with separated builder and factory interfaces. +## Class diagram ![alt text](./etc/object-mother.png "Object Mother") ## Applicability diff --git a/object-mother/etc/object-mother.urm.puml b/object-mother/etc/object-mother.urm.puml new file mode 100644 index 000000000000..1bb52f2ed6c6 --- /dev/null +++ b/object-mother/etc/object-mother.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.objectmother { + class King { + ~ isDrunk : boolean + ~ isHappy : boolean + + King() + + flirt(queen : Queen) + + isHappy() : boolean + + makeDrunk() + + makeHappy() + + makeSober() + + makeUnhappy() + } + class Queen { + - isDrunk : boolean + - isFlirty : boolean + - isHappy : boolean + + Queen() + + getFlirted(king : King) : boolean + + isFlirty() : boolean + + makeDrunk() + + makeHappy() + + makeSober() + + makeUnhappy() + + setFlirtiness(flirtiness : boolean) + } + interface Royalty { + + makeDrunk() {abstract} + + makeHappy() {abstract} + + makeSober() {abstract} + + makeUnhappy() {abstract} + } + class RoyaltyObjectMother { + + RoyaltyObjectMother() + + createDrunkKing() : King {static} + + createFlirtyQueen() : Queen {static} + + createHappyDrunkKing() : King {static} + + createHappyKing() : King {static} + + createNotFlirtyQueen() : Queen {static} + + createSoberUnhappyKing() : King {static} + } +} +King ..|> Royalty +Queen ..|> Royalty +@enduml \ No newline at end of file diff --git a/object-pool/README.md b/object-pool/README.md index cf36d98809e7..f6b80539b3b4 100644 --- a/object-pool/README.md +++ b/object-pool/README.md @@ -16,6 +16,7 @@ short periods of time it is advantageous to utilize the Object Pool pattern. The Object Pool provides a cache for instantiated objects tracking which ones are in use and which are available. +## Class diagram ![alt text](./etc/object-pool.png "Object Pool") ## Applicability diff --git a/object-pool/etc/object-pool.urm.puml b/object-pool/etc/object-pool.urm.puml new file mode 100644 index 000000000000..7f75ff248267 --- /dev/null +++ b/object-pool/etc/object-pool.urm.puml @@ -0,0 +1,30 @@ +@startuml +package com.iluwatar.object.pool { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + abstract class ObjectPool { + - available : Set + - inUse : Set + + ObjectPool() + + checkIn(instance : T) + + checkOut() : T + # create() : T {abstract} + + toString() : String + } + class Oliphaunt { + - counter : AtomicInteger {static} + - id : int + + Oliphaunt() + + getId() : int + + toString() : String + } + class OliphauntPool { + + OliphauntPool() + # create() : Oliphaunt + } +} +OliphauntPool --|> ObjectPool +@enduml \ No newline at end of file diff --git a/observer/README.md b/observer/README.md index afeb0b37d68d..12a0a4677e84 100644 --- a/observer/README.md +++ b/observer/README.md @@ -19,6 +19,7 @@ Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. +## Class diagram ![alt text](./etc/observer.png "Observer") ## Applicability diff --git a/observer/etc/observer.urm.puml b/observer/etc/observer.urm.puml new file mode 100644 index 000000000000..bea9aab533d3 --- /dev/null +++ b/observer/etc/observer.urm.puml @@ -0,0 +1,80 @@ +@startuml +package com.iluwatar.observer { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Hobbits { + - LOGGER : Logger {static} + + Hobbits() + + update(currentWeather : WeatherType) + } + class Orcs { + - LOGGER : Logger {static} + + Orcs() + + update(currentWeather : WeatherType) + } + class Weather { + - LOGGER : Logger {static} + - currentWeather : WeatherType + - observers : List + + Weather() + + addObserver(obs : WeatherObserver) + - notifyObservers() + + removeObserver(obs : WeatherObserver) + + timePasses() + } + interface WeatherObserver { + + update(WeatherType) {abstract} + } + enum WeatherType { + + COLD {static} + + RAINY {static} + + SUNNY {static} + + WINDY {static} + + toString() : String + + valueOf(name : String) : WeatherType {static} + + values() : WeatherType[] {static} + } +} +package com.iluwatar.observer.generic { + class GHobbits { + - LOGGER : Logger {static} + + GHobbits() + + update(weather : GWeather, weatherType : WeatherType) + } + class GOrcs { + - LOGGER : Logger {static} + + GOrcs() + + update(weather : GWeather, weatherType : WeatherType) + } + class GWeather { + - LOGGER : Logger {static} + - currentWeather : WeatherType + + GWeather() + + timePasses() + } + abstract class Observable, A> { + # observers : List> + + Observable, A>() + + addObserver(observer : O extends Observer) + + notifyObservers(argument : A) + + removeObserver(observer : O extends Observer) + } + interface Observer, O extends Observer, A> { + + update(S extends Observable, A) {abstract} + } + interface Race { + } +} +Weather --> "-currentWeather" WeatherType +GWeather --> "-currentWeather" WeatherType +Weather --> "-observers" WeatherObserver +Hobbits ..|> WeatherObserver +Orcs ..|> WeatherObserver +GHobbits ..|> Race +GOrcs ..|> Race +GWeather --|> Observable +Race --|> Observer +@enduml \ No newline at end of file diff --git a/page-object/README.md b/page-object/README.md index 16c021dfbb7f..13586e0644ba 100644 --- a/page-object/README.md +++ b/page-object/README.md @@ -13,7 +13,7 @@ tags: Page Object encapsulates the UI, hiding the underlying UI widgetry of an application (commonly a web application) and providing an application-specific API to allow the manipulation of UI components required for tests. In doing so, it allows the test class itself to focus on the test logic instead. - +## Class diagram ![alt text](./etc/page-object.png "Page Object") diff --git a/page-object/etc/page-object.urm.puml b/page-object/etc/page-object.urm.puml new file mode 100644 index 000000000000..02af47ddf261 --- /dev/null +++ b/page-object/etc/page-object.urm.puml @@ -0,0 +1,2 @@ +@startuml +@enduml \ No newline at end of file diff --git a/page-object/sample-application/etc/sample-application.urm.puml b/page-object/sample-application/etc/sample-application.urm.puml new file mode 100644 index 000000000000..306208a83610 --- /dev/null +++ b/page-object/sample-application/etc/sample-application.urm.puml @@ -0,0 +1,9 @@ +@startuml +package com.iluwatar.pageobject { + class App { + - LOGGER : Logger {static} + - App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/page-object/test-automation/etc/test-automation.urm.puml b/page-object/test-automation/etc/test-automation.urm.puml new file mode 100644 index 000000000000..7cfb56723a27 --- /dev/null +++ b/page-object/test-automation/etc/test-automation.urm.puml @@ -0,0 +1,51 @@ +@startuml +package com.iluwatar.pageobject { + class AlbumListPage { + - ALBUM_LIST_HTML_FILE : String {static} + - LOGGER : Logger {static} + - PAGE_URL : String {static} + - page : HtmlPage + + AlbumListPage(webClient : WebClient) + + isAt() : boolean + + navigateToPage() : AlbumListPage + + selectAlbum(albumTitle : String) : AlbumPage + } + class AlbumPage { + - ALBUM_PAGE_HTML_FILE : String {static} + - LOGGER : Logger {static} + - PAGE_URL : String {static} + - page : HtmlPage + + AlbumPage(webClient : WebClient) + + cancelChanges() : AlbumListPage + + changeAlbumRating(albumRating : String) : AlbumPage + + changeAlbumTitle(albumTitle : String) : AlbumPage + + changeAlbumYear(year : int) : AlbumPage + + changeArtist(artist : String) : AlbumPage + + changeNumberOfSongs(numberOfSongs : int) : AlbumPage + + isAt() : boolean + + navigateToPage() : AlbumPage + + saveChanges() : AlbumPage + } + class LoginPage { + - LOGGER : Logger {static} + - LOGIN_PAGE_HTML_FILE : String {static} + - PAGE_URL : String {static} + - page : HtmlPage + + LoginPage(webClient : WebClient) + + enterPassword(password : String) : LoginPage + + enterUsername(username : String) : LoginPage + + isAt() : boolean + + login() : AlbumListPage + + navigateToPage() : LoginPage + } + abstract class Page { + + AUT_PATH : String {static} + # webClient : WebClient + + Page(webClient : WebClient) + + isAt() : boolean {abstract} + } +} +AlbumListPage --|> Page +AlbumPage --|> Page +LoginPage --|> Page +@enduml \ No newline at end of file diff --git a/partial-response/README.md b/partial-response/README.md index f81dffaa1df3..ebec3d97a54d 100644 --- a/partial-response/README.md +++ b/partial-response/README.md @@ -15,6 +15,7 @@ tags: Send partial response from server to client on need basis. Client will specify the the fields that it need to server, instead of serving all details for resource. +## Class diagram ![alt text](./etc/partial-response.urm.png "partial-response") ## Applicability diff --git a/partial-response/etc/partial-response.urm.puml b/partial-response/etc/partial-response.urm.puml new file mode 100644 index 000000000000..4b041a8a0066 --- /dev/null +++ b/partial-response/etc/partial-response.urm.puml @@ -0,0 +1,31 @@ +@startuml +package com.iluwatar.partialresponse { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class FieldJsonMapper { + + FieldJsonMapper() + - getString(video : Video, declaredField : Field) : String + + toJson(video : Video, fields : String[]) : String + } + class Video { + - description : String + - director : String + - id : Integer + - language : String + - length : Integer + - title : String + + Video(id : Integer, title : String, len : Integer, desc : String, director : String, lang : String) + + toString() : String + } + class VideoResource { + - fieldJsonMapper : FieldJsonMapper + - videos : Map + + VideoResource(fieldJsonMapper : FieldJsonMapper, videos : Map) + + getDetails(id : Integer, fields : String[]) : String + } +} +VideoResource --> "-fieldJsonMapper" FieldJsonMapper +@enduml \ No newline at end of file diff --git a/pipeline/README.md b/pipeline/README.md index e990affd62c7..2afa6da9aa10 100644 --- a/pipeline/README.md +++ b/pipeline/README.md @@ -13,6 +13,9 @@ tags: ## Intent Allows processing of data in a series of stages by giving in an initial input and passing the processed output to be used by the next stages. +## Class diagram +![alt text](./etc/pipeline.urm.png "Pipeline pattern class diagram") + ## Applicability Use the Pipeline pattern when you want to @@ -34,4 +37,4 @@ Use the Pipeline pattern when you want to * [The Pipeline Pattern — for fun and profit](https://medium.com/@aaronweatherall/the-pipeline-pattern-for-fun-and-profit-9b5f43a98130) * [The Pipeline design pattern (in Java)](https://medium.com/@deepakbapat/the-pipeline-design-pattern-in-java-831d9ce2fe21) -* [Pipelines | Microsoft Docs](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff963548(v=pandp.10)) \ No newline at end of file +* [Pipelines | Microsoft Docs](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff963548(v=pandp.10)) diff --git a/pipeline/etc/pipeline.urm.png b/pipeline/etc/pipeline.urm.png new file mode 100644 index 000000000000..4110f1624665 Binary files /dev/null and b/pipeline/etc/pipeline.urm.png differ diff --git a/pipeline/etc/pipeline.urm.puml b/pipeline/etc/pipeline.urm.puml new file mode 100644 index 000000000000..50bee48c567e --- /dev/null +++ b/pipeline/etc/pipeline.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.pipeline { + class App { + + App() + + main(args : String[]) {static} + } + ~class ConvertToCharArrayHandler { + - LOGGER : Logger {static} + ~ ConvertToCharArrayHandler() + + process(input : String) : char[] + } + ~interface Handler { + + process(I) : O {abstract} + } + ~class Pipeline { + - currentHandler : Handler + ~ Pipeline(currentHandler : Handler) + ~ addHandler(newHandler : Handler) : Pipeline + ~ execute(input : I) : O + } + ~class RemoveAlphabetsHandler { + - LOGGER : Logger {static} + ~ RemoveAlphabetsHandler() + + process(input : String) : String + } + ~class RemoveDigitsHandler { + - LOGGER : Logger {static} + ~ RemoveDigitsHandler() + + process(input : String) : String + } +} +Pipeline --> "-currentHandler" Handler +ConvertToCharArrayHandler ..|> Handler +RemoveAlphabetsHandler ..|> Handler +RemoveDigitsHandler ..|> Handler +@enduml \ No newline at end of file diff --git a/poison-pill/README.md b/poison-pill/README.md index 0815b376edf8..a4f8420864aa 100644 --- a/poison-pill/README.md +++ b/poison-pill/README.md @@ -14,6 +14,7 @@ tags: Poison Pill is known predefined data item that allows to provide graceful shutdown for separate distributed consumption process. +## Class diagram ![alt text](./etc/poison-pill.png "Poison Pill") ## Applicability diff --git a/poison-pill/etc/poison-pill.urm.puml b/poison-pill/etc/poison-pill.urm.puml new file mode 100644 index 000000000000..923449e10b4a --- /dev/null +++ b/poison-pill/etc/poison-pill.urm.puml @@ -0,0 +1,71 @@ +@startuml +package com.iluwatar.poison.pill { + class App { + + App() + + main(args : String[]) {static} + } + class Consumer { + - LOGGER : Logger {static} + - name : String + - queue : MqSubscribePoint + + Consumer(name : String, queue : MqSubscribePoint) + + consume() + } + interface Message { + + POISON_PILL : Message {static} + + addHeader(Headers, String) {abstract} + + getBody() : String {abstract} + + getHeader(Headers) : String {abstract} + + getHeaders() : Map {abstract} + + setBody(String) {abstract} + } + enum Headers { + + DATE {static} + + SENDER {static} + + valueOf(name : String) : Headers {static} + + values() : Headers[] {static} + } + interface MessageQueue { + } + interface MqPublishPoint { + + put(Message) {abstract} + } + interface MqSubscribePoint { + + take() : Message {abstract} + } + class Producer { + - LOGGER : Logger {static} + - isStopped : boolean + - name : String + - queue : MqPublishPoint + + Producer(name : String, queue : MqPublishPoint) + + send(body : String) + + stop() + } + class SimpleMessage { + - body : String + - headers : Map + + SimpleMessage() + + addHeader(header : Headers, value : String) + + getBody() : String + + getHeader(header : Headers) : String + + getHeaders() : Map + + setBody(body : String) + } + class SimpleMessageQueue { + - queue : BlockingQueue + + SimpleMessageQueue(bound : int) + + put(msg : Message) + + take() : Message + } +} +SimpleMessageQueue --> "-queue" Message +Headers ..+ Message +Consumer --> "-queue" MqSubscribePoint +Producer --> "-queue" MqPublishPoint +Message --> "-POISON_PILL" Message +MessageQueue --|> MqPublishPoint +MessageQueue --|> MqSubscribePoint +SimpleMessage ..|> Message +SimpleMessageQueue ..|> MessageQueue +@enduml \ No newline at end of file diff --git a/pom.xml b/pom.xml index 63cef06f5a35..7c2789da5346 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,7 @@ 2.3.2 1.3.2 1.19.0 + 1.4.8 abstract-factory @@ -498,6 +499,31 @@ + + + com.iluwatar.urm + urm-maven-plugin + 1.4.8 + + + ${project.basedir}/etc + + com.iluwatar + + true + false + plantuml + + + + process-classes + + map + + + + + diff --git a/priority-queue/README.md b/priority-queue/README.md index bc8d7b8cfed0..37a6e908d901 100644 --- a/priority-queue/README.md +++ b/priority-queue/README.md @@ -15,6 +15,9 @@ Prioritize requests sent to services so that requests with a higher priority are ## Explanation Applications may delegate specific tasks to other services; for example, to perform background processing or to integrate with other applications or services. In the cloud, a message queue is typically used to delegate tasks to background processing. In many cases the order in which requests are received by a service is not important. However, in some cases it may be necessary to prioritize specific requests. These requests should be processed earlier than others of a lower priority that may have been sent previously by the application. +## Class diagram +![alt text](./etc/priority-queue.urm.png "Priority Queue pattern class diagram") + ## Applicability Use the Property pattern when diff --git a/priority-queue/etc/priority-queue.urm.png b/priority-queue/etc/priority-queue.urm.png new file mode 100644 index 000000000000..e0b4295a5986 Binary files /dev/null and b/priority-queue/etc/priority-queue.urm.png differ diff --git a/priority-queue/etc/priority-queue.urm.puml b/priority-queue/etc/priority-queue.urm.puml new file mode 100644 index 000000000000..cee118f57671 --- /dev/null +++ b/priority-queue/etc/priority-queue.urm.puml @@ -0,0 +1,54 @@ +@startuml +package com.iluwatar.priority.queue { + class Application { + + Application() + + main(args : String[]) {static} + } + class Message { + - message : String + - priority : int + + Message(message : String, priority : int) + + compareTo(o : Message) : int + + toString() : String + } + class PriorityMessageQueue { + - LOGGER : Logger {static} + - capacity : int + - queue : T[] + - size : int + + PriorityMessageQueue(queue : T[]) + + add(t : T extends Comparable) + - ensureCapacity() + - hasLeftChild(index : int) : boolean + - hasParent(index : int) : boolean + - hasRightChild(index : int) : boolean + + isEmpty() : boolean + - left(parentIndex : int) : T extends Comparable + - leftChildIndex(parentPos : int) : int + - maxHeapifyDown() + - maxHeapifyUp() + - parent(childIndex : int) : T extends Comparable + - parentIndex(pos : int) : int + + print() + + remove() : T extends Comparable + - right(parentIndex : int) : T extends Comparable + - rightChildIndex(parentPos : int) : int + - swap(fpos : int, tpos : int) + } + class QueueManager { + - messagePriorityMessageQueue : PriorityMessageQueue + + QueueManager(initialCapacity : int) + + publishMessage(message : Message) + + receiveMessage() : Message + } + class Worker { + - LOGGER : Logger {static} + - queueManager : QueueManager + + Worker(queueManager : QueueManager) + - processMessage(message : Message) + + run() + } +} +QueueManager --> "-messagePriorityMessageQueue" PriorityMessageQueue +Worker --> "-queueManager" QueueManager +@enduml \ No newline at end of file diff --git a/private-class-data/README.md b/private-class-data/README.md index 981208fa3f0e..7937697c22cc 100644 --- a/private-class-data/README.md +++ b/private-class-data/README.md @@ -15,6 +15,7 @@ Private Class Data design pattern seeks to reduce exposure of attributes by limiting their visibility. It reduces the number of class attributes by encapsulating them in single Data object. +## Class diagram ![alt text](./etc/private-class-data.png "Private Class Data") ## Applicability diff --git a/private-class-data/etc/private-class-data.urm.puml b/private-class-data/etc/private-class-data.urm.puml new file mode 100644 index 000000000000..990b53342521 --- /dev/null +++ b/private-class-data/etc/private-class-data.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.privateclassdata { + class App { + + App() + + main(args : String[]) {static} + } + class ImmutableStew { + - LOGGER : Logger {static} + - data : StewData + + ImmutableStew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + } + class Stew { + - LOGGER : Logger {static} + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + + taste() + } + class StewData { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + StewData(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + getNumCarrots() : int + + getNumMeat() : int + + getNumPeppers() : int + + getNumPotatoes() : int + } +} +ImmutableStew --> "-data" StewData +@enduml \ No newline at end of file diff --git a/producer-consumer/README.md b/producer-consumer/README.md index 1bb84c35fbb2..12147af42016 100644 --- a/producer-consumer/README.md +++ b/producer-consumer/README.md @@ -16,6 +16,7 @@ Producer Consumer Design pattern is a classic concurrency pattern which reduces coupling between Producer and Consumer by separating Identification of work with Execution of Work. +## Class diagram ![alt text](./etc/producer-consumer.png "Producer Consumer") ## Applicability diff --git a/producer-consumer/etc/producer-consumer.urm.puml b/producer-consumer/etc/producer-consumer.urm.puml new file mode 100644 index 000000000000..e36374b2fdd2 --- /dev/null +++ b/producer-consumer/etc/producer-consumer.urm.puml @@ -0,0 +1,40 @@ +@startuml +package com.iluwatar.producer.consumer { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Consumer { + - LOGGER : Logger {static} + - name : String + - queue : ItemQueue + + Consumer(name : String, queue : ItemQueue) + + consume() + } + class Item { + - id : int + - producer : String + + Item(producer : String, id : int) + + getId() : int + + getProducer() : String + } + class ItemQueue { + - queue : BlockingQueue + + ItemQueue() + + put(item : Item) + + take() : Item + } + class Producer { + - RANDOM : Random {static} + - itemId : int + - name : String + - queue : ItemQueue + + Producer(name : String, queue : ItemQueue) + + produce() + } +} +Consumer --> "-queue" ItemQueue +Producer --> "-queue" ItemQueue +ItemQueue --> "-queue" Item +@enduml \ No newline at end of file diff --git a/promise/README.md b/promise/README.md index fa9556547f29..3eb5efcb6f12 100644 --- a/promise/README.md +++ b/promise/README.md @@ -20,6 +20,7 @@ allows you to associate dependent promises to an asynchronous action's eventual failure reason. Promises are a way to write async code that still appears as though it is executing in a synchronous way. +## Class diagram ![alt text](./etc/promise.png "Promise") ## Applicability diff --git a/promise/etc/promise.urm.puml b/promise/etc/promise.urm.puml new file mode 100644 index 000000000000..f3a28718e43b --- /dev/null +++ b/promise/etc/promise.urm.puml @@ -0,0 +1,79 @@ +@startuml +package com.iluwatar.promise { + class App { + - DEFAULT_URL : String {static} + - LOGGER : Logger {static} + - executor : ExecutorService + - stopLatch : CountDownLatch + - App() + - calculateLineCount() + - calculateLowestFrequencyChar() + - characterFrequency() : Promise> + - countLines() : Promise + - download(urlString : String) : Promise + - lowestFrequencyChar() : Promise + + main(args : String[]) {static} + - promiseUsage() + - stop() + - taskCompleted() + } + class Promise { + - exceptionHandler : Consumer + - fulfillmentAction : Runnable + + Promise() + + fulfill(value : T) + + fulfillExceptionally(exception : Exception) + + fulfillInAsync(task : Callable, executor : Executor) : Promise + - handleException(exception : Exception) + + onError(exceptionHandler : Consumer) : Promise + - postFulfillment() + + thenAccept(action : Consumer) : Promise + + thenApply(func : Function) : Promise + } + -class ConsumeAction { + - action : Consumer + - dest : Promise + - src : Promise + - ConsumeAction(src : Promise, dest : Promise, action : Consumer) + + run() + } + -class TransformAction { + - dest : Promise + - func : Function + - src : Promise + - TransformAction(src : Promise, dest : Promise, func : Function) + + run() + } + ~class PromiseSupport { + - COMPLETED : int {static} + - FAILED : int {static} + - LOGGER : Logger {static} + - RUNNING : int {static} + - exception : Exception + - lock : Object + - state : int + - value : T + ~ PromiseSupport() + + cancel(mayInterruptIfRunning : boolean) : boolean + ~ fulfill(value : T) + ~ fulfillExceptionally(exception : Exception) + + get() : T + + get(timeout : long, unit : TimeUnit) : T + + isCancelled() : boolean + + isDone() : boolean + } + class Utility { + - LOGGER : Logger {static} + + Utility() + + characterFrequency(fileLocation : String) : Map {static} + + countLines(fileLocation : String) : Integer {static} + + downloadFile(urlString : String) : String {static} + + lowestFrequencyChar(charFrequency : Map) : Character {static} + } +} +TransformAction --+ Promise +TransformAction --> "-src" Promise +ConsumeAction --+ Promise +ConsumeAction --> "-src" Promise +Promise --|> PromiseSupport +@enduml \ No newline at end of file diff --git a/property/README.md b/property/README.md index 0ac5c7a6c682..60d82434edf8 100644 --- a/property/README.md +++ b/property/README.md @@ -13,6 +13,7 @@ tags: Create hierarchy of objects and new objects using already existing objects as parents. +## Class diagram ![alt text](./etc/property.png "Property") ## Applicability diff --git a/property/etc/property.urm.puml b/property/etc/property.urm.puml new file mode 100644 index 000000000000..f281cd87319f --- /dev/null +++ b/property/etc/property.urm.puml @@ -0,0 +1,54 @@ +@startuml +package com.iluwatar.property { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Character { + - name : String + - properties : Map + - prototype : Prototype + - type : Type + + Character() + + Character(name : String, prototype : Character) + + Character(type : Type, prototype : Prototype) + + get(stat : Stats) : Integer + + has(stat : Stats) : boolean + + name() : String + + remove(stat : Stats) + + set(stat : Stats, val : Integer) + + toString() : String + + type() : Type + } + enum Type { + + MAGE {static} + + ROGUE {static} + + WARRIOR {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} + } + interface Prototype { + + get(Stats) : Integer {abstract} + + has(Stats) : boolean {abstract} + + remove(Stats) {abstract} + + set(Stats, Integer) {abstract} + } + enum Stats { + + AGILITY {static} + + ARMOR {static} + + ATTACK_POWER {static} + + ENERGY {static} + + INTELLECT {static} + + RAGE {static} + + SPIRIT {static} + + STRENGTH {static} + + valueOf(name : String) : Stats {static} + + values() : Stats[] {static} + } +} +Character --> "-prototype" Prototype +Type ..+ Character +Character --> "-type" Type +Character ..|> Prototype +@enduml \ No newline at end of file diff --git a/prototype/README.md b/prototype/README.md index 4cb8c0365c83..fe796183cb89 100644 --- a/prototype/README.md +++ b/prototype/README.md @@ -62,6 +62,9 @@ cloned.setName("Dolly"); System.out.println(cloned.getName()); // Dolly ``` +## Class diagram +![alt text](./etc/prototype.urm.png "Prototype pattern class diagram") + ## Applicability Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and diff --git a/prototype/etc/prototype.urm.png b/prototype/etc/prototype.urm.png new file mode 100644 index 000000000000..b23c6d1cfb85 Binary files /dev/null and b/prototype/etc/prototype.urm.png differ diff --git a/prototype/etc/prototype.urm.puml b/prototype/etc/prototype.urm.puml new file mode 100644 index 000000000000..bd1576f3bfe2 --- /dev/null +++ b/prototype/etc/prototype.urm.puml @@ -0,0 +1,105 @@ +@startuml +package com.iluwatar.prototype { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + abstract class Beast { + + Beast() + + Beast(source : Beast) + + copy() : Beast {abstract} + + equals(obj : Object) : boolean + } + class ElfBeast { + - helpType : String + + ElfBeast(elfBeast : ElfBeast) + + ElfBeast(helpType : String) + + copy() : ElfBeast + + equals(obj : Object) : boolean + + toString() : String + } + class ElfMage { + - helpType : String + + ElfMage(elfMage : ElfMage) + + ElfMage(helpType : String) + + copy() : ElfMage + + equals(obj : Object) : boolean + + toString() : String + } + class ElfWarlord { + - helpType : String + + ElfWarlord(elfWarlord : ElfWarlord) + + ElfWarlord(helpType : String) + + copy() : ElfWarlord + + equals(obj : Object) : boolean + + toString() : String + } + interface HeroFactory { + + createBeast() : Beast {abstract} + + createMage() : Mage {abstract} + + createWarlord() : Warlord {abstract} + } + class HeroFactoryImpl { + - beast : Beast + - mage : Mage + - warlord : Warlord + + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) + + createBeast() : Beast + + createMage() : Mage + + createWarlord() : Warlord + } + abstract class Mage { + + Mage() + + Mage(source : Mage) + + copy() : Mage {abstract} + + equals(obj : Object) : boolean + } + class OrcBeast { + - weapon : String + + OrcBeast(orcBeast : OrcBeast) + + OrcBeast(weapon : String) + + copy() : OrcBeast + + equals(obj : Object) : boolean + + toString() : String + } + class OrcMage { + - weapon : String + + OrcMage(orcMage : OrcMage) + + OrcMage(weapon : String) + + copy() : OrcMage + + equals(obj : Object) : boolean + + toString() : String + } + class OrcWarlord { + - weapon : String + + OrcWarlord(orcWarlord : OrcWarlord) + + OrcWarlord(weapon : String) + + copy() : OrcWarlord + + equals(obj : Object) : boolean + + toString() : String + } + interface Prototype { + + copy() : Object {abstract} + } + abstract class Warlord { + + Warlord() + + Warlord(source : Warlord) + + copy() : Warlord {abstract} + + equals(obj : Object) : boolean + } +} +HeroFactoryImpl --> "-beast" Beast +HeroFactoryImpl --> "-warlord" Warlord +HeroFactoryImpl --> "-mage" Mage +Beast ..|> Prototype +ElfBeast --|> Beast +ElfMage --|> Mage +ElfWarlord --|> Warlord +HeroFactoryImpl ..|> HeroFactory +Mage ..|> Prototype +OrcBeast --|> Beast +OrcMage --|> Mage +OrcWarlord --|> Warlord +Warlord ..|> Prototype +@enduml \ No newline at end of file diff --git a/proxy/README.md b/proxy/README.md index 71eba2280bf4..e94ebfbc67c0 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -109,6 +109,9 @@ proxy.enter(new Wizard("Green wizard")); // Green wizard is not allowed to enter proxy.enter(new Wizard("Brown wizard")); // Brown wizard is not allowed to enter! ``` +## Class diagram +![alt text](./etc/proxy.urm.png "Proxy pattern class diagram") + ## Applicability Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here diff --git a/proxy/etc/proxy.urm.png b/proxy/etc/proxy.urm.png new file mode 100644 index 000000000000..a0c94fc7c717 Binary files /dev/null and b/proxy/etc/proxy.urm.png differ diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml new file mode 100644 index 000000000000..ffe0fa446074 --- /dev/null +++ b/proxy/etc/proxy.urm.puml @@ -0,0 +1,32 @@ +@startuml +package com.iluwatar.proxy { + class App { + + App() + + main(args : String[]) {static} + } + class IvoryTower { + - LOGGER : Logger {static} + + IvoryTower() + + enter(wizard : Wizard) + } + class Wizard { + - name : String + + Wizard(name : String) + + toString() : String + } + interface WizardTower { + + enter(Wizard) {abstract} + } + class WizardTowerProxy { + - LOGGER : Logger {static} + - NUM_WIZARDS_ALLOWED : int {static} + - numWizards : int + - tower : WizardTower + + WizardTowerProxy(tower : WizardTower) + + enter(wizard : Wizard) + } +} +WizardTowerProxy --> "-tower" WizardTower +IvoryTower ..|> WizardTower +WizardTowerProxy ..|> WizardTower +@enduml \ No newline at end of file diff --git a/queue-load-leveling/README.md b/queue-load-leveling/README.md index 13821a54e68a..f965448b2ffd 100644 --- a/queue-load-leveling/README.md +++ b/queue-load-leveling/README.md @@ -16,9 +16,9 @@ intermittent heavy loads that may otherwise cause the service to fail or the tas This pattern can help to minimize the impact of peaks in demand on availability and responsiveness for both the task and the service. +## Class diagram ![alt text](./etc/queue-load-leveling.gif "queue-load-leveling") - ## Applicability * This pattern is ideally suited to any type of application that uses services that may be subject to overloading. diff --git a/queue-load-leveling/etc/queue-load-leveling.urm.puml b/queue-load-leveling/etc/queue-load-leveling.urm.puml new file mode 100644 index 000000000000..ca90842d92dd --- /dev/null +++ b/queue-load-leveling/etc/queue-load-leveling.urm.puml @@ -0,0 +1,44 @@ +@startuml +package com.iluwatar.queue.load.leveling { + class App { + - LOGGER : Logger {static} + - SHUTDOWN_TIME : int {static} + + App() + + main(args : String[]) {static} + } + class Message { + - msg : String + + Message(msg : String) + + getMsg() : String + + toString() : String + } + class MessageQueue { + - LOGGER : Logger {static} + - blkQueue : BlockingQueue + + MessageQueue() + + retrieveMsg() : Message + + submitMsg(msg : Message) + } + class ServiceExecutor { + - LOGGER : Logger {static} + - msgQueue : MessageQueue + + ServiceExecutor(msgQueue : MessageQueue) + + run() + } + interface Task { + + submit(Message) {abstract} + } + class TaskGenerator { + - LOGGER : Logger {static} + - msgCount : int + - msgQueue : MessageQueue + + TaskGenerator(msgQueue : MessageQueue, msgCount : int) + + run() + + submit(msg : Message) + } +} +MessageQueue --> "-blkQueue" Message +ServiceExecutor --> "-msgQueue" MessageQueue +TaskGenerator --> "-msgQueue" MessageQueue +TaskGenerator ..|> Task +@enduml \ No newline at end of file diff --git a/reactor/README.md b/reactor/README.md index ba96f9c68391..e18595656ed7 100644 --- a/reactor/README.md +++ b/reactor/README.md @@ -14,6 +14,7 @@ tags: ## Intent The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. +## Class diagram ![Reactor](./etc/reactor.png "Reactor") ## Applicability diff --git a/reactor/etc/reactor.urm.puml b/reactor/etc/reactor.urm.puml new file mode 100644 index 000000000000..cc56eae7a1c8 --- /dev/null +++ b/reactor/etc/reactor.urm.puml @@ -0,0 +1,155 @@ +@startuml +package com.iluwatar.reactor.framework { + abstract class AbstractNioChannel { + - channel : SelectableChannel + - channelToPendingWrites : Map> + - handler : ChannelHandler + - reactor : NioReactor + + AbstractNioChannel(handler : ChannelHandler, channel : SelectableChannel) + + bind() {abstract} + # doWrite(Object, SelectionKey) {abstract} + ~ flush(key : SelectionKey) + + getHandler() : ChannelHandler + + getInterestedOps() : int {abstract} + + getJavaChannel() : SelectableChannel + + read(SelectionKey) : Object {abstract} + ~ setReactor(reactor : NioReactor) + + write(data : Object, key : SelectionKey) + } + interface ChannelHandler { + + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} + } + interface Dispatcher { + + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} + + stop() {abstract} + } + class NioDatagramChannel { + - LOGGER : Logger {static} + - port : int + + NioDatagramChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : DatagramChannel + + read(key : SelectionKey) : DatagramPacket + + write(data : Object, key : SelectionKey) + } + class DatagramPacket { + - data : ByteBuffer + - receiver : SocketAddress + - sender : SocketAddress + + DatagramPacket(data : ByteBuffer) + + getData() : ByteBuffer + + getReceiver() : SocketAddress + + getSender() : SocketAddress + + setReceiver(receiver : SocketAddress) + + setSender(sender : SocketAddress) + } + class NioReactor { + - LOGGER : Logger {static} + - dispatcher : Dispatcher + - pendingCommands : Queue + - reactorMain : ExecutorService + - selector : Selector + + NioReactor(dispatcher : Dispatcher) + + changeOps(key : SelectionKey, interestedOps : int) + - dispatchReadEvent(key : SelectionKey, readObject : Object) + - eventLoop() + - onChannelAcceptable(key : SelectionKey) + - onChannelReadable(key : SelectionKey) + - onChannelWritable(key : SelectionKey) {static} + - processKey(key : SelectionKey) + - processPendingCommands() + + registerChannel(channel : AbstractNioChannel) : NioReactor + + start() + + stop() + } + ~class ChangeKeyOpsCommand { + - interestedOps : int + - key : SelectionKey + + ChangeKeyOpsCommand(this$0 : SelectionKey, key : int) + + run() + + toString() : String + } + class NioServerSocketChannel { + - LOGGER : Logger {static} + - port : int + + NioServerSocketChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : ServerSocketChannel + + read(key : SelectionKey) : ByteBuffer + } + class SameThreadDispatcher { + + SameThreadDispatcher() + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + class ThreadPoolDispatcher { + - executorService : ExecutorService + + ThreadPoolDispatcher(poolSize : int) + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } +} +package com.iluwatar.reactor.app { + class App { + - channels : List + - dispatcher : Dispatcher + - reactor : NioReactor + + App(dispatcher : Dispatcher) + + main(args : String[]) {static} + + start() + + stop() + - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + } + class AppClient { + - LOGGER : Logger {static} + - service : ExecutorService + + AppClient() + - artificialDelayOf(millis : long) {static} + + main(args : String[]) {static} + + start() + + stop() + } + ~class TcpLoggingClient { + - clientName : String + - serverPort : int + + TcpLoggingClient(clientName : String, serverPort : int) + + run() + - sendLogRequests(writer : PrintWriter, inputStream : InputStream) + } + ~class UdpLoggingClient { + - clientName : String + - remoteAddress : InetSocketAddress + + UdpLoggingClient(clientName : String, port : int) + + run() + } + class LoggingHandler { + - ACK : byte[] {static} + - LOGGER : Logger {static} + + LoggingHandler() + - doLogging(data : ByteBuffer) {static} + + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} + - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} + } +} +AbstractNioChannel --> "-handler" ChannelHandler +UdpLoggingClient ..+ AppClient +TcpLoggingClient ..+ AppClient +AbstractNioChannel --> "-reactor" NioReactor +NioReactor --> "-dispatcher" Dispatcher +App --> "-reactor" NioReactor +App --> "-channels" AbstractNioChannel +DatagramPacket ..+ NioDatagramChannel +App --> "-dispatcher" Dispatcher +ChangeKeyOpsCommand --+ NioReactor +LoggingHandler ..|> ChannelHandler +NioDatagramChannel --|> AbstractNioChannel +NioServerSocketChannel --|> AbstractNioChannel +SameThreadDispatcher ..|> Dispatcher +ThreadPoolDispatcher ..|> Dispatcher +@enduml \ No newline at end of file diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md index 40b71136167b..30cbc0dc5d0a 100644 --- a/reader-writer-lock/README.md +++ b/reader-writer-lock/README.md @@ -11,21 +11,22 @@ tags: - Performance --- -**Intent:** +## Intent Suppose we have a shared memory area with the basic constraints detailed above. It is possible to protect the shared data behind a mutual exclusion mutex, in which case no two threads can access the data at the same time. However, this solution is suboptimal, because it is possible that a reader R1 might have the lock, and then another reader R2 requests access. It would be foolish for R2 to wait until R1 was done before starting its own read operation; instead, R2 should start right away. This is the motivation for the Reader Writer Lock pattern. +## Class diagram ![alt text](./etc/reader-writer-lock.png "Reader writer lock") -**Applicability:** +## Applicability Application need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations. -**Real world examples:** +## Real world examples * [Java Reader Writer Lock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html) -**Credits** +## Credits * [Readers–writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) diff --git a/reader-writer-lock/etc/reader-writer-lock.urm.puml b/reader-writer-lock/etc/reader-writer-lock.urm.puml new file mode 100644 index 000000000000..f0e33ab3cb40 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.reader.writer.lock { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Reader { + - LOGGER : Logger {static} + - name : String + - readLock : Lock + - readingTime : long + + Reader(name : String, readLock : Lock) + + Reader(name : String, readLock : Lock, readingTime : long) + + read() + + run() + } + class ReaderWriterLock { + - LOGGER : Logger {static} + - currentReaderCount : int + - globalMutex : Set + - readerLock : ReadLock + - readerMutex : Object + - writerLock : WriteLock + + ReaderWriterLock() + - doesWriterOwnThisLock() : boolean + - isLockFree() : boolean + + readLock() : Lock + + writeLock() : Lock + } + -class ReadLock { + - ReadLock() + - acquireForReaders() + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + -class WriteLock { + - WriteLock() + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class Writer { + - LOGGER : Logger {static} + - name : String + - writeLock : Lock + - writingTime : long + + Writer(name : String, writeLock : Lock) + + Writer(name : String, writeLock : Lock, writingTime : long) + + run() + + write() + } +} +ReaderWriterLock --> "-readerLock" ReadLock +ReadLock --+ ReaderWriterLock +WriteLock --+ ReaderWriterLock +ReaderWriterLock --> "-writerLock" WriteLock +@enduml \ No newline at end of file diff --git a/repository/README.md b/repository/README.md index b1f4fb97f56c..400e37eb0d64 100644 --- a/repository/README.md +++ b/repository/README.md @@ -17,6 +17,7 @@ to minimize scattering and duplication of query code. The Repository pattern is especially useful in systems where number of domain classes is large or heavy querying is utilized. +## Class diagram ![alt text](./etc/repository.png "Repository") ## Applicability @@ -35,4 +36,4 @@ Use the Repository pattern when * [Don’t use DAO, use Repository](http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/) * [Advanced Spring Data JPA - Specifications and Querydsl](https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/) -* [Repository Pattern Benefits and Spring Implementation](https://stackoverflow.com/questions/40068965/repository-pattern-benefits-and-spring-implementation) \ No newline at end of file +* [Repository Pattern Benefits and Spring Implementation](https://stackoverflow.com/questions/40068965/repository-pattern-benefits-and-spring-implementation) diff --git a/repository/etc/repository.urm.puml b/repository/etc/repository.urm.puml new file mode 100644 index 000000000000..10768260cefc --- /dev/null +++ b/repository/etc/repository.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.repository { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class AppConfig { + - LOGGER : Logger {static} + + AppConfig() + + dataSource() : DataSource + + entityManagerFactory() : LocalContainerEntityManagerFactoryBean + - jpaProperties() : Properties {static} + + main(args : String[]) {static} + + transactionManager() : JpaTransactionManager + } + class Person { + - age : int + - id : Long + - name : String + - surname : String + + Person() + + Person(name : String, surname : String, age : int) + + equals(obj : Object) : boolean + + getAge() : int + + getId() : Long + + getName() : String + + getSurname() : String + + hashCode() : int + + setAge(age : int) + + setId(id : Long) + + setName(name : String) + + setSurname(surname : String) + + toString() : String + } + interface PersonRepository { + + findByName(String) : Person {abstract} + } + class PersonSpecifications { + + PersonSpecifications() + } + class AgeBetweenSpec { + - from : int + - to : int + + AgeBetweenSpec(from : int, to : int) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class NameEqualSpec { + + name : String + + NameEqualSpec(name : String) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } +} +NameEqualSpec ..+ PersonSpecifications +AgeBetweenSpec ..+ PersonSpecifications +@enduml \ No newline at end of file diff --git a/resource-acquisition-is-initialization/README.md b/resource-acquisition-is-initialization/README.md index 821f220d74de..3591cf915b02 100644 --- a/resource-acquisition-is-initialization/README.md +++ b/resource-acquisition-is-initialization/README.md @@ -13,6 +13,7 @@ tags: ## Intent Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. +## Class diagram ![alt text](./etc/resource-acquisition-is-initialization.png "Resource Acquisition Is Initialization") ## Applicability diff --git a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml new file mode 100644 index 000000000000..11309f0a6230 --- /dev/null +++ b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml @@ -0,0 +1,19 @@ +@startuml +package com.iluwatar.resource.acquisition.is.initialization { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class SlidingDoor { + - LOGGER : Logger {static} + + SlidingDoor() + + close() + } + class TreasureChest { + - LOGGER : Logger {static} + + TreasureChest() + + close() + } +} +@enduml \ No newline at end of file diff --git a/retry/README.md b/retry/README.md index 1b9987e666b8..59958403c79a 100644 --- a/retry/README.md +++ b/retry/README.md @@ -18,8 +18,6 @@ Transparently retry certain operations that involve communication with external resources, particularly over the network, isolating calling code from the retry implementation details. -![alt text](./etc/retry.png "Retry") - ## Explanation The `Retry` pattern consists retrying operations on remote resources over the network a set number of times. It closely depends on both business and technical @@ -131,6 +129,9 @@ attempts were left. [1] Please note that *Hystrix* is a complete implementation of the *Circuit Breaker* pattern, of which the *Retry* pattern can be considered a subset of. +## Class diagram +![alt text](./etc/retry.png "Retry") + ## Applicability Whenever an application needs to communicate with an external resource, particularly in a cloud environment, and if the business requirements allow it. diff --git a/retry/etc/retry.urm.puml b/retry/etc/retry.urm.puml new file mode 100644 index 000000000000..ab2f7cf3a65f --- /dev/null +++ b/retry/etc/retry.urm.puml @@ -0,0 +1,54 @@ +@startuml +package com.iluwatar.retry { + class App { + - LOG : Logger {static} + - op : BusinessOperation {static} + + App() + - errorNoRetry() {static} + - errorWithRetry() {static} + - errorWithRetryExponentialBackoff() {static} + + main(args : String[]) {static} + - noErrors() {static} + } + interface BusinessOperation { + + perform() : T {abstract} + } + class FindCustomer { + - customerId : String + - errors : Deque + + FindCustomer(customerId : String, errors : BusinessException[]) + + perform() : String + } + class Retry { + - attempts : AtomicInteger + - delay : long + - errors : List + - maxAttempts : int + - op : BusinessOperation + - test : Predicate + + Retry(op : BusinessOperation, maxAttempts : int, delay : long, ignoreTests : Predicate[]) + + attempts() : int + + errors() : List + + perform() : T + } + class RetryExponentialBackoff { + - RANDOM : Random {static} + - attempts : AtomicInteger + - errors : List + - maxAttempts : int + - maxDelay : long + - op : BusinessOperation + - test : Predicate + + RetryExponentialBackoff(op : BusinessOperation, maxAttempts : int, maxDelay : long, ignoreTests : Predicate[]) + + attempts() : int + + errors() : List + + perform() : T + } +} +RetryExponentialBackoff --> "-op" BusinessOperation +Retry --> "-op" BusinessOperation +App --> "-op" BusinessOperation +FindCustomer ..|> BusinessOperation +Retry ..|> BusinessOperation +RetryExponentialBackoff ..|> BusinessOperation +@enduml \ No newline at end of file diff --git a/role-object/README.md b/role-object/README.md index 0e960140c987..e5378afdec39 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -18,6 +18,9 @@ Adapt an object to different client’s needs through transparently attached rol the object has to play in that client’s context. The object manages its role set dynamically. By representing roles as individual objects, different contexts are kept separate and system configuration is simplified. +## Class diagram +![alt text](./etc/role-object.urm.png "Role Object pattern class diagram") + ## Applicability Use the Role Object pattern, if: - you want to handle a key abstraction in different contexts and you do not want to put the resulting context specific interfaces into the same class interface. diff --git a/role-object/etc/role-object.urm.png b/role-object/etc/role-object.urm.png new file mode 100644 index 000000000000..65201c68aff4 Binary files /dev/null and b/role-object/etc/role-object.urm.png differ diff --git a/role-object/etc/role-object.urm.puml b/role-object/etc/role-object.urm.puml new file mode 100644 index 000000000000..241a146f0b9f --- /dev/null +++ b/role-object/etc/role-object.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.roleobject { + class ApplicationRoleObject { + - logger : Logger {static} + + ApplicationRoleObject() + + main(args : String[]) {static} + } + class BorrowerRole { + - name : String + + BorrowerRole() + + borrow() : String + + getName() : String + + setName(name : String) + } + abstract class Customer { + + Customer() + + addRole(Role) : boolean {abstract} + + getRole(Role, Class) : Optional {abstract} + + hasRole(Role) : boolean {abstract} + + newCustomer() : Customer {static} + + newCustomer(role : Role[]) : Customer {static} + + remRole(Role) : boolean {abstract} + } + class CustomerCore { + - roles : Map + + CustomerCore() + + addRole(role : Role) : boolean + + getRole(role : Role, expectedRole : Class) : Optional + + hasRole(role : Role) : boolean + + remRole(role : Role) : boolean + + toString() : String + } + abstract class CustomerRole { + + CustomerRole() + } + class InvestorRole { + - amountToInvest : long + - name : String + + InvestorRole() + + getAmountToInvest() : long + + getName() : String + + invest() : String + + setAmountToInvest(amountToInvest : long) + + setName(name : String) + } + enum Role { + + Borrower {static} + + Investor {static} + - logger : Logger {static} + - typeCst : Class + + instance() : Optional + + valueOf(name : String) : Role {static} + + values() : Role[] {static} + } +} +BorrowerRole --|> CustomerRole +CustomerCore --|> Customer +CustomerRole --|> CustomerCore +InvestorRole --|> CustomerRole +@enduml \ No newline at end of file diff --git a/saga/README.md b/saga/README.md index 546ad598b7c0..bfb39461a8a8 100644 --- a/saga/README.md +++ b/saga/README.md @@ -35,6 +35,9 @@ In this approach, there is a Saga orchestrator that manages all the transactions the participant services to execute local transactions based on events. This orchestrator can also be though of as a Saga Manager. +## Class diagram +![alt text](./etc/saga.urm.png "Saga pattern class diagram") + ## Applicability Use the Saga pattern, if: - you need to perform a group of operations related to different microservices atomically diff --git a/saga/etc/saga.urm.png b/saga/etc/saga.urm.png new file mode 100644 index 000000000000..025be6bbdedf Binary files /dev/null and b/saga/etc/saga.urm.png differ diff --git a/saga/etc/saga.urm.puml b/saga/etc/saga.urm.puml new file mode 100644 index 000000000000..e44b5366b021 --- /dev/null +++ b/saga/etc/saga.urm.puml @@ -0,0 +1,222 @@ +@startuml +package com.iluwatar.saga.orchestration { + class ChapterResult { + - state : State + - value : K + ~ ChapterResult(value : K, state : State) + + failure(val : K) : ChapterResult {static} + + getValue() : K + + isSuccess() : boolean + + success(val : K) : ChapterResult {static} + } + enum State { + + FAILURE {static} + + SUCCESS {static} + + valueOf(name : String) : State {static} + + values() : State[] {static} + } + class FlyBookingService { + + FlyBookingService() + + getName() : String + } + class HotelBookingService { + + HotelBookingService() + + getName() : String + + rollback(value : String) : ChapterResult + } + interface OrchestrationChapter { + + getName() : String {abstract} + + process(K) : ChapterResult {abstract} + + rollback(K) : ChapterResult {abstract} + } + class OrderService { + + OrderService() + + getName() : String + } + class Saga { + - chapters : List + - Saga() + + chapter(name : String) : Saga + + create() : Saga {static} + + get(idx : int) : Chapter + + isPresent(idx : int) : boolean + } + class Chapter { + ~ name : String + + Chapter(name : String) + + getName() : String + } + enum Result { + + CRASHED {static} + + FINISHED {static} + + ROLLBACK {static} + + valueOf(name : String) : Result {static} + + values() : Result[] {static} + } + class SagaApplication { + - LOGGER : Logger {static} + + SagaApplication() + + main(args : String[]) {static} + - newSaga() : Saga {static} + - serviceDiscovery() : ServiceDiscoveryService {static} + } + class SagaOrchestrator { + - LOGGER : Logger {static} + - saga : Saga + - sd : ServiceDiscoveryService + - state : CurrentState + + SagaOrchestrator(saga : Saga, sd : ServiceDiscoveryService) + + execute(value : K) : Result + } + -class CurrentState { + ~ currentNumber : int + ~ isForward : boolean + ~ CurrentState() + ~ back() : int + ~ cleanUp() + ~ current() : int + ~ directionToBack() + ~ forward() : int + ~ isForward() : boolean + } + abstract class Service { + # LOGGER : Logger {static} + + Service() + + getName() : String {abstract} + + process(value : K) : ChapterResult + + rollback(value : K) : ChapterResult + } + class ServiceDiscoveryService { + - services : Map> + + ServiceDiscoveryService() + + discover(orchestrationChapterService : OrchestrationChapter) : ServiceDiscoveryService + + find(service : String) : Optional> + } + class WithdrawMoneyService { + + WithdrawMoneyService() + + getName() : String + + process(value : String) : ChapterResult + } +} +package com.iluwatar.saga.choreography { + interface ChoreographyChapter { + + execute(Saga) : Saga {abstract} + + getName() : String {abstract} + + process(Saga) : Saga {abstract} + + rollback(Saga) : Saga {abstract} + } + class FlyBookingService { + + FlyBookingService(service : ServiceDiscoveryService) + + getName() : String + } + class HotelBookingService { + + HotelBookingService(service : ServiceDiscoveryService) + + getName() : String + } + class OrderService { + + OrderService(service : ServiceDiscoveryService) + + getName() : String + } + class Saga { + - chapters : List + - finished : boolean + - forward : boolean + - pos : int + - Saga() + ~ back() : int + + chapter(name : String) : Saga + + create() : Saga {static} + ~ forward() : int + ~ getCurrent() : Chapter + + getCurrentValue() : Object + + getResult() : SagaResult + ~ isCurrentSuccess() : boolean + ~ isForward() : boolean + ~ isPresent() : boolean + + setCurrentStatus(result : ChapterResult) + + setCurrentValue(value : Object) + ~ setFinished(finished : boolean) + + setInValue(value : Object) : Saga + + toString() : String + } + class Chapter { + - inValue : Object + - name : String + - result : ChapterResult + + Chapter(name : String) + + getInValue() : Object + + getName() : String + + isSuccess() : boolean + + setInValue(object : Object) + + setResult(result : ChapterResult) + } + enum ChapterResult { + + INIT {static} + + ROLLBACK {static} + + SUCCESS {static} + + valueOf(name : String) : ChapterResult {static} + + values() : ChapterResult[] {static} + } + enum SagaResult { + + FINISHED {static} + + PROGRESS {static} + + ROLLBACKED {static} + + valueOf(name : String) : SagaResult {static} + + values() : SagaResult[] {static} + } + class SagaApplication { + - LOGGER : Logger {static} + + SagaApplication() + + main(args : String[]) {static} + - newSaga(value : Object) : Saga {static} + - serviceDiscovery() : ServiceDiscoveryService {static} + } + abstract class Service { + # LOGGER : Logger {static} + - sd : ServiceDiscoveryService + + Service(service : ServiceDiscoveryService) + + execute(saga : Saga) : Saga + - isSagaFinished(saga : Saga) : boolean + + process(saga : Saga) : Saga + + rollback(saga : Saga) : Saga + - serviceNotFoundException(chServiceName : String) : Supplier + } + class ServiceDiscoveryService { + - services : Map + + ServiceDiscoveryService() + + discover(chapterService : ChoreographyChapter) : ServiceDiscoveryService + + find(service : String) : Optional + + findAny() : ChoreographyChapter + } + class WithdrawMoneyService { + + WithdrawMoneyService(service : ServiceDiscoveryService) + + getName() : String + + process(saga : Saga) : Saga + } +} +SagaOrchestrator --> "-saga" Saga +SagaOrchestrator --> "-sd" ServiceDiscoveryService +SagaOrchestrator --> "-state" CurrentState +CurrentState ..+ SagaOrchestrator +Chapter ..+ Saga +Saga --> "-chapters" Chapter +Chapter --> "-result" ChapterResult +ChapterResult ..+ Saga +ChapterResult --> "-state" State +State ..+ ChapterResult +Result ..+ Saga +Service --> "-sd" ServiceDiscoveryService +SagaResult ..+ Saga +Saga --> "-chapters" Chapter +Chapter ..+ Saga +FlyBookingService --|> Service +HotelBookingService --|> Service +OrderService --|> Service +Service ..|> ChoreographyChapter +WithdrawMoneyService --|> Service +FlyBookingService --|> Service +HotelBookingService --|> Service +OrderService --|> Service +Service ..|> OrchestrationChapter +WithdrawMoneyService --|> Service +@enduml \ No newline at end of file diff --git a/semaphore/README.md b/semaphore/README.md index 46ccd7b8ea22..a59d7b398f57 100644 --- a/semaphore/README.md +++ b/semaphore/README.md @@ -19,6 +19,7 @@ of the semaphore, can access the resources at any given time. A semaphore which only allows one concurrent access to a resource is called a binary semaphore. +## Class diagram ![alt text](./etc/semaphore.png "Semaphore") ## Applicability diff --git a/semaphore/etc/semaphore.urm.puml b/semaphore/etc/semaphore.urm.puml new file mode 100644 index 000000000000..168fd17e711a --- /dev/null +++ b/semaphore/etc/semaphore.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.semaphore { + class App { + + App() + + main(args : String[]) {static} + } + class Fruit { + - type : FruitType + + Fruit(type : FruitType) + + getType() : FruitType + + toString() : String + } + enum FruitType { + + APPLE {static} + + LEMON {static} + + ORANGE {static} + + valueOf(name : String) : FruitType {static} + + values() : FruitType[] {static} + } + class FruitBowl { + - fruit : List + + FruitBowl() + + countFruit() : int + + put(f : Fruit) + + take() : Fruit + + toString() : String + } + class FruitShop { + - available : boolean[] + - bowls : FruitBowl[] + - semaphore : Semaphore + + FruitShop() + + countFruit() : int + + returnBowl(bowl : FruitBowl) + + takeBowl() : FruitBowl + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class Semaphore { + - counter : int + - licenses : int + + Semaphore(licenses : int) + + acquire() + + getAvailableLicenses() : int + + getNumLicenses() : int + + release() + } +} +FruitType ..+ Fruit +Fruit --> "-type" FruitType +FruitShop --> "-semaphore" Semaphore +FruitBowl --> "-fruit" Fruit +Semaphore ..|> Lock +@enduml \ No newline at end of file diff --git a/servant/README.md b/servant/README.md index 3e82ab2cf904..67470b678dbc 100644 --- a/servant/README.md +++ b/servant/README.md @@ -14,6 +14,7 @@ Servant is used for providing some behavior to a group of classes. Instead of defining that behavior in each class - or when we cannot factor out this behavior in the common parent class - it is defined once in the Servant. +## Class diagram ![alt text](./etc/servant-pattern.png "Servant") ## Applicability diff --git a/servant/etc/servant.urm.puml b/servant/etc/servant.urm.puml new file mode 100644 index 000000000000..26ea0c28530d --- /dev/null +++ b/servant/etc/servant.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.servant { + class App { + - LOGGER : Logger {static} + - jenkins : Servant {static} + - travis : Servant {static} + + App() + + main(args : String[]) {static} + + scenario(servant : Servant, compliment : int) {static} + } + class King { + - complimentReceived : boolean + - isDrunk : boolean + - isHappy : boolean + - isHungry : boolean + + King() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + } + class Queen { + - complimentReceived : boolean + - isDrunk : boolean + - isFlirty : boolean + - isHappy : boolean + - isHungry : boolean + + Queen() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + + setFlirtiness(f : boolean) + } + ~interface Royalty { + + changeMood() {abstract} + + getDrink() {abstract} + + getFed() {abstract} + + getMood() : boolean {abstract} + + receiveCompliments() {abstract} + } + class Servant { + + name : String + + Servant(name : String) + + checkIfYouWillBeHanged(tableGuests : List) : boolean + + feed(r : Royalty) + + giveCompliments(r : Royalty) + + giveWine(r : Royalty) + } +} +App --> "-jenkins" Servant +King ..|> Royalty +Queen ..|> Royalty +@enduml \ No newline at end of file diff --git a/serverless/README.md b/serverless/README.md index 16589b7bd1bd..46e646f069b3 100644 --- a/serverless/README.md +++ b/serverless/README.md @@ -59,6 +59,9 @@ dynamically manages the allocation of machine resources. Pricing is based on the actual amount of resources consumed by an application, rather than on pre-purchased units of capacity. +## Class diagram +![alt text](./etc/serverless.urm.png "Serverless pattern class diagram") + ## Serverless framework [Serverless](https://serverless.com/) is a toolkit for deploying and operating serverless architectures. diff --git a/serverless/etc/serverless.urm.png b/serverless/etc/serverless.urm.png new file mode 100644 index 000000000000..18149d562742 Binary files /dev/null and b/serverless/etc/serverless.urm.png differ diff --git a/serverless/etc/serverless.urm.puml b/serverless/etc/serverless.urm.puml new file mode 100644 index 000000000000..27ff043fd6a8 --- /dev/null +++ b/serverless/etc/serverless.urm.puml @@ -0,0 +1,138 @@ +@startuml +package com.iluwatar.serverless.faas.api { + class LambdaInfoApiHandler { + - LOG : Logger {static} + - SUCCESS_STATUS_CODE : Integer {static} + + LambdaInfoApiHandler() + + handleRequest(input : Map, context : Context) : ApiGatewayResponse + - headers() : Map + - lambdaInfo(context : Context) : LambdaInfo + } +} +package com.iluwatar.serverless.baas.model { + class Address { + - addressLineOne : String + - addressLineTwo : String + - city : String + - serialVersionUID : long {static} + - state : String + - zipCode : String + + Address() + + equals(o : Object) : boolean + + getAddressLineOne() : String + + getAddressLineTwo() : String + + getCity() : String + + getState() : String + + getZipCode() : String + + hashCode() : int + + setAddressLineOne(addressLineOne : String) + + setAddressLineTwo(addressLineTwo : String) + + setCity(city : String) + + setState(state : String) + + setZipCode(zipCode : String) + + toString() : String + } + class Person { + - address : Address + - firstName : String + - id : String + - lastName : String + - serialVersionUID : long {static} + + Person() + + equals(o : Object) : boolean + + getAddress() : Address + + getFirstName() : String + + getId() : String + + getLastName() : String + + hashCode() : int + + setAddress(address : Address) + + setFirstName(firstName : String) + + setId(id : String) + + setLastName(lastName : String) + + toString() : String + } +} +package com.iluwatar.serverless.faas { + class ApiGatewayResponse { + - body : String + - headers : Map + - isBase64Encoded : Boolean + - serialVersionUID : long {static} + - statusCode : Integer + + ApiGatewayResponse(statusCode : Integer, body : String, headers : Map, isBase64Encoded : Boolean) + + getBody() : String + + getHeaders() : Map + + getStatusCode() : Integer + + isBase64Encoded() : Boolean + } + class ApiGatewayResponseBuilder { + - OBJECT_MAPPER : ObjectMapper {static} + - body : T extends Serializable + - headers : Map + - isBase64Encoded : Boolean + - statusCode : Integer + + ApiGatewayResponseBuilder() + + base64Encoded(isBase64Encoded : Boolean) : ApiGatewayResponseBuilder + + body(body : T extends Serializable) : ApiGatewayResponseBuilder + + build() : ApiGatewayResponse + + headers(headers : Map) : ApiGatewayResponseBuilder + + statusCode(statusCode : Integer) : ApiGatewayResponseBuilder + } + class LambdaInfo { + - awsRequestId : String + - functionName : String + - functionVersion : String + - logGroupName : String + - logStreamName : String + - memoryLimitInMb : Integer + - serialVersionUID : long {static} + + LambdaInfo() + + equals(o : Object) : boolean + + getAwsRequestId() : String + + getFunctionName() : String + + getFunctionVersion() : String + + getLogGroupName() : String + + getLogStreamName() : String + + getMemoryLimitInMb() : Integer + + hashCode() : int + + setAwsRequestId(awsRequestId : String) + + setFunctionName(functionName : String) + + setFunctionVersion(functionVersion : String) + + setLogGroupName(logGroupName : String) + + setLogStreamName(logStreamName : String) + + setMemoryLimitInMb(memoryLimitInMb : Integer) + + toString() : String + } +} +package com.iluwatar.serverless.baas.api { + abstract class AbstractDynamoDbHandler { + - dynamoDbMapper : DynamoDBMapper + - objectMapper : ObjectMapper + + AbstractDynamoDbHandler() + # apiGatewayProxyResponseEvent(statusCode : Integer, body : T extends Serializable) : APIGatewayProxyResponseEvent + # getDynamoDbMapper() : DynamoDBMapper + # getObjectMapper() : ObjectMapper + # headers() : Map + - initAmazonDynamoDb() + + setDynamoDbMapper(dynamoDbMapper : DynamoDBMapper) + } + class FindPersonApiHandler { + - LOG : Logger {static} + - SUCCESS_STATUS_CODE : Integer {static} + + FindPersonApiHandler() + + handleRequest(apiGatewayProxyRequestEvent : APIGatewayProxyRequestEvent, context : Context) : APIGatewayProxyResponseEvent + } + class SavePersonApiHandler { + - BAD_REQUEST_STATUS_CODE : Integer {static} + - CREATED_STATUS_CODE : Integer {static} + - LOG : Logger {static} + + SavePersonApiHandler() + + handleRequest(apiGatewayProxyRequestEvent : APIGatewayProxyRequestEvent, context : Context) : APIGatewayProxyResponseEvent + } +} +ApiGatewayResponseBuilder ..+ ApiGatewayResponse +Person --> "-address" Address +Access ..+ JsonProperty +FindPersonApiHandler --|> AbstractDynamoDbHandler +SavePersonApiHandler --|> AbstractDynamoDbHandler +@enduml \ No newline at end of file diff --git a/service-layer/README.md b/service-layer/README.md index 9b685d4e3f4b..9abb6294a531 100644 --- a/service-layer/README.md +++ b/service-layer/README.md @@ -17,6 +17,7 @@ others. Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic. The Service Layer fulfills this role. +## Class diagram ![alt text](./etc/service-layer.png "Service Layer") ## Applicability diff --git a/service-layer/etc/service-layer.urm.puml b/service-layer/etc/service-layer.urm.puml new file mode 100644 index 000000000000..e663e6c4a678 --- /dev/null +++ b/service-layer/etc/service-layer.urm.puml @@ -0,0 +1,159 @@ +@startuml +package com.iluwatar.servicelayer.hibernate { + class HibernateUtil { + - LOGGER : Logger {static} + - sessionFactory : SessionFactory {static} + - HibernateUtil() + + dropSession() {static} + + getSessionFactory() : SessionFactory {static} + } +} +package com.iluwatar.servicelayer.common { + abstract class BaseEntity { + + BaseEntity() + + getId() : Long {abstract} + + getName() : String {abstract} + + setId(Long) {abstract} + + setName(String) {abstract} + } + interface Dao { + + delete(E extends BaseEntity) {abstract} + + find(Long) : E extends BaseEntity {abstract} + + findAll() : List {abstract} + + merge(E extends BaseEntity) : E extends BaseEntity {abstract} + + persist(E extends BaseEntity) {abstract} + } + abstract class DaoBaseImpl { + # persistentClass : Class + + DaoBaseImpl() + + delete(entity : E extends BaseEntity) + + find(id : Long) : E extends BaseEntity + + findAll() : List + # getSessionFactory() : SessionFactory + + merge(entity : E extends BaseEntity) : E extends BaseEntity + + persist(entity : E extends BaseEntity) + } +} +package com.iluwatar.servicelayer.magic { + interface MagicService { + + findAllSpellbooks() : List {abstract} + + findAllSpells() : List {abstract} + + findAllWizards() : List {abstract} + + findWizardsWithSpell(String) : List {abstract} + + findWizardsWithSpellbook(String) : List {abstract} + } + class MagicServiceImpl { + - spellDao : SpellDao + - spellbookDao : SpellbookDao + - wizardDao : WizardDao + + MagicServiceImpl(wizardDao : WizardDao, spellbookDao : SpellbookDao, spellDao : SpellDao) + + findAllSpellbooks() : List + + findAllSpells() : List + + findAllWizards() : List + + findWizardsWithSpell(name : String) : List + + findWizardsWithSpellbook(name : String) : List + } +} +package com.iluwatar.servicelayer.wizard { + class Wizard { + - id : Long + - name : String + - spellbooks : Set + + Wizard() + + Wizard(name : String) + + addSpellbook(spellbook : Spellbook) + + getId() : Long + + getName() : String + + getSpellbooks() : Set + + setId(id : Long) + + setName(name : String) + + setSpellbooks(spellbooks : Set) + + toString() : String + } + interface WizardDao { + + findByName(String) : Wizard {abstract} + } + class WizardDaoImpl { + + WizardDaoImpl() + + findByName(name : String) : Wizard + } +} +package com.iluwatar.servicelayer.spellbook { + class Spellbook { + - id : Long + - name : String + - spells : Set + - wizards : Set + + Spellbook() + + Spellbook(name : String) + + addSpell(spell : Spell) + + getId() : Long + + getName() : String + + getSpells() : Set + + getWizards() : Set + + setId(id : Long) + + setName(name : String) + + setSpells(spells : Set) + + setWizards(wizards : Set) + + toString() : String + } + interface SpellbookDao { + + findByName(String) : Spellbook {abstract} + } + class SpellbookDaoImpl { + + SpellbookDaoImpl() + + findByName(name : String) : Spellbook + } +} +package com.iluwatar.servicelayer.spell { + class Spell { + - id : Long + - name : String + - spellbook : Spellbook + + Spell() + + Spell(name : String) + + getId() : Long + + getName() : String + + getSpellbook() : Spellbook + + setId(id : Long) + + setName(name : String) + + setSpellbook(spellbook : Spellbook) + + toString() : String + } + interface SpellDao { + + findByName(String) : Spell {abstract} + } + class SpellDaoImpl { + + SpellDaoImpl() + + findByName(name : String) : Spell + } +} +package com.iluwatar.servicelayer.app { + class App { + - LOGGER : Logger {static} + + App() + + initData() {static} + + main(args : String[]) {static} + + queryData() {static} + } +} +MagicServiceImpl --> "-wizardDao" WizardDao +MagicServiceImpl --> "-spellbookDao" SpellbookDao +MagicServiceImpl --> "-spellDao" SpellDao +Spellbook --> "-spells" Spell +Spellbook --> "-wizards" Wizard +DaoBaseImpl ..|> Dao +MagicServiceImpl ..|> MagicService +Spell --|> BaseEntity +SpellDao --|> Dao +SpellDaoImpl ..|> SpellDao +SpellDaoImpl --|> DaoBaseImpl +Spellbook --|> BaseEntity +SpellbookDao --|> Dao +SpellbookDaoImpl ..|> SpellbookDao +SpellbookDaoImpl --|> DaoBaseImpl +Wizard --|> BaseEntity +WizardDao --|> Dao +WizardDaoImpl ..|> WizardDao +WizardDaoImpl --|> DaoBaseImpl +@enduml \ No newline at end of file diff --git a/service-locator/README.md b/service-locator/README.md index 974a2a5feade..0be75f11710a 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -14,6 +14,7 @@ tags: Encapsulate the processes involved in obtaining a service with a strong abstraction layer. +## Class diagram ![alt text](./etc/service-locator.png "Service Locator") ## Applicability diff --git a/service-locator/etc/service-locator.urm.puml b/service-locator/etc/service-locator.urm.puml new file mode 100644 index 000000000000..38fe7ea1b0d0 --- /dev/null +++ b/service-locator/etc/service-locator.urm.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.servicelocator { + class App { + + App() + + main(args : String[]) {static} + } + class InitContext { + - LOGGER : Logger {static} + + InitContext() + + lookup(serviceName : String) : Object + } + interface Service { + + execute() {abstract} + + getId() : int {abstract} + + getName() : String {abstract} + } + class ServiceCache { + - LOGGER : Logger {static} + - serviceCache : Map + + ServiceCache() + + addService(newService : Service) + + getService(serviceName : String) : Service + } + class ServiceImpl { + - LOGGER : Logger {static} + - id : int + - serviceName : String + + ServiceImpl(serviceName : String) + + execute() + + getId() : int + + getName() : String + } + class ServiceLocator { + - serviceCache : ServiceCache {static} + - ServiceLocator() + + getService(serviceJndiName : String) : Service {static} + } +} +ServiceLocator --> "-serviceCache" ServiceCache +ServiceImpl ..|> Service +@enduml \ No newline at end of file diff --git a/sharding/README.md b/sharding/README.md index 3184e405ed17..aeeb71828fdb 100644 --- a/sharding/README.md +++ b/sharding/README.md @@ -13,6 +13,9 @@ tags: Sharding pattern means divide the data store into horizontal partitions or shards. Each shard has the same schema, but holds its own distinct subset of the data. A shard is a data store in its own right (it can contain the data for many entities of different types), running on a server acting as a storage node. +## Class diagram +![alt text](./etc/sharding.urm.png "Sharding pattern class diagram") + ## Applicability This pattern offers the following benefits: @@ -23,4 +26,4 @@ This pattern offers the following benefits: ## Credits -* [Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications - Sharding Pattern](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589797(v=pandp.10)?redirectedfrom=MSDN) \ No newline at end of file +* [Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications - Sharding Pattern](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589797(v=pandp.10)?redirectedfrom=MSDN) diff --git a/sharding/etc/sharding.urm.png b/sharding/etc/sharding.urm.png new file mode 100644 index 000000000000..e7f412af3f01 Binary files /dev/null and b/sharding/etc/sharding.urm.png differ diff --git a/sharding/etc/sharding.urm.puml b/sharding/etc/sharding.urm.puml new file mode 100644 index 000000000000..a51d027d5d5e --- /dev/null +++ b/sharding/etc/sharding.urm.puml @@ -0,0 +1,71 @@ +@startuml +package com.iluwatar.sharding { + class App { + + App() + + main(args : String[]) {static} + } + class Data { + - key : int + - type : DataType + - value : String + + Data(key : int, value : String, type : DataType) + + getKey() : int + + getType() : DataType + + getValue() : String + + setKey(key : int) + + setType(type : DataType) + + setValue(value : String) + + toString() : String + } + ~enum DataType { + + type1 {static} + + type2 {static} + + type3 {static} + + valueOf(name : String) : DataType {static} + + values() : DataType[] {static} + } + class HashShardManager { + - LOGGER : Logger {static} + + HashShardManager() + # allocateShard(data : Data) : int + + storeData(data : Data) : int + } + class LookupShardManager { + - LOGGER : Logger {static} + - lookupMap : Map + + LookupShardManager() + # allocateShard(data : Data) : int + + storeData(data : Data) : int + } + class RangeShardManager { + - LOGGER : Logger {static} + + RangeShardManager() + # allocateShard(data : Data) : int + + storeData(data : Data) : int + } + class Shard { + - dataStore : Map + - id : int + + Shard(id : int) + + clearData() + + getDataById(id : int) : Data + + getId() : int + + storeData(data : Data) + } + abstract class ShardManager { + - LOGGER : Logger {static} + # shardMap : Map + + ShardManager() + + addNewShard(shard : Shard) : boolean + # allocateShard(Data) : int {abstract} + + getShardById(shardId : int) : Shard + + removeShardById(shardId : int) : boolean + + storeData(Data) : int {abstract} + } +} +DataType ..+ Data +Data --> "-type" DataType +HashShardManager --|> ShardManager +LookupShardManager --|> ShardManager +RangeShardManager --|> ShardManager +@enduml \ No newline at end of file diff --git a/singleton/README.md b/singleton/README.md index 52aca8e162e6..d82f7d862f87 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -48,6 +48,9 @@ EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; assertEquals(enumIvoryTower1, enumIvoryTower2); // true ``` +## Class diagram +![alt text](./etc/singleton.urm.png "Singleton pattern class diagram") + ## Applicability Use the Singleton pattern when diff --git a/singleton/etc/singleton.urm.png b/singleton/etc/singleton.urm.png new file mode 100644 index 000000000000..46584af40039 Binary files /dev/null and b/singleton/etc/singleton.urm.png differ diff --git a/singleton/etc/singleton.urm.puml b/singleton/etc/singleton.urm.puml new file mode 100644 index 000000000000..371c9e2393d1 --- /dev/null +++ b/singleton/etc/singleton.urm.puml @@ -0,0 +1,44 @@ +@startuml +package com.iluwatar.singleton { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + enum EnumIvoryTower { + + INSTANCE {static} + + toString() : String + + valueOf(name : String) : EnumIvoryTower {static} + + values() : EnumIvoryTower[] {static} + } + class InitializingOnDemandHolderIdiom { + - InitializingOnDemandHolderIdiom() + + getInstance() : InitializingOnDemandHolderIdiom {static} + } + -class HelperHolder { + - INSTANCE : InitializingOnDemandHolderIdiom {static} + - HelperHolder() + } + class IvoryTower { + - INSTANCE : IvoryTower {static} + - IvoryTower() + + getInstance() : IvoryTower {static} + } + class ThreadSafeDoubleCheckLocking { + - flag : boolean {static} + - instance : ThreadSafeDoubleCheckLocking {static} + - ThreadSafeDoubleCheckLocking() + + getInstance() : ThreadSafeDoubleCheckLocking {static} + } + class ThreadSafeLazyLoadedIvoryTower { + - instance : ThreadSafeLazyLoadedIvoryTower {static} + - ThreadSafeLazyLoadedIvoryTower() + + getInstance() : ThreadSafeLazyLoadedIvoryTower {static} + } +} +IvoryTower --> "-INSTANCE" IvoryTower +ThreadSafeDoubleCheckLocking --> "-instance" ThreadSafeDoubleCheckLocking +ThreadSafeLazyLoadedIvoryTower --> "-instance" ThreadSafeLazyLoadedIvoryTower +HelperHolder ..+ InitializingOnDemandHolderIdiom +HelperHolder --> "-INSTANCE" InitializingOnDemandHolderIdiom +@enduml \ No newline at end of file diff --git a/spatial-partition/README.md b/spatial-partition/README.md index 6e55f9544ff2..5206708bcdbd 100644 --- a/spatial-partition/README.md +++ b/spatial-partition/README.md @@ -14,11 +14,6 @@ As explained in the book [Game Programming Patterns](http://gameprogrammingpatte > efficiently locate objects by storing them in a data structure organized by their positions. -## Applicability -This pattern can be used: -* When you need to keep track of a large number of objects' positions, which are getting updated every frame. -* When it is acceptable to trade memory for speed, since creating and updating data structure will use up extra memory. - ## Explanation Say, you are building a war game with hundreds, or maybe even thousands of players, who are clashing on the battle field. Each player's position is getting updated every frame. The simple way to handle all interactions taking place on the field is to check each player's position against every other player's position: @@ -48,7 +43,14 @@ The idea behind the Spatial Partition design pattern is to enable quick location In our implementation, we use the Quadtree data structure which will reduce the time complexity of finding the objects within a certain range from O(n^2) to O(nlogn), decreasing the computations required significantly in case of large number of objects. +## Class diagram +![alt text](./etc/spatial-partition.urm.png "Spatial Partition pattern class diagram") + +## Applicability +This pattern can be used: +* When you need to keep track of a large number of objects' positions, which are getting updated every frame. +* When it is acceptable to trade memory for speed, since creating and updating data structure will use up extra memory. + ## Credits * [Game Programming Patterns/Spatial Partition](http://gameprogrammingpatterns.com/spatial-partition.html) by Bob Nystrom * [Quadtree tutorial](https://www.youtube.com/watch?v=OJxEcs0w_kE) by Daniel Schiffman - diff --git a/spatial-partition/etc/spatial-partition.urm.png b/spatial-partition/etc/spatial-partition.urm.png new file mode 100644 index 000000000000..5172bdb36a79 Binary files /dev/null and b/spatial-partition/etc/spatial-partition.urm.png differ diff --git a/spatial-partition/etc/spatial-partition.urm.puml b/spatial-partition/etc/spatial-partition.urm.puml new file mode 100644 index 000000000000..5962fcc4baca --- /dev/null +++ b/spatial-partition/etc/spatial-partition.urm.puml @@ -0,0 +1,72 @@ +@startuml +package com.iluwatar.spatialpartition { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + ~ noSpatialPartition(height : int, width : int, numOfMovements : int, bubbles : Hashtable) {static} + ~ withSpatialPartition(height : int, width : int, numOfMovements : int, bubbles : Hashtable) {static} + } + class Bubble { + - LOGGER : Logger {static} + - RANDOM : Random {static} + ~ radius : int + ~ Bubble(x : int, y : int, id : int, radius : int) + ~ handleCollision(bubblesToCheck : ArrayList>, allBubbles : Hashtable) + ~ move() + ~ pop(allBubbles : Hashtable) + ~ touches(b : Bubble) : boolean + } + abstract class Point { + + coordinateX : int + + coordinateY : int + + id : int + ~ Point(x : int, y : int, id : int) + ~ handleCollision(ArrayList>, Hashtable) {abstract} + ~ move() {abstract} + ~ touches(T) : boolean {abstract} + } + class QuadTree { + ~ boundary : Rect + ~ capacity : int + ~ divided : boolean + ~ northeast : QuadTree + ~ northwest : QuadTree + ~ points : Hashtable> + ~ southeast : QuadTree + ~ southwest : QuadTree + ~ QuadTree(boundary : Rect, capacity : int) + ~ divide() + ~ insert(p : Point) + ~ query(r : Rect, relevantPoints : ArrayList>) : ArrayList> + } + class Rect { + ~ coordinateX : double + ~ coordinateY : double + ~ height : double + ~ width : double + ~ Rect(x : double, y : double, width : double, height : double) + ~ contains(p : Point) : boolean + ~ intersects(other : Rect) : boolean + } + class SpatialPartitionBubbles { + ~ bubbles : Hashtable + ~ quadTree : QuadTree + ~ SpatialPartitionBubbles(bubbles : Hashtable, quadTree : QuadTree) + ~ handleCollisionsUsingQt(b : Bubble) + } + abstract class SpatialPartitionGeneric { + ~ playerPositions : Hashtable + ~ quadTree : QuadTree + + SpatialPartitionGeneric() + ~ handleCollisionsUsingQt(T) {abstract} + } +} +SpatialPartitionBubbles --> "-quadTree" QuadTree +SpatialPartitionGeneric --> "-quadTree" QuadTree +QuadTree --> "-boundary" Rect +QuadTree --> "-northwest" QuadTree +QuadTree --> "-southwest" QuadTree +Bubble --|> Point +SpatialPartitionBubbles --|> SpatialPartitionGeneric +@enduml \ No newline at end of file diff --git a/specification/README.md b/specification/README.md index 1764f448bb78..8c32a7875149 100644 --- a/specification/README.md +++ b/specification/README.md @@ -19,14 +19,6 @@ candidate, from the candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for validation and for building to order. -![alt text](./etc/specification.png "Specification") - -## Applicability -Use the Specification pattern when - -* You need to select a subset of objects based on some criteria, and to refresh the selection at various times. -* You need to check that only suitable objects are used for a certain role (validation). - ## Explanation Real world example @@ -190,6 +182,15 @@ Instead, we just create an instance of ``AbstractSelector`` "on the spot", using | Composite Specification | There are a lot of selection criteria that can be combined in multiple ways, hence it is not feasible to create a class for each selector | + Very flexible, without requiring many specialized classes | - Somewhat more difficult to comprehend | | | | + Supports logical operations | - You still need to create the base classes used as leaves | +## Class diagram +![alt text](./etc/specification.png "Specification") + +## Applicability +Use the Specification pattern when + +* You need to select a subset of objects based on some criteria, and to refresh the selection at various times. +* You need to check that only suitable objects are used for a certain role (validation). + ## Related patterns * Repository diff --git a/specification/etc/specification.urm.puml b/specification/etc/specification.urm.puml new file mode 100644 index 000000000000..21cae1c3c2a4 --- /dev/null +++ b/specification/etc/specification.urm.puml @@ -0,0 +1,177 @@ +@startuml +package com.iluwatar.specification.creature { + abstract class AbstractCreature { + - color : Color + - mass : Mass + - movement : Movement + - name : String + - size : Size + + AbstractCreature(name : String, size : Size, movement : Movement, color : Color, mass : Mass) + + getColor() : Color + + getMass() : Mass + + getMovement() : Movement + + getName() : String + + getSize() : Size + + toString() : String + } + interface Creature { + + getColor() : Color {abstract} + + getMass() : Mass {abstract} + + getMovement() : Movement {abstract} + + getName() : String {abstract} + + getSize() : Size {abstract} + } + class Dragon { + + Dragon() + + Dragon(mass : Mass) + } + class Goblin { + + Goblin() + + Goblin(mass : Mass) + } + class KillerBee { + + KillerBee() + + KillerBee(mass : Mass) + } + class Octopus { + + Octopus() + + Octopus(mass : Mass) + } + class Shark { + + Shark() + + Shark(mass : Mass) + } + class Troll { + + Troll() + + Troll(mass : Mass) + } +} +package com.iluwatar.specification.property { + enum Color { + + DARK {static} + + GREEN {static} + + LIGHT {static} + + RED {static} + - title : String + + toString() : String + + valueOf(name : String) : Color {static} + + values() : Color[] {static} + } + class Mass { + - title : String + - value : double + + Mass(value : double) + + equals(obj : Object) : boolean + + greaterThan(other : Mass) : boolean + + greaterThanOrEq(other : Mass) : boolean + + smallerThan(other : Mass) : boolean + + smallerThanOrEq(other : Mass) : boolean + + toString() : String + } + enum Movement { + + FLYING {static} + + SWIMMING {static} + + WALKING {static} + - title : String + + toString() : String + + valueOf(name : String) : Movement {static} + + values() : Movement[] {static} + } + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } +} +package com.iluwatar.specification.selector { + abstract class AbstractSelector { + + AbstractSelector() + + and(other : AbstractSelector) : AbstractSelector + + not() : AbstractSelector + + or(other : AbstractSelector) : AbstractSelector + } + class ColorSelector { + - color : Color + + ColorSelector(c : Color) + + test(t : Creature) : boolean + } + class ConjunctionSelector { + - leafComponents : List> + ~ ConjunctionSelector(selectors : AbstractSelector[]) + + test(t : T) : boolean + } + class DisjunctionSelector { + - leafComponents : List> + ~ DisjunctionSelector(selectors : AbstractSelector[]) + + test(t : T) : boolean + } + class MassEqualSelector { + - mass : Mass + + MassEqualSelector(mass : double) + + test(t : Creature) : boolean + } + class MassGreaterThanSelector { + - mass : Mass + + MassGreaterThanSelector(mass : double) + + test(t : Creature) : boolean + } + class MassSmallerThanOrEqSelector { + - mass : Mass + + MassSmallerThanOrEqSelector(mass : double) + + test(t : Creature) : boolean + } + class MovementSelector { + - movement : Movement + + MovementSelector(m : Movement) + + test(t : Creature) : boolean + } + class NegationSelector { + - component : AbstractSelector + ~ NegationSelector(selector : AbstractSelector) + + test(t : T) : boolean + } + class SizeSelector { + - size : Size + + SizeSelector(s : Size) + + test(t : Creature) : boolean + } +} +package com.iluwatar.specification.app { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } +} +SizeSelector --> "-size" Size +AbstractCreature --> "-mass" Mass +MassEqualSelector --> "-mass" Mass +AbstractCreature --> "-color" Color +MassGreaterThanSelector --> "-mass" Mass +MovementSelector --> "-movement" Movement +NegationSelector --> "-component" AbstractSelector +AbstractCreature --> "-movement" Movement +MassSmallerThanOrEqSelector --> "-mass" Mass +AbstractCreature --> "-size" Size +ColorSelector --> "-color" Color +AbstractCreature ..|> Creature +Dragon --|> AbstractCreature +Goblin --|> AbstractCreature +KillerBee --|> AbstractCreature +Octopus --|> AbstractCreature +Shark --|> AbstractCreature +Troll --|> AbstractCreature +ColorSelector --|> AbstractSelector +ConjunctionSelector --|> AbstractSelector +DisjunctionSelector --|> AbstractSelector +MassEqualSelector --|> AbstractSelector +MassGreaterThanSelector --|> AbstractSelector +MassSmallerThanOrEqSelector --|> AbstractSelector +MovementSelector --|> AbstractSelector +NegationSelector --|> AbstractSelector +SizeSelector --|> AbstractSelector +@enduml \ No newline at end of file diff --git a/state/README.md b/state/README.md index 549afa61f9a3..726d842ae38d 100644 --- a/state/README.md +++ b/state/README.md @@ -17,6 +17,7 @@ Objects for States Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. +## Class diagram ![alt text](./etc/state_1.png "State") ## Applicability diff --git a/state/etc/state.urm.puml b/state/etc/state.urm.puml new file mode 100644 index 000000000000..207a227d83d7 --- /dev/null +++ b/state/etc/state.urm.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.state { + class AngryState { + - LOGGER : Logger {static} + - mammoth : Mammoth + + AngryState(mammoth : Mammoth) + + observe() + + onEnterState() + } + class App { + + App() + + main(args : String[]) {static} + } + class Mammoth { + - state : State + + Mammoth() + - changeStateTo(newState : State) + + observe() + + timePasses() + + toString() : String + } + class PeacefulState { + - LOGGER : Logger {static} + - mammoth : Mammoth + + PeacefulState(mammoth : Mammoth) + + observe() + + onEnterState() + } + interface State { + + observe() {abstract} + + onEnterState() {abstract} + } +} +PeacefulState --> "-mammoth" Mammoth +AngryState --> "-mammoth" Mammoth +Mammoth --> "-state" State +AngryState ..|> State +PeacefulState ..|> State +@enduml \ No newline at end of file diff --git a/step-builder/README.md b/step-builder/README.md index bc636e37ae06..b4394496d138 100644 --- a/step-builder/README.md +++ b/step-builder/README.md @@ -13,6 +13,7 @@ tags: An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object. +## Class diagram ![alt text](./etc/step-builder.png "Step Builder") ## Applicability diff --git a/step-builder/etc/step-builder.urm.puml b/step-builder/etc/step-builder.urm.puml new file mode 100644 index 000000000000..4a56e1e9bdbe --- /dev/null +++ b/step-builder/etc/step-builder.urm.puml @@ -0,0 +1,91 @@ +@startuml +package com.iluwatar.stepbuilder { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Character { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + + Character(name : String) + + getAbilities() : List + + getFighterClass() : String + + getName() : String + + getSpell() : String + + getWeapon() : String + + getWizardClass() : String + + setAbilities(abilities : List) + + setFighterClass(fighterClass : String) + + setName(name : String) + + setSpell(spell : String) + + setWeapon(weapon : String) + + setWizardClass(wizardClass : String) + + toString() : String + } + class CharacterStepBuilder { + - CharacterStepBuilder() + + newBuilder() : NameStep {static} + } + interface AbilityStep { + + noAbilities() : BuildStep {abstract} + + noMoreAbilities() : BuildStep {abstract} + + withAbility(String) : AbilityStep {abstract} + } + interface BuildStep { + + build() : Character {abstract} + } + -class CharacterSteps { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + - CharacterSteps() + + build() : Character + + fighterClass(fighterClass : String) : WeaponStep + + name(name : String) : ClassStep + + noAbilities() : BuildStep + + noMoreAbilities() : BuildStep + + noSpell() : BuildStep + + noWeapon() : BuildStep + + withAbility(ability : String) : AbilityStep + + withSpell(spell : String) : AbilityStep + + withWeapon(weapon : String) : AbilityStep + + wizardClass(wizardClass : String) : SpellStep + } + interface ClassStep { + + fighterClass(String) : WeaponStep {abstract} + + wizardClass(String) : SpellStep {abstract} + } + interface NameStep { + + name(String) : ClassStep {abstract} + } + interface SpellStep { + + noSpell() : BuildStep {abstract} + + withSpell(String) : AbilityStep {abstract} + } + interface WeaponStep { + + noWeapon() : BuildStep {abstract} + + withWeapon(String) : AbilityStep {abstract} + } +} +WeaponStep ..+ CharacterStepBuilder +AbilityStep ..+ CharacterStepBuilder +ClassStep ..+ CharacterStepBuilder +CharacterSteps ..+ CharacterStepBuilder +SpellStep ..+ CharacterStepBuilder +BuildStep ..+ CharacterStepBuilder +NameStep ..+ CharacterStepBuilder +CharacterSteps ..|> NameStep +CharacterSteps ..|> ClassStep +CharacterSteps ..|> WeaponStep +CharacterSteps ..|> SpellStep +CharacterSteps ..|> AbilityStep +CharacterSteps ..|> BuildStep +@enduml \ No newline at end of file diff --git a/strategy/README.md b/strategy/README.md index 4c7147bed9cb..b6f41c02712a 100644 --- a/strategy/README.md +++ b/strategy/README.md @@ -18,6 +18,7 @@ Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. +## Class diagram ![alt text](./etc/strategy_1.png "Strategy") ## Applicability diff --git a/strategy/etc/strategy.urm.puml b/strategy/etc/strategy.urm.puml new file mode 100644 index 000000000000..8b9daabb9db4 --- /dev/null +++ b/strategy/etc/strategy.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.strategy { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class DragonSlayer { + - strategy : DragonSlayingStrategy + + DragonSlayer(strategy : DragonSlayingStrategy) + + changeStrategy(strategy : DragonSlayingStrategy) + + goToBattle() + } + interface DragonSlayingStrategy { + + execute() {abstract} + } + class MeleeStrategy { + - LOGGER : Logger {static} + + MeleeStrategy() + + execute() + } + class ProjectileStrategy { + - LOGGER : Logger {static} + + ProjectileStrategy() + + execute() + } + class SpellStrategy { + - LOGGER : Logger {static} + + SpellStrategy() + + execute() + } +} +DragonSlayer --> "-strategy" DragonSlayingStrategy +MeleeStrategy ..|> DragonSlayingStrategy +ProjectileStrategy ..|> DragonSlayingStrategy +SpellStrategy ..|> DragonSlayingStrategy +@enduml \ No newline at end of file diff --git a/subclass-sandbox/README.md b/subclass-sandbox/README.md index b4c8952b0507..8fb0342792d1 100644 --- a/subclass-sandbox/README.md +++ b/subclass-sandbox/README.md @@ -12,6 +12,9 @@ tags: ## Intent The subclass sandbox pattern describes a basic idea, while not having a lot of detailed mechanics. You will need the pattern when you have several similar subclasses. If you have to make a tiny change, then change the base class, while all subclasses shouldn't have to be touched. So the base class has to be able to provide all of the operations a derived class needs to perform. +## Class diagram +![alt text](./etc/subclass-sandbox.urm.png "Subclass Sandbox pattern class diagram") + ## Applicability The Subclass Sandbox pattern is a very simple, common pattern lurking in lots of codebases, even outside of games. If you have a non-virtual protected method laying around, you’re probably already using something like this. Subclass Sandbox is a good fit when: diff --git a/subclass-sandbox/etc/subclass-sandbox.urm.png b/subclass-sandbox/etc/subclass-sandbox.urm.png new file mode 100644 index 000000000000..db81e12cc815 Binary files /dev/null and b/subclass-sandbox/etc/subclass-sandbox.urm.png differ diff --git a/subclass-sandbox/etc/subclass-sandbox.urm.puml b/subclass-sandbox/etc/subclass-sandbox.urm.puml new file mode 100644 index 000000000000..a1f863b697c9 --- /dev/null +++ b/subclass-sandbox/etc/subclass-sandbox.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.subclasssandbox { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class GroundDive { + + GroundDive() + # activate() + } + class SkyLaunch { + + SkyLaunch() + # activate() + } + abstract class Superpower { + # logger : Logger + + Superpower() + # activate() {abstract} + # move(x : double, y : double, z : double) + # playSound(soundName : String, volumn : int) + # spawnParticles(particleType : String, count : int) + } +} +GroundDive --|> Superpower +SkyLaunch --|> Superpower +@enduml \ No newline at end of file diff --git a/template-method/README.md b/template-method/README.md index 22c2ac2c3e65..b1ad04fb63e5 100644 --- a/template-method/README.md +++ b/template-method/README.md @@ -17,6 +17,7 @@ an algorithm without changing the algorithm's structure. To make sure that subclasses don’t override the template method, the template method should be declared `final`. +## Class diagram ![alt text](./etc/template-method_1.png "Template Method") ## Applicability diff --git a/template-method/etc/template-method.urm.puml b/template-method/etc/template-method.urm.puml new file mode 100644 index 000000000000..a6e2dc3d20fd --- /dev/null +++ b/template-method/etc/template-method.urm.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.templatemethod { + class App { + + App() + + main(args : String[]) {static} + } + class HalflingThief { + - method : StealingMethod + + HalflingThief(method : StealingMethod) + + changeMethod(method : StealingMethod) + + steal() + } + class HitAndRunMethod { + - LOGGER : Logger {static} + + HitAndRunMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } + abstract class StealingMethod { + - LOGGER : Logger {static} + + StealingMethod() + # confuseTarget(String) {abstract} + # pickTarget() : String {abstract} + + steal() + # stealTheItem(String) {abstract} + } + class SubtleMethod { + - LOGGER : Logger {static} + + SubtleMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } +} +HalflingThief --> "-method" StealingMethod +HitAndRunMethod --|> StealingMethod +SubtleMethod --|> StealingMethod +@enduml \ No newline at end of file diff --git a/thread-pool/README.md b/thread-pool/README.md index 9806fa8e0b31..445fba07bdc0 100644 --- a/thread-pool/README.md +++ b/thread-pool/README.md @@ -17,6 +17,7 @@ the system spend more time creating and destroying the threads than executing the actual tasks. Thread Pool solves this problem by reusing existing threads and eliminating the latency of creating new threads. +## Class diagram ![alt text](./etc/thread-pool.png "Thread Pool") ## Applicability diff --git a/thread-pool/etc/thread-pool.urm.puml b/thread-pool/etc/thread-pool.urm.puml new file mode 100644 index 000000000000..251033c8157c --- /dev/null +++ b/thread-pool/etc/thread-pool.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.threadpool { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class CoffeeMakingTask { + - TIME_PER_CUP : int {static} + + CoffeeMakingTask(numCups : int) + + toString() : String + } + class PotatoPeelingTask { + - TIME_PER_POTATO : int {static} + + PotatoPeelingTask(numPotatoes : int) + + toString() : String + } + abstract class Task { + - ID_GENERATOR : AtomicInteger {static} + - id : int + - timeMs : int + + Task(timeMs : int) + + getId() : int + + getTimeMs() : int + + toString() : String + } + class Worker { + - LOGGER : Logger {static} + - task : Task + + Worker(task : Task) + + run() + } +} +Worker --> "-task" Task +CoffeeMakingTask --|> Task +PotatoPeelingTask --|> Task +@enduml \ No newline at end of file diff --git a/throttling/README.md b/throttling/README.md index 73d982bffba6..ad0361f3914d 100644 --- a/throttling/README.md +++ b/throttling/README.md @@ -12,6 +12,9 @@ tags: ## Intent Ensure that a given client is not able to access service resources more than the assigned limit. +## Class diagram +![alt text](./etc/throttling-pattern.png "Throttling pattern class diagram") + ## Applicability The Throttling pattern should be used: diff --git a/throttling/etc/throttling.urm.puml b/throttling/etc/throttling.urm.puml new file mode 100644 index 000000000000..797006627b84 --- /dev/null +++ b/throttling/etc/throttling.urm.puml @@ -0,0 +1,47 @@ +@startuml +package com.iluwatar.throttling { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + - makeServiceCalls(tenant : Tenant, callsCount : CallsCount) {static} + } + ~class B2BService { + - LOGGER : Logger {static} + - callsCount : CallsCount + + B2BService(timer : Throttler, callsCount : CallsCount) + + dummyCustomerApi(tenant : Tenant) : int + - getRandomCustomerId() : int + } + class CallsCount { + - LOGGER : Logger {static} + - tenantCallsCount : Map + + CallsCount() + + addTenant(tenantName : String) + + getCount(tenantName : String) : long + + incrementCount(tenantName : String) + + reset() + } + class Tenant { + - allowedCallsPerSecond : int + - name : String + + Tenant(name : String, allowedCallsPerSecond : int, callsCount : CallsCount) + + getAllowedCallsPerSecond() : int + + getName() : String + } +} +package com.iluwatar.throttling.timer { + class ThrottleTimerImpl { + - callsCount : CallsCount + - throttlePeriod : int + + ThrottleTimerImpl(throttlePeriod : int, callsCount : CallsCount) + + start() + } + interface Throttler { + + start() {abstract} + } +} +B2BService --> "-callsCount" CallsCount +ThrottleTimerImpl --> "-callsCount" CallsCount +ThrottleTimerImpl ..|> Throttler +@enduml \ No newline at end of file diff --git a/tls/README.md b/tls/README.md index 3fb5e9a6b128..ac4aa249048b 100644 --- a/tls/README.md +++ b/tls/README.md @@ -13,6 +13,7 @@ tags: ## Intent Securing variables global to a thread against being spoiled by other threads. That is needed if you use class variables or static variables in your Callable object or Runnable object that are not read-only. +## Class diagram ![alt text](./etc/tls.png "Thread Local Storage") ## Applicability diff --git a/tls/etc/tls.urm.puml b/tls/etc/tls.urm.puml new file mode 100644 index 000000000000..12bcea0643b6 --- /dev/null +++ b/tls/etc/tls.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.tls { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + - printAndCountDates(res : Result) : int {static} + - printAndCountExceptions(res : Result) : int {static} + } + class DateFormatCallable { + - LOGGER : Logger {static} + - dateValue : String + - df : ThreadLocal + + DateFormatCallable(inDateFormat : String, inDateValue : String) + + call() : Result + } + class Result { + - dateList : List + - exceptionList : List + + Result() + + getDateList() : List + + getExceptionList() : List + } +} +@enduml \ No newline at end of file diff --git a/tolerant-reader/README.md b/tolerant-reader/README.md index be0085f2c571..5ba0fd09f0b9 100644 --- a/tolerant-reader/README.md +++ b/tolerant-reader/README.md @@ -15,6 +15,7 @@ robust communication systems. The idea is to be as tolerant as possible when reading data from another service. This way, when the communication schema changes, the readers must not break. +## Class diagram ![alt text](./etc/tolerant-reader.png "Tolerant Reader") ## Applicability diff --git a/tolerant-reader/etc/tolerant-reader.urm.puml b/tolerant-reader/etc/tolerant-reader.urm.puml new file mode 100644 index 000000000000..a73394a4ed7d --- /dev/null +++ b/tolerant-reader/etc/tolerant-reader.urm.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.tolerantreader { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class RainbowFish { + - age : int + - lengthMeters : int + - name : String + - serialVersionUID : long {static} + - weightTons : int + + RainbowFish(name : String, age : int, lengthMeters : int, weightTons : int) + + getAge() : int + + getLengthMeters() : int + + getName() : String + + getWeightTons() : int + } + class RainbowFishSerializer { + - RainbowFishSerializer() + + readV1(filename : String) : RainbowFish {static} + + writeV1(rainbowFish : RainbowFish, filename : String) {static} + + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} + } + class RainbowFishV2 { + - angry : boolean + - hungry : boolean + - serialVersionUID : long {static} + - sleeping : boolean + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int) + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int, sleeping : boolean, hungry : boolean, angry : boolean) + + getAngry() : boolean + + getHungry() : boolean + + getSleeping() : boolean + } +} +RainbowFishV2 --|> RainbowFish +@enduml \ No newline at end of file diff --git a/trampoline/README.md b/trampoline/README.md index dc0bb270cb78..77b4daa7b7c0 100644 --- a/trampoline/README.md +++ b/trampoline/README.md @@ -18,10 +18,11 @@ It is possible by representing a computation in one of 2 states : done | more (completed with result, or a reference to the reminder of the computation, something like the way a java.util.Supplier does). - ## Explanation Trampoline pattern allows to define recursive algorithms by iterative loop. +## Class diagram +![alt text](./etc/trampoline.urm.png "Trampoline pattern class diagram") ## Applicability Use the Trampoline pattern when @@ -41,5 +42,3 @@ Trampolines in Java usually involve the creation of a GenericListener to pass ev ## Credits * [library 'cyclops-react' uses the pattern](https://github.com/aol/cyclops-react) - - diff --git a/trampoline/etc/trampoline.urm.png b/trampoline/etc/trampoline.urm.png new file mode 100644 index 000000000000..f2e9c7439a32 Binary files /dev/null and b/trampoline/etc/trampoline.urm.png differ diff --git a/trampoline/etc/trampoline.urm.puml b/trampoline/etc/trampoline.urm.puml new file mode 100644 index 000000000000..31ca84b6da4a --- /dev/null +++ b/trampoline/etc/trampoline.urm.puml @@ -0,0 +1,18 @@ +@startuml +package com.iluwatar.trampoline { + interface Trampoline { + + complete() : boolean + + done(result : T) : Trampoline {static} + + get() : T {abstract} + + jump() : Trampoline + + more(trampoline : Trampoline>) : Trampoline {static} + + result() : T + } + class TrampolineApp { + - log : Logger {static} + + TrampolineApp() + + loop(times : int, prod : int) : Trampoline {static} + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/twin/README.md b/twin/README.md index 3795236bb528..cc7198ff431a 100644 --- a/twin/README.md +++ b/twin/README.md @@ -13,6 +13,7 @@ tags: Twin pattern is a design pattern which provides a standard solution to simulate multiple inheritance in java +## Class diagram ![alt text](./etc/twin.png "Twin") ## Applicability diff --git a/twin/etc/twin.urm.puml b/twin/etc/twin.urm.puml new file mode 100644 index 000000000000..e48298f18262 --- /dev/null +++ b/twin/etc/twin.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.twin { + class App { + + App() + + main(args : String[]) {static} + - waiting() {static} + } + class BallItem { + - LOGGER : Logger {static} + - isSuspended : boolean + - twin : BallThread + + BallItem() + + click() + + doDraw() + + move() + + setTwin(twin : BallThread) + } + abstract class GameItem { + - LOGGER : Logger {static} + + GameItem() + + click() {abstract} + + doDraw() {abstract} + + draw() + } +} +BallItem --|> GameItem +@enduml \ No newline at end of file diff --git a/typeobjectpattern/README.md b/typeobjectpattern/README.md index 97aa64e0b603..4cc49e9b7ac4 100644 --- a/typeobjectpattern/README.md +++ b/typeobjectpattern/README.md @@ -1,6 +1,6 @@ --- layout: pattern -title: Type-object +title: Type-Object folder: typeobjectpattern permalink: /patterns/typeobjectpattern/ categories: Game Programming Patterns/Behavioral Patterns @@ -14,15 +14,18 @@ As explained in the book Game Programming Patterns by Robert Nystrom, type objec > Allowing flexible creation of new “classes” by creating a single class, each instance of which represents a different type of object +## Explanation +Say, we are working on a game which has a hero and many monsters which are going to attack the hero. These monsters have certain attributes like attack, points etc. and come in different 'breeds' like zombie or ogres. The obvious answer is to have a base Monster class which has some fields and methods, which may be overriden by subclasses like the Zombie or Ogre class. But as we continue to build the game, there may be more and more breeds of monsters added and certain attributes may need to be changed in the existing monsters too. The OOP solution of inheriting from the base class would not be an efficient method in this case. +Using the type-object pattern, instead of creating many classes inheriting from a base class, we have 1 class with a field which represents the 'type' of object. This makes the code cleaner and object instantiation also becomes as easy as parsing a json file with the object properties. + +## Class diagram +![alt text](./etc/typeobjectpattern.urm.png "Type-Object pattern class diagram") + ## Applicability This pattern can be used when: * We don’t know what types we will need up front. * We want to be able to modify or add new types without having to recompile or change code. * Only difference between the different 'types' of objects is the data, not the behaviour. - -## Explanation -Say, we are working on a game which has a hero and many monsters which are going to attack the hero. These monsters have certain attributes like attack, points etc. and come in different 'breeds' like zombie or ogres. The obvious answer is to have a base Monster class which has some fields and methods, which may be overriden by subclasses like the Zombie or Ogre class. But as we continue to build the game, there may be more and more breeds of monsters added and certain attributes may need to be changed in the existing monsters too. The OOP solution of inheriting from the base class would not be an efficient method in this case. -Using the type-object pattern, instead of creating many classes inheriting from a base class, we have 1 class with a field which represents the 'type' of object. This makes the code cleaner and object instantiation also becomes as easy as parsing a json file with the object properties. ## Credits * [Game Programming Patterns/Type Object](http://gameprogrammingpatterns.com/type-object.html) by Robert Nystrom diff --git a/typeobjectpattern/etc/typeobjectpattern.urm.png b/typeobjectpattern/etc/typeobjectpattern.urm.png new file mode 100644 index 000000000000..477dac5f2a2e Binary files /dev/null and b/typeobjectpattern/etc/typeobjectpattern.urm.png differ diff --git a/typeobjectpattern/etc/typeobjectpattern.urm.puml b/typeobjectpattern/etc/typeobjectpattern.urm.puml new file mode 100644 index 000000000000..80a95b0b4a9a --- /dev/null +++ b/typeobjectpattern/etc/typeobjectpattern.urm.puml @@ -0,0 +1,72 @@ +@startuml +package com.iluwatar.typeobject { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Candy { + ~ name : String + ~ parent : Candy + ~ parentName : String + - points : int + - type : Type + ~ Candy(name : String, parentName : String, type : Type, points : int) + ~ getPoints() : int + ~ getType() : Type + ~ setPoints(a : int) + } + ~enum Type { + + crushableCandy {static} + + rewardFruit {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} + } + class CandyGame { + - LOGGER : Logger {static} + ~ cells : Cell[][] + ~ pool : CellPool + ~ totalPoints : int + ~ CandyGame(num : int, pool : CellPool) + ~ adjacentCells(y : int, x : int) : List + ~ continueRound() : boolean + ~ handleChange(points : int) + ~ numOfSpaces(num : int) : String {static} + ~ printGameStatus() + ~ round(timeSoFar : int, totalTime : int) + } + class Cell { + ~ candy : Candy + ~ positionX : int + ~ positionY : int + ~ Cell() + ~ Cell(candy : Candy, positionX : int, positionY : int) + ~ crush(pool : CellPool, cellMatrix : Cell[][]) + ~ fillThisSpace(pool : CellPool, cellMatrix : Cell[][]) + ~ handleCrush(c : Cell, pool : CellPool, cellMatrix : Cell[][]) + ~ interact(c : Cell, pool : CellPool, cellMatrix : Cell[][]) : int + } + class CellPool { + - RANDOM : Random {static} + ~ pointer : int + ~ pool : List + ~ randomCode : Candy[] + ~ CellPool(num : int) + ~ addNewCell(c : Cell) + ~ assignRandomCandytypes() : Candy[] + ~ getNewCell() : Cell + } + class JsonParser { + ~ candies : Hashtable + ~ JsonParser() + ~ parse() + ~ setParentAndPoints() + } +} +Cell --> "-candy" Candy +Type ..+ Candy +Candy --> "-type" Type +Candy --> "-parent" Candy +CandyGame --> "-pool" CellPool +CellPool --> "-pool" Cell +@enduml \ No newline at end of file diff --git a/unit-of-work/README.md b/unit-of-work/README.md index b2fbde8ff3a6..a7c9112203c4 100644 --- a/unit-of-work/README.md +++ b/unit-of-work/README.md @@ -16,6 +16,7 @@ tags: When a business transaction is completed, all the these updates are sent as one big unit of work to be persisted in a database in one go so as to minimize database trips. +## Class diagram ![alt text](etc/unit-of-work.urm.png "unit-of-work") ## Applicability diff --git a/update-method/README.md b/update-method/README.md index 50cba44f34c5..73c5fe358c41 100644 --- a/update-method/README.md +++ b/update-method/README.md @@ -12,6 +12,14 @@ tags: ## Intent Update method pattern simulates a collection of independent objects by telling each to process one frame of behavior at a time. +## Explanation +The game world maintains a collection of objects. Each object implements an update method that simulates one frame of the object’s behavior. Each frame, the game updates every object in the collection. + +To learn more about how the game loop runs and when the update methods are invoked, please refer to Game Loop Pattern. + +## Class diagram +![alt text](./etc/update-method.urm.png "Update Method pattern class diagram") + ## Applicability If the Game Loop pattern is the best thing since sliced bread, then the Update Method pattern is its butter. A wide swath of games featuring live entities that the player interacts with use this pattern in some form or other. If the game has space marines, dragons, Martians, ghosts, or athletes, there’s a good chance it uses this pattern. @@ -25,11 +33,6 @@ Update methods work well when: - The objects need to be simulated over time. -## Explanation -The game world maintains a collection of objects. Each object implements an update method that simulates one frame of the object’s behavior. Each frame, the game updates every object in the collection. - -To learn more about how the game loop runs and when the update methods are invoked, please refer to Game Loop Pattern. - ## Credits -* [Game Programming Patterns - Update Method](http://gameprogrammingpatterns.com/update-method.html) \ No newline at end of file +* [Game Programming Patterns - Update Method](http://gameprogrammingpatterns.com/update-method.html) diff --git a/update-method/etc/update-method.urm.png b/update-method/etc/update-method.urm.png new file mode 100644 index 000000000000..ddc47b5fe145 Binary files /dev/null and b/update-method/etc/update-method.urm.png differ diff --git a/update-method/etc/update-method.urm.puml b/update-method/etc/update-method.urm.puml new file mode 100644 index 000000000000..53d2a6eb6a2e --- /dev/null +++ b/update-method/etc/update-method.urm.puml @@ -0,0 +1,51 @@ +@startuml +package com.iluwatar.updatemethod { + class App { + - GAME_RUNNING_TIME : int {static} + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + abstract class Entity { + # id : int + # logger : Logger + # position : int + + Entity(id : int) + + getPosition() : int + + setPosition(position : int) + + update() {abstract} + } + class Skeleton { + - PATROLLING_LEFT_BOUNDING : int {static} + - PATROLLING_RIGHT_BOUNDING : int {static} + # patrollingLeft : boolean + + Skeleton(id : int) + + Skeleton(id : int, postition : int) + + update() + } + class Statue { + # delay : int + # frames : int + + Statue(id : int) + + Statue(id : int, delay : int) + - shootLightning() + + update() + } + class World { + - LOGGER : Logger {static} + # entities : List + # isRunning : boolean + + World() + + addEntity(entity : Entity) + - gameLoop() + - processInput() + - render() + + run() + + stop() + - update() + } +} +World --> "-entities" Entity +Skeleton --|> Entity +Statue --|> Entity +@enduml \ No newline at end of file diff --git a/value-object/README.md b/value-object/README.md index 83223d8a2377..4fa384037de1 100644 --- a/value-object/README.md +++ b/value-object/README.md @@ -14,6 +14,7 @@ Provide objects which follow value semantics rather than reference semantics. This means value objects' equality are not based on identity. Two value objects are equal when they have the same value, not necessarily being the same object. +## Class diagram ![alt text](./etc/value-object.png "Value Object") ## Applicability diff --git a/value-object/etc/value-object.urm.puml b/value-object/etc/value-object.urm.puml new file mode 100644 index 000000000000..6149ead9ba36 --- /dev/null +++ b/value-object/etc/value-object.urm.puml @@ -0,0 +1,22 @@ +@startuml +package com.iluwatar.value.object { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class HeroStat { + - intelligence : int + - luck : int + - strength : int + - HeroStat(strength : int, intelligence : int, luck : int) + + equals(obj : Object) : boolean + + getIntelligence() : int + + getLuck() : int + + getStrength() : int + + hashCode() : int + + toString() : String + + valueOf(strength : int, intelligence : int, luck : int) : HeroStat {static} + } +} +@enduml \ No newline at end of file diff --git a/visitor/README.md b/visitor/README.md index bda789a185c2..f7a1dc17a87a 100644 --- a/visitor/README.md +++ b/visitor/README.md @@ -15,6 +15,7 @@ Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. +## Class diagram ![alt text](./etc/visitor_1.png "Visitor") ## Applicability diff --git a/visitor/etc/visitor.urm.puml b/visitor/etc/visitor.urm.puml new file mode 100644 index 000000000000..36a67b41514b --- /dev/null +++ b/visitor/etc/visitor.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.visitor { + class App { + + App() + + main(args : String[]) {static} + } + class Commander { + + Commander(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class CommanderVisitor { + - LOGGER : Logger {static} + + CommanderVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + class Sergeant { + + Sergeant(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class SergeantVisitor { + - LOGGER : Logger {static} + + SergeantVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + class Soldier { + + Soldier(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class SoldierVisitor { + - LOGGER : Logger {static} + + SoldierVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + abstract class Unit { + - children : Unit[] + + Unit(children : Unit[]) + + accept(visitor : UnitVisitor) + } + interface UnitVisitor { + + visitCommander(Commander) {abstract} + + visitSergeant(Sergeant) {abstract} + + visitSoldier(Soldier) {abstract} + } +} +Commander --|> Unit +CommanderVisitor ..|> UnitVisitor +Sergeant --|> Unit +SergeantVisitor ..|> UnitVisitor +Soldier --|> Unit +SoldierVisitor ..|> UnitVisitor +@enduml \ No newline at end of file