126
chain/README.md
@ -16,7 +16,131 @@ 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.
|
||||
|
||||

|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> The Orc King gives loud orders to his army. The closest one to react is the commander, then officer and then soldier. The commander, officer and soldier here form a chain of responsibility.
|
||||
|
||||
In plain words
|
||||
|
||||
> It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Translating our example with orcs from above. First we have the request class
|
||||
|
||||
```
|
||||
public class Request {
|
||||
|
||||
private final RequestType requestType;
|
||||
private final String requestDescription;
|
||||
private boolean handled;
|
||||
|
||||
public Request(final RequestType requestType, final String requestDescription) {
|
||||
this.requestType = Objects.requireNonNull(requestType);
|
||||
this.requestDescription = Objects.requireNonNull(requestDescription);
|
||||
}
|
||||
|
||||
public String getRequestDescription() { return requestDescription; }
|
||||
|
||||
public RequestType getRequestType() { return requestType; }
|
||||
|
||||
public void markHandled() { this.handled = true; }
|
||||
|
||||
public boolean isHandled() { return this.handled; }
|
||||
|
||||
@Override
|
||||
public String toString() { return getRequestDescription(); }
|
||||
}
|
||||
|
||||
public enum RequestType {
|
||||
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
|
||||
}
|
||||
```
|
||||
|
||||
Then the request handler hierarchy
|
||||
|
||||
```
|
||||
public abstract class RequestHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
|
||||
private RequestHandler next;
|
||||
|
||||
public RequestHandler(RequestHandler next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public void handleRequest(Request req) {
|
||||
if (next != null) {
|
||||
next.handleRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
protected void printHandling(Request req) {
|
||||
LOGGER.info("{} handling request \"{}\"", this, req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
}
|
||||
|
||||
public class OrcCommander extends RequestHandler {
|
||||
public OrcCommander(RequestHandler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(Request req) {
|
||||
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
|
||||
printHandling(req);
|
||||
req.markHandled();
|
||||
} else {
|
||||
super.handleRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Orc commander";
|
||||
}
|
||||
}
|
||||
|
||||
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander
|
||||
|
||||
```
|
||||
|
||||
Then we have the Orc King who gives the orders and forms the chain
|
||||
|
||||
```
|
||||
public class OrcKing {
|
||||
RequestHandler chain;
|
||||
|
||||
public OrcKing() {
|
||||
buildChain();
|
||||
}
|
||||
|
||||
private void buildChain() {
|
||||
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
|
||||
}
|
||||
|
||||
public void makeRequest(Request req) {
|
||||
chain.handleRequest(req);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then it is used as follows
|
||||
|
||||
```
|
||||
OrcKing king = new OrcKing();
|
||||
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle"
|
||||
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner"
|
||||
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax"
|
||||
```
|
||||
|
||||
## Applicability
|
||||
Use Chain of Responsibility when
|
||||
|
Before Width: | Height: | Size: 30 KiB |
@ -1,109 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.chain.Request" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/Request.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="196" width="228" x="168" y="182"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.chain.OrcOfficer" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="194" x="168" y="609"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.chain.OrcCommander" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/OrcCommander.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="217" x="402" y="609"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="4" language="java" name="com.iluwatar.chain.RequestHandler" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/RequestHandler.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="141" width="218" x="451" y="418"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="5" language="java" name="com.iluwatar.chain.OrcSoldier" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="194" x="659" y="609"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="6" language="java" name="com.iluwatar.chain.OrcKing" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/OrcKing.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="188" x="451" y="182"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<enumeration id="7" language="java" name="com.iluwatar.chain.RequestType" project="chain"
|
||||
file="/chain/src/main/java/com/iluwatar/chain/RequestType.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="142" width="243" x="168" y="418"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</enumeration>
|
||||
<association id="8">
|
||||
<end type="SOURCE" refId="6" navigable="false">
|
||||
<attribute id="9" name="chain"/>
|
||||
<multiplicity id="10" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="4" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<association id="11">
|
||||
<end type="SOURCE" refId="4" navigable="false">
|
||||
<attribute id="12" name="next"/>
|
||||
<multiplicity id="13" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="4" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<generalization id="14">
|
||||
<end type="SOURCE" refId="5"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</generalization>
|
||||
<association id="15">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="16" name="requestType"/>
|
||||
<multiplicity id="17" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="7" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<generalization id="18">
|
||||
<end type="SOURCE" refId="2"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</generalization>
|
||||
<generalization id="19">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</generalization>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
@ -1,61 +0,0 @@
|
||||
@startuml
|
||||
package com.iluwatar.chain {
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class OrcCommander {
|
||||
+ OrcCommander(handler : RequestHandler)
|
||||
+ handleRequest(req : Request)
|
||||
+ toString() : String
|
||||
}
|
||||
class OrcKing {
|
||||
~ chain : RequestHandler
|
||||
+ OrcKing()
|
||||
- buildChain()
|
||||
+ makeRequest(req : Request)
|
||||
}
|
||||
class OrcOfficer {
|
||||
+ OrcOfficer(handler : RequestHandler)
|
||||
+ handleRequest(req : Request)
|
||||
+ toString() : String
|
||||
}
|
||||
class OrcSoldier {
|
||||
+ OrcSoldier(handler : RequestHandler)
|
||||
+ handleRequest(req : Request)
|
||||
+ toString() : String
|
||||
}
|
||||
class Request {
|
||||
- handled : boolean
|
||||
- requestDescription : String
|
||||
- requestType : RequestType
|
||||
+ Request(requestType : RequestType, requestDescription : String)
|
||||
+ getRequestDescription() : String
|
||||
+ getRequestType() : RequestType
|
||||
+ isHandled() : boolean
|
||||
+ markHandled()
|
||||
+ toString() : String
|
||||
}
|
||||
abstract class RequestHandler {
|
||||
- LOGGER : Logger {static}
|
||||
- next : RequestHandler
|
||||
+ RequestHandler(next : RequestHandler)
|
||||
+ handleRequest(req : Request)
|
||||
# printHandling(req : Request)
|
||||
+ toString() : String {abstract}
|
||||
}
|
||||
enum RequestType {
|
||||
+ COLLECT_TAX {static}
|
||||
+ DEFEND_CASTLE {static}
|
||||
+ TORTURE_PRISONER {static}
|
||||
+ valueOf(name : String) : RequestType {static}
|
||||
+ values() : RequestType[] {static}
|
||||
}
|
||||
}
|
||||
RequestHandler --> "-next" RequestHandler
|
||||
Request --> "-requestType" RequestType
|
||||
OrcKing --> "-chain" RequestHandler
|
||||
OrcCommander --|> RequestHandler
|
||||
OrcOfficer --|> RequestHandler
|
||||
OrcSoldier --|> RequestHandler
|
||||
@enduml
|
Before Width: | Height: | Size: 60 KiB |
@ -85,9 +85,7 @@
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="LeftCurly">
|
||||
<property name="maxLineLength" value="120"/>
|
||||
</module>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
|
43
dao/src/main/java/com/iluwatar/dao/CustomException.java
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.dao;
|
||||
|
||||
/**
|
||||
*
|
||||
* Custom exception
|
||||
*
|
||||
*/
|
||||
public class CustomException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CustomException() {}
|
||||
|
||||
public CustomException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CustomException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -36,12 +36,16 @@ import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* An implementation of {@link CustomerDao} that persists customers in RDBMS.
|
||||
* An implementation of {@link CustomerDao} that persists customers in RDBMS.
|
||||
*
|
||||
*/
|
||||
public class DbCustomerDao implements CustomerDao {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(DbCustomerDao.class);
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
/**
|
||||
@ -65,8 +69,8 @@ public class DbCustomerDao implements CustomerDao {
|
||||
Connection connection;
|
||||
try {
|
||||
connection = getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); //NOSONAR
|
||||
ResultSet resultSet = statement.executeQuery(); //NOSONAR
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); // NOSONAR
|
||||
ResultSet resultSet = statement.executeQuery(); // NOSONAR
|
||||
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Customer>(Long.MAX_VALUE,
|
||||
Spliterator.ORDERED) {
|
||||
|
||||
@ -79,12 +83,12 @@ public class DbCustomerDao implements CustomerDao {
|
||||
action.accept(createCustomer(resultSet));
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException(e); // NOSONAR
|
||||
}
|
||||
}
|
||||
}, false).onClose(() -> mutedClose(connection, statement, resultSet));
|
||||
} catch (SQLException e) {
|
||||
throw new Exception(e.getMessage(), e);
|
||||
throw new CustomException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +102,7 @@ public class DbCustomerDao implements CustomerDao {
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.info("Exception thrown " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,19 +117,26 @@ public class DbCustomerDao implements CustomerDao {
|
||||
*/
|
||||
@Override
|
||||
public Optional<Customer> getById(int id) throws Exception {
|
||||
|
||||
ResultSet resultSet = null;
|
||||
|
||||
try (Connection connection = getConnection();
|
||||
PreparedStatement statement =
|
||||
connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
|
||||
|
||||
statement.setInt(1, id);
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
resultSet = statement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
return Optional.of(createCustomer(resultSet));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new Exception(ex.getMessage(), ex);
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
} finally {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +158,7 @@ public class DbCustomerDao implements CustomerDao {
|
||||
statement.execute();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
throw new Exception(ex.getMessage(), ex);
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +175,7 @@ public class DbCustomerDao implements CustomerDao {
|
||||
statement.setInt(3, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new Exception(ex.getMessage(), ex);
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +190,7 @@ public class DbCustomerDao implements CustomerDao {
|
||||
statement.setInt(1, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new Exception(ex.getMessage(), ex);
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,9 +105,9 @@ troll.attack(); // The troll tries to grab you!
|
||||
troll.fleeBattle(); // The troll shrieks in horror and runs away!
|
||||
|
||||
// change the behavior of the simple troll by adding a decorator
|
||||
Troll clubbed = new ClubbedTroll(troll);
|
||||
clubbed.attack(); // The troll tries to grab you! The troll swings at you with a club!
|
||||
clubbed.fleeBattle(); // The troll shrieks in horror and runs away!
|
||||
troll = new ClubbedTroll(troll);
|
||||
troll.attack(); // The troll tries to grab you! The troll swings at you with a club!
|
||||
troll.fleeBattle(); // The troll shrieks in horror and runs away!
|
||||
```
|
||||
|
||||
## Applicability
|
||||
|
@ -57,9 +57,9 @@ public class App {
|
||||
|
||||
// change the behavior of the simple troll by adding a decorator
|
||||
LOGGER.info("A troll with huge club surprises you.");
|
||||
Troll clubbed = new ClubbedTroll(troll);
|
||||
clubbed.attack();
|
||||
clubbed.fleeBattle();
|
||||
LOGGER.info("Clubbed troll power {}.\n", clubbed.getAttackPower());
|
||||
troll = new ClubbedTroll(troll);
|
||||
troll.attack();
|
||||
troll.fleeBattle();
|
||||
LOGGER.info("Clubbed troll power {}.\n", troll.getAttackPower());
|
||||
}
|
||||
}
|
||||
|
183
facade/README.md
@ -15,7 +15,188 @@ tags:
|
||||
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.
|
||||
|
||||

|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> How does a goldmine work? "Well, the miners go down there and dig gold!" you say. That is what you believe because you are using a simple interface that goldmine provides on the outside, internally it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a facade.
|
||||
|
||||
In plain words
|
||||
|
||||
> Facade pattern provides a simplified interface to a complex subsystem.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> A facade is an object that provides a simplified interface to a larger body of code, such as a class library.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Taking our goldmine example from above. Here we have the dwarven mine worker hierarchy
|
||||
|
||||
```
|
||||
public abstract class DwarvenMineWorker {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class);
|
||||
|
||||
public void goToSleep() {
|
||||
LOGGER.info("{} goes to sleep.", name());
|
||||
}
|
||||
|
||||
public void wakeUp() {
|
||||
LOGGER.info("{} wakes up.", name());
|
||||
}
|
||||
|
||||
public void goHome() {
|
||||
LOGGER.info("{} goes home.", name());
|
||||
}
|
||||
|
||||
public void goToMine() {
|
||||
LOGGER.info("{} goes to the mine.", name());
|
||||
}
|
||||
|
||||
private void action(Action action) {
|
||||
switch (action) {
|
||||
case GO_TO_SLEEP:
|
||||
goToSleep();
|
||||
break;
|
||||
case WAKE_UP:
|
||||
wakeUp();
|
||||
break;
|
||||
case GO_HOME:
|
||||
goHome();
|
||||
break;
|
||||
case GO_TO_MINE:
|
||||
goToMine();
|
||||
break;
|
||||
case WORK:
|
||||
work();
|
||||
break;
|
||||
default:
|
||||
LOGGER.info("Undefined action");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void action(Action... actions) {
|
||||
for (Action action : actions) {
|
||||
action(action);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void work();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
static enum Action {
|
||||
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
|
||||
}
|
||||
}
|
||||
|
||||
public class DwarvenTunnelDigger extends DwarvenMineWorker {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class);
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} creates another promising tunnel.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarven tunnel digger";
|
||||
}
|
||||
}
|
||||
|
||||
public class DwarvenGoldDigger extends DwarvenMineWorker {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class);
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} digs for gold.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarf gold digger";
|
||||
}
|
||||
}
|
||||
|
||||
public class DwarvenCartOperator extends DwarvenMineWorker {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class);
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} moves gold chunks out of the mine.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarf cart operator";
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To operate all these goldmine workers we have the facade
|
||||
|
||||
```
|
||||
public class DwarvenGoldmineFacade {
|
||||
|
||||
private final List<DwarvenMineWorker> workers;
|
||||
|
||||
public DwarvenGoldmineFacade() {
|
||||
workers = new ArrayList<>();
|
||||
workers.add(new DwarvenGoldDigger());
|
||||
workers.add(new DwarvenCartOperator());
|
||||
workers.add(new DwarvenTunnelDigger());
|
||||
}
|
||||
|
||||
public void startNewDay() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
|
||||
}
|
||||
|
||||
public void digOutGold() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.WORK);
|
||||
}
|
||||
|
||||
public void endDay() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
|
||||
}
|
||||
|
||||
private static void makeActions(Collection<DwarvenMineWorker> workers,
|
||||
DwarvenMineWorker.Action... actions) {
|
||||
for (DwarvenMineWorker worker : workers) {
|
||||
worker.action(actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now to use the facade
|
||||
|
||||
```
|
||||
DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();
|
||||
facade.startNewDay();
|
||||
// Dwarf gold digger wakes up.
|
||||
// Dwarf gold digger goes to the mine.
|
||||
// Dwarf cart operator wakes up.
|
||||
// Dwarf cart operator goes to the mine.
|
||||
// Dwarven tunnel digger wakes up.
|
||||
// Dwarven tunnel digger goes to the mine.
|
||||
facade.digOutGold();
|
||||
// Dwarf gold digger digs for gold.
|
||||
// Dwarf cart operator moves gold chunks out of the mine.
|
||||
// Dwarven tunnel digger creates another promising tunnel.
|
||||
facade.endDay();
|
||||
// Dwarf gold digger goes home.
|
||||
// Dwarf gold digger goes to sleep.
|
||||
// Dwarf cart operator goes home.
|
||||
// Dwarf cart operator goes to sleep.
|
||||
// Dwarven tunnel digger goes home.
|
||||
// Dwarven tunnel digger goes to sleep.
|
||||
```
|
||||
|
||||
## Applicability
|
||||
Use the Facade pattern when
|
||||
|
Before Width: | Height: | Size: 33 KiB |
@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.facade.DwarvenGoldmineFacade" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="160" width="376" x="50" y="263"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.facade.DwarvenMineWorker" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="231" width="171" x="50" y="463"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.facade.DwarvenCartOperator" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="181" x="-272" y="734"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="4" language="java" name="com.iluwatar.facade.DwarvenGoldDigger" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="169" x="-51" y="734"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<enumeration id="5" language="java" name="com.iluwatar.facade.DwarvenMineWorker.Action" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="178" width="164" x="158" y="734"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</enumeration>
|
||||
<class id="6" language="java" name="com.iluwatar.facade.DwarvenTunnelDigger" project="facade"
|
||||
file="/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="184" x="362" y="734"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<generalization id="7">
|
||||
<end type="SOURCE" refId="6"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</generalization>
|
||||
<generalization id="8">
|
||||
<end type="SOURCE" refId="4"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</generalization>
|
||||
<generalization id="9">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</generalization>
|
||||
<association id="10">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="11" name="workers"/>
|
||||
<multiplicity id="12" minimum="0" maximum="2147483647"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="2" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<nesting id="13">
|
||||
<end type="SOURCE" refId="2"/>
|
||||
<end type="TARGET" refId="5"/>
|
||||
</nesting>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
@ -1,60 +0,0 @@
|
||||
@startuml
|
||||
package com.iluwatar.facade {
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class DwarvenCartOperator {
|
||||
- LOGGER : Logger {static}
|
||||
+ DwarvenCartOperator()
|
||||
+ name() : String
|
||||
+ work()
|
||||
}
|
||||
class DwarvenGoldDigger {
|
||||
- LOGGER : Logger {static}
|
||||
+ DwarvenGoldDigger()
|
||||
+ name() : String
|
||||
+ work()
|
||||
}
|
||||
class DwarvenGoldmineFacade {
|
||||
- workers : List<DwarvenMineWorker>
|
||||
+ DwarvenGoldmineFacade()
|
||||
+ digOutGold()
|
||||
+ endDay()
|
||||
- makeActions(workers : Collection<DwarvenMineWorker>, actions : Action[]) {static}
|
||||
+ startNewDay()
|
||||
}
|
||||
abstract class DwarvenMineWorker {
|
||||
- LOGGER : Logger {static}
|
||||
+ DwarvenMineWorker()
|
||||
- action(action : Action)
|
||||
+ action(actions : Action[])
|
||||
+ goHome()
|
||||
+ goToMine()
|
||||
+ goToSleep()
|
||||
+ name() : String {abstract}
|
||||
+ wakeUp()
|
||||
+ work() {abstract}
|
||||
}
|
||||
~enum Action {
|
||||
+ GO_HOME {static}
|
||||
+ GO_TO_MINE {static}
|
||||
+ GO_TO_SLEEP {static}
|
||||
+ WAKE_UP {static}
|
||||
+ WORK {static}
|
||||
+ valueOf(name : String) : Action {static}
|
||||
+ values() : Action[] {static}
|
||||
}
|
||||
class DwarvenTunnelDigger {
|
||||
- LOGGER : Logger {static}
|
||||
+ DwarvenTunnelDigger()
|
||||
+ name() : String
|
||||
+ work()
|
||||
}
|
||||
}
|
||||
DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker
|
||||
Action ..+ DwarvenMineWorker
|
||||
DwarvenCartOperator --|> DwarvenMineWorker
|
||||
DwarvenGoldDigger --|> DwarvenMineWorker
|
||||
DwarvenTunnelDigger --|> DwarvenMineWorker
|
||||
@enduml
|
Before Width: | Height: | Size: 43 KiB |
@ -16,7 +16,100 @@ tags:
|
||||
Use sharing to support large numbers of fine-grained objects
|
||||
efficiently.
|
||||
|
||||

|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is no need to create new object for each of them. Instead one object instance can represent multiple shelf items so memory footprint remains small.
|
||||
|
||||
In plain words
|
||||
|
||||
> It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
|
||||
|
||||
**Programmatic example**
|
||||
|
||||
Translating our alchemist shop example from above. First of all we have different potion types
|
||||
|
||||
```
|
||||
public interface Potion {
|
||||
void drink();
|
||||
}
|
||||
|
||||
public class HealingPotion implements Potion {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HealingPotion.class);
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
||||
public class HolyWaterPotion implements Potion {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HolyWaterPotion.class);
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
||||
public class InvisibilityPotion implements Potion {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(InvisibilityPotion.class);
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then the actual Flyweight object which is the factory for creating potions
|
||||
|
||||
```
|
||||
public class PotionFactory {
|
||||
|
||||
private final Map<PotionType, Potion> potions;
|
||||
|
||||
public PotionFactory() {
|
||||
potions = new EnumMap<>(PotionType.class);
|
||||
}
|
||||
|
||||
Potion createPotion(PotionType type) {
|
||||
Potion potion = potions.get(type);
|
||||
if (potion == null) {
|
||||
switch (type) {
|
||||
case HEALING:
|
||||
potion = new HealingPotion();
|
||||
potions.put(type, potion);
|
||||
break;
|
||||
case HOLY_WATER:
|
||||
potion = new HolyWaterPotion();
|
||||
potions.put(type, potion);
|
||||
break;
|
||||
case INVISIBILITY:
|
||||
potion = new InvisibilityPotion();
|
||||
potions.put(type, potion);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And it can be used as below
|
||||
|
||||
```
|
||||
PotionFactory factory = new PotionFactory();
|
||||
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
|
||||
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
|
||||
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
|
||||
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
|
||||
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
|
||||
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
|
||||
```
|
||||
|
||||
## Applicability
|
||||
The Flyweight pattern's effectiveness depends heavily on how
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@ -1,135 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.flyweight.PoisonPotion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="129" x="297" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.flyweight.AlchemistShop" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="124" width="129" x="466" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.flyweight.HealingPotion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="129" x="635" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<enumeration id="4" language="java" name="com.iluwatar.flyweight.PotionType" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="178" width="185" x="72" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</enumeration>
|
||||
<class id="5" language="java" name="com.iluwatar.flyweight.PotionFactory" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="212" x="804" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="6" language="java" name="com.iluwatar.flyweight.InvisibilityPotion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="141" x="1056" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="7" language="java" name="com.iluwatar.flyweight.Potion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="88" width="129" x="797" y="472"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<class id="8" language="java" name="com.iluwatar.flyweight.StrengthPotion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="132" x="1237" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="9" language="java" name="com.iluwatar.flyweight.HolyWaterPotion" project="flyweight"
|
||||
file="/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="106" width="141" x="1409" y="254"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<realization id="10">
|
||||
<end type="SOURCE" refId="6"/>
|
||||
<end type="TARGET" refId="7"/>
|
||||
</realization>
|
||||
<association id="11">
|
||||
<end type="SOURCE" refId="2" navigable="false">
|
||||
<attribute id="12" name="topShelf"/>
|
||||
<multiplicity id="13" minimum="0" maximum="2147483647"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="7" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<realization id="14">
|
||||
<end type="SOURCE" refId="8"/>
|
||||
<end type="TARGET" refId="7"/>
|
||||
</realization>
|
||||
<association id="15">
|
||||
<end type="SOURCE" refId="2" navigable="false">
|
||||
<attribute id="16" name="bottomShelf"/>
|
||||
<multiplicity id="17" minimum="0" maximum="2147483647"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="7" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<realization id="18">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="7"/>
|
||||
</realization>
|
||||
<realization id="19">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="7"/>
|
||||
</realization>
|
||||
<realization id="20">
|
||||
<end type="SOURCE" refId="9"/>
|
||||
<end type="TARGET" refId="7"/>
|
||||
</realization>
|
||||
<association id="21">
|
||||
<end type="SOURCE" refId="5" navigable="false">
|
||||
<attribute id="22" name="potions"/>
|
||||
<multiplicity id="23" minimum="0" maximum="2147483647"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="7" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
@ -1,66 +0,0 @@
|
||||
@startuml
|
||||
package com.iluwatar.flyweight {
|
||||
class AlchemistShop {
|
||||
- LOGGER : Logger {static}
|
||||
- bottomShelf : List<Potion>
|
||||
- topShelf : List<Potion>
|
||||
+ AlchemistShop()
|
||||
+ enumerate()
|
||||
- fillShelves()
|
||||
+ getBottomShelf() : List<Potion>
|
||||
+ getTopShelf() : List<Potion>
|
||||
}
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class HealingPotion {
|
||||
- LOGGER : Logger {static}
|
||||
+ HealingPotion()
|
||||
+ drink()
|
||||
}
|
||||
class HolyWaterPotion {
|
||||
- LOGGER : Logger {static}
|
||||
+ HolyWaterPotion()
|
||||
+ drink()
|
||||
}
|
||||
class InvisibilityPotion {
|
||||
- LOGGER : Logger {static}
|
||||
+ InvisibilityPotion()
|
||||
+ drink()
|
||||
}
|
||||
class PoisonPotion {
|
||||
- LOGGER : Logger {static}
|
||||
+ PoisonPotion()
|
||||
+ drink()
|
||||
}
|
||||
interface Potion {
|
||||
+ drink() {abstract}
|
||||
}
|
||||
class PotionFactory {
|
||||
- potions : Map<PotionType, Potion>
|
||||
+ PotionFactory()
|
||||
~ createPotion(type : PotionType) : Potion
|
||||
}
|
||||
enum PotionType {
|
||||
+ HEALING {static}
|
||||
+ HOLY_WATER {static}
|
||||
+ INVISIBILITY {static}
|
||||
+ POISON {static}
|
||||
+ STRENGTH {static}
|
||||
+ valueOf(name : String) : PotionType {static}
|
||||
+ values() : PotionType[] {static}
|
||||
}
|
||||
class StrengthPotion {
|
||||
- LOGGER : Logger {static}
|
||||
+ StrengthPotion()
|
||||
+ drink()
|
||||
}
|
||||
}
|
||||
AlchemistShop --> "-topShelf" Potion
|
||||
HealingPotion ..|> Potion
|
||||
HolyWaterPotion ..|> Potion
|
||||
InvisibilityPotion ..|> Potion
|
||||
PoisonPotion ..|> Potion
|
||||
StrengthPotion ..|> Potion
|
||||
@enduml
|
Before Width: | Height: | Size: 62 KiB |
@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory;
|
||||
* <p>
|
||||
* It is responsible for reading and loading the contents of a given file.
|
||||
*/
|
||||
public class FileLoader implements Serializable{
|
||||
public class FileLoader implements Serializable {
|
||||
|
||||
/**
|
||||
* Generated serial version UID
|
||||
|
@ -30,7 +30,7 @@ import java.io.Serializable;
|
||||
* <p>
|
||||
* It is responsible for reacting to the user's actions and update the View component.
|
||||
*/
|
||||
public class FileSelectorPresenter implements Serializable{
|
||||
public class FileSelectorPresenter implements Serializable {
|
||||
|
||||
/**
|
||||
* Generated serial version UID
|
||||
|
@ -28,7 +28,7 @@ import java.io.Serializable;
|
||||
* This interface represents the View component in the Model-View-Presenter pattern. It can be
|
||||
* implemented by either the GUI components, or by the Stub.
|
||||
*/
|
||||
public interface FileSelectorView extends Serializable{
|
||||
public interface FileSelectorView extends Serializable {
|
||||
|
||||
/**
|
||||
* Opens the view.
|
||||
|
@ -28,7 +28,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* Application Test Entrypoint
|
||||
*/
|
||||
public class AppTest{
|
||||
public class AppTest {
|
||||
@Test
|
||||
public void test() throws IOException {
|
||||
String[] args = {};
|
||||
|
@ -29,4 +29,4 @@ Use the Naked Objects pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Richard Pawson - Naked Objects](https://isis.apache.org/resources/thesis/Pawson-Naked-Objects-thesis.pdf)
|
||||
* [Richard Pawson - Naked Objects](http://downloads.nakedobjects.net/resources/Pawson%20thesis.pdf)
|
||||
|
29
partial-response/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Partial Response
|
||||
folder: partial-response
|
||||
permalink: /patterns/partial-response/
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Java
|
||||
- KISS
|
||||
- YAGNI
|
||||
- Difficulty-Beginner
|
||||
---
|
||||
|
||||
## Intent
|
||||
Send partial response from server to client on need basis. Client will specify the the fields
|
||||
that it need to server, instead of serving all details for resource.
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
Use the Partial Response pattern when
|
||||
|
||||
* Client need only subset of data from resource.
|
||||
* To avoid too much data transfer over wire
|
||||
|
||||
## Credits
|
||||
|
||||
* [Common Design Patterns](https://cloud.google.com/apis/design/design_patterns)
|
||||
* [Partial Response in RESTful API Design](http://yaoganglian.com/2013/07/01/partial-response/)
|
65
partial-response/etc/partial-response.ucls
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.2.1" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
|
||||
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
|
||||
<class id="1" language="java" name="com.iluwatar.partialresponse.Video" project="partial-response"
|
||||
file="/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="322" y="457"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.partialresponse.FieldJsonMapper" project="partial-response"
|
||||
file="/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="772" y="412"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.partialresponse.VideoClientApp" project="partial-response"
|
||||
file="/partial-response/src/main/java/com/iluwatar/partialresponse/VideoClientApp.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="215" y="125"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="4" language="java" name="com.iluwatar.partialresponse.VideoResource" project="partial-response"
|
||||
file="/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="101" width="319" x="476" y="66"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<association id="5">
|
||||
<end type="SOURCE" refId="4" navigable="false">
|
||||
<attribute id="6" name="videos"/>
|
||||
<multiplicity id="7" minimum="0" maximum="2147483647"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="1" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<association id="8">
|
||||
<end type="SOURCE" refId="4" navigable="false">
|
||||
<attribute id="9" name="fieldJsonMapper"/>
|
||||
<multiplicity id="10" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="2" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
BIN
partial-response/etc/partial-response.urm.png
Normal file
After Width: | Height: | Size: 27 KiB |
31
partial-response/etc/partial-response.urm.puml
Normal file
@ -0,0 +1,31 @@
|
||||
@startuml
|
||||
package com.iluwatar.partialresponse {
|
||||
class FieldJsonMapper {
|
||||
+ FieldJsonMapper()
|
||||
- getString(video : Video, declaredField : Field) : String
|
||||
+ toJson(video : Video, fields : String[]) : String
|
||||
}
|
||||
class Video {
|
||||
- description : String
|
||||
- director : String
|
||||
- id : Integer
|
||||
- language : String
|
||||
- length : Integer
|
||||
- title : String
|
||||
+ Video(id : Integer, title : String, length : Integer, description : String, director : String, language : String)
|
||||
+ toString() : String
|
||||
}
|
||||
class VideoClientApp {
|
||||
- LOGGER : Logger {static}
|
||||
+ VideoClientApp()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class VideoResource {
|
||||
- fieldJsonMapper : FieldJsonMapper
|
||||
- videos : Map<Integer, Video>
|
||||
+ VideoResource(fieldJsonMapper : FieldJsonMapper, videos : Map<Integer, Video>)
|
||||
+ getDetails(id : Integer, fields : String[]) : String
|
||||
}
|
||||
}
|
||||
VideoResource --> "-fieldJsonMapper" FieldJsonMapper
|
||||
@enduml
|
49
partial-response/pom.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.17.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>partial-response</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The Partial response pattern is a design pattern in which client specifies fields to fetch to serve.
|
||||
* Here {@link App} is playing as client for {@link VideoResource} server.
|
||||
* Client ask for specific fields information in video to server.
|
||||
* <p>
|
||||
* <p>
|
||||
* {@link VideoResource} act as server to serve video information.
|
||||
*/
|
||||
|
||||
public class App {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Method as act client and request to server for video details.
|
||||
*
|
||||
* @param args program argument.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map<Integer, Video> videos = new HashMap<>();
|
||||
videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English"));
|
||||
videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese"));
|
||||
videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English"));
|
||||
VideoResource videoResource = new VideoResource(new FieldJsonMapper(), videos);
|
||||
|
||||
|
||||
LOGGER.info("Retrieving full response from server:-");
|
||||
LOGGER.info("Get all video information:");
|
||||
String videoDetails = videoResource.getDetails(1);
|
||||
LOGGER.info(videoDetails);
|
||||
|
||||
LOGGER.info("----------------------------------------------------------");
|
||||
|
||||
LOGGER.info("Retrieving partial response from server:-");
|
||||
LOGGER.info("Get video @id, @title, @director:");
|
||||
String specificFieldsDetails = videoResource.getDetails(3, "id", "title", "director");
|
||||
LOGGER.info(specificFieldsDetails);
|
||||
|
||||
LOGGER.info("Get video @id, @length:");
|
||||
String videoLength = videoResource.getDetails(3, "id", "length");
|
||||
LOGGER.info(videoLength);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Map a video to json
|
||||
*/
|
||||
public class FieldJsonMapper {
|
||||
|
||||
/**
|
||||
* @param video object containing video information
|
||||
* @param fields fields information to get
|
||||
* @return json of required fields from video
|
||||
*/
|
||||
public String toJson(Video video, String[] fields) throws Exception {
|
||||
StringBuilder json = new StringBuilder().append("{");
|
||||
|
||||
for (int i = 0, fieldsLength = fields.length; i < fieldsLength; i++) {
|
||||
json.append(getString(video, Video.class.getDeclaredField(fields[i])));
|
||||
if (i != fieldsLength - 1) {
|
||||
json.append(",");
|
||||
}
|
||||
}
|
||||
json.append("}");
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
private String getString(Video video, Field declaredField) throws IllegalAccessException {
|
||||
declaredField.setAccessible(true);
|
||||
Object value = declaredField.get(video);
|
||||
if (declaredField.get(video) instanceof Integer) {
|
||||
return "\"" + declaredField.getName() + "\"" + ": " + value;
|
||||
}
|
||||
return "\"" + declaredField.getName() + "\"" + ": " + "\"" + value.toString() + "\"";
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
/**
|
||||
* {@link Video} is a entity to serve from server.It contains all video related information..
|
||||
* <p>
|
||||
*/
|
||||
public class Video {
|
||||
private final Integer id;
|
||||
private final String title;
|
||||
private final Integer length;
|
||||
private final String description;
|
||||
private final String director;
|
||||
private final String language;
|
||||
|
||||
/**
|
||||
* @param id video unique id
|
||||
* @param title video title
|
||||
* @param length video length in minutes
|
||||
* @param description video description by publisher
|
||||
* @param director video director name
|
||||
* @param language video language {private, public}
|
||||
*/
|
||||
public Video(Integer id, String title, Integer length, String description, String director, String language) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.length = length;
|
||||
this.description = description;
|
||||
this.director = director;
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return json representaion of video
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{"
|
||||
+ "\"id\": " + id + ","
|
||||
+ "\"title\": \"" + title + "\","
|
||||
+ "\"length\": " + length + ","
|
||||
+ "\"description\": \"" + description + "\","
|
||||
+ "\"director\": \"" + director + "\","
|
||||
+ "\"language\": \"" + language + "\","
|
||||
+ "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The resource class which serves video information.
|
||||
* This class act as server in the demo. Which has all video details.
|
||||
*/
|
||||
public class VideoResource {
|
||||
private FieldJsonMapper fieldJsonMapper;
|
||||
private Map<Integer, Video> videos;
|
||||
|
||||
/**
|
||||
* @param fieldJsonMapper map object to json.
|
||||
* @param videos initialize resource with existing videos. Act as database.
|
||||
*/
|
||||
public VideoResource(FieldJsonMapper fieldJsonMapper, Map<Integer, Video> videos) {
|
||||
this.fieldJsonMapper = fieldJsonMapper;
|
||||
this.videos = videos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id video id
|
||||
* @param fields fields to get information about
|
||||
* @return full response if no fields specified else partial response for given field.
|
||||
*/
|
||||
public String getDetails(Integer id, String... fields) throws Exception {
|
||||
if (fields.length == 0) {
|
||||
return videos.get(id).toString();
|
||||
}
|
||||
return fieldJsonMapper.toJson(videos.get(id), fields);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Application test
|
||||
*/
|
||||
public class AppTest {
|
||||
|
||||
@Test
|
||||
public void main() throws Exception {
|
||||
String[] args = {};
|
||||
App.main(args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* tests {@link FieldJsonMapper}.
|
||||
*/
|
||||
public class FieldJsonMapperTest {
|
||||
private FieldJsonMapper mapper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mapper = new FieldJsonMapper();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnJsonForSpecifiedFieldsInVideo() throws Exception {
|
||||
String[] fields = new String[]{"id", "title", "length"};
|
||||
Video video = new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese");
|
||||
|
||||
String jsonFieldResponse = mapper.toJson(video, fields);
|
||||
|
||||
String expectedDetails = "{\"id\": 2,\"title\": \"Godzilla Resurgence\",\"length\": 120}";
|
||||
assertEquals(expectedDetails, jsonFieldResponse);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Gopinath Langote
|
||||
*
|
||||
* 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.partialresponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* tests {@link VideoResource}.
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VideoResourceTest {
|
||||
@Mock
|
||||
private FieldJsonMapper fieldJsonMapper;
|
||||
|
||||
private VideoResource resource;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Map<Integer, Video> videos = new HashMap<>();
|
||||
videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English"));
|
||||
videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese"));
|
||||
videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English"));
|
||||
resource = new VideoResource(fieldJsonMapper, videos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGiveVideoDetailsById() throws Exception {
|
||||
String actualDetails = resource.getDetails(1);
|
||||
|
||||
String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178,\"description\": "
|
||||
+ "\"epic science fiction film\",\"director\": \"James Cameron\",\"language\": \"English\",}";
|
||||
assertEquals(expectedDetails, actualDetails);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGiveSpecifiedFieldsInformationOfVideo() throws Exception {
|
||||
String[] fields = new String[]{"id", "title", "length"};
|
||||
|
||||
String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178}";
|
||||
when(fieldJsonMapper.toJson(any(Video.class), eq(fields))).thenReturn(expectedDetails);
|
||||
|
||||
String actualFieldsDetails = resource.getDetails(2, fields);
|
||||
|
||||
assertEquals(expectedDetails, actualFieldsDetails);
|
||||
}
|
||||
}
|
8
pom.xml
@ -145,6 +145,8 @@
|
||||
<module>cqrs</module>
|
||||
<module>event-sourcing</module>
|
||||
<module>data-transfer-object</module>
|
||||
<module>throttling</module>
|
||||
<module>partial-response</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -470,6 +472,10 @@
|
||||
<param>bridge</param>
|
||||
<param>composite</param>
|
||||
<param>decorator</param>
|
||||
<param>facade</param>
|
||||
<param>flyweight</param>
|
||||
<param>proxy</param>
|
||||
<param>chain</param>
|
||||
</skipForProjects>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@ -486,4 +492,4 @@
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
</project>
|
||||
</project>
|
@ -65,7 +65,7 @@ public class App {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md";
|
||||
private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/promise/README.md";
|
||||
private final ExecutorService executor;
|
||||
private final CountDownLatch stopLatch;
|
||||
|
||||
|
@ -27,11 +27,16 @@ import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A really simplified implementation of future that allows completing it successfully with a value
|
||||
* or exceptionally with an exception.
|
||||
*/
|
||||
class PromiseSupport<T> implements Future<T> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PromiseSupport.class);
|
||||
|
||||
private static final int RUNNING = 1;
|
||||
private static final int FAILED = 2;
|
||||
@ -80,40 +85,34 @@ class PromiseSupport<T> implements Future<T> {
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
} else if (state == FAILED) {
|
||||
throw new ExecutionException(exception);
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
synchronized (lock) {
|
||||
while (state == RUNNING) {
|
||||
lock.wait();
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
} else {
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
}
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
} else if (state == FAILED) {
|
||||
throw new ExecutionException(exception);
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
lock.wait(unit.toMillis(timeout));
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
} else if (state == FAILED) {
|
||||
throw new ExecutionException(exception);
|
||||
} else {
|
||||
throw new TimeoutException();
|
||||
throws ExecutionException, TimeoutException {
|
||||
synchronized (lock) {
|
||||
while (state == RUNNING) {
|
||||
try {
|
||||
lock.wait(unit.toMillis(timeout));
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.warn("Interrupted!", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
}
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
}
|
@ -18,7 +18,97 @@ Surrogate
|
||||
Provide a surrogate or placeholder for another object to control
|
||||
access to it.
|
||||
|
||||

|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> Imagine a tower where the local wizards go to study their spells. The ivory tower can only be accessed through a proxy which ensures that only the first three wizards can enter. Here the proxy represents the functionality of the tower and adds access control to it.
|
||||
|
||||
In plain words
|
||||
|
||||
> Using the proxy pattern, a class represents the functionality of another class.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Taking our wizard tower example from above. Firstly we have the wizard tower interface and the ivory tower class
|
||||
|
||||
```
|
||||
public interface WizardTower {
|
||||
|
||||
void enter(Wizard wizard);
|
||||
}
|
||||
|
||||
public class IvoryTower implements WizardTower {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
|
||||
|
||||
public void enter(Wizard wizard) {
|
||||
LOGGER.info("{} enters the tower.", wizard);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Then a simple wizard class
|
||||
|
||||
```
|
||||
public class Wizard {
|
||||
|
||||
private final String name;
|
||||
|
||||
public Wizard(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then we have the proxy to add access control to wizard tower
|
||||
|
||||
```
|
||||
public class WizardTowerProxy implements WizardTower {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);
|
||||
|
||||
private static final int NUM_WIZARDS_ALLOWED = 3;
|
||||
|
||||
private int numWizards;
|
||||
|
||||
private final WizardTower tower;
|
||||
|
||||
public WizardTowerProxy(WizardTower tower) {
|
||||
this.tower = tower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(Wizard wizard) {
|
||||
if (numWizards < NUM_WIZARDS_ALLOWED) {
|
||||
tower.enter(wizard);
|
||||
numWizards++;
|
||||
} else {
|
||||
LOGGER.info("{} is not allowed to enter!", wizard);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here is tower entering scenario
|
||||
|
||||
```
|
||||
WizardTowerProxy proxy = new WizardTowerProxy(new IvoryTower());
|
||||
proxy.enter(new Wizard("Red wizard")); // Red wizard enters the tower.
|
||||
proxy.enter(new Wizard("White wizard")); // White wizard enters the tower.
|
||||
proxy.enter(new Wizard("Black wizard")); // Black wizard enters the tower.
|
||||
proxy.enter(new Wizard("Green wizard")); // Green wizard is not allowed to enter!
|
||||
proxy.enter(new Wizard("Brown wizard")); // Brown wizard is not allowed to enter!
|
||||
```
|
||||
|
||||
## Applicability
|
||||
Proxy is applicable whenever there is a need for a more
|
||||
|
Before Width: | Height: | Size: 22 KiB |
@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.11" icons="true" automaticImage="PNG" always-add-relationships="false"
|
||||
generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
|
||||
router="FAN">
|
||||
<class id="1" language="java" name="com.iluwatar.proxy.WizardTowerProxy" project="proxy"
|
||||
file="/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="149" width="191" x="388" y="271"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.proxy.Wizard" project="proxy"
|
||||
file="/proxy/src/main/java/com/iluwatar/proxy/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="113" width="102" x="619" y="271"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="3" language="java" name="com.iluwatar.proxy.WizardTower" project="proxy"
|
||||
file="/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="77" width="116" x="388" y="460"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<class id="4" language="java" name="com.iluwatar.proxy.IvoryTower" project="proxy"
|
||||
file="/proxy/src/main/java/com/iluwatar/proxy/IvoryTower.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="113" width="116" x="761" y="271"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="5" language="java" name="com.iluwatar.proxy.App" project="proxy"
|
||||
file="/proxy/src/main/java/com/iluwatar/proxy/App.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="95" width="114" x="917" y="271"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<association id="6">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="7" name="tower"/>
|
||||
<multiplicity id="8" minimum="0" maximum="1"/>
|
||||
</end>
|
||||
<end type="TARGET" refId="3" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<realization id="9">
|
||||
<end type="SOURCE" refId="4"/>
|
||||
<end type="TARGET" refId="3"/>
|
||||
</realization>
|
||||
<realization id="10">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="3"/>
|
||||
</realization>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
@ -1,32 +0,0 @@
|
||||
@startuml
|
||||
package com.iluwatar.proxy {
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class IvoryTower {
|
||||
- LOGGER : Logger {static}
|
||||
+ IvoryTower()
|
||||
+ enter(wizard : Wizard)
|
||||
}
|
||||
class Wizard {
|
||||
- name : String
|
||||
+ Wizard(name : String)
|
||||
+ toString() : String
|
||||
}
|
||||
interface WizardTower {
|
||||
+ enter(Wizard) {abstract}
|
||||
}
|
||||
class WizardTowerProxy {
|
||||
- LOGGER : Logger {static}
|
||||
- NUM_WIZARDS_ALLOWED : int {static}
|
||||
- numWizards : int
|
||||
- tower : WizardTower
|
||||
+ WizardTowerProxy(tower : WizardTower)
|
||||
+ enter(wizard : Wizard)
|
||||
}
|
||||
}
|
||||
WizardTowerProxy --> "-tower" WizardTower
|
||||
IvoryTower ..|> WizardTower
|
||||
WizardTowerProxy ..|> WizardTower
|
||||
@enduml
|
@ -28,7 +28,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* Application Test Entrypoint
|
||||
*/
|
||||
public class AppTest{
|
||||
public class AppTest {
|
||||
@Test
|
||||
public void test() throws IOException {
|
||||
String[] args = {};
|
||||
|
19
throttling/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Throttling
|
||||
folder: throttling
|
||||
permalink: /patterns/throttling/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Java
|
||||
- Difficulty-Beginner
|
||||
---
|
||||
|
||||
## Intent
|
||||
Ensure that a given client is not able to access service resources more than the assigned limit.
|
||||
|
||||
## Applicability
|
||||
The Throttling pattern should be used:
|
||||
|
||||
* when a service access needs to be restricted to not have high impacts on the performance of the service.
|
||||
* when multiple clients are consuming the same service resources and restriction has to be made according to the usage per client.
|
BIN
throttling/etc/throttling-pattern.png
Normal file
After Width: | Height: | Size: 59 KiB |
29
throttling/etc/throttling.urm.puml
Normal file
@ -0,0 +1,29 @@
|
||||
@startuml
|
||||
package com.iluwatar.tls {
|
||||
class App {
|
||||
- LOGGER : Logger {static}
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
- makeServiceCalls(service : B2BService) {static}
|
||||
}
|
||||
~class B2BService {
|
||||
- LOGGER : Logger {static}
|
||||
- callsCounter : int
|
||||
- tenant : Tenant
|
||||
+ B2BService(tenant : Tenant)
|
||||
+ dummyCustomerApi() : int
|
||||
+ getCurrentCallsCount() : int
|
||||
- getRandomCustomerId() : int
|
||||
}
|
||||
class Tenant {
|
||||
- allowedCallsPerSecond : int
|
||||
- name : String
|
||||
+ Tenant(name : String, allowedCallsPerSecond : int)
|
||||
+ getAllowedCallsPerSecond() : int
|
||||
+ getName() : String
|
||||
+ setAllowedCallsPerSecond(allowedCallsPerSecond : int)
|
||||
+ setName(name : String)
|
||||
}
|
||||
}
|
||||
B2BService --> "-tenant" Tenant
|
||||
@enduml
|
44
throttling/pom.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.17.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>throttling</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
90
throttling/src/main/java/com/iluwatar/throttling/App.java
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 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.throttling;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iluwatar.throttling.timer.Throttler;
|
||||
import com.iluwatar.throttling.timer.ThrottleTimerImpl;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Throttling pattern is a design pattern to throttle or limit the use of resources or even a complete service by
|
||||
* users or a particular tenant. This can allow systems to continue to function and meet service level agreements,
|
||||
* even when an increase in demand places load on resources.
|
||||
* <p>
|
||||
* In this example we have ({@link App}) as the initiating point of the service.
|
||||
* This is a time based throttling, i.e. only a certain number of calls are allowed per second.
|
||||
* </p>
|
||||
* ({@link Tenant}) is the Tenant POJO class with which many tenants can be created
|
||||
* ({@link B2BService}) is the service which is consumed by the tenants and is throttled.
|
||||
*/
|
||||
public class App {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Application entry point
|
||||
* @param args main arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
Tenant adidas = new Tenant("Adidas", 5);
|
||||
Tenant nike = new Tenant("Nike", 6);
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
|
||||
executorService.execute(() -> makeServiceCalls(adidas));
|
||||
executorService.execute(() -> makeServiceCalls(nike));
|
||||
|
||||
executorService.shutdown();
|
||||
try {
|
||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Executor Service terminated: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make calls to the B2BService dummy API
|
||||
* @param service an instance of B2BService
|
||||
*/
|
||||
private static void makeServiceCalls(Tenant tenant) {
|
||||
Throttler timer = new ThrottleTimerImpl(10);
|
||||
B2BService service = new B2BService(timer);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
service.dummyCustomerApi(tenant);
|
||||
// Sleep is introduced to keep the output in check and easy to view and analyze the results.
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Thread interrupted: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iluwatar.throttling.timer.Throttler;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* A service which accepts a tenant and throttles the resource based on the time given to the tenant.
|
||||
*/
|
||||
class B2BService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(B2BService.class);
|
||||
|
||||
public B2BService(Throttler timer) {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return customer id which is randomly generated
|
||||
*/
|
||||
public int dummyCustomerApi(Tenant tenant) {
|
||||
String tenantName = tenant.getName();
|
||||
int count = CallsCount.getCount(tenantName);
|
||||
LOGGER.debug("Counter for {} : {} ", tenant.getName(), count);
|
||||
if (count >= tenant.getAllowedCallsPerSecond()) {
|
||||
LOGGER.error("API access per second limit reached for: {}", tenantName);
|
||||
return -1;
|
||||
}
|
||||
CallsCount.incrementCount(tenantName);
|
||||
return getRandomCustomerId();
|
||||
}
|
||||
|
||||
private int getRandomCustomerId() {
|
||||
return ThreadLocalRandom.current().nextInt(1, 10000);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A class to keep track of the counter of different Tenants
|
||||
* @author drastogi
|
||||
*
|
||||
*/
|
||||
public final class CallsCount {
|
||||
private static Map<String, Integer> tenantCallsCount = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Add a new tenant to the map.
|
||||
* @param tenantName name of the tenant.
|
||||
*/
|
||||
public static void addTenant(String tenantName) {
|
||||
if (!tenantCallsCount.containsKey(tenantName)) {
|
||||
tenantCallsCount.put(tenantName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the count of the specified tenant.
|
||||
* @param tenantName name of the tenant.
|
||||
*/
|
||||
public static void incrementCount(String tenantName) {
|
||||
tenantCallsCount.put(tenantName, tenantCallsCount.get(tenantName) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tenantName name of the tenant.
|
||||
* @return the count of the tenant.
|
||||
*/
|
||||
public static int getCount(String tenantName) {
|
||||
return tenantCallsCount.get(tenantName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the count of all the tenants in the map.
|
||||
*/
|
||||
public static void reset() {
|
||||
for (Entry<String, Integer> e : tenantCallsCount.entrySet()) {
|
||||
tenantCallsCount.put(e.getKey(), 0);
|
||||
}
|
||||
}
|
||||
}
|
57
throttling/src/main/java/com/iluwatar/throttling/Tenant.java
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
|
||||
/**
|
||||
* A Pojo class to create a basic Tenant with the allowed calls per second.
|
||||
*/
|
||||
public class Tenant {
|
||||
|
||||
private String name;
|
||||
private int allowedCallsPerSecond;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name Name of the tenant
|
||||
* @param allowedCallsPerSecond The number of calls allowed for a particular tenant.
|
||||
* @throws InvalidParameterException If number of calls is less than 0, throws exception.
|
||||
*/
|
||||
public Tenant(String name, int allowedCallsPerSecond) {
|
||||
if (allowedCallsPerSecond < 0) {
|
||||
throw new InvalidParameterException("Number of calls less than 0 not allowed");
|
||||
}
|
||||
this.name = name;
|
||||
this.allowedCallsPerSecond = allowedCallsPerSecond;
|
||||
CallsCount.addTenant(name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAllowedCallsPerSecond() {
|
||||
return allowedCallsPerSecond;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.throttling.timer;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import com.iluwatar.throttling.CallsCount;
|
||||
|
||||
/**
|
||||
* Implementation of throttler interface. This class resets the counter every second.
|
||||
* @author drastogi
|
||||
*
|
||||
*/
|
||||
public class ThrottleTimerImpl implements Throttler {
|
||||
|
||||
private int throttlePeriod;
|
||||
|
||||
public ThrottleTimerImpl(int throttlePeriod) {
|
||||
this.throttlePeriod = throttlePeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* A timer is initiated with this method. The timer runs every second and resets the
|
||||
* counter.
|
||||
*/
|
||||
public void start() {
|
||||
new Timer(true).schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
CallsCount.reset();
|
||||
}
|
||||
}, 0, throttlePeriod);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.throttling.timer;
|
||||
|
||||
/**
|
||||
* An interface for defining the structure of different types of throttling ways.
|
||||
* @author drastogi
|
||||
*
|
||||
*/
|
||||
public interface Throttler {
|
||||
|
||||
void start();
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Application test
|
||||
*/
|
||||
public class AppTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
final String[] args = {};
|
||||
App.main(args);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.iluwatar.throttling.timer.ThrottleTimerImpl;
|
||||
import com.iluwatar.throttling.timer.Throttler;
|
||||
|
||||
/**
|
||||
* B2BServiceTest class to test the B2BService
|
||||
*/
|
||||
public class B2BServiceTest {
|
||||
|
||||
@Test
|
||||
public void dummyCustomerApiTest() {
|
||||
Tenant tenant = new Tenant("testTenant", 2);
|
||||
Throttler timer = new ThrottleTimerImpl(10);
|
||||
B2BService service = new B2BService(timer);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
service.dummyCustomerApi(tenant);
|
||||
}
|
||||
|
||||
int counter = CallsCount.getCount(tenant.getName());
|
||||
Assert.assertTrue("Counter limit must be reached", counter == 2);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.throttling;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.iluwatar.throttling.Tenant;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
|
||||
/**
|
||||
* TenantTest to test the creation of Tenant with valid parameters.
|
||||
*/
|
||||
public class TenantTest {
|
||||
|
||||
@Test(expected = InvalidParameterException.class)
|
||||
public void constructorTest() {
|
||||
Tenant tenant = new Tenant("FailTenant", -1);
|
||||
}
|
||||
}
|