From 5ad99be224166a664706058f2845fa2a48cca6b5 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 25 Jan 2016 21:10:58 +0000 Subject: [PATCH 01/19] #354 Add maven model for feature toggle design pattern --- feature-toggle/pom.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 feature-toggle/pom.xml diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml new file mode 100644 index 00000000..71aa837d --- /dev/null +++ b/feature-toggle/pom.xml @@ -0,0 +1,15 @@ + + + + java-design-patterns + com.iluwatar + 1.10.0-SNAPSHOT + + 4.0.0 + + feature-toggle + + + \ No newline at end of file From cf10bd1d05da1254ccbf36a4d4673f8fe585f1e0 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 25 Jan 2016 21:11:26 +0000 Subject: [PATCH 02/19] #354 Add maven model for feature toggle design pattern --- feature-toggle/pom.xml | 24 ++++++++++++++++++++++++ pom.xml | 1 + 2 files changed, 25 insertions(+) diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 71aa837d..98c140f3 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -1,4 +1,28 @@ + + diff --git a/pom.xml b/pom.xml index c929f294..68208288 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,7 @@ publish-subscribe delegation event-driven-architecture + feature-toggle From d7526fc7c0ecff5dc2d56768ecde3f878f935fa5 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 25 Jan 2016 21:14:24 +0000 Subject: [PATCH 03/19] #354 Add maven model for feature toggle design pattern --- abstract-factory/index.md | 13 +- adapter/index.md | 13 +- async-method-invocation/index.md | 8 +- bridge/index.md | 12 +- builder/index.md | 10 +- business-delegate/index.md | 6 +- caching/index.md | 8 +- callback/index.md | 8 +- chain/index.md | 10 +- command/index.md | 15 +- composite/index.md | 10 +- dao/index.md | 8 +- decorator/etc/decorator.png | Bin 8109 -> 29945 bytes decorator/etc/decorator.ucls | 45 ++-- decorator/etc/decorator_1.png | Bin 24245 -> 0 bytes decorator/index.md | 13 +- .../main/java/com/iluwatar/decorator/App.java | 4 +- .../{SmartTroll.java => SmartHostile.java} | 12 +- ...rtTrollTest.java => SmartHostileTest.java} | 6 +- delegation/index.md | 13 +- dependency-injection/index.md | 6 +- double-checked-locking/index.md | 6 +- double-dispatch/index.md | 8 +- event-aggregator/index.md | 8 +- event-driven-architecture/index.md | 11 +- execute-around/index.md | 6 +- facade/index.md | 8 +- factory-method/index.md | 11 +- fluentinterface/index.md | 13 +- flux/index.md | 8 +- flyweight/index.md | 10 +- front-controller/index.md | 10 +- half-sync-half-async/index.md | 10 +- intercepting-filter/index.md | 10 +- interpreter/index.md | 8 +- iterator/index.md | 13 +- layers/index.md | 9 +- lazy-loading/index.md | 8 +- mediator/index.md | 8 +- memento/index.md | 13 +- message-channel/index.md | 12 +- model-view-controller/index.md | 8 +- model-view-presenter/index.md | 8 +- monostate/index.md | 10 +- multiton/index.md | 6 +- naked-objects/index.md | 10 +- null-object/index.md | 6 +- object-pool/index.md | 6 +- observer/index.md | 15 +- poison-pill/index.md | 8 +- pom.xml | 16 +- private-class-data/index.md | 6 +- producer-consumer/index.md | 6 +- property/index.md | 8 +- prototype/index.md | 10 +- proxy/index.md | 15 +- publish-subscribe/index.md | 10 +- reactor/index.md | 10 +- reader-writer-lock/etc/reader-writer-lock.png | Bin 0 -> 39623 bytes .../etc/reader-writer-lock.ucls | 86 +++++++ reader-writer-lock/index.md | 29 +++ reader-writer-lock/pom.xml | 24 ++ .../com/iluwatar/reader/writer/lock/App.java | 56 +++++ .../iluwatar/reader/writer/lock/Reader.java | 40 ++++ .../reader/writer/lock/ReaderWriterLock.java | 211 ++++++++++++++++++ .../iluwatar/reader/writer/lock/Writer.java | 40 ++++ .../iluwatar/reader/writer/lock/AppTest.java | 16 ++ .../writer/lock/ReaderAndWriterTest.java | 81 +++++++ .../reader/writer/lock/ReaderTest.java | 50 +++++ .../reader/writer/lock/StdOutTest.java | 53 +++++ .../reader/writer/lock/WriterTest.java | 50 +++++ repository/index.md | 11 +- .../index.md | 6 +- servant/index.md | 6 +- service-layer/index.md | 8 +- service-locator/index.md | 10 +- singleton/index.md | 12 +- specification/index.md | 8 +- state/index.md | 11 +- step-builder/index.md | 8 +- strategy/index.md | 11 +- template-method/index.md | 8 +- thread-pool/index.md | 6 +- tolerant-reader/index.md | 8 +- twin/index.md | 8 +- visitor/index.md | 10 +- 86 files changed, 1165 insertions(+), 270 deletions(-) delete mode 100644 decorator/etc/decorator_1.png rename decorator/src/main/java/com/iluwatar/decorator/{SmartTroll.java => SmartHostile.java} (56%) rename decorator/src/test/java/com/iluwatar/decorator/{SmartTrollTest.java => SmartHostileTest.java} (85%) create mode 100644 reader-writer-lock/etc/reader-writer-lock.png create mode 100644 reader-writer-lock/etc/reader-writer-lock.ucls create mode 100644 reader-writer-lock/index.md create mode 100644 reader-writer-lock/pom.xml create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java diff --git a/abstract-factory/index.md b/abstract-factory/index.md index f31ed2d0..485599b9 100644 --- a/abstract-factory/index.md +++ b/abstract-factory/index.md @@ -10,24 +10,27 @@ tags: - Difficulty-Intermediate --- -**Also known as:** Kit +## Also known as +Kit -**Intent:** Provide an interface for creating families of related or dependent +## Intent +Provide an interface for creating families of related or dependent objects without specifying their concrete classes. ![alt text](./etc/abstract-factory_1.png "Abstract Factory") -**Applicability:** Use the Abstract Factory pattern when +## Applicability +Use the Abstract Factory pattern when * a system should be independent of how its products are created, composed and represented * a system should be configured with one of multiple families of products * a family of related product objects is designed to be used together, and you need to enforce this constraint * you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations -**Real world examples:** +## Real world examples * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/adapter/index.md b/adapter/index.md index f77018e4..4263eb32 100644 --- a/adapter/index.md +++ b/adapter/index.md @@ -10,24 +10,27 @@ tags: - Difficulty-Beginner --- -**Also known as:** Wrapper +## Also known as +Wrapper -**Intent:** Convert the interface of a class into another interface the clients +## Intent +Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. ![alt text](./etc/adapter.png "Adapter") -**Applicability:** Use the Adapter pattern when +## Applicability +Use the Adapter pattern when * you want to use an existing class, and its interface does not match the one you need * you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces * you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class. -**Real world examples:** +## Real world examples * [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/async-method-invocation/index.md b/async-method-invocation/index.md index b9fadb88..93c0249d 100644 --- a/async-method-invocation/index.md +++ b/async-method-invocation/index.md @@ -10,21 +10,23 @@ tags: - Functional --- -**Intent:** Asynchronous method invocation is pattern where the calling thread +## Intent +Asynchronous method invocation is pattern where the calling thread 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. ![alt text](./etc/async-method-invocation.png "Async Method Invocation") -**Applicability:** Use async method invocation pattern when +## Applicability +Use async method invocation pattern when * you have multiple independent tasks that can run in parallel * you need to improve the performance of a group of sequential tasks * you have limited amount of processing capacity or long running tasks and the caller should not wait the tasks to be ready -**Real world examples:** +## Real world examples * [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java) * [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET) diff --git a/bridge/index.md b/bridge/index.md index 008325ce..49dad14e 100644 --- a/bridge/index.md +++ b/bridge/index.md @@ -10,15 +10,17 @@ tags: - Difficulty-Intermediate --- -**Also known as:** Handle/Body +## Also known as +Handle/Body -**Intent:** Decouple an abstraction from its implementation so that the two can +## Intent +Decouple an abstraction from its implementation so that the two can vary independently. - ![alt text](./etc/bridge.png "Bridge") -**Applicability:** Use the Bridge pattern when +## Applicability +Use the Bridge pattern when * you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time. * both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently @@ -26,6 +28,6 @@ vary independently. * you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies * you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/builder/index.md b/builder/index.md index 8f299d11..05056e7c 100644 --- a/builder/index.md +++ b/builder/index.md @@ -10,22 +10,24 @@ tags: - Difficulty-Intermediate --- -**Intent:** Separate the construction of a complex object from its +## Intent +Separate the construction of a complex object from its representation so that the same construction process can create different representations. ![alt text](./etc/builder_1.png "Builder") -**Applicability:** Use the Builder pattern when +## Applicability +Use the Builder pattern when * the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled * the construction process must allow different representations for the object that's constructed -**Real world examples:** +## Real world examples * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/business-delegate/index.md b/business-delegate/index.md index 0b28a5a2..7d548da1 100644 --- a/business-delegate/index.md +++ b/business-delegate/index.md @@ -9,14 +9,16 @@ tags: - Difficulty-Intermediate --- -**Intent:** The Business Delegate pattern adds an abstraction layer between +## Intent +The Business Delegate pattern adds an abstraction layer between 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. ![alt text](./etc/business-delegate.png "Business Delegate") -**Applicability:** Use the Business Delegate pattern when +## Applicability +Use the Business Delegate pattern when * you want loose coupling between presentation and business tiers * you want to orchestrate calls to multiple business services diff --git a/caching/index.md b/caching/index.md index d15fbb7d..2b89d055 100644 --- a/caching/index.md +++ b/caching/index.md @@ -10,17 +10,19 @@ tags: - Performance --- -**Intent:** To avoid expensive re-acquisition of resources by not releasing +## Intent +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. ![alt text](./etc/caching.png "Caching") -**Applicability:** Use the Caching pattern(s) when +## Applicability +Use the Caching pattern(s) when * Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead. -**Credits** +## Credits * [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) diff --git a/callback/index.md b/callback/index.md index a70da1ff..be73dc78 100644 --- a/callback/index.md +++ b/callback/index.md @@ -11,16 +11,18 @@ tags: - Idiom --- -**Intent:** Callback is a piece of executable code that is passed as an +## Intent +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. ![alt text](./etc/callback.png "Callback") -**Applicability:** Use the Callback pattern when +## Applicability +Use the Callback pattern when * when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. -**Real world examples:** +## Real world examples * [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. diff --git a/chain/index.md b/chain/index.md index 5432b51b..ef18f6f6 100644 --- a/chain/index.md +++ b/chain/index.md @@ -10,23 +10,25 @@ tags: - Difficulty-Intermediate --- -**Intent:** Avoid coupling the sender of a request to its receiver by giving +## Intent +Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ![alt text](./etc/chain_1.png "Chain of Responsibility") -**Applicability:** Use Chain of Responsibility when +## Applicability +Use Chain of Responsibility when * more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically * you want to issue a request to one of several objects without specifying the receiver explicitly * the set of objects that can handle a request should be specified dynamically -**Real world examples:** +## Real world examples * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/command/index.md b/command/index.md index 4052a8ae..2b931153 100644 --- a/command/index.md +++ b/command/index.md @@ -10,15 +10,18 @@ tags: - Difficulty-Intermediate --- -**Also known as:** Action, Transaction +## Also known as +Action, Transaction -**Intent:** Encapsulate a request as an object, thereby letting you +## Intent +Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. ![alt text](./etc/command.png "Command") -**Applicability:** Use the Command pattern when you want to +## Applicability +Use the Command pattern when you want to * parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks. * specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there @@ -26,16 +29,16 @@ support undoable operations. * support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation * structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions -**Typical Use Case:** +## Typical Use Case * to keep a history of requests * implement callback functionality * implement the undo functionality -**Real world examples:** +## Real world examples * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/composite/index.md b/composite/index.md index bf5a920a..8b980292 100644 --- a/composite/index.md +++ b/composite/index.md @@ -10,22 +10,24 @@ tags: - Difficulty-Intermediate --- -**Intent:** Compose objects into tree structures to represent part-whole +## Intent +Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. ![alt text](./etc/composite_1.png "Composite") -**Applicability:** Use the Composite pattern when +## Applicability +Use the Composite pattern when * you want to represent part-whole hierarchies of objects * you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly -**Real world examples:** +## Real world examples * [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) * [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/dao/index.md b/dao/index.md index 91f6dc77..785a1c36 100644 --- a/dao/index.md +++ b/dao/index.md @@ -9,16 +9,18 @@ tags: - Difficulty-Beginner --- -**Intent:** Object provides an abstract interface to some type of database or +## Intent +Object provides an abstract interface to some type of database or other persistence mechanism. ![alt text](./etc/dao.png "Data Access Object") -**Applicability:** Use the Data Access Object in any of the following situations +## Applicability +Use the Data Access Object in any of the following situations * when you want to consolidate how the data layer is accessed * when you want to avoid writing multiple data retrieval/persistence layers -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/decorator/etc/decorator.png b/decorator/etc/decorator.png index 1e4bfdac2b201b9d0a7277f4ebc531102a411327..47a87b20b33dd4964ad94d7309bd63eb7b828d70 100644 GIT binary patch literal 29945 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 literal 8109 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 G!T%5UTm=aL diff --git a/decorator/etc/decorator.ucls b/decorator/etc/decorator.ucls index 7adb8c3a..a5353d4e 100644 --- a/decorator/etc/decorator.ucls +++ b/decorator/etc/decorator.ucls @@ -1,18 +1,18 @@ - + - + - - + + @@ -21,29 +21,46 @@ - + - + + + + + + + + + + + + - - - - + + + + - - + + + + + + diff --git a/decorator/etc/decorator_1.png b/decorator/etc/decorator_1.png deleted file mode 100644 index 5a7afe2d1a2ccdc7c23f748e35df829385a13ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24245 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 diff --git a/decorator/index.md b/decorator/index.md index ca6c7bb5..5494ca94 100644 --- a/decorator/index.md +++ b/decorator/index.md @@ -10,20 +10,23 @@ tags: - Difficulty-Beginner --- -**Also known as:** Wrapper +## Also known as +Wrapper -**Intent:** Attach additional responsibilities to an object dynamically. +## Intent +Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. -![alt text](./etc/decorator_1.png "Decorator") +![alt text](./etc/decorator.png "Decorator") -**Applicability:** Use Decorator +## Applicability +Use Decorator * to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index d58d3b61..242e72d1 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -8,7 +8,7 @@ * runtime. *

* In this example we show how the simple {@link Troll} first attacks and then flees the battle. - * Then we decorate the {@link Troll} with a {@link SmartTroll} and perform the attack again. You + * Then we decorate the {@link Troll} with a {@link SmartHostile} and perform the attack again. You * can see how the behavior changes after the decoration. * */ @@ -30,7 +30,7 @@ public static void main(String[] args) { // change the behavior of the simple troll by adding a decorator System.out.println("\nA smart looking troll surprises you."); - Hostile smart = new SmartTroll(troll); + Hostile smart = new SmartHostile(troll); smart.attack(); smart.fleeBattle(); System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java similarity index 56% rename from decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java rename to decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java index 93927237..93f49468 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java @@ -1,34 +1,34 @@ package com.iluwatar.decorator; /** - * SmartTroll is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface + * SmartHostile is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface * are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile} * object. * */ -public class SmartTroll implements Hostile { +public class SmartHostile implements Hostile { private Hostile decorated; - public SmartTroll(Hostile decorated) { + public SmartHostile(Hostile decorated) { this.decorated = decorated; } @Override public void attack() { - System.out.println("The troll throws a rock at you!"); + System.out.println("It throws a rock at you!"); decorated.attack(); } @Override public int getAttackPower() { - // decorated troll power + 20 because it is smart + // decorated hostile's power + 20 because it is smart return decorated.getAttackPower() + 20; } @Override public void fleeBattle() { - System.out.println("The troll calls for help!"); + System.out.println("It calls for help!"); decorated.fleeBattle(); } } diff --git a/decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java similarity index 85% rename from decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java rename to decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java index faafc4a8..e5be32ea 100644 --- a/decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java +++ b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java @@ -11,15 +11,15 @@ * * @author Jeroen Meulemeester */ -public class SmartTrollTest { +public class SmartHostileTest { @Test - public void testSmartTroll() throws Exception { + public void testSmartHostile() throws Exception { // Create a normal troll first, but make sure we can spy on it later on. final Hostile simpleTroll = spy(new Troll()); // Now we want to decorate the troll to make it smarter ... - final Hostile smartTroll = new SmartTroll(simpleTroll); + final Hostile smartTroll = new SmartHostile(simpleTroll); assertEquals(30, smartTroll.getAttackPower()); verify(simpleTroll, times(1)).getAttackPower(); diff --git a/delegation/index.md b/delegation/index.md index 2064a5bf..e5c0c637 100644 --- a/delegation/index.md +++ b/delegation/index.md @@ -9,19 +9,22 @@ tags: - Difficulty-Beginner --- -**Also known as:** Proxy Pattern +## Also known as +Proxy Pattern -**Intent:** It is a technique where an object expresses certain behavior to the outside but in +## Intent +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. ![alt text](./etc/delegation.png "Delegate") -**Applicability:** Use the Delegate pattern in order to achieve the following +## Applicability +Use the Delegate pattern in order to achieve the following * Reduce the coupling of methods to their class * Components that behave identically, but realize that this situation can change in the future. -**Credits** +## Credits * [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) -* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) \ No newline at end of file +* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/dependency-injection/index.md b/dependency-injection/index.md index 4caa30c9..735f589b 100644 --- a/dependency-injection/index.md +++ b/dependency-injection/index.md @@ -9,7 +9,8 @@ tags: - Difficulty-Beginner --- -**Intent:** Dependency Injection is a software design pattern in which one or +## Intent +Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own @@ -18,7 +19,8 @@ inversion of control and single responsibility principles. ![alt text](./etc/dependency-injection.png "Dependency Injection") -**Applicability:** Use the Dependency Injection pattern when +## Applicability +Use the Dependency Injection pattern when * when you need to remove knowledge of concrete implementation from object * to enable unit testing of classes in isolation using mock objects or stubs diff --git a/double-checked-locking/index.md b/double-checked-locking/index.md index 05ec2006..da1fdd1a 100644 --- a/double-checked-locking/index.md +++ b/double-checked-locking/index.md @@ -10,14 +10,16 @@ tags: - Idiom --- -**Intent:** Reduce the overhead of acquiring a lock by first testing the +## Intent +Reduce the overhead of acquiring a lock by first testing the 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. ![alt text](./etc/double_checked_locking_1.png "Double Checked Locking") -**Applicability:** Use the Double Checked Locking pattern when +## Applicability +Use the Double Checked Locking pattern when * there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not. * there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method. diff --git a/double-dispatch/index.md b/double-dispatch/index.md index a660e0f8..ae87208a 100644 --- a/double-dispatch/index.md +++ b/double-dispatch/index.md @@ -10,15 +10,17 @@ tags: - Idiom --- -**Intent:** Double Dispatch pattern is a way to create maintainable dynamic +## Intent +Double Dispatch pattern is a way to create maintainable dynamic behavior based on receiver and parameter types. ![alt text](./etc/double-dispatch.png "Double Dispatch") -**Applicability:** Use the Double Dispatch pattern when +## Applicability +Use the Double Dispatch pattern when * the dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type. -**Real world examples:** +## Real world examples * [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html) diff --git a/event-aggregator/index.md b/event-aggregator/index.md index 05e94e0d..5462a2a5 100644 --- a/event-aggregator/index.md +++ b/event-aggregator/index.md @@ -9,7 +9,8 @@ tags: - Difficulty-Beginner --- -**Intent:** A system with lots of objects can lead to complexities when a +## Intent +A system with lots of objects can lead to complexities when a client wants to subscribe to events. The client has to find and register for each object individually, if each object has multiple events then each event requires a separate subscription. An Event Aggregator acts as a single source @@ -18,7 +19,8 @@ allowing clients to register with just the aggregator. ![alt text](./etc/classes.png "Event Aggregator") -**Applicability:** Use the Event Aggregator pattern when +## Applicability +Use the Event Aggregator pattern when * Event Aggregator is a good choice when you have lots of objects that are potential event sources. Rather than have the observer deal with registering @@ -26,6 +28,6 @@ allowing clients to register with just the aggregator. Aggregator. As well as simplifying registration, a Event Aggregator also simplifies the memory management issues in using observers. -**Credits:** +## Credits * [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html) diff --git a/event-driven-architecture/index.md b/event-driven-architecture/index.md index 6c2ae99c..7c786b76 100644 --- a/event-driven-architecture/index.md +++ b/event-driven-architecture/index.md @@ -3,25 +3,26 @@ title: Event Driven Architecture folder: event-driven-architecture permalink: /patterns/event-driven-architecture - -**Intent:** Send and notify state changes of your objects to other applications using an Event-driven Architecture. +## Intent +Send and notify state changes of your objects to other applications using an Event-driven Architecture. ![alt text](./etc/eda.png "Event Driven Architecture") -**Applicability:** Use an Event-driven architecture when +## Applicability +Use an Event-driven architecture when * you want to create a loosely coupled system * you want to build a more responsive system * you want a system that is easier to extend -**Real world examples:** +## Real world examples * SendGrid, an email API, sends events whenever an email is processed, delivered, opened etc... (https://sendgrid.com/docs/API_Reference/Webhooks/event.html) * Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events) * Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda) * MySQL runs triggers based on events such as inserts and update events happening on database tables. -**Credits:** +## Credits * [Event-driven architecture - Wikipedia](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html) diff --git a/execute-around/index.md b/execute-around/index.md index 8e62d5a8..ec543bdc 100644 --- a/execute-around/index.md +++ b/execute-around/index.md @@ -10,13 +10,15 @@ tags: - Idiom --- -**Intent:** Execute Around idiom frees the user from certain actions that +## Intent +Execute Around idiom frees the user from certain actions that 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. ![alt text](./etc/execute-around.png "Execute Around") -**Applicability:** Use the Execute Around idiom when +## Applicability +Use the Execute Around idiom when * you use an API that requires methods to be called in pairs such as open/close or allocate/deallocate. diff --git a/facade/index.md b/facade/index.md index 9ad9cb98..c416552c 100644 --- a/facade/index.md +++ b/facade/index.md @@ -10,17 +10,19 @@ tags: - Difficulty-Beginner --- -**Intent:** Provide a unified interface to a set of interfaces in a subsystem. +## Intent +Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. ![alt text](./etc/facade_1.png "Facade") -**Applicability:** Use the Facade pattern when +## Applicability +Use the Facade pattern when * you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade. * there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. * you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/factory-method/index.md b/factory-method/index.md index 3568a110..42237883 100644 --- a/factory-method/index.md +++ b/factory-method/index.md @@ -10,20 +10,23 @@ tags: - Gang Of Four --- -**Also known as:** Virtual Constructor +## Also known as +Virtual Constructor -**Intent:** Define an interface for creating an object, but let subclasses +## Intent +Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. ![alt text](./etc/factory-method_1.png "Factory Method") -**Applicability:** Use the Factory Method pattern when +## Applicability +Use the Factory Method pattern when * a class can't anticipate the class of objects it must create * a class wants its subclasses to specify the objects it creates * classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/fluentinterface/index.md b/fluentinterface/index.md index 0cabed59..767792da 100644 --- a/fluentinterface/index.md +++ b/fluentinterface/index.md @@ -10,9 +10,10 @@ tags: - Functional --- -**Intent:** A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. +## Intent +A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. -**Implementation:** +## Implementation A fluent interface can be implemented using any of @@ -22,13 +23,13 @@ A fluent interface can be implemented using any of ![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") - -**Applicability:** Use the Fluent Interface pattern when +## Applicability +Use the Fluent Interface pattern when * you provide an API that would benefit from a DSL-like usage * you have objects that are difficult to configure or use -**Real world examples:** +## Real world examples * [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html) * [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained) @@ -36,7 +37,7 @@ A fluent interface can be implemented using any of * [Mockito](http://mockito.org/) * [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial) -**Credits** +## Credits * [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) * [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) diff --git a/flux/index.md b/flux/index.md index c1ef0b4f..7ac312c4 100644 --- a/flux/index.md +++ b/flux/index.md @@ -9,17 +9,19 @@ tags: - Difficulty-Intermediate --- -**Intent:** Flux eschews MVC in favor of a unidirectional data flow. When a +## Intent +Flux eschews MVC in favor of a unidirectional data flow. When a 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. ![alt text](./etc/flux.png "Flux") -**Applicability:** Use the Flux pattern when +## Applicability +Use the Flux pattern when * you want to focus on creating explicit and understandable update paths for your application's data, which makes tracing changes during development simpler and makes bugs easier to track down and fix. -**Credits:** +## Credits * [Flux - Application architecture for building user interfaces](http://facebook.github.io/flux/) diff --git a/flyweight/index.md b/flyweight/index.md index 41a45f55..a98dced8 100644 --- a/flyweight/index.md +++ b/flyweight/index.md @@ -11,12 +11,14 @@ tags: - Performance --- -**Intent:** Use sharing to support large numbers of fine-grained objects +## Intent +Use sharing to support large numbers of fine-grained objects efficiently. ![alt text](./etc/flyweight_1.png "Flyweight") -**Applicability:** The Flyweight pattern's effectiveness depends heavily on how +## 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 true @@ -26,10 +28,10 @@ true * many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed * the application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. -**Real world examples:** +## Real world examples * [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/front-controller/index.md b/front-controller/index.md index 603bfef2..a462a08e 100644 --- a/front-controller/index.md +++ b/front-controller/index.md @@ -9,23 +9,25 @@ tags: - Difficulty-Intermediate --- -**Intent:** Introduce a common handler for all requests for a web site. This +## Intent +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. ![alt text](./etc/front-controller.png "Front Controller") -**Applicability:** Use the Front Controller pattern when +## Applicability +Use the Front Controller pattern when * you want to encapsulate common request handling functionality in single place * you want to implements dynamic request handling i.e. change routing without modifying code * make web server configuration portable, you only need to register the handler web server specific way -**Real world examples:** +## Real world examples * [Apache Struts](https://struts.apache.org/) -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) * [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/half-sync-half-async/index.md b/half-sync-half-async/index.md index 5eb93c05..8a091f81 100644 --- a/half-sync-half-async/index.md +++ b/half-sync-half-async/index.md @@ -9,13 +9,15 @@ tags: - Difficulty-Intermediate --- -**Intent:** The Half-Sync/Half-Async pattern decouples synchronous I/O from +## Intent +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. ![Half-Sync/Half-Async class diagram](./etc/half-sync-half-async.png) -**Applicability:** Use Half-Sync/Half-Async pattern when +## Applicability +Use Half-Sync/Half-Async pattern when * a system possesses following characteristics: * the system must perform tasks in response to external events that occur asynchronously, like hardware interrupts in OS @@ -23,13 +25,13 @@ degrading execution efficiency. * the higher level tasks in the system can be simplified significantly if I/O is performed synchronously. * one or more tasks in a system must run in a single thread of control, while other tasks may benefit from multi-threading. -**Real world examples:** +## Real world examples * [BSD Unix networking subsystem](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) * [Real Time CORBA](http://www.omg.org/news/meetings/workshops/presentations/realtime2001/4-3_Pyarali_thread-pool.pdf) * [Android AsyncTask framework](http://developer.android.com/reference/android/os/AsyncTask.html) -**Credits:** +## Credits * [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/intercepting-filter/index.md b/intercepting-filter/index.md index 3aa70ba0..327f091b 100644 --- a/intercepting-filter/index.md +++ b/intercepting-filter/index.md @@ -9,22 +9,24 @@ tags: - Difficulty-Intermediate --- -**Intent:** Provide pluggable filters to conduct necessary pre-processing and +## 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") -**Applicability:** Use the Intercepting Filter pattern when +## Applicability +Use the Intercepting Filter pattern when * a system uses pre-processing or post-processing requests * a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers * you want a modular approach to configuring pre-processing and post-processing schemes -**Real world examples:** +## Real world examples * [Struts 2 - Interceptors](https://struts.apache.org/docs/interceptors.html) -**Credits:** +## Credits * [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) * [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/interpreter/index.md b/interpreter/index.md index dd6a7eda..87c1c47f 100644 --- a/interpreter/index.md +++ b/interpreter/index.md @@ -10,19 +10,21 @@ tags: - Difficulty-Intermediate --- -**Intent:** Given a language, define a representation for its grammar along +## Intent +Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. ![alt text](./etc/interpreter_1.png "Interpreter") -**Applicability:** Use the Interpreter pattern when there is a language to +## Applicability +Use the Interpreter pattern when there is a language to interpret, and you can represent statements in the language as abstract syntax trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/index.md b/iterator/index.md index fe6c1fe3..d6be7758 100644 --- a/iterator/index.md +++ b/iterator/index.md @@ -10,23 +10,26 @@ tags: - Gang Of Four --- -**Also known as:** Cursor +## Also known as +Cursor -**Intent:** Provide a way to access the elements of an aggregate object +## Intent +Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. ![alt text](./etc/iterator_1.png "Iterator") -**Applicability:** Use the Iterator pattern +## Applicability +Use the Iterator pattern * to access an aggregate object's contents without exposing its internal representation * to support multiple traversals of aggregate objects * to provide a uniform interface for traversing different aggregate structures -**Real world examples:** +## Real world examples * [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/layers/index.md b/layers/index.md index 5f746d4d..8e8eda36 100644 --- a/layers/index.md +++ b/layers/index.md @@ -10,18 +10,19 @@ tags: - Spring --- -**Intent:** Layers is an architectural style where software responsibilities are +## Intent +Layers is an architectural style where software responsibilities are divided among the different layers of the application. ![alt text](./etc/layers.png "Layers") -**Applicability:** Use the Layers architecture when +## Applicability +Use the Layers architecture when * you want clearly divide software responsibilities into differents 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:** +## Credits * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) - diff --git a/lazy-loading/index.md b/lazy-loading/index.md index 6f2501b7..8a06700d 100644 --- a/lazy-loading/index.md +++ b/lazy-loading/index.md @@ -11,17 +11,19 @@ tags: - Performance --- -**Intent:** Lazy loading is a design pattern commonly used to defer +## Intent +Lazy loading is a design pattern commonly used to defer 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. ![alt text](./etc/lazy-loading.png "Lazy Loading") -**Applicability:** Use the Lazy Loading idiom when +## Applicability +Use the Lazy Loading idiom when * eager loading is expensive or the object to be loaded might not be needed at all -**Real world examples:** +## Real world examples * JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY diff --git a/mediator/index.md b/mediator/index.md index cb4ce7fb..c7a0478c 100644 --- a/mediator/index.md +++ b/mediator/index.md @@ -10,18 +10,20 @@ tags: - Difficulty-Intermediate --- -**Intent:** Define an object that encapsulates how a set of objects interact. +## Intent +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. ![alt text](./etc/mediator_1.png "Mediator") -**Applicability:** Use the Mediator pattern when +## Applicability +Use the Mediator pattern when * a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/memento/index.md b/memento/index.md index 7322aef5..463b5fec 100644 --- a/memento/index.md +++ b/memento/index.md @@ -10,22 +10,25 @@ tags: - Difficulty-Intermediate --- -**Also known as:** Token +## Also known as +Token -**Intent:** Without violating encapsulation, capture and externalize an +## Intent +Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. ![alt text](./etc/memento.png "Memento") -**Applicability:** Use the Memento pattern when +## Applicability +Use the Memento pattern when * a snapshot of an object's state must be saved so that it can be restored to that state later, and * a direct interface to obtaining the state would expose implementation details and break the object's encapsulation -**Real world examples:** +## Real world examples * [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/message-channel/index.md b/message-channel/index.md index 3b742a98..8aebd015 100644 --- a/message-channel/index.md +++ b/message-channel/index.md @@ -7,18 +7,20 @@ categories: Integration tags: - Java - EIP - - Camel + - Apache Camel™ --- -**Intent:** When two applications communicate using a messaging system they do it by using logical addresses +## Intent +When two applications communicate using a messaging system they do it by using logical addresses of the system, so called Message Channels. ![alt text](./etc/message-channel.png "Message Channel") -**Applicability:** Use the Message Channel pattern when +## Applicability +Use the Message Channel pattern when * two or more applications need to communicate using a messaging system -**Real world examples:** +## Real world examples -* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html) +* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html) \ No newline at end of file diff --git a/model-view-controller/index.md b/model-view-controller/index.md index 3d1d3e92..bc96f7ab 100644 --- a/model-view-controller/index.md +++ b/model-view-controller/index.md @@ -9,18 +9,20 @@ tags: - Difficulty-Intermediate --- -**Intent:** Separate the user interface into three interconnected components: +## Intent +Separate the user interface into three interconnected components: 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. ![alt text](./etc/model-view-controller.png "Model-View-Controller") -**Applicability:** Use the Model-View-Controller pattern when +## Applicability +Use the Model-View-Controller pattern when * you want to clearly separate the domain data from its user interface representation -**Credits:** +## Credits * [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/model-view-presenter/index.md b/model-view-presenter/index.md index a65a9a65..a3b921ce 100644 --- a/model-view-presenter/index.md +++ b/model-view-presenter/index.md @@ -9,17 +9,19 @@ tags: - Difficulty-Intermediate --- -**Intent:** Apply a "Separation of Concerns" principle in a way that allows +## Intent +Apply a "Separation of Concerns" principle in a way that allows developers to build and test user interfaces. ![alt text](./etc/model-view-presenter_1.png "Model-View-Presenter") -**Applicability:** Use the Model-View-Presenter in any of the following +## Applicability +Use the Model-View-Presenter in any of the following situations * when you want to improve the "Separation of Concerns" principle in presentation logic * when a user interface development and testing is necessary. -**Real world examples:** +## Real world examples * [MVP4J](https://github.com/amineoualialami/mvp4j) diff --git a/monostate/index.md b/monostate/index.md index 4176af3c..415d13f0 100644 --- a/monostate/index.md +++ b/monostate/index.md @@ -9,22 +9,24 @@ tags: - Difficulty-Beginner --- -**Intent:** Enforces a behaviour like sharing the same state amongst all instances. +## Intent +Enforces a behaviour like sharing the same state amongst all instances. ![alt text](./etc/monostate.png "MonoState") -**Applicability:** Use the Monostate pattern when +## Applicability +Use the Monostate pattern when * The same state must be shared across all instances of a class. * Typically this pattern might be used everywhere a Singleton might be used. Singleton usage however is not transparent, Monostate usage is. * Monostate has one major advantage over singleton. The subclasses might decorate the shared state as they wish and hence can provide dynamically different behaviour than the base class. -**Typical Use Case:** +## Typical Use Case * the logging class * managing a connection to a database * file manager -**Real world examples:** +## Real world examples Yet to see this. diff --git a/multiton/index.md b/multiton/index.md index 6491de44..68fb6bbc 100644 --- a/multiton/index.md +++ b/multiton/index.md @@ -9,11 +9,13 @@ tags: - Difficulty-Beginner --- -**Intent:** Ensure a class only has limited number of instances, and provide a +## Intent +Ensure a class only has limited number of instances, and provide a global point of access to them. ![alt text](./etc/multiton.png "Multiton") -**Applicability:** Use the Multiton pattern when +## Applicability +Use the Multiton pattern when * there must be specific number of instances of a class, and they must be accessible to clients from a well-known access point diff --git a/naked-objects/index.md b/naked-objects/index.md index e3dd09a8..eb1c083b 100644 --- a/naked-objects/index.md +++ b/naked-objects/index.md @@ -9,22 +9,24 @@ tags: - Difficulty-Expert --- -**Intent:** The Naked Objects architectural pattern is well suited for rapid +## Intent +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. ![alt text](./etc/naked-objects.png "Naked Objects") -**Applicability:** Use the Naked Objects pattern when +## Applicability +Use the Naked Objects pattern when * you are prototyping and need fast development cycle * an autogenerated user interface is good enough * you want to automatically publish the domain as REST services -**Real world examples:** +## Real world examples * [Apache Isis](https://isis.apache.org/) -**Credits:** +## Credits * [Richard Pawson - Naked Objects](http://downloads.nakedobjects.net/resources/Pawson%20thesis.pdf) diff --git a/null-object/index.md b/null-object/index.md index 6b659b5c..b5fb279d 100644 --- a/null-object/index.md +++ b/null-object/index.md @@ -9,7 +9,8 @@ tags: - Difficulty-Beginner --- -**Intent:** In most object-oriented languages, such as Java or C#, references +## Intent +In most object-oriented languages, such as Java or C#, references may be null. These references need to be checked to ensure they are not null before invoking any methods, because methods typically cannot be invoked on null references. Instead of using a null reference to convey absence of an @@ -20,6 +21,7 @@ Object is very predictable and has no side effects: it does nothing. ![alt text](./etc/null-object.png "Null Object") -**Applicability:** Use the Null Object pattern when +## Applicability +Use the Null Object pattern when * you want to avoid explicit null checks and keep the algorithm elegant and easy to read. diff --git a/object-pool/index.md b/object-pool/index.md index 0d37041c..cf36d988 100644 --- a/object-pool/index.md +++ b/object-pool/index.md @@ -10,14 +10,16 @@ tags: - Performance --- -**Intent:** When objects are expensive to create and they are needed only for +## Intent +When objects are expensive to create and they are needed only for 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. ![alt text](./etc/object-pool.png "Object Pool") -**Applicability:** Use the Object Pool pattern when +## Applicability +Use the Object Pool pattern when * the objects are expensive to create (allocation cost) * you need a large number of short-lived objects (memory fragmentation) diff --git a/observer/index.md b/observer/index.md index ea1667ef..7e83e899 100644 --- a/observer/index.md +++ b/observer/index.md @@ -10,28 +10,31 @@ tags: - Gang Of Four --- -**Also known as:** Dependents, Publish-Subscribe +## Also known as +Dependents, Publish-Subscribe -**Intent:** Define a one-to-many dependency between objects so that when one +## Intent +Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. ![alt text](./etc/observer_1.png "Observer") -**Applicability:** Use the Observer pattern in any of the following situations +## Applicability +Use the Observer pattern in any of the following situations * when an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently * when a change to one object requires changing others, and you don't know how many objects need to be changed * when an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled -**Typical Use Case:** +## Typical Use Case * changing in one object leads to a change in other objects -**Real world examples:** +## Real world examples * [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/poison-pill/index.md b/poison-pill/index.md index 90dff3c3..eb37c5ad 100644 --- a/poison-pill/index.md +++ b/poison-pill/index.md @@ -9,15 +9,17 @@ tags: - Difficulty-Intermediate --- -**Intent:** Poison Pill is known predefined data item that allows to provide +## Intent +Poison Pill is known predefined data item that allows to provide graceful shutdown for separate distributed consumption process. ![alt text](./etc/poison-pill.png "Poison Pill") -**Applicability:** Use the Poison Pill idiom when +## Applicability +Use the Poison Pill idiom when * need to send signal from one thread/process to another to terminate -**Real world examples:** +## Real world examples * [akka.actor.PoisonPill](http://doc.akka.io/docs/akka/2.1.4/java/untyped-actors.html) diff --git a/pom.xml b/pom.xml index 68208288..772ef721 100644 --- a/pom.xml +++ b/pom.xml @@ -11,18 +11,19 @@ UTF-8 5.0.1.Final - 4.1.7.RELEASE - 1.9.0.RELEASE - 1.4.188 + 4.2.4.RELEASE + 1.9.2.RELEASE + 1.4.190 4.12 3.0 4.0.0 0.7.2.201409121644 1.4 - 2.15.3 + 2.16.1 1.2.17 - 18.0 - 1.14.0 + 19.0 + 1.15.1 + 1.10.19 abstract-factory @@ -61,6 +62,7 @@ intercepting-filter producer-consumer poison-pill + reader-writer-lock lazy-loading service-layer specification @@ -145,7 +147,7 @@ org.mockito mockito-core - 1.10.19 + ${mockito.version} test diff --git a/private-class-data/index.md b/private-class-data/index.md index 4c09df0d..981208fa 100644 --- a/private-class-data/index.md +++ b/private-class-data/index.md @@ -10,12 +10,14 @@ tags: - Idiom --- -**Intent:** Private Class Data design pattern seeks to reduce exposure of +## Intent +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. ![alt text](./etc/private-class-data.png "Private Class Data") -**Applicability:** Use the Private Class Data pattern when +## Applicability +Use the Private Class Data pattern when * you want to prevent write access to class data members diff --git a/producer-consumer/index.md b/producer-consumer/index.md index 124c98a2..c666ab12 100644 --- a/producer-consumer/index.md +++ b/producer-consumer/index.md @@ -10,13 +10,15 @@ tags: - I/O --- -**Intent:** Producer Consumer Design pattern is a classic concurrency pattern which reduces +## Intent +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. ![alt text](./etc/producer-consumer.png "Producer Consumer") -**Applicability:** Use the Producer Consumer idiom when +## Applicability +Use the Producer Consumer idiom when * decouple system by separate work in two process produce and consume. * addresses the issue of different timing require to produce work or consuming work diff --git a/property/index.md b/property/index.md index 9949239c..0ac5c7a6 100644 --- a/property/index.md +++ b/property/index.md @@ -9,15 +9,17 @@ tags: - Difficulty-Beginner --- -**Intent:** Create hierarchy of objects and new objects using already existing +## Intent +Create hierarchy of objects and new objects using already existing objects as parents. ![alt text](./etc/property.png "Property") -**Applicability:** Use the Property pattern when +## Applicability +Use the Property pattern when * when you like to have objects with dynamic set of fields and prototype inheritance -**Real world examples:** +## Real world examples * [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) prototype inheritance diff --git a/prototype/index.md b/prototype/index.md index 457c2f46..632daca9 100644 --- a/prototype/index.md +++ b/prototype/index.md @@ -10,21 +10,23 @@ tags: - Difficulty-Beginner --- -**Intent:** Specify the kinds of objects to create using a prototypical +## Intent +Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. ![alt text](./etc/prototype_1.png "Prototype") -**Applicability:** Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and +## Applicability +Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and * when the classes to instantiate are specified at run-time, for example, by dynamic loading; or * to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or * when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state -**Real world examples:** +## Real world examples * [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/proxy/index.md b/proxy/index.md index 2f16527a..a3e03708 100644 --- a/proxy/index.md +++ b/proxy/index.md @@ -10,14 +10,17 @@ tags: - Difficulty-Beginner --- -**Also known as:** Surrogate +## Also known as +Surrogate -**Intent:** Provide a surrogate or placeholder for another object to control +## Intent +Provide a surrogate or placeholder for another object to control access to it. ![alt text](./etc/proxy_1.png "Proxy") -**Applicability:** Proxy is applicable whenever there is a need for a more +## Applicability +Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here are several common situations in which the Proxy pattern is applicable @@ -25,7 +28,7 @@ are several common situations in which the Proxy pattern is applicable * a virtual proxy creates expensive objects on demand. * a protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights. -**Typical Use Case:** +## Typical Use Case * control access to another object * lazy initialization @@ -33,11 +36,11 @@ are several common situations in which the Proxy pattern is applicable * facilitate network connection * to count references to an object -**Real world examples:** +## Real world examples * [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) * [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/publish-subscribe/index.md b/publish-subscribe/index.md index cd1ad397..bd83e37f 100644 --- a/publish-subscribe/index.md +++ b/publish-subscribe/index.md @@ -7,13 +7,15 @@ categories: Integration tags: - Java - EIP - - Camel + - Apache Camel™ --- -**Intent:** Broadcast messages from sender to all the interested receivers. +## Intent +Broadcast messages from sender to all the interested receivers. ![alt text](./etc/publish-subscribe.png "Publish Subscribe Channel") -**Applicability:** Use the Publish Subscribe Channel pattern when +## Applicability +Use the Publish Subscribe Channel pattern when -* two or more applications need to communicate using a messaging system for broadcasts. +* two or more applications need to communicate using a messaging system for broadcasts. \ No newline at end of file diff --git a/reactor/index.md b/reactor/index.md index 9071413d..b9ba9894 100644 --- a/reactor/index.md +++ b/reactor/index.md @@ -10,21 +10,23 @@ tags: - I/O --- -**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. +## 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. ![Reactor](./etc/reactor.png "Reactor") -**Applicability:** Use Reactor pattern when +## Applicability +Use Reactor pattern when * a server application needs to handle concurrent service requests from multiple clients. * a server application needs to be available for receiving requests from new clients even when handling older client requests. * a server must maximize throughput, minimize latency and use CPU efficiently without blocking. -**Real world examples:** +## Real world examples * [Spring Reactor](http://projectreactor.io/) -**Credits** +## Credits * [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf) * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/reader-writer-lock/etc/reader-writer-lock.png b/reader-writer-lock/etc/reader-writer-lock.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b6005309263d2dc8baf7505fd68091a580011b GIT binary patch literal 39623 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 literal 0 HcmV?d00001 diff --git a/reader-writer-lock/etc/reader-writer-lock.ucls b/reader-writer-lock/etc/reader-writer-lock.ucls new file mode 100644 index 00000000..920904e7 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.ucls @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reader-writer-lock/index.md b/reader-writer-lock/index.md new file mode 100644 index 00000000..91d16892 --- /dev/null +++ b/reader-writer-lock/index.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Reader Writer Lock +folder: reader-writer-lock +permalink: /patterns/reader-writer-lock/ +categories: Concurrent +tags: +- Java +--- + +**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. + +![alt text](./etc/reader-writer-lock.png "Reader writer lock") + +**Applicability:** + +Application need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations. + +**Real world examples:** + +* [Java Reader Writer Lock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html) + +**Credits** + +* [Readers–writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) + +* [Readers–writers_problem](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem) \ No newline at end of file diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml new file mode 100644 index 00000000..14b17011 --- /dev/null +++ b/reader-writer-lock/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.10.0-SNAPSHOT + + reader-writer-lock + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java new file mode 100644 index 00000000..b11b11be --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java @@ -0,0 +1,56 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +/** + * + * In a multiple thread applications, the threads may try to synchronize the shared resources + * regardless of read or write operation. It leads to a low performance especially in a "read more + * write less" system as indeed the read operations are thread-safe to another read operation. + *

+ * Reader writer lock is a synchronization primitive that try to resolve this problem. This pattern + * allows concurrent access for read-only operations, while write operations require exclusive + * access. This means that multiple threads can read the data in parallel but an exclusive lock is + * needed for writing or modifying data. When a writer is writing the data, all other writers or + * readers will be blocked until the writer is finished writing. + * + *

+ * This example use two mutex to demonstrate the concurrent access of multiple readers and writers. + * + * + * @author hongshuwei@gmail.com + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + ExecutorService executeService = Executors.newFixedThreadPool(10); + ReaderWriterLock lock = new ReaderWriterLock(); + + // Start 5 readers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock()))); + + // Start 5 writers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock()))); + // In the system console, it can see that the read operations are executed concurrently while + // write operations are exclusive. + executeService.shutdown(); + try { + executeService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java new file mode 100644 index 00000000..21452808 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java @@ -0,0 +1,40 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Reader class, read when it acquired the read lock + */ +public class Reader implements Runnable { + + private Lock readLock; + + private String name; + + public Reader(String name, Lock readLock) { + this.name = name; + this.readLock = readLock; + } + + @Override + public void run() { + readLock.lock(); + try { + read(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + readLock.unlock(); + } + } + + /** + * Simulate the read operation + * + */ + public void read() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java new file mode 100644 index 00000000..b7edd149 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java @@ -0,0 +1,211 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * Class responsible for control the access for reader or writer + * + * Allows multiple readers to hold the lock at same time, but if any writer holds the lock then + * readers wait. If reader holds the lock then writer waits. This lock is not fair. + */ +public class ReaderWriterLock implements ReadWriteLock { + + + private Object readerMutex = new Object(); + + private int currentReaderCount = 0; + + /** + * Global mutex is used to indicate that whether reader or writer gets the lock in the moment. + *

+ * 1. When it contains the reference of {@link readerLock}, it means that the lock is acquired by + * the reader, another reader can also do the read operation concurrently.
+ * 2. When it contains the reference of reference of {@link writerLock}, it means that the lock is + * acquired by the writer exclusively, no more reader or writer can get the lock. + *

+ * This is the most important field in this class to control the access for reader/writer. + */ + private Set globalMutex = new HashSet<>(); + + private ReadLock readerLock = new ReadLock(); + private WriteLock writerLock = new WriteLock(); + + @Override + public Lock readLock() { + return readerLock; + } + + @Override + public Lock writeLock() { + return writerLock; + } + + /** + * return true when globalMutex hold the reference of writerLock + */ + private boolean doesWriterOwnThisLock() { + return globalMutex.contains(writerLock); + } + + /** + * return true when globalMutex hold the reference of readerLock + */ + private boolean doesReaderOwnThisLock() { + return globalMutex.contains(readerLock); + } + + /** + * Nobody get the lock when globalMutex contains nothing + * + */ + private boolean isLockFree() { + return globalMutex.isEmpty(); + } + + private void waitUninterruptibly(Object o) { + try { + o.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Reader Lock, can be access for more than one reader concurrently if no writer get the lock + */ + private class ReadLock implements Lock { + + @Override + public void lock() { + + synchronized (readerMutex) { + + currentReaderCount++; + if (currentReaderCount == 1) { + // Try to get the globalMutex lock for the first reader + synchronized (globalMutex) { + while (true) { + // If the no one get the lock or the lock is locked by reader, just set the reference + // to the globalMutex to indicate that the lock is locked by Reader. + if (isLockFree() || doesReaderOwnThisLock()) { + globalMutex.add(this); + break; + } else { + // If lock is acquired by the write, let the thread wait until the writer release + // the lock + waitUninterruptibly(globalMutex); + } + } + } + + } + } + } + + @Override + public void unlock() { + + synchronized (readerMutex) { + currentReaderCount--; + // Release the lock only when it is the last reader, it is ensure that the lock is released + // when all reader is completely. + if (currentReaderCount == 0) { + synchronized (globalMutex) { + // Notify the waiter, mostly the writer + globalMutex.remove(this); + globalMutex.notifyAll(); + } + } + } + + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + } + + /** + * Writer Lock, can only be accessed by one writer concurrently + */ + private class WriteLock implements Lock { + + @Override + public void lock() { + + synchronized (globalMutex) { + + while (true) { + // When there is no one acquired the lock, just put the writeLock reference to the + // globalMutex to indicate that the lock is acquired by one writer. + // It is ensure that writer can only get the lock when no reader/writer acquired the lock. + if (isLockFree()) { + globalMutex.add(this); + break; + } else if (doesWriterOwnThisLock()) { + // Wait when other writer get the lock + waitUninterruptibly(globalMutex); + } else if (doesReaderOwnThisLock()) { + // Wait when other reader get the lock + waitUninterruptibly(globalMutex); + } else { + throw new AssertionError("it should never reach here"); + } + } + } + } + + @Override + public void unlock() { + + synchronized (globalMutex) { + globalMutex.remove(this); + // Notify the waiter, other writer or reader + globalMutex.notifyAll(); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java new file mode 100644 index 00000000..ae7b1708 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java @@ -0,0 +1,40 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Writer class, write when it acquired the write lock + */ +public class Writer implements Runnable { + + private Lock writeLock = null; + + private String name; + + public Writer(String name, Lock writeLock) { + this.name = name; + this.writeLock = writeLock; + } + + + @Override + public void run() { + writeLock.lock(); + try { + write(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + writeLock.unlock(); + } + } + + /** + * Simulate the write operation + */ + public void write() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java new file mode 100644 index 00000000..5dd6feaa --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java @@ -0,0 +1,16 @@ +package com.iluwatar.reader.writer.lock; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java new file mode 100644 index 00000000..e29f5788 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -0,0 +1,81 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderAndWriterTest extends StdOutTest { + + + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testReadAndWrite() throws Exception { + + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + ExecutorService executeService = Executors.newFixedThreadPool(2); + executeService.submit(reader1); + // Let reader1 execute first + Thread.sleep(150); + executeService.submit(writer1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + } + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testWriteAndRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + executeService.submit(writer1); + // Let writer1 execute first + Thread.sleep(150); + executeService.submit(reader1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + } +} + diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java new file mode 100644 index 00000000..e76fe29c --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -0,0 +1,50 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderTest extends StdOutTest { + + /** + * Verify that multiple readers can get the read lock concurrently + */ + @Test + public void testRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); + + executeService.submit(reader1); + Thread.sleep(150); + executeService.submit(reader2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + // Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads + // can be performed in the same time. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 2 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 2 finish"); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java new file mode 100644 index 00000000..762574b6 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java @@ -0,0 +1,53 @@ +package com.iluwatar.reader.writer.lock; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java new file mode 100644 index 00000000..ed37bf3e --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -0,0 +1,50 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class WriterTest extends StdOutTest { + + /** + * Verify that multiple writers will get the lock in order. + */ + @Test + public void testWrite() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + Writer writer2 = spy(new Writer("Writer 2", lock.writeLock())); + + executeService.submit(writer1); + // Let write1 execute first + Thread.sleep(150); + executeService.submit(writer2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + // Write operation will hold the write lock 250 milliseconds, so here we verify that when two + // writer execute concurrently, the second writer can only writes only when the first one is + // finished. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 2 begin"); + inOrder.verify(getStdOutMock()).println("Writer 2 finish"); + } +} diff --git a/repository/index.md b/repository/index.md index 697c708f..67b3ea44 100644 --- a/repository/index.md +++ b/repository/index.md @@ -10,7 +10,8 @@ tags: - Spring --- -**Intent:** Repository layer is added between the domain and data mapping +## Intent +Repository layer is added between the domain and data mapping layers to isolate domain objects from details of the database access code and 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 @@ -18,19 +19,19 @@ querying is utilized. ![alt text](./etc/repository.png "Repository") -**Applicability:** Use the Repository pattern when +## Applicability +Use the Repository pattern when * the number of domain objects is large * you want to avoid duplication of query code * you want to keep the database querying code in single place * you have multiple data sources -**Real world examples:** +## Real world examples * [Spring Data](http://projects.spring.io/spring-data/) -**Credits:** +## Credits * [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/) - diff --git a/resource-acquisition-is-initialization/index.md b/resource-acquisition-is-initialization/index.md index e808783f..821f220d 100644 --- a/resource-acquisition-is-initialization/index.md +++ b/resource-acquisition-is-initialization/index.md @@ -10,10 +10,12 @@ tags: - Idiom --- -**Intent:** Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. +## Intent +Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. ![alt text](./etc/resource-acquisition-is-initialization.png "Resource Acquisition Is Initialization") -**Applicability:** Use the Resource Acquisition Is Initialization pattern when +## Applicability +Use the Resource Acquisition Is Initialization pattern when * you have resources that must be closed in every condition diff --git a/servant/index.md b/servant/index.md index 9cf20a53..895b8750 100644 --- a/servant/index.md +++ b/servant/index.md @@ -9,12 +9,14 @@ tags: - Difficulty-Beginner --- -**Intent:** Servant is used for providing some behavior to a group of classes. +## Intent +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. ![alt text](./etc/servant-pattern.png "Servant") -**Applicability:** Use the Servant pattern when +## Applicability +Use the Servant pattern when * when we want some objects to perform a common action and don't want to define this action as a method in every class. diff --git a/service-layer/index.md b/service-layer/index.md index 68f4f613..9b685d4e 100644 --- a/service-layer/index.md +++ b/service-layer/index.md @@ -9,7 +9,8 @@ tags: - Difficulty-Intermediate --- -**Intent:** Service Layer is an abstraction over domain logic. Typically +## Intent +Service Layer is an abstraction over domain logic. Typically applications require multiple kinds of interfaces to the data they store and logic they implement: data loaders, user interfaces, integration gateways, and others. Despite their different purposes, these interfaces often need common @@ -18,12 +19,13 @@ its business logic. The Service Layer fulfills this role. ![alt text](./etc/service-layer.png "Service Layer") -**Applicability:** Use the Service Layer pattern when +## Applicability +Use the Service Layer pattern when * you want to encapsulate domain logic under API * you need to implement multiple interfaces with common logic and data -**Credits:** +## Credits * [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html) * [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/service-locator/index.md b/service-locator/index.md index 7357a0ac..af4d8c3a 100644 --- a/service-locator/index.md +++ b/service-locator/index.md @@ -10,12 +10,14 @@ tags: - Performance --- -**Intent:** Encapsulate the processes involved in obtaining a service with a +## Intent +Encapsulate the processes involved in obtaining a service with a strong abstraction layer. ![alt text](./etc/service-locator.png "Service Locator") -**Applicability:** The service locator pattern is applicable whenever we want +## Applicability +The service locator pattern is applicable whenever we want to locate/fetch various services using JNDI which, typically, is a redundant and expensive lookup. The service Locator pattern addresses this expensive lookup by making use of caching techniques ie. for the very first time a @@ -24,12 +26,12 @@ the relevant service and then finally caches this service object. Now, further lookups of the same service via Service Locator is done in its cache which improves the performance of application to great extent. -**Typical Use Case:** +## Typical Use Case * when network hits are expensive and time consuming * lookups of services are done quite frequently * large number of services are being used -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/singleton/index.md b/singleton/index.md index 18c13744..dcbd6390 100644 --- a/singleton/index.md +++ b/singleton/index.md @@ -10,26 +10,28 @@ tags: - Difficulty-Beginner --- -**Intent:** Ensure a class only has one instance, and provide a global point of +## Intent +Ensure a class only has one instance, and provide a global point of access to it. ![alt text](./etc/singleton_1.png "Singleton") -**Applicability:** Use the Singleton pattern when +## Applicability +Use the Singleton pattern when * there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point * when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code -**Typical Use Case:** +## Typical Use Case * the logging class * managing a connection to a database * file manager -**Real world examples:** +## Real world examples * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/specification/index.md b/specification/index.md index f95c7921..df6a4c3e 100644 --- a/specification/index.md +++ b/specification/index.md @@ -9,18 +9,20 @@ tags: - Difficulty-Beginner --- -**Intent:** Specification pattern separates the statement of how to match a +## Intent +Specification pattern separates the statement of how to match a 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 +## 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) -**Credits:** +## Credits * [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) diff --git a/state/index.md b/state/index.md index 3beeb480..f5cb189f 100644 --- a/state/index.md +++ b/state/index.md @@ -10,18 +10,21 @@ tags: - Gang Of Four --- -**Also known as:** Objects for States +## Also known as +Objects for States -**Intent:** Allow an object to alter its behavior when its internal state +## Intent +Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. ![alt text](./etc/state_1.png "State") -**Applicability:** Use the State pattern in either of the following cases +## Applicability +Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/step-builder/index.md b/step-builder/index.md index 7ca93c27..bc636e37 100644 --- a/step-builder/index.md +++ b/step-builder/index.md @@ -9,13 +9,15 @@ tags: - Difficulty-Intermediate --- -**Intent:** An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. +## Intent +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. ![alt text](./etc/step-builder.png "Step Builder") -**Applicability:** Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. +## Applicability +Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. -**Credits:** +## Credits * [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) diff --git a/strategy/index.md b/strategy/index.md index 28827601..9b35b806 100644 --- a/strategy/index.md +++ b/strategy/index.md @@ -10,21 +10,24 @@ tags: - Gang Of Four --- -**Also known as:** Policy +## Also known as +Policy -**Intent:** Define a family of algorithms, encapsulate each one, and make them +## Intent +Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. ![alt text](./etc/strategy_1.png "Strategy") -**Applicability:** Use the Strategy pattern when +## Applicability +Use the Strategy pattern when * many related classes differ only in their behavior. Strategies provide a way to configure a class either one of many behaviors * you need different variants of an algorithm. for example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms * an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures * a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/template-method/index.md b/template-method/index.md index 8b8b7878..ad972f06 100644 --- a/template-method/index.md +++ b/template-method/index.md @@ -10,18 +10,20 @@ tags: - Gang Of Four --- -**Intent:** Define the skeleton of an algorithm in an operation, deferring some +## Intent +Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. ![alt text](./etc/template-method_1.png "Template Method") -**Applicability:** The Template Method pattern should be used +## Applicability +The Template Method pattern should be used * to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary * when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations * to control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/thread-pool/index.md b/thread-pool/index.md index d4b61607..9806fa8e 100644 --- a/thread-pool/index.md +++ b/thread-pool/index.md @@ -10,7 +10,8 @@ tags: - Performance --- -**Intent:** It is often the case that tasks to be executed are short-lived and +## Intent +It is often the case that tasks to be executed are short-lived and the number of tasks is large. Creating a new thread for each task would make the system spend more time creating and destroying the threads than executing the actual tasks. Thread Pool solves this problem by reusing existing threads @@ -18,6 +19,7 @@ and eliminating the latency of creating new threads. ![alt text](./etc/thread-pool.png "Thread Pool") -**Applicability:** Use the Thread Pool pattern when +## Applicability +Use the Thread Pool pattern when * you have a large number of short-lived tasks to be executed in parallel diff --git a/tolerant-reader/index.md b/tolerant-reader/index.md index 895886f7..be0085f2 100644 --- a/tolerant-reader/index.md +++ b/tolerant-reader/index.md @@ -9,17 +9,19 @@ tags: - Difficulty-Beginner --- -**Intent:** Tolerant Reader is an integration pattern that helps creating +## Intent +Tolerant Reader is an integration pattern that helps creating 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. ![alt text](./etc/tolerant-reader.png "Tolerant Reader") -**Applicability:** Use the Tolerant Reader pattern when +## Applicability +Use the Tolerant Reader pattern when * the communication schema can evolve and change and yet the receiving side should not break -**Credits:** +## Credits * [Martin Fowler - Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html) diff --git a/twin/index.md b/twin/index.md index e0e44904..3795236b 100644 --- a/twin/index.md +++ b/twin/index.md @@ -9,16 +9,18 @@ tags: - Difficulty-Intermediate --- -**Intent:** Twin pattern is a design pattern which provides a standard solution to simulate multiple +## Intent + Twin pattern is a design pattern which provides a standard solution to simulate multiple inheritance in java ![alt text](./etc/twin.png "Twin") -**Applicability:** Use the Twin idiom when +## Applicability +Use the Twin idiom when * to simulate multiple inheritance in a language that does not support this feature. * to avoid certain problems of multiple inheritance such as name clashes. -**Credits:** +## Credits * [Twin – A Design Pattern for Modeling Multiple Inheritance](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf) diff --git a/visitor/index.md b/visitor/index.md index 760f2c70..c1e24a62 100644 --- a/visitor/index.md +++ b/visitor/index.md @@ -10,22 +10,24 @@ tags: - Gang Of Four --- -**Intent:** Represent an operation to be performed on the elements of an object +## Intent +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. ![alt text](./etc/visitor_1.png "Visitor") -**Applicability:** Use the Visitor pattern when +## Applicability +Use the Visitor pattern when * an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes * many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them * the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes -**Real world examples:** +## Real world examples * [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From 89e0e3e73e55d8bcab5154e196cd66c2013393ae Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 25 Jan 2016 21:16:35 +0000 Subject: [PATCH 04/19] #354 Remove generated copyright banner --- feature-toggle/pom.xml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 98c140f3..249d999a 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -1,27 +1,4 @@ - Date: Mon, 25 Jan 2016 21:29:05 +0000 Subject: [PATCH 05/19] #354 Add Blank index.md --- feature-toggle/index.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 feature-toggle/index.md diff --git a/feature-toggle/index.md b/feature-toggle/index.md new file mode 100644 index 00000000..e69de29b From 72733acfc67055300a3ebf5352d2467ca8c4ad19 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 18:41:08 +0000 Subject: [PATCH 06/19] #354 added usergroup for version of feature toggle --- feature-toggle/pom.xml | 8 ++++ .../com/iluwatar/featuretoggle/user/User.java | 7 ++++ .../featuretoggle/user/UserGroup.java | 37 +++++++++++++++++++ .../featuretoggle/user/UserGroupTest.java | 37 +++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java create mode 100644 feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 249d999a..9a2b04de 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -13,4 +13,12 @@ feature-toggle + + + junit + junit + test + + + \ No newline at end of file diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java new file mode 100644 index 00000000..f2574799 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -0,0 +1,7 @@ +package com.iluwatar.featuretoggle.user; + +/** + * Created by joseph on 26/01/16. + */ +public class User { +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java new file mode 100644 index 00000000..315c88b4 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -0,0 +1,37 @@ +package com.iluwatar.featuretoggle.user; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by joseph on 26/01/16. + */ +public class UserGroup { + + private static List freeGroup = new ArrayList<>(); + private static List paidGroup = new ArrayList<>(); + + public static void addUserToFreeGroup(final User user){ + if(paidGroup.contains(user)){ + throw new IllegalArgumentException("User all ready member of paid group."); + }else{ + if(!freeGroup.contains(user)){ + freeGroup.add(user); + } + } + } + + public static void addUserToPaidGroup(final User user){ + if(freeGroup.contains(user)){ + throw new IllegalArgumentException("User all ready member of free group."); + }else{ + if(!paidGroup.contains(user)){ + paidGroup.add(user); + } + } + } + + public static boolean isPaid(User user) { + return paidGroup.contains(user); + } +} diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java new file mode 100644 index 00000000..f74d7906 --- /dev/null +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -0,0 +1,37 @@ +package com.iluwatar.featuretoggle.user; + +import org.junit.Test; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; + +public class UserGroupTest { + + @Test + public void testAddUserToFreeGroup() throws Exception { + User user = new User(); + UserGroup.addUserToFreeGroup(user); + assertFalse(UserGroup.isPaid(user)); + } + + @Test + public void testAddUserToPaidGroup() throws Exception { + User user = new User(); + UserGroup.addUserToPaidGroup(user); + assertTrue(UserGroup.isPaid(user)); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToPaidWhenOnFree() throws Exception { + User user = new User(); + UserGroup.addUserToFreeGroup(user); + UserGroup.addUserToPaidGroup(user); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToFreeWhenOnPaid() throws Exception { + User user = new User(); + UserGroup.addUserToPaidGroup(user); + UserGroup.addUserToFreeGroup(user); + } +} \ No newline at end of file From a1ede8980f5d68e960b8a03eae9bebd33bb45e7b Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 18:49:25 +0000 Subject: [PATCH 07/19] #354 Added WelcomeMessage Service and Tests for tier example of featureToggle --- .../featuretoggle/pattern/Service.java | 11 ++++++ .../TieredFeatureToggleVersion.java | 21 ++++++++++ .../TieredFeatureToggleVersionTest.java | 39 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java create mode 100644 feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java new file mode 100644 index 00000000..058a8f59 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -0,0 +1,11 @@ +package com.iluwatar.featuretoggle.pattern; + +import com.iluwatar.featuretoggle.user.User; + +/** + * Created by joseph on 26/01/16. + */ +public interface Service { + + public String getWelcomeMessage(User user); +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java new file mode 100644 index 00000000..3a27453b --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -0,0 +1,21 @@ +package com.iluwatar.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +/** + * Created by joseph on 26/01/16. + */ +public class TieredFeatureToggleVersion implements Service { + + @Override + public String getWelcomeMessage(User user) { + if(UserGroup.isPaid(user)){ + return "You're amazing thanks for paying for this awesome software."; + } + + return "I suppose you can use this software."; + } + +} diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java new file mode 100644 index 00000000..b793c461 --- /dev/null +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -0,0 +1,39 @@ +package com.iluwatar.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Created by joseph on 26/01/16. + */ +public class TieredFeatureToggleVersionTest { + + User paidUser = new User(); + User freeUser = new User(); + + @Before + public void setUp() throws Exception { + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + + } + + @Test + public void testGetWelcomeMessageForPaidUser() throws Exception { + Service service = new TieredFeatureToggleVersion(); + String welcomeMessage = service.getWelcomeMessage(paidUser); + assertEquals("You're amazing thanks for paying for this awesome software.",welcomeMessage); + } + + @Test + public void testGetWelcomeMessageForFreeUser() throws Exception { + Service service = new TieredFeatureToggleVersion(); + String welcomeMessage = service.getWelcomeMessage(freeUser); + assertEquals("I suppose you can use this software.",welcomeMessage); + } +} \ No newline at end of file From 32f9cf3ab1ece2cbf2061b537fb5b4491aa2755e Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 18:58:35 +0000 Subject: [PATCH 08/19] #354 Some clean up and show the difference between paid and free a bit more. --- .../featuretoggle/pattern/Service.java | 7 +++---- .../TieredFeatureToggleVersion.java | 5 +---- .../com/iluwatar/featuretoggle/user/User.java | 13 +++++++++--- .../featuretoggle/user/UserGroup.java | 3 --- .../TieredFeatureToggleVersionTest.java | 21 ++++++++----------- .../featuretoggle/user/UserGroupTest.java | 8 +++---- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java index 058a8f59..e8798314 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -2,10 +2,9 @@ import com.iluwatar.featuretoggle.user.User; -/** - * Created by joseph on 26/01/16. - */ + public interface Service { - public String getWelcomeMessage(User user); + String getWelcomeMessage(User user); + } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index 3a27453b..16e7c5da 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -4,15 +4,12 @@ import com.iluwatar.featuretoggle.user.User; import com.iluwatar.featuretoggle.user.UserGroup; -/** - * Created by joseph on 26/01/16. - */ public class TieredFeatureToggleVersion implements Service { @Override public String getWelcomeMessage(User user) { if(UserGroup.isPaid(user)){ - return "You're amazing thanks for paying for this awesome software."; + return "You're amazing " + user.getName() + ". Thanks for paying for this awesome software."; } return "I suppose you can use this software."; diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java index f2574799..a712e4f0 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -1,7 +1,14 @@ package com.iluwatar.featuretoggle.user; -/** - * Created by joseph on 26/01/16. - */ public class User { + + private String name; + + public User(String name) { + this.name = name; + } + + public String getName() { + return name; + } } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index 315c88b4..92b94f67 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -3,9 +3,6 @@ import java.util.ArrayList; import java.util.List; -/** - * Created by joseph on 26/01/16. - */ public class UserGroup { private static List freeGroup = new ArrayList<>(); diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java index b793c461..66b6bfc4 100644 --- a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -8,32 +8,29 @@ import static org.junit.Assert.*; -/** - * Created by joseph on 26/01/16. - */ public class TieredFeatureToggleVersionTest { - User paidUser = new User(); - User freeUser = new User(); + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + final Service service = new TieredFeatureToggleVersion(); @Before public void setUp() throws Exception { UserGroup.addUserToPaidGroup(paidUser); UserGroup.addUserToFreeGroup(freeUser); - } @Test public void testGetWelcomeMessageForPaidUser() throws Exception { - Service service = new TieredFeatureToggleVersion(); - String welcomeMessage = service.getWelcomeMessage(paidUser); - assertEquals("You're amazing thanks for paying for this awesome software.",welcomeMessage); + final String welcomeMessage = service.getWelcomeMessage(paidUser); + final String expected = "You're amazing Jamie Coder. Thanks for paying for this awesome software."; + assertEquals(expected,welcomeMessage); } @Test public void testGetWelcomeMessageForFreeUser() throws Exception { - Service service = new TieredFeatureToggleVersion(); - String welcomeMessage = service.getWelcomeMessage(freeUser); - assertEquals("I suppose you can use this software.",welcomeMessage); + final String welcomeMessage = service.getWelcomeMessage(freeUser); + final String expected = "I suppose you can use this software."; + assertEquals(expected,welcomeMessage); } } \ No newline at end of file diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java index f74d7906..372e58c1 100644 --- a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -9,28 +9,28 @@ public class UserGroupTest { @Test public void testAddUserToFreeGroup() throws Exception { - User user = new User(); + User user = new User("Free User"); UserGroup.addUserToFreeGroup(user); assertFalse(UserGroup.isPaid(user)); } @Test public void testAddUserToPaidGroup() throws Exception { - User user = new User(); + User user = new User("Paid User"); UserGroup.addUserToPaidGroup(user); assertTrue(UserGroup.isPaid(user)); } @Test(expected = IllegalArgumentException.class) public void testAddUserToPaidWhenOnFree() throws Exception { - User user = new User(); + User user = new User("Paid User"); UserGroup.addUserToFreeGroup(user); UserGroup.addUserToPaidGroup(user); } @Test(expected = IllegalArgumentException.class) public void testAddUserToFreeWhenOnPaid() throws Exception { - User user = new User(); + User user = new User("Free User"); UserGroup.addUserToPaidGroup(user); UserGroup.addUserToFreeGroup(user); } From 91b2379fd0d767bc2456b4d5a7fa9498f2fd2785 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 19:20:28 +0000 Subject: [PATCH 09/19] #354 Fixed CheckStyle Issues --- feature-toggle/pom.xml | 16 +++--- .../featuretoggle/pattern/Service.java | 2 +- .../TieredFeatureToggleVersion.java | 14 ++--- .../com/iluwatar/featuretoggle/user/User.java | 14 ++--- .../featuretoggle/user/UserGroup.java | 53 +++++++++++-------- .../TieredFeatureToggleVersionTest.java | 42 +++++++-------- .../featuretoggle/user/UserGroupTest.java | 48 ++++++++--------- 7 files changed, 100 insertions(+), 89 deletions(-) diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 9a2b04de..c6dde055 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -1,7 +1,7 @@ - java-design-patterns @@ -14,11 +14,11 @@ - - junit - junit - test - + + junit + junit + test + - + \ No newline at end of file diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java index e8798314..843ee173 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -5,6 +5,6 @@ public interface Service { - String getWelcomeMessage(User user); + String getWelcomeMessage(User user); } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index 16e7c5da..8411339e 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -6,13 +6,13 @@ public class TieredFeatureToggleVersion implements Service { - @Override - public String getWelcomeMessage(User user) { - if(UserGroup.isPaid(user)){ - return "You're amazing " + user.getName() + ". Thanks for paying for this awesome software."; - } - - return "I suppose you can use this software."; + @Override + public String getWelcomeMessage(User user) { + if (UserGroup.isPaid(user)) { + return "You're amazing " + user.getName() + ". Thanks for paying for this awesome software."; } + return "I suppose you can use this software."; + } + } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java index a712e4f0..732d06c6 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -2,13 +2,13 @@ public class User { - private String name; + private String name; - public User(String name) { - this.name = name; - } + public User(String name) { + this.name = name; + } - public String getName() { - return name; - } + public String getName() { + return name; + } } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index 92b94f67..1328afbc 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -3,32 +3,43 @@ import java.util.ArrayList; import java.util.List; +/** + * Contains the lists of users of different groups paid and free + */ public class UserGroup { - private static List freeGroup = new ArrayList<>(); - private static List paidGroup = new ArrayList<>(); + private static List freeGroup = new ArrayList<>(); + private static List paidGroup = new ArrayList<>(); - public static void addUserToFreeGroup(final User user){ - if(paidGroup.contains(user)){ - throw new IllegalArgumentException("User all ready member of paid group."); - }else{ - if(!freeGroup.contains(user)){ - freeGroup.add(user); - } - } + /** + * + * @param user {@link User} to be added to the free group + */ + public static void addUserToFreeGroup(final User user) { + if (paidGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of paid group."); + } else { + if (!freeGroup.contains(user)) { + freeGroup.add(user); + } } + } - public static void addUserToPaidGroup(final User user){ - if(freeGroup.contains(user)){ - throw new IllegalArgumentException("User all ready member of free group."); - }else{ - if(!paidGroup.contains(user)){ - paidGroup.add(user); - } - } + /** + * + * @param user {@link User} to be added to the paid group + */ + public static void addUserToPaidGroup(final User user) { + if (freeGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of free group."); + } else { + if (!paidGroup.contains(user)) { + paidGroup.add(user); + } } + } - public static boolean isPaid(User user) { - return paidGroup.contains(user); - } + public static boolean isPaid(User user) { + return paidGroup.contains(user); + } } diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java index 66b6bfc4..bf9487a2 100644 --- a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -6,31 +6,31 @@ import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class TieredFeatureToggleVersionTest { - final User paidUser = new User("Jamie Coder"); - final User freeUser = new User("Alan Defect"); - final Service service = new TieredFeatureToggleVersion(); + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + final Service service = new TieredFeatureToggleVersion(); - @Before - public void setUp() throws Exception { - UserGroup.addUserToPaidGroup(paidUser); - UserGroup.addUserToFreeGroup(freeUser); - } + @Before + public void setUp() throws Exception { + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + } - @Test - public void testGetWelcomeMessageForPaidUser() throws Exception { - final String welcomeMessage = service.getWelcomeMessage(paidUser); - final String expected = "You're amazing Jamie Coder. Thanks for paying for this awesome software."; - assertEquals(expected,welcomeMessage); - } + @Test + public void testGetWelcomeMessageForPaidUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(paidUser); + final String expected = "You're amazing Jamie Coder. Thanks for paying for this awesome software."; + assertEquals(expected, welcomeMessage); + } - @Test - public void testGetWelcomeMessageForFreeUser() throws Exception { - final String welcomeMessage = service.getWelcomeMessage(freeUser); - final String expected = "I suppose you can use this software."; - assertEquals(expected,welcomeMessage); - } + @Test + public void testGetWelcomeMessageForFreeUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(freeUser); + final String expected = "I suppose you can use this software."; + assertEquals(expected, welcomeMessage); + } } \ No newline at end of file diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java index 372e58c1..0b3ca6ba 100644 --- a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java +++ b/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -7,31 +7,31 @@ public class UserGroupTest { - @Test - public void testAddUserToFreeGroup() throws Exception { - User user = new User("Free User"); - UserGroup.addUserToFreeGroup(user); - assertFalse(UserGroup.isPaid(user)); - } + @Test + public void testAddUserToFreeGroup() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToFreeGroup(user); + assertFalse(UserGroup.isPaid(user)); + } - @Test - public void testAddUserToPaidGroup() throws Exception { - User user = new User("Paid User"); - UserGroup.addUserToPaidGroup(user); - assertTrue(UserGroup.isPaid(user)); - } + @Test + public void testAddUserToPaidGroup() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToPaidGroup(user); + assertTrue(UserGroup.isPaid(user)); + } - @Test(expected = IllegalArgumentException.class) - public void testAddUserToPaidWhenOnFree() throws Exception { - User user = new User("Paid User"); - UserGroup.addUserToFreeGroup(user); - UserGroup.addUserToPaidGroup(user); - } + @Test(expected = IllegalArgumentException.class) + public void testAddUserToPaidWhenOnFree() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToFreeGroup(user); + UserGroup.addUserToPaidGroup(user); + } - @Test(expected = IllegalArgumentException.class) - public void testAddUserToFreeWhenOnPaid() throws Exception { - User user = new User("Free User"); - UserGroup.addUserToPaidGroup(user); - UserGroup.addUserToFreeGroup(user); - } + @Test(expected = IllegalArgumentException.class) + public void testAddUserToFreeWhenOnPaid() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToPaidGroup(user); + UserGroup.addUserToFreeGroup(user); + } } \ No newline at end of file From d627b7af6b5992bda71a3436c73f8b41f7e13ca2 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 20:24:43 +0000 Subject: [PATCH 10/19] #354 Moved Tests to the correct area --- .../pattern/tieredversion/TieredFeatureToggleVersionTest.java | 0 .../java}/com/iluwatar/featuretoggle/user/UserGroupTest.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename feature-toggle/src/{main/test => test/java}/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java (100%) rename feature-toggle/src/{main/test => test/java}/com/iluwatar/featuretoggle/user/UserGroupTest.java (100%) diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java similarity index 100% rename from feature-toggle/src/main/test/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java rename to feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java diff --git a/feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java similarity index 100% rename from feature-toggle/src/main/test/com/iluwatar/featuretoggle/user/UserGroupTest.java rename to feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java From ba1d3a0fbfbeb09be8c7fa79dcf37777bc6eca32 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 21:08:28 +0000 Subject: [PATCH 11/19] #354 Added Configuration Based Example of Feature Toggle --- .../PropertiesFeatureToggleVersion.java | 27 ++++++++++++++++ .../TieredFeatureToggleVersion.java | 2 +- .../com/iluwatar/featuretoggle/user/User.java | 3 +- .../PropertiesFeatureToggleVersionTest.java | 31 +++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java create mode 100644 feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java new file mode 100644 index 00000000..bccf5bd6 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -0,0 +1,27 @@ +package com.iluwatar.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; + +import java.util.Properties; + +public class PropertiesFeatureToggleVersion implements Service { + + private Properties properties; + + public PropertiesFeatureToggleVersion(final Properties properties) { + this.properties = properties; + } + + @Override + public String getWelcomeMessage(final User user) { + + final boolean enhancedWelcome = (boolean) properties.get("enhancedWelcome"); + + if (enhancedWelcome) { + return "Welcome " + user + ". You're using the enhanced welcome message."; + } + + return "Welcome to the application."; + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index 8411339e..330015ac 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -9,7 +9,7 @@ public class TieredFeatureToggleVersion implements Service { @Override public String getWelcomeMessage(User user) { if (UserGroup.isPaid(user)) { - return "You're amazing " + user.getName() + ". Thanks for paying for this awesome software."; + return "You're amazing " + user + ". Thanks for paying for this awesome software."; } return "I suppose you can use this software."; diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java index 732d06c6..12b4cc06 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -8,7 +8,8 @@ public User(String name) { this.name = name; } - public String getName() { + @Override + public String toString() { return name; } } diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java new file mode 100644 index 00000000..de10480c --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -0,0 +1,31 @@ + +package com.iluwatar.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import org.junit.Test; + +import java.util.Properties; + +import static org.junit.Assert.assertEquals; + +public class PropertiesFeatureToggleVersionTest { + + @Test + public void testFeatureTurnedOn() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome",true); + Service service = new PropertiesFeatureToggleVersion(properties); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.",welcomeMessage); + } + + @Test + public void testFeatureTurnedOff() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome",false); + Service service = new PropertiesFeatureToggleVersion(properties); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome to the application.",welcomeMessage); + } +} \ No newline at end of file From 4a49f82f2366e9e39f7298a11e7e09de6e82401c Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 21:18:47 +0000 Subject: [PATCH 12/19] #354 add general boolean method to services for feature status. Change user.toString --- .../featuretoggle/pattern/Service.java | 2 ++ .../PropertiesFeatureToggleVersion.java | 25 +++++++++++++++---- .../TieredFeatureToggleVersion.java | 5 ++++ .../PropertiesFeatureToggleVersionTest.java | 4 +++ .../TieredFeatureToggleVersionTest.java | 6 +++++ 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java index 843ee173..c3849a45 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -7,4 +7,6 @@ public interface Service { String getWelcomeMessage(User user); + boolean isEnhanced(); + } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java index bccf5bd6..aa795b97 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -7,21 +7,36 @@ public class PropertiesFeatureToggleVersion implements Service { - private Properties properties; + private boolean isEnhanced; + /** + * + * @param properties {@link Properties} used to configure the service and toggle features. + */ public PropertiesFeatureToggleVersion(final Properties properties) { - this.properties = properties; + if (properties == null) { + throw new IllegalArgumentException("No Properties Provided."); + } else { + try { + isEnhanced = (boolean) properties.get("enhancedWelcome"); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid Enhancement Settings Provided."); + } + } } @Override public String getWelcomeMessage(final User user) { - final boolean enhancedWelcome = (boolean) properties.get("enhancedWelcome"); - - if (enhancedWelcome) { + if (isEnhanced()) { return "Welcome " + user + ". You're using the enhanced welcome message."; } return "Welcome to the application."; } + + @Override + public boolean isEnhanced() { + return isEnhanced; + } } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index 330015ac..2942618f 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -15,4 +15,9 @@ public String getWelcomeMessage(User user) { return "I suppose you can use this software."; } + @Override + public boolean isEnhanced() { + return true; + } + } diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java index de10480c..7a07193c 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -8,6 +8,8 @@ import java.util.Properties; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class PropertiesFeatureToggleVersionTest { @@ -16,6 +18,7 @@ public void testFeatureTurnedOn() throws Exception { final Properties properties = new Properties(); properties.put("enhancedWelcome",true); Service service = new PropertiesFeatureToggleVersion(properties); + assertTrue(service.isEnhanced()); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.",welcomeMessage); } @@ -25,6 +28,7 @@ public void testFeatureTurnedOff() throws Exception { final Properties properties = new Properties(); properties.put("enhancedWelcome",false); Service service = new PropertiesFeatureToggleVersion(properties); + assertFalse(service.isEnhanced()); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); assertEquals("Welcome to the application.",welcomeMessage); } diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java index bf9487a2..f96e99ba 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class TieredFeatureToggleVersionTest { @@ -33,4 +34,9 @@ public void testGetWelcomeMessageForFreeUser() throws Exception { final String expected = "I suppose you can use this software."; assertEquals(expected, welcomeMessage); } + + @Test + public void testIsEnhancedAlwaysTrueAsTiered() throws Exception { + assertTrue(service.isEnhanced()); + } } \ No newline at end of file From 77e14f00693114168853b62595144684ef87af07 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Tue, 26 Jan 2016 23:38:28 +0000 Subject: [PATCH 13/19] #354 Add tests for Properties --- .../com/iluwatar/featuretoggle/user/UserGroup.java | 1 + .../PropertiesFeatureToggleVersionTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index 1328afbc..d3052ac2 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -11,6 +11,7 @@ public class UserGroup { private static List freeGroup = new ArrayList<>(); private static List paidGroup = new ArrayList<>(); + /** * * @param user {@link User} to be added to the free group diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java index 7a07193c..714eb507 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -13,6 +13,18 @@ public class PropertiesFeatureToggleVersionTest { + @Test(expected = IllegalArgumentException.class) + public void testNullPropertiesPassed() throws Exception { + new PropertiesFeatureToggleVersion(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNonBooleanProperty() throws Exception { + final Properties properties = new Properties(); + properties.setProperty("enhancedWelcome","Something"); + new PropertiesFeatureToggleVersion(properties); + } + @Test public void testFeatureTurnedOn() throws Exception { final Properties properties = new Properties(); From 98326a1e5e27d112f91949bcdfede5ecfda40704 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Thu, 28 Jan 2016 21:14:40 +0000 Subject: [PATCH 14/19] #354 Start Adding Java docs --- .../com/iluwatar/featuretoggle/user/User.java | 12 ++++++++++++ .../iluwatar/featuretoggle/user/UserGroup.java | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java index 12b4cc06..70b24138 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -1,13 +1,25 @@ package com.iluwatar.featuretoggle.user; +/** + * Used to demonstrate the purpose of the feature toggle. This class actually has nothing to do with the pattern. + */ public class User { private String name; + /** + * Default Constructor setting the username. + * + * @param name {@link String} to represent the name of the user. + */ public User(String name) { this.name = name; } + /** + * {@inheritDoc} + * @return The {@link String} representation of the User, in this case just return the name of the user. + */ @Override public String toString() { return name; diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index d3052ac2..fec972eb 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -4,7 +4,10 @@ import java.util.List; /** - * Contains the lists of users of different groups paid and free + * Contains the lists of users of different groups paid and free. Used to demonstrate the tiered example of feature + * toggle. Allowing certain features to be available to only certain groups of users. + * + * @see User */ public class UserGroup { @@ -13,10 +16,13 @@ public class UserGroup { /** + * Add the passed {@link User} to the free user group list. * * @param user {@link User} to be added to the free group + * @throws IllegalArgumentException + * @see User */ - public static void addUserToFreeGroup(final User user) { + public static void addUserToFreeGroup(final User user) throws IllegalArgumentException { if (paidGroup.contains(user)) { throw new IllegalArgumentException("User all ready member of paid group."); } else { @@ -27,10 +33,13 @@ public static void addUserToFreeGroup(final User user) { } /** + * Add the passed {@link User} to the paid user group list. * * @param user {@link User} to be added to the paid group + * @throws IllegalArgumentException + * @see User */ - public static void addUserToPaidGroup(final User user) { + public static void addUserToPaidGroup(final User user) throws IllegalArgumentException { if (freeGroup.contains(user)) { throw new IllegalArgumentException("User all ready member of free group."); } else { From c2e750aca1fa5669a8eb7b354784b881ad7bd20c Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Sun, 31 Jan 2016 13:46:34 +0000 Subject: [PATCH 15/19] #354 finished method javadocs --- .../featuretoggle/pattern/Service.java | 22 ++++++++++++++- .../PropertiesFeatureToggleVersion.java | 27 +++++++++++++++++++ .../TieredFeatureToggleVersion.java | 21 +++++++++++++++ .../featuretoggle/user/UserGroup.java | 11 ++++++-- 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java index c3849a45..97078071 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -2,11 +2,31 @@ import com.iluwatar.featuretoggle.user.User; - +/** + * Simple interfaces to allow the calling of the method to generate the welcome message for a given user. While there is + * a helper method to gather the the status of the feature toggle. In some cases there is no need for the + * {@link Service#isEnhanced()} in {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} + * where the toggle is determined by the actual {@link User}. + * + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User + */ public interface Service { + /** + * Generates a welcome message for the passed user. + * + * @param user the {@link User} to be used if the message is to be personalised. + * @return Generated {@link String} welcome message + */ String getWelcomeMessage(User user); + /** + * Returns if the welcome message to be displayed will be the enhanced version. + * + * @return Boolean {@value true} if enhanced. + */ boolean isEnhanced(); } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java index aa795b97..947efec6 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -5,13 +5,21 @@ import java.util.Properties; +/** + * + */ public class PropertiesFeatureToggleVersion implements Service { private boolean isEnhanced; /** + * Creates an instance of {@link PropertiesFeatureToggleVersion} using the passed {@link Properties} to determine, + * the status of the feature toggle {@link PropertiesFeatureToggleVersion#isEnhanced()}. There is also some defensive + * code to ensure the {@link Properties} passed are as expected. * * @param properties {@link Properties} used to configure the service and toggle features. + * @throws IllegalArgumentException when the passed {@link Properties} is not as expected + * @see Properties */ public PropertiesFeatureToggleVersion(final Properties properties) { if (properties == null) { @@ -25,6 +33,18 @@ public PropertiesFeatureToggleVersion(final Properties properties) { } } + /** + * Generate a welcome message based on the user being passed and the status of the feature toggle. If the enhanced + * version is enabled, then the message will be personalised with the name of the passed {@link User}. However if + * disabled then a generic version fo the message is returned. + * + * @param user the {@link User} to be displayed in the message if the enhanced version is enabled see + * {@link PropertiesFeatureToggleVersion#isEnhanced()}. If the enhanced version is enabled, then the + * message will be personalised with the name of the passed {@link User}. However if disabled then a + * generic version fo the message is returned. + * @return Resulting welcome message. + * @see User + */ @Override public String getWelcomeMessage(final User user) { @@ -35,6 +55,13 @@ public String getWelcomeMessage(final User user) { return "Welcome to the application."; } + /** + * Method that checks if the welcome message to be returned is the enhanced venison or not. For this service it will + * see the value of the boolean that was set in the constructor + * {@link PropertiesFeatureToggleVersion#PropertiesFeatureToggleVersion(Properties)} + * + * @return Boolean value {@value true} if enhanced. + */ @Override public boolean isEnhanced() { return isEnhanced; diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index 2942618f..a011b7b6 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -4,8 +4,22 @@ import com.iluwatar.featuretoggle.user.User; import com.iluwatar.featuretoggle.user.UserGroup; +/** + * + */ public class TieredFeatureToggleVersion implements Service { + /** + * Generates a welcome message from the passed {@link User}. The resulting message depends on the group of the + * {@link User}. So if the {@link User} is in the {@link UserGroup#paidGroup} then the enhanced version of the + * welcome message will be returned where the username is displayed. + * + * @param user the {@link User} to generate the welcome message for, different messages are displayed if the user is + * in the {@link UserGroup#isPaid(User)} or {@link UserGroup#freeGroup} + * @return Resulting welcome message. + * @see User + * @see UserGroup + */ @Override public String getWelcomeMessage(User user) { if (UserGroup.isPaid(user)) { @@ -15,6 +29,13 @@ public String getWelcomeMessage(User user) { return "I suppose you can use this software."; } + /** + * Method that checks if the welcome message to be returned is the enhanced version. For this instance as the logic + * is driven by the user group. This method is a little redundant. However can be used to show that there is an + * enhanced version available. + * + * @return Boolean value {@value true} if enhanced. + */ @Override public boolean isEnhanced() { return true; diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index fec972eb..2bdfcfdf 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -19,7 +19,7 @@ public class UserGroup { * Add the passed {@link User} to the free user group list. * * @param user {@link User} to be added to the free group - * @throws IllegalArgumentException + * @throws IllegalArgumentException when user is already added to the paid group * @see User */ public static void addUserToFreeGroup(final User user) throws IllegalArgumentException { @@ -36,7 +36,7 @@ public static void addUserToFreeGroup(final User user) throws IllegalArgumentExc * Add the passed {@link User} to the paid user group list. * * @param user {@link User} to be added to the paid group - * @throws IllegalArgumentException + * @throws IllegalArgumentException when the user is already added to the free group * @see User */ public static void addUserToPaidGroup(final User user) throws IllegalArgumentException { @@ -49,6 +49,13 @@ public static void addUserToPaidGroup(final User user) throws IllegalArgumentExc } } + /** + * Method to take a {@link User} to determine if the user is in the {@link UserGroup#paidGroup}. + * + * @param user {@link User} to check if they are in the {@link UserGroup#paidGroup} + * + * @return true if the {@link User} is in {@link UserGroup#paidGroup} + */ public static boolean isPaid(User user) { return paidGroup.contains(user); } From 8603333e5d065207e8af68fe229e7252726e68e7 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Sun, 31 Jan 2016 14:10:34 +0000 Subject: [PATCH 16/19] #354 Adding Class Diagram --- feature-toggle/etc/feature-toggle.png | Bin 0 -> 55425 bytes feature-toggle/etc/feature-toggle.ucls | 111 +++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 feature-toggle/etc/feature-toggle.png create mode 100644 feature-toggle/etc/feature-toggle.ucls diff --git a/feature-toggle/etc/feature-toggle.png b/feature-toggle/etc/feature-toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..5c118e57e44e86a22a426dcfa9aff8291dc246ac GIT binary patch literal 55425 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ⅈ_4h)^X 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 xw7`b``B#ay@U58d!Tbqz^ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From bf5ba442608f720880b1bd635c5d8dd94f87369c Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Sun, 31 Jan 2016 23:24:35 +0000 Subject: [PATCH 17/19] #354 add App.java --- .../java/com/iluwatar/featuretoggle/App.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java new file mode 100644 index 00000000..74db991b --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.featuretoggle; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +import java.util.Properties; + +/** + * + */ +public class App { + + /** + * + */ + public static void main(String[] args) { + final Properties properties = new Properties(); + properties.put("enhancedWelcome", true); + Service service = new PropertiesFeatureToggleVersion(properties); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessage); + + final Properties turnedOff = new Properties(); + turnedOff.put("enhancedWelcome", false); + Service turnedOffService = new PropertiesFeatureToggleVersion(turnedOff); + final String welcomeMessageturnedOff = turnedOffService.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessageturnedOff); + + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + + final String welcomeMessagePaidUser = service.getWelcomeMessage(paidUser); + final String welcomeMessageFreeUser = service.getWelcomeMessage(freeUser); + System.out.println(welcomeMessageFreeUser); + System.out.println(welcomeMessagePaidUser); + } +} From 37da470178e05a7461c50c433c292199c211dcd2 Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 1 Feb 2016 18:45:54 +0000 Subject: [PATCH 18/19] #354 Finish Readme --- feature-toggle/index.md | 32 +++++++++++++++++++ .../java/com/iluwatar/featuretoggle/App.java | 6 ++-- .../featuretoggle/pattern/Service.java | 6 ++-- .../PropertiesFeatureToggleVersion.java | 15 +++++++-- .../TieredFeatureToggleVersion.java | 15 +++++++-- .../com/iluwatar/featuretoggle/user/User.java | 6 ++-- .../featuretoggle/user/UserGroup.java | 6 ++-- .../PropertiesFeatureToggleVersionTest.java | 16 +++++----- .../TieredFeatureToggleVersionTest.java | 6 ++-- .../featuretoggle/user/UserGroupTest.java | 6 ++-- 10 files changed, 82 insertions(+), 32 deletions(-) diff --git a/feature-toggle/index.md b/feature-toggle/index.md index e69de29b..51747ac0 100644 --- a/feature-toggle/index.md +++ b/feature-toggle/index.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Feature Toggle +folder: feature-toggle +permalink: /patterns/feature-toggle/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Feature Flag + +## Intent +Used to switch code execution paths based on properties or groupings. Allowing new features to be released, tested +and rolled out. Allowing switching back to the older feature quickly if needed. It should be noted that this pattern, +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. + +![alt text](./etc/feature-toggle.png "Feature Toggle") + +## Applicability +Use the Feature Toogle pattern when + +* Giving different features to different users. +* Rolling out a new feature incrementally. +* Switching between development and production environments. + +## Credits + +* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html) \ No newline at end of file diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java index 74db991b..71cdb378 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java index 337fdc38..d2542b2b 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java index 7c4e8e3b..761d7d39 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,7 +28,16 @@ import java.util.Properties; /** + * This example of the Feature Toogle pattern is less dynamic version than + * {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} where the feature is turned on + * or off at the time of creation of the service. This example uses simple Java {@link Properties} however it could as + * easily be done with an external configuration file loaded by Spring and so on. A good example of when to use this + * version of the feature toggle is when new features are being developed. So you could have a configuration property + * boolean named development or some sort of system environment variable. * + * @see Service + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User */ public class PropertiesFeatureToggleVersion implements Service { diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java index e62ea1a6..124c9533 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,7 +27,16 @@ import com.iluwatar.featuretoggle.user.UserGroup; /** + * This example of the Feature Toogle pattern shows how it could be implemented based on a {@link User}. Therefore + * showing its use within a tiered application where the paying users get access to different content or + * better versions of features. So in this instance a {@link User} is passed in and if they are found to be + * on the {@link UserGroup#isPaid(User)} they are welcomed with a personalised message. While the other is more + * generic. However this pattern is limited to simple examples such as the one below. * + * @see Service + * @see User + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see UserGroup */ public class TieredFeatureToggleVersion implements Service { diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java index b9882a71..ce7b54b7 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java index 1fe55611..c9d9fd02 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java index 61c20ba7..69afc9bb 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -43,27 +43,27 @@ public void testNullPropertiesPassed() throws Exception { @Test(expected = IllegalArgumentException.class) public void testNonBooleanProperty() throws Exception { final Properties properties = new Properties(); - properties.setProperty("enhancedWelcome","Something"); + properties.setProperty("enhancedWelcome", "Something"); new PropertiesFeatureToggleVersion(properties); } @Test public void testFeatureTurnedOn() throws Exception { final Properties properties = new Properties(); - properties.put("enhancedWelcome",true); + properties.put("enhancedWelcome", true); Service service = new PropertiesFeatureToggleVersion(properties); assertTrue(service.isEnhanced()); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); - assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.",welcomeMessage); + assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.", welcomeMessage); } @Test public void testFeatureTurnedOff() throws Exception { final Properties properties = new Properties(); - properties.put("enhancedWelcome",false); + properties.put("enhancedWelcome", false); Service service = new PropertiesFeatureToggleVersion(properties); assertFalse(service.isEnhanced()); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); - assertEquals("Welcome to the application.",welcomeMessage); + assertEquals("Welcome to the application.", welcomeMessage); } } \ No newline at end of file diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java index 04122160..dca1d9b8 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java index 716060e6..6659815d 100644 --- a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -1,17 +1,17 @@ /** * The MIT License * Copyright (c) 2014 Ilkka Seppälä - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE From c7f4311706c33a743aeb9a853d303877d3a2e68b Mon Sep 17 00:00:00 2001 From: Joseph McCarthy Date: Mon, 1 Feb 2016 18:57:05 +0000 Subject: [PATCH 19/19] #354 Clean up --- .../java/com/iluwatar/featuretoggle/App.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java index 71cdb378..debe9958 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -31,26 +31,57 @@ import java.util.Properties; /** + * The Feature Toggle pattern allows for complete code executions to be turned on or off with ease. This allows features + * to be controlled by either dynamic methods just as {@link User} information or by {@link Properties}. In the App + * below there are two examples. Firstly the {@link Properties} version of the feature toggle, where the enhanced + * version of the welcome message which is personalised is turned either on or off at instance creation. This method + * is not as dynamic as the {@link User} driven version where the feature of the personalised welcome message is + * dependant on the {@link UserGroup} the {@link User} is in. So if the user is a memeber of the + * {@link UserGroup#isPaid(User)} then they get an ehanced version of the welcome message. + * + * Note that this pattern can easily introduce code complexity, and if not kept in check can result in redundant + * unmaintained code within the codebase. * */ public class App { /** + * Block 1 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to enabled. + * + * Block 2 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to disabled. Notice the difference with the printed welcome message the username is not included. + * + * Block 3 shows the {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} being + * set up with two users on who is on the free level, while the other is on the paid level. When the + * {@link Service#getWelcomeMessage(User)} is called with the paid {@link User} note that the welcome message + * contains their username, while the same service call with the free tier user is more generic. No username is + * printed. * + * @see User + * @see UserGroup + * @see Service + * @see PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion; */ public static void main(String[] args) { + final Properties properties = new Properties(); properties.put("enhancedWelcome", true); Service service = new PropertiesFeatureToggleVersion(properties); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); System.out.println(welcomeMessage); + // --------------------------------------------- + final Properties turnedOff = new Properties(); turnedOff.put("enhancedWelcome", false); Service turnedOffService = new PropertiesFeatureToggleVersion(turnedOff); final String welcomeMessageturnedOff = turnedOffService.getWelcomeMessage(new User("Jamie No Code")); System.out.println(welcomeMessageturnedOff); + // -------------------------------------------- + final User paidUser = new User("Jamie Coder"); final User freeUser = new User("Alan Defect");