├── .gitignore
├── src
├── test
│ ├── java
│ │ └── jps
│ │ │ └── tutorial
│ │ │ └── java8
│ │ │ └── test
│ │ │ ├── TestSupport.java
│ │ │ ├── streams
│ │ │ ├── ComparatorTest.java
│ │ │ └── GroupTest.java
│ │ │ ├── TestExecutionListener.java
│ │ │ ├── patterns
│ │ │ ├── FactoryTest.java
│ │ │ ├── TemplateTest.java
│ │ │ ├── ObserverTest.java
│ │ │ ├── DecoratorTest.java
│ │ │ ├── CommandTest.java
│ │ │ ├── BaseTemplatePatternTest.java
│ │ │ ├── StrategyTest.java
│ │ │ └── ChainTest.java
│ │ │ └── DataUtils.java
│ └── resources
│ │ ├── testng.xml
│ │ └── log4j.properties
└── main
│ └── java
│ └── jsp
│ └── tutorial
│ └── java8
│ └── patterns
│ ├── decorator
│ ├── WebFilter.java
│ ├── SimpleFilter.java
│ ├── WebFilterDecorator.java
│ ├── WebMailsFilter.java
│ ├── NewsSitesFilter.java
│ ├── WebFiltersAlgorithms.java
│ └── readme.md
│ ├── template
│ ├── IEvaluationProcess.java
│ ├── EngineerEvaluationProcess.java
│ ├── ManagerEvaluationProcess.java
│ ├── EvaluationProcess.java
│ ├── EvaluationProcessLambda.java
│ ├── ProcessEvaluationAlgorithms.java
│ └── readme.md
│ ├── chain
│ ├── UnsupportedRequestException.java
│ ├── FirstLevelHDSupport.java
│ ├── ThirdLevelHDSupport.java
│ ├── SecondLevelHDSupport.java
│ ├── SupportRequest.java
│ ├── HDSupport.java
│ ├── HDSupportHandlerAlgorithms.java
│ └── readme.md
│ ├── strategy
│ ├── HiringStrategy.java
│ ├── GradeHiringStrategy.java
│ ├── DegreeRelevantHiringStrategy.java
│ ├── StrategyAlgorithms.java
│ └── readme.md
│ ├── observer
│ ├── JobListener.java
│ ├── SalesDepartment.java
│ ├── LogisticsDepartment.java
│ ├── EngineeringDepartment.java
│ ├── JobNotificationSystem.java
│ ├── CompanyJobNotificationSystem.java
│ ├── JobPorcessingAlgorithms.java
│ └── readme.md
│ ├── command
│ ├── MessageData.java
│ ├── SMSCommand.java
│ ├── EmailCommand.java
│ ├── MessageDispather.java
│ ├── SMSDispatcher.java
│ ├── EmailDispatcher.java
│ └── readme.md
│ ├── factory
│ ├── HomeComputer.java
│ ├── ServerComputer.java
│ ├── Computer.java
│ ├── ComputerFactory.java
│ ├── readme.md
│ └── ComputerSpecs.java
│ ├── JobInfo.java
│ ├── ContactInfo.java
│ ├── Employer.java
│ ├── VariousUtils.java
│ ├── Degree.java
│ └── Candidate.java
├── roadmap.md
├── readme.md
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | !.gitignore
3 | target/
4 | test-output/
5 | $*
6 | *~
7 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/TestSupport.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test;
2 |
3 | import org.testng.annotations.Listeners;
4 |
5 | /**
6 | * @author John Psoroulas
7 | *
8 | */
9 | @Listeners(value = {TestExecutionListener.class})
10 | public class TestSupport {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/resources/testng.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/roadmap.md:
--------------------------------------------------------------------------------
1 | ### Design patterns samples
2 |
3 | ##### Creational Design Patterns
4 | * Factory (x)
5 |
6 | ##### Behavioral Design Patterns
7 | * Strategy (x)
8 | * Template method (x)
9 | * Observer (x)
10 | * Chain of Responsibility (x)
11 | * Command (x)
12 |
13 | ##### Structural Design Patterns
14 | * Decorator (x)
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/WebFilter.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * The common API for the Web filters (the Component).
7 | *
8 | * @author John Psoroulas
9 | */
10 | public interface WebFilter {
11 |
12 | /**
13 | * Filter the specified URLs
14 | *
15 | * @param urls the URLs
16 | * @return the filtered URLs.
17 | */
18 | public List filter(List urls);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Root logger option
2 | log4j.rootLogger=INFO, stdout
3 |
4 | # Direct log messages to stdout
5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
6 | log4j.appender.stdout.Target=System.out
7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
8 | #log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c:%L - %m%n
9 | #log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
10 | log4j.appender.stdout.layout.ConversionPattern=%m%n
11 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/IEvaluationProcess.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * It is not included at the template method pattern. Added to have a common base API for
7 | * {@link EvaluationProcess} and {@link EvaluationProcessLambda} for programming purposes.
8 | *
9 | * @author John Psoroulas
10 | */
11 | public interface IEvaluationProcess {
12 |
13 | int evaluate(Candidate candidate);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/EngineerEvaluationProcess.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * The evaluation process for Engineer job type (the ).
7 | *
8 | * @author John Psoroulas
9 | */
10 | public class EngineerEvaluationProcess extends EvaluationProcess {
11 |
12 | @Override
13 | protected int specialStep(Candidate candidate) {
14 | return ProcessEvaluationAlgorithms.engineer(candidate);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/ManagerEvaluationProcess.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * The evaluation process for Manager job type (the <).
7 | *
8 | * @author John Psoroulas
9 | */
10 | public class ManagerEvaluationProcess extends EvaluationProcess {
11 |
12 | @Override
13 | protected int specialStep(Candidate candidate) {
14 | return ProcessEvaluationAlgorithms.management(candidate);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/UnsupportedRequestException.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | /**
4 | * Raised when all the levels of support cannot handle the {@link SupportRequest request}.
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class UnsupportedRequestException extends RuntimeException {
9 |
10 | private static final long serialVersionUID = 1L;
11 |
12 | public UnsupportedRequestException() {
13 | }
14 |
15 | public UnsupportedRequestException(String message) {
16 | super(message);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/strategy/HiringStrategy.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.strategy;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * The hiring strategy API (the Strategy).
7 | *
8 | * @author John Psoroulas
9 | */
10 | public interface HiringStrategy {
11 |
12 | /**
13 | * Decides whether a {@link Candidate} should be hired or not.
14 | *
15 | * @param candidate the candidate
16 | * @return true if the candidate is hired, otherwise false.
17 | */
18 | boolean hire(Candidate candidate);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/FirstLevelHDSupport.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | /**
4 | * The first level support (the ConcreteHandler1).
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class FirstLevelHDSupport extends HDSupport {
9 |
10 | public FirstLevelHDSupport() {
11 | }
12 |
13 | public FirstLevelHDSupport(HDSupport nextSupport) {
14 | super(nextSupport);
15 | }
16 |
17 | @Override
18 | public SupportRequest doHandle(SupportRequest request) {
19 | return HDSupportHandlerAlgorithms.firstLevelSupport(request);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/ThirdLevelHDSupport.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | /**
4 | * The third level support (the ConcreteHandler3).
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class ThirdLevelHDSupport extends HDSupport {
9 |
10 | public ThirdLevelHDSupport() {
11 | }
12 |
13 | public ThirdLevelHDSupport(HDSupport nextSupport) {
14 | super(nextSupport);
15 | }
16 |
17 | @Override
18 | public SupportRequest doHandle(SupportRequest request) {
19 | return HDSupportHandlerAlgorithms.thirdLevelSupport(request);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/JobListener.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import jsp.tutorial.java8.patterns.JobInfo;
4 |
5 | /**
6 | * The common API implemented by the company's departments (the Observer)
7 | * to get notified when a new job is available.
8 | *
9 | * @author John Psoroulas
10 | */
11 | public interface JobListener {
12 |
13 | /**
14 | * Invoked when a new job is available with the specific information.
15 | *
16 | * @param info the job's information
17 | */
18 | public void onNewJobAvailable(JobInfo info);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/SecondLevelHDSupport.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | /**
4 | * The second level support (the ConcreteHandler2).
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class SecondLevelHDSupport extends HDSupport {
9 |
10 | public SecondLevelHDSupport() {
11 | }
12 |
13 | public SecondLevelHDSupport(HDSupport nextSupport) {
14 | super(nextSupport);
15 | }
16 |
17 | @Override
18 | public SupportRequest doHandle(SupportRequest request) {
19 | return HDSupportHandlerAlgorithms.secondLevelSupport(request);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/MessageData.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | /**
4 | * A container for the minimal required data for sending a message for both email and SMS.
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class MessageData {
9 |
10 | private String[] recipients;
11 |
12 | private String message;
13 |
14 | public MessageData(String message, String... recipients) {
15 | this.message = message;
16 | this.recipients = recipients;
17 | }
18 |
19 | public String getMessage() {
20 | return message;
21 | }
22 |
23 | public String[] getRecipients() {
24 | return recipients;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/SMSCommand.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | /**
4 | * The command for sending a SMS (the ConcreteCommand2).
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class SMSCommand implements Runnable {
9 |
10 | private MessageDispather dispatcher;
11 |
12 | private MessageData messageData;
13 |
14 | public SMSCommand(MessageDispather dispatcher, MessageData messageData) {
15 | this.dispatcher = dispatcher;
16 | this.messageData = messageData;
17 | }
18 |
19 | @Override
20 | public void run() {
21 | dispatcher.send(messageData.getMessage(), messageData.getRecipients());
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/HomeComputer.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.factory;
2 |
3 | import java.util.Optional;
4 |
5 | import jsp.tutorial.java8.patterns.factory.ComputerSpecs.ComputerSpecsBuilder;
6 |
7 | /**
8 | * The computer implementation of the model {@link ComputerModel#HOME}.
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class HomeComputer extends Computer {
13 |
14 | public HomeComputer() {
15 | super(ComputerSpecsBuilder.buildStandardSpec());
16 | }
17 |
18 | public HomeComputer(ComputerSpecs spec) {
19 | super(Optional.ofNullable(spec)
20 | .orElseGet(ComputerSpecsBuilder::buildStandardSpec));
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/EmailCommand.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | /**
4 | * The command for sending an email (a ConcreteCommand1).
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class EmailCommand implements Runnable {
9 |
10 | private MessageDispather dispatcher;
11 |
12 | private MessageData messageData;
13 |
14 | public EmailCommand(MessageDispather dispatcher, MessageData messageData) {
15 | this.dispatcher = dispatcher;
16 | this.messageData = messageData;
17 | }
18 |
19 | @Override
20 | public void run() {
21 | dispatcher.send(messageData.getMessage(), messageData.getRecipients());
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/ServerComputer.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.factory;
2 |
3 | import java.util.Optional;
4 |
5 | import jsp.tutorial.java8.patterns.factory.ComputerSpecs.ComputerSpecsBuilder;
6 |
7 | /**
8 | * The computer implementation of the model {@link ComputerModel#SERVER}.
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class ServerComputer extends Computer {
13 |
14 | public ServerComputer() {
15 | super(ComputerSpecsBuilder.buildExtendedSpec());
16 | }
17 |
18 | public ServerComputer(ComputerSpecs spec) {
19 | super(Optional.ofNullable(spec)
20 | .orElseGet(ComputerSpecsBuilder::buildExtendedSpec));
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/Computer.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.factory;
2 |
3 | /**
4 | * The common interface of the family objects.
5 | * Holds some base functionality for the computer class hierarchy.
6 | *
7 | * @author John Psoroulas
8 | */
9 | public abstract class Computer {
10 |
11 | public enum ComputerModel {
12 | HOME, SERVER;
13 | }
14 |
15 | protected ComputerSpecs specs;
16 |
17 | public Computer(ComputerSpecs spec) {
18 | this.specs = spec;
19 | }
20 |
21 | /**
22 | * Returns the computer specs.
23 | *
24 | * @return the specs.
25 | */
26 | public ComputerSpecs getSpecs() {
27 | return specs;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/SimpleFilter.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * A simple web filter. Filters only some of the social media sites (the ConcreteComponent).
10 | *
11 | * @author John Psoroulas
12 | */
13 | public class SimpleFilter implements WebFilter {
14 |
15 | private static final Logger LOG = LoggerFactory.getLogger(SimpleFilter.class);
16 |
17 | @Override
18 | public List filter(List urls) {
19 | LOG.info("Filter social media sites");
20 | return WebFiltersAlgorithms.removeSocialNetworks(urls);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/WebFilterDecorator.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Wraps the {@link WebFilter} component to enable additional filtering (e.g. news
7 | * websites and web mails) (the Decorator).
8 | *
9 | * @author John Psoroulas
10 | */
11 | public class WebFilterDecorator implements WebFilter {
12 |
13 | /**
14 | * The wrapped filter.
15 | */
16 | protected WebFilter webFilter;
17 |
18 | public WebFilterDecorator(WebFilter webFilter) {
19 | this.webFilter = webFilter;
20 | }
21 |
22 | @Override
23 | public List filter(List urls) {
24 | return webFilter.filter(urls);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/strategy/GradeHiringStrategy.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.strategy;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * This hiring strategy uses the Candidate's degree grade as hiring criteria (the
7 | * ConcreteStrategy2).
8 | *
9 | * @author John Psoroulas
10 | */
11 | public class GradeHiringStrategy implements HiringStrategy {
12 |
13 | private int threashold;
14 |
15 | public GradeHiringStrategy() {
16 | this(8);
17 | }
18 |
19 | public GradeHiringStrategy(int threashold) {
20 | this.threashold = threashold;
21 | }
22 |
23 | @Override
24 | public boolean hire(Candidate candidate) {
25 | return StrategyAlgorithms.grade(candidate, threashold);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/JobInfo.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | /**
4 | * Holds the information about the job.
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class JobInfo {
9 |
10 | /**
11 | * Some job categories for easiest filtering by the departments.
12 | */
13 | public enum JobCategory {
14 | ENGINEERING, SALES, LOGISTICS;
15 | }
16 |
17 | private JobCategory category;
18 |
19 | private String description;
20 |
21 | public JobInfo(JobCategory category, String description) {
22 | this.category = category;
23 | this.description = description;
24 | }
25 |
26 | public JobCategory getJobCategory() {
27 | return category;
28 | }
29 |
30 | public String getDescription() {
31 | return description;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/WebMailsFilter.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * Filters the web-mails (the ConcreteDecorator1}).
10 | *
11 | * @author John Psoroulas
12 | */
13 | public class WebMailsFilter extends WebFilterDecorator {
14 |
15 | private static final Logger LOG = LoggerFactory.getLogger(WebMailsFilter.class);
16 |
17 | public WebMailsFilter(WebFilter webFilter) {
18 | super(webFilter);
19 | }
20 |
21 | @Override
22 | public List filter(List urls) {
23 | LOG.info("Filter web mails");
24 | return WebFiltersAlgorithms.removeWebMails(super.filter(urls));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/NewsSitesFilter.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * Filters the news web sites (the ConcreteDecorator2}).
10 | *
11 | * @author John Psoroulas
12 | */
13 | public class NewsSitesFilter extends WebFilterDecorator {
14 |
15 | private static final Logger LOG = LoggerFactory.getLogger(NewsSitesFilter.class);
16 |
17 | public NewsSitesFilter(WebFilter webFilter) {
18 | super(webFilter);
19 | }
20 |
21 | @Override
22 | public List filter(List urls) {
23 | LOG.info("Filter news web sites");
24 | return WebFiltersAlgorithms.removeNewsSites(super.filter(urls));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/SalesDepartment.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import jsp.tutorial.java8.patterns.JobInfo;
7 |
8 | /**
9 | * The company's sales department (the ConcreteObserver2), implements the
10 | * {@link JobListener} to receive notifications when a new job is available.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public class SalesDepartment implements JobListener {
15 |
16 | private static final Logger LOG = LoggerFactory.getLogger(LogisticsDepartment.class);
17 |
18 | @Override
19 | public void onNewJobAvailable(JobInfo job) {
20 | LOG.info("New job notification received by Sales department");
21 | JobPorcessingAlgorithms.sales(job);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/LogisticsDepartment.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import jsp.tutorial.java8.patterns.JobInfo;
7 |
8 | /**
9 | * The company's logistics department (the ConcreteObserver3), implements the
10 | * {@link JobListener} to receive notifications when a new job is available.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public class LogisticsDepartment implements JobListener {
15 |
16 | private static final Logger LOG = LoggerFactory.getLogger(LogisticsDepartment.class);
17 |
18 | @Override
19 | public void onNewJobAvailable(JobInfo job) {
20 | LOG.info("New job notification received by Logistics department");
21 | JobPorcessingAlgorithms.logistics(job);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/EngineeringDepartment.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import jsp.tutorial.java8.patterns.JobInfo;
7 |
8 | /**
9 | * The company's engineering department (the ConcreteObserver1), implements the
10 | * {@link JobListener} to receive notifications when a new job is available.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public class EngineeringDepartment implements JobListener {
15 |
16 | private static final Logger LOG = LoggerFactory.getLogger(EngineeringDepartment.class);
17 |
18 | @Override
19 | public void onNewJobAvailable(JobInfo job) {
20 | LOG.info("New job notification received by Engineering department");
21 | JobPorcessingAlgorithms.engineering(job);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/strategy/DegreeRelevantHiringStrategy.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.strategy;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
5 |
6 | /**
7 | * This hiring strategy uses the Candidate's degree's field as hiring criteria (the
8 | * ConcreteStrategy1).
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class DegreeRelevantHiringStrategy implements HiringStrategy {
13 |
14 | private DegreeField degreeField;
15 |
16 | public DegreeRelevantHiringStrategy() {
17 | this(DegreeField.ENGINEERING);
18 | }
19 |
20 | public DegreeRelevantHiringStrategy(DegreeField degreeField) {
21 | this.degreeField = degreeField;
22 | }
23 |
24 | @Override
25 | public boolean hire(Candidate candidate) {
26 | return StrategyAlgorithms.degree(candidate, degreeField);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/MessageDispather.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | import java.util.Arrays;
4 |
5 | import org.apache.commons.lang3.Validate;
6 |
7 | /**
8 | * A common API for {@link SMSDispatcher} and {@link EmailDispatcher}.
9 | * This is not prerequisite for the command pattern since a 'receiver' could
10 | * be any object. This base class is added only for code readability and DRY.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public abstract class MessageDispather {
15 |
16 | public void send(String message, String... recipients) {
17 | // Perform some validation
18 | Validate.notNull(message, "Undefined message!");
19 | Validate.notEmpty(recipients, "At least one recipient should be specified");
20 | Arrays.stream(recipients).forEach(Validate::notBlank);
21 |
22 | // Call the actual functionality
23 | doSend(message, recipients);
24 | }
25 |
26 | protected abstract void doSend(String message, String... recipients);
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/JobNotificationSystem.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import jsp.tutorial.java8.patterns.JobInfo;
4 |
5 | /**
6 | * The abstraction for the Job system notification (the Subject).
7 | *
8 | * @author John Psoroulas
9 | */
10 | public interface JobNotificationSystem {
11 |
12 | /**
13 | * Registers the specific listener/observer to receive notifications when a new internal job is
14 | * available.
15 | *
16 | * @param listener the listener
17 | */
18 | void addListener(JobListener listener);
19 |
20 | /**
21 | * Unregisters the specific listener/observer from receiving notifications when a new internal job
22 | * is available.
23 | *
24 | * @param listener the listener
25 | */
26 | void removeListener(JobListener listener);
27 |
28 | /**
29 | * Notifies the registered listeners about the new internal job.
30 | *
31 | * @param info contains information about the job
32 | */
33 | void notify(JobInfo info);
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/SMSDispatcher.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | import java.util.Arrays;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import jsp.tutorial.java8.patterns.VariousUtils;
10 |
11 | /**
12 | * The class that actually knows how to send a SMS message (the Receiver2).
13 | * The command pattern does not dictate the 'receivers' to have any common API.
14 | * It is just used for readability purposes.
15 | *
16 | * @author John Psoroulas
17 | */
18 | public class SMSDispatcher extends MessageDispather {
19 |
20 | private static final Logger LOG = LoggerFactory.getLogger(SMSDispatcher.class);
21 |
22 | @Override
23 | public void doSend(String message, String... recipients) {
24 | /* Some dummy implementation */
25 | Arrays.stream(recipients).forEach(
26 | recipient -> {
27 | LOG.info("Sending SMS to {}, : {}", recipient, message);
28 | VariousUtils.keepBusy(TimeUnit.MILLISECONDS, 10);
29 | });
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/EmailDispatcher.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.command;
2 |
3 | import java.util.Arrays;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import jsp.tutorial.java8.patterns.VariousUtils;
10 |
11 | /**
12 | * The class that actually knows how to send a email message (the Receiver1).
13 | * The command pattern does not dictate the 'receivers' to have any common API.
14 | * It is just used for readability purposes.
15 | *
16 | * @author John Psoroulas
17 | */
18 | public class EmailDispatcher extends MessageDispather {
19 |
20 | private static final Logger LOG = LoggerFactory.getLogger(EmailDispatcher.class);
21 |
22 | @Override
23 | public void doSend(String message, String... recipients) {
24 | /* Some dummy implementation */
25 | Arrays.stream(recipients).forEach(
26 | recipient -> {
27 | LOG.info("Sending email to {}, : {}", recipient, message);
28 | VariousUtils.keepBusy(TimeUnit.MILLISECONDS, 10);
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/CompanyJobNotificationSystem.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import org.apache.commons.lang3.event.EventListenerSupport;
4 |
5 | import jsp.tutorial.java8.patterns.JobInfo;
6 |
7 | /**
8 | * The job notification system implementation for the company (the ConcreteSubject).
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class CompanyJobNotificationSystem implements JobNotificationSystem {
13 |
14 | /**
15 | * This object holds the company's departments list interested in receiving notifications when a
16 | * new job is available.
17 | */
18 | private EventListenerSupport listeners = EventListenerSupport.create(JobListener.class);
19 |
20 | @Override
21 | public void addListener(JobListener listener) {
22 | listeners.addListener(listener);
23 | }
24 |
25 | @Override
26 | public void removeListener(JobListener listener) {
27 | listeners.removeListener(listener);
28 | }
29 |
30 | @Override
31 | public void notify(JobInfo info) {
32 | listeners.fire().onNewJobAvailable(info);
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/ContactInfo.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | import org.apache.commons.lang3.Validate;
4 |
5 | /**
6 | * Candidate contact information.
7 | *
8 | * @author John Psoroulas
9 | */
10 | public class ContactInfo {
11 |
12 | private String contact;
13 |
14 | private boolean email;
15 |
16 | public ContactInfo(String contact, boolean email) {
17 | Validate.notBlank("Undefined contact!");
18 | this.contact = contact;
19 | this.email = email;
20 | }
21 |
22 | public ContactInfo(String contact) {
23 | this(contact, true);
24 | }
25 |
26 | public String getContact() {
27 | return contact;
28 | }
29 |
30 | public void setContact(String contact) {
31 | this.contact = contact;
32 | }
33 |
34 | public boolean isEmail() {
35 | return email;
36 | }
37 |
38 | public void setEmail(boolean email) {
39 | this.email = email;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | StringBuilder builder = new StringBuilder();
45 | builder
46 | .append("ContactInfo [contact=")
47 | .append(contact)
48 | .append(", email=")
49 | .append(email)
50 | .append("]");
51 | return builder.toString();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/EvaluationProcess.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 |
5 | /**
6 | * The Template class for job candidates evaluation process.
7 | *
8 | * @author John Psoroulas
9 | */
10 | public abstract class EvaluationProcess implements IEvaluationProcess {
11 |
12 | /**
13 | * Evaluates the {@link Candidate} for a specific job type.
14 | *
15 | * @param candidate, the candidate
16 | * @return the candidate score
17 | */
18 | @Override
19 | public int evaluate(Candidate candidate) {
20 | return commonStep(candidate) + specialStep(candidate);
21 | }
22 |
23 | /**
24 | * The first common evaluation step is expressed with an algorithm that considers the age of the
25 | * candidate.
26 | *
27 | * @param candidate the candidate
28 | * @return the score
29 | */
30 | protected int commonStep(Candidate candidate) {
31 | return ProcessEvaluationAlgorithms.common(candidate);
32 | }
33 |
34 | /**
35 | * The second evaluation step depends on the job type.
36 | *
37 | * @param candidate
38 | * @return the candidate's score
39 | */
40 | protected abstract int specialStep(Candidate candidate);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/Employer.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | import java.util.Optional;
4 |
5 | import jsp.tutorial.java8.patterns.strategy.HiringStrategy;
6 |
7 | /**
8 | * The employer that decides who candidate is hired based on the applied {@link HiringStrategy}.
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class Employer {
13 |
14 | /* Lets assume that the Employer will have the authorization to hire people,
15 | * if the a hiringStrategy is set. This can be expressed with the Optional in Java 8 */
16 | private Optional hiringStrategy = Optional.empty();
17 |
18 | public Employer() {
19 | }
20 |
21 | public Employer(HiringStrategy hiringStrategy) {
22 | this.hiringStrategy = Optional.of(hiringStrategy);
23 | }
24 |
25 | public void setHiringStrategy(HiringStrategy hiringStrategy) {
26 | this.hiringStrategy = Optional.ofNullable(hiringStrategy);
27 | }
28 |
29 | public boolean hire(Candidate candidate) {
30 | /* If the hiringStrategy is set, the 'mapper' at the map is called which determines the
31 | * result of the method call. Otherwise, returns false. The 'orElseThrow can be used' according
32 | * the requirements */
33 | return hiringStrategy
34 | .map(s -> s.hire(candidate))
35 | .orElse(Boolean.FALSE);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/SupportRequest.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | /**
4 | * The support client request.
5 | *
6 | * @author John Psoroulas
7 | */
8 | public class SupportRequest {
9 |
10 | /**
11 | * Some request types (used by the various support levels to decide
12 | * whether can handle a request or not).
13 | */
14 | public enum RequestType {
15 | LEVEL1, LEVEL2, LEVEL3, LEVEL4
16 | }
17 |
18 | private RequestType type;
19 |
20 | private boolean handled = false;
21 |
22 | public SupportRequest() {
23 | this.type = RequestType.LEVEL1;
24 | }
25 |
26 | public SupportRequest(RequestType type) {
27 | this.type = type;
28 | }
29 |
30 | public boolean isHandled() {
31 | return handled;
32 | }
33 |
34 | public void setHandled(boolean handled) {
35 | this.handled = handled;
36 | }
37 |
38 | public RequestType getType() {
39 | return type;
40 | }
41 |
42 | public void setType(RequestType type) {
43 | this.type = type;
44 | }
45 |
46 | @Override
47 | public String toString() {
48 | StringBuilder builder = new StringBuilder();
49 | builder
50 | .append("SupportRequest [type=")
51 | .append(type)
52 | .append(", handled=")
53 | .append(handled)
54 | .append("]");
55 | return builder.toString();
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/strategy/StrategyAlgorithms.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.strategy;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
5 |
6 | /**
7 | * Provides the generic forms of the applied hiring strategies algorithms.
8 | *
9 | * @author John Psoroulas
10 | */
11 | public interface StrategyAlgorithms {
12 |
13 | /**
14 | * Degree relevant hiring strategy algorithm, the specified candidate is hired if he has a
15 | * degree relevant to the specified field.
16 | *
17 | * @param candidate the candidate
18 | * @param field the degree field
19 | * @return true if the candidate is hired, otherwise false.
20 | */
21 | static boolean degree(Candidate candidate, DegreeField field) {
22 | return candidate.getDegrees().stream()
23 | .anyMatch(d -> field == d.getField());
24 | }
25 |
26 | /**
27 | * Grade hiring strategy algorithm, the specified candidate is hired if he has any degree with
28 | * grade greater than the specified threshold.
29 | *
30 | * @param candidate the candidate
31 | * @param threshold the threshold
32 | * @return true if the candidate is hired, otherwise false.
33 | */
34 | static boolean grade(Candidate candidate, int threshold) {
35 | return candidate.getDegrees().stream()
36 | .anyMatch(d -> threshold <= d.getGrade());
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/HDSupport.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | import org.apache.commons.lang3.Validate;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | /**
8 | * The support levels abstraction (the Handler).
9 | *
10 | * @author John Psoroulas
11 | */
12 | public abstract class HDSupport {
13 |
14 | private static final Logger LOG = LoggerFactory.getLogger(HDSupport.class);
15 |
16 | /**
17 | * Holds the next support level.
18 | */
19 | private HDSupport nextSupport;
20 |
21 | public HDSupport() {
22 | }
23 |
24 | public HDSupport(HDSupport nextSupport) {
25 | this.nextSupport = nextSupport;
26 | }
27 |
28 | public SupportRequest handle(SupportRequest request) {
29 | Validate.notNull(request, "Support request is required!");
30 | request = doHandle(request);
31 | if(request.isHandled()) {
32 | LOG.info("Request type {} handled by {}",
33 | request.getType(), getClass().getSimpleName());
34 | return request;
35 | }else {
36 | if(null != nextSupport) {
37 | return nextSupport.handle(request);
38 | }
39 | throw new UnsupportedRequestException(request.getType().toString());
40 | }
41 | }
42 |
43 | public HDSupport getNextSupport() {
44 | return nextSupport;
45 | }
46 |
47 | public void setNextSupport(HDSupport nextSupport) {
48 | this.nextSupport = nextSupport;
49 | }
50 |
51 | protected abstract SupportRequest doHandle(SupportRequest request);
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/WebFiltersAlgorithms.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.decorator;
2 |
3 | import java.util.List;
4 | import java.util.stream.Collectors;
5 |
6 | /**
7 | * The web filters algorithms.
8 | *
9 | * @author John Psoroulas
10 | */
11 | public interface WebFiltersAlgorithms {
12 |
13 | /**
14 | * Removes from the specified URL list, the entries that do not refer to social network sites.
15 | *
16 | * @param url, the URL list
17 | * @return the updated list
18 | */
19 | static List removeSocialNetworks(List urls) {
20 | return urls.stream()
21 | .filter(url -> !(url.contains("facebook")
22 | || url.contains("twitter")))
23 | .collect(Collectors.toList());
24 | }
25 |
26 | /**
27 | * Removes from the specified URL list, the entries that do not refer to 'web-mails' sites.
28 | *
29 | * @param url, the URL list
30 | * @return the updated list
31 | */
32 | static List removeWebMails(List urls) {
33 | return urls.stream()
34 | .filter(url -> !url.contains("mail"))
35 | .collect(Collectors.toList());
36 | }
37 |
38 | /**
39 | * Removes from the specified URL list, the entries that do not refer to 'news' sites.
40 | *
41 | * @param url, the URL list
42 | * @return the updated list
43 | */
44 | static List removeNewsSites(List urls) {
45 | return urls.stream()
46 | .filter(url -> !url.contains("news"))
47 | .collect(Collectors.toList());
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/EvaluationProcessLambda.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import java.util.function.ToIntFunction;
4 |
5 | import jsp.tutorial.java8.patterns.Candidate;
6 |
7 | /**
8 | * The class for job candidates evaluation process using lambda approach.
9 | *
10 | * @author John Psoroulas
11 | */
12 | public class EvaluationProcessLambda implements IEvaluationProcess {
13 |
14 | private ToIntFunction specialEvaluation;
15 |
16 | public EvaluationProcessLambda(ToIntFunction specialEvaluation) {
17 | this.specialEvaluation = specialEvaluation;
18 | }
19 |
20 | public ToIntFunction getSpecialEvaluation() {
21 | return specialEvaluation;
22 | }
23 |
24 | /**
25 | * Evaluates the {@link Candidate} for a specific job type.
26 | *
27 | * @param candidate, the candidate
28 | * @param specialEvaluation the special evaluation lambda function depended on the job type
29 | * @return the candidate score
30 | */
31 | @Override
32 | public int evaluate(Candidate candidate) {
33 | return commonStep(candidate) + getSpecialEvaluation().applyAsInt(candidate);
34 | }
35 |
36 | /**
37 | * The first common evaluation step is expressed with an algorithm that considers the age of the
38 | * candidate.
39 | *
40 | * @param candidate the candidate
41 | * @return the candidate's score
42 | */
43 | protected int commonStep(Candidate candidate) {
44 | return ProcessEvaluationAlgorithms.common(candidate);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/ProcessEvaluationAlgorithms.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.template;
2 |
3 | import jsp.tutorial.java8.patterns.Candidate;
4 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
5 |
6 | /**
7 | * The applied process evaluation algorithms with the form of lambdas
8 | * To comply with the DRY principle, use them at both 'old' and 'lambda' way implementations.
9 | *
10 | * @author John Psoroulas
11 | */
12 | public interface ProcessEvaluationAlgorithms {
13 | /**
14 | * Give some extra points if the specified candidate is less than 50 years old.
15 | *
16 | * @param c the candidate
17 | * @return the score
18 | */
19 | static int common(Candidate c) {
20 | return c.getAge() > 50
21 | ? 10
22 | : 20;
23 | }
24 |
25 | /**
26 | * Give 10 extra points for each candidate's degree relevant with engineering and physical
27 | * sciences.
28 | *
29 | * @param c the candidate
30 | * @return the score
31 | */
32 | static int engineer(Candidate c) {
33 | return c.getDegrees().stream()
34 | .mapToInt(d -> DegreeField.MANAGEMENT == d.getField()
35 | ? 10
36 | : 20)
37 | .sum();
38 | }
39 |
40 | /**
41 | * Give 10 points if the specified candidate has a degree relevant with Management otherwise no
42 | * points are given. Only one Management degree counts to the score.
43 | *
44 | * @param c the candidate
45 | * @return the score
46 | */
47 | static int management(Candidate c) {
48 | return c.getDegrees().stream()
49 | .filter(d -> DegreeField.MANAGEMENT == d.getField())
50 | .findAny()
51 | .map(d -> 10)
52 | .orElse(0);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/HDSupportHandlerAlgorithms.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.chain;
2 |
3 | import jsp.tutorial.java8.patterns.chain.SupportRequest.RequestType;
4 |
5 | /**
6 | * Provides the support levels algorithms for handling the {@link SupportRequest}.
7 | *
8 | * @author John Psoroulas
9 | */
10 | public interface HDSupportHandlerAlgorithms {
11 |
12 | /**
13 | * The first level support algorithm implementation for a support request.
14 | *
15 | * @param request the support request.
16 | * @return the support request
17 | */
18 | static SupportRequest firstLevelSupport(SupportRequest request) {
19 | /* Can handle only the request LEVEL1 */
20 | if(RequestType.LEVEL1 == request.getType()) {
21 | request.setHandled(true);
22 | }
23 | return request;
24 | }
25 |
26 | /**
27 | * The second level support algorithm implementation for a support request.
28 | *
29 | * @param request the support request.
30 | * @return the support request
31 | */
32 | static SupportRequest secondLevelSupport(SupportRequest request) {
33 | /* Can handle only the request LEVEL2 */
34 | if(RequestType.LEVEL2 == request.getType()) {
35 | request.setHandled(true);
36 | }
37 | return request;
38 | }
39 |
40 | /**
41 | * The third level support algorithm for a support request.
42 | *
43 | * @param request the support request.
44 | * @return the support request
45 | */
46 | static SupportRequest thirdLevelSupport(SupportRequest request) {
47 | /* Can handle only the request LEVEL3 */
48 | if(RequestType.LEVEL3 == request.getType()) {
49 | request.setHandled(true);
50 | }
51 | return request;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/ComputerFactory.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.factory;
2 |
3 | import java.util.EnumMap;
4 | import java.util.function.Supplier;
5 |
6 | import jsp.tutorial.java8.patterns.factory.Computer.ComputerModel;
7 |
8 | /**
9 | * The factory for creating {@link Computer}.
10 | *
11 | * @author John Psoroulas
12 | */
13 | public final class ComputerFactory {
14 |
15 | private ComputerFactory() {
16 | }
17 |
18 | /**
19 | * Creates a {@link Computer} of the specified model using the 'old-way'.
20 | *
21 | * @param model the model
22 | * @return the computer
23 | */
24 | public static Computer create(ComputerModel model) {
25 | Computer computer = null;
26 | switch(model) {
27 | case HOME:
28 | computer = new HomeComputer();
29 | break;
30 | case SERVER:
31 | computer = new ServerComputer();
32 | break;
33 | default:
34 | throw new IllegalStateException("Model " + model + " is not available yet!");
35 | }
36 | return computer;
37 | }
38 |
39 | /**
40 | * Holds the computer constructors.
41 | */
42 | private static final EnumMap> customComputers =
43 | new EnumMap<>(ComputerModel.class);
44 | static {
45 | customComputers.put(ComputerModel.HOME, HomeComputer::new);
46 | customComputers.put(ComputerModel.SERVER, ServerComputer::new);
47 | }
48 |
49 | /**
50 | * Creates a {@link Computer} of the specified model using the 'lambda-way'.
51 | *
52 | * @param model the model
53 | * @return the computer
54 | */
55 | public static Computer createLambda(ComputerModel model) {
56 | return customComputers.get(model).get();
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/JobPorcessingAlgorithms.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.observer;
2 |
3 | import jsp.tutorial.java8.patterns.JobInfo;
4 | import jsp.tutorial.java8.patterns.JobInfo.JobCategory;
5 |
6 | /**
7 | * Holds the departments processing algorithms when get notified about the new job.
8 | *
9 | * @author John Psoroulas
10 | */
11 | public interface JobPorcessingAlgorithms {
12 |
13 | /**
14 | * The job processing algorithm implementation for {@link EngineeringDepartment}.
15 | *
16 | * @param job the job information
17 | */
18 | public static void engineering(JobInfo job) {
19 | /* Some dummy logic here, for example reject the job immediately for any job category but engineering */
20 | if(JobCategory.ENGINEERING != job.getJobCategory()) {
21 | return;
22 | }
23 | /* Some other logic here ... */
24 | }
25 |
26 | /**
27 | * The job processing algorithm implementation for {@link LogisticsDepartment}.
28 | *
29 | * @param job the job information
30 | */
31 | public static void logistics(JobInfo job) {
32 | /* Some dummy logic here, for example reject the job immediately for job category engineering */
33 | if(JobCategory.LOGISTICS == job.getJobCategory()) {
34 | return;
35 | }
36 | /* Some other logic here ... */
37 | }
38 |
39 | /**
40 | * The job processing algorithm implementation for {@link SalesDepartment}.
41 | *
42 | * @param job the job information
43 | */
44 | public static void sales(JobInfo job) {
45 | /* Some dummy logic here, for example reject the job immediately for job category logistics */
46 | if(JobCategory.LOGISTICS == job.getJobCategory()) {
47 | return;
48 | }
49 | /* Some other logic here ... */
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/VariousUtils.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | import java.util.concurrent.ExecutorService;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | /**
10 | *
11 | * @author John Psoroulas
12 | */
13 | public final class VariousUtils {
14 |
15 | private static final Logger LOG = LoggerFactory.getLogger(VariousUtils.class);
16 |
17 | private VariousUtils() {
18 | }
19 |
20 | public static void keepBusy(TimeUnit unit, long time) {
21 | try {
22 | unit.sleep(time);
23 | }catch(InterruptedException e) {
24 | /* Restore threads interruption status */
25 | Thread.currentThread().interrupt();
26 | }
27 | }
28 |
29 | /**
30 | * Shutdown the executor after waiting for the specified timeout (in ms)
31 | * @param executor
32 | * @param timeout
33 | */
34 | public static void shutdownExecutorService(ExecutorService executor, long timeout) {
35 | shutdownExecutorService(
36 | executor,
37 | timeout,
38 | TimeUnit.MILLISECONDS,
39 | 1);
40 | }
41 |
42 | public static void shutdownExecutorService(
43 | ExecutorService executor,
44 | long timeout,
45 | TimeUnit units,
46 | int maxAttempts) {
47 | /* Disable new tasks from being submitted and waits currently running tasks to terminate */
48 | executor.shutdown();
49 | try {
50 | int attempt = 0;
51 | do {
52 | LOG.info("Awaiting tasks completion ...");
53 | attempt++;
54 | /* Wait a while for existing tasks to terminate */
55 | }while(attempt <= maxAttempts
56 | && !executor.awaitTermination(timeout, units));
57 | executor.shutdownNow();/* Cancel currently executing tasks */
58 | }catch(InterruptedException e) {
59 | executor.shutdownNow();/* (Re-)Cancel if current thread also interrupted */
60 | Thread.currentThread().interrupt();/* Preserve interrupt status */
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/streams/ComparatorTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.streams;
2 |
3 | import static java.util.Comparator.comparing;
4 |
5 | import java.util.List;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.testng.Assert;
10 | import org.testng.annotations.Test;
11 |
12 | import jps.tutorial.java8.test.DataUtils;
13 | import jps.tutorial.java8.test.TestSupport;
14 | import jsp.tutorial.java8.patterns.Candidate;
15 |
16 | /**
17 | *
18 | * @author John Psoroulas
19 | */
20 | @Test(enabled = true)
21 | public class ComparatorTest extends TestSupport {
22 |
23 | private static final Logger LOG = LoggerFactory.getLogger(ComparatorTest.class);
24 |
25 | /**
26 | * Simple sorting test
27 | */
28 | public void simpleSort() {
29 | /* Generate candidates list with random data */
30 | List candidates = DataUtils.buildCandidates(5);
31 | /* Sort by the list by name using Comparator interface */
32 | candidates.sort((c1, c2) -> c1.getName().compareTo(c2.getName()));
33 | /* Print the sorted collection */
34 | // DataUtils.printList(candidates);
35 | /* Check the order */
36 | Assert.assertTrue(DataUtils.isSorted(
37 | candidates,
38 | (a, b) -> a.getName().compareTo(b.getName()) < 0));
39 | }
40 |
41 | /**
42 | * Complex sorting test
43 | */
44 | public void multiSort() {
45 | /* Build the candidates list with common name */
46 | List candidates = DataUtils.buildCandidatesByName("Javaman", 5);
47 | /* Sort by the list by name and age using Collections API and method references */
48 | candidates.sort(
49 | comparing(Candidate::getName)
50 | .thenComparingInt(Candidate::getAge));
51 | /* Print the sorted collection */
52 | // DataUtils.printList(candidates);
53 | /* Check the order */
54 | Assert.assertTrue(DataUtils.isSorted(
55 | candidates,
56 | (a, b) -> a.getAge() - b.getAge() < 0));
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/TestExecutionListener.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test;
2 |
3 | import java.util.Date;
4 |
5 | import org.perf4j.StopWatch;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.testng.ITestContext;
9 | import org.testng.ITestListener;
10 | import org.testng.ITestResult;
11 |
12 | /**
13 | * Calculates the execution timers for the test suite
14 | * @author John Psoroulas
15 | */
16 | public class TestExecutionListener implements ITestListener {
17 |
18 | private static final Logger logger = LoggerFactory.getLogger(TestExecutionListener.class);
19 |
20 | private StopWatch stopWatch = new StopWatch();
21 |
22 | @Override
23 | public void onTestStart(ITestResult result) {
24 | result.getMethod().getMethodName();
25 | String mthName = result.getMethod().getMethodName();
26 | stopWatch.start();
27 | logger.info("------------------------------------------------------");
28 | logger.info("Test: {} START on {}", mthName, new Date(stopWatch.getStartTime()));
29 | logger.info("------------------------------------------------------");
30 | }
31 |
32 | @Override
33 | public void onTestSuccess(ITestResult result) {
34 | stopWatch.stop();
35 | String mthName = result.getMethod().getMethodName();
36 | logger.info("------------------------------------------------------");
37 | logger.info("Test: {} FINISH, EXECUTION TIME {} sec ", mthName, stopWatch.getElapsedTime() / 1000.);
38 | logger.info("------------------------------------------------------");
39 | }
40 |
41 | @Override
42 | public void onTestFailure(ITestResult result) {
43 | }
44 |
45 | @Override
46 | public void onTestSkipped(ITestResult result) {
47 | }
48 |
49 | @Override
50 | public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
51 | }
52 |
53 | @Override
54 | public void onStart(ITestContext context) {
55 | }
56 |
57 | @Override
58 | public void onFinish(ITestContext context) {
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/Degree.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | import org.apache.commons.lang3.Validate;
4 |
5 | /**
6 | * The diploma degree.
7 | *
8 | * @author John Psoroulas
9 | */
10 | public class Degree {
11 |
12 | public enum DegreeField {
13 | ENGINEERING, CHEMISTRY, PHYSICS, MANAGEMENT
14 | }
15 |
16 | private DegreeField field;
17 |
18 | private int grade;
19 |
20 | private String holderName;
21 |
22 | private Degree(DegreeBuilder builder) {
23 | this.holderName = builder.holderName;
24 | this.field = builder.field;
25 | this.grade = builder.grade;
26 | }
27 |
28 | public int getGrade() {
29 | return grade;
30 | }
31 |
32 | public DegreeField getField() {
33 | return field;
34 | }
35 |
36 | public String getHolderName() {
37 | return holderName;
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | StringBuilder builder = new StringBuilder();
43 | builder
44 | .append("Degree [field=")
45 | .append(field)
46 | .append(", grade=")
47 | .append(grade)
48 | .append(", holderName=")
49 | .append(holderName)
50 | .append("]");
51 | return builder.toString();
52 | }
53 |
54 | public static class DegreeBuilder {
55 | private DegreeField field;
56 |
57 | private int grade;
58 |
59 | private String holderName;
60 |
61 | public DegreeBuilder withField(DegreeField field) {
62 | this.field = field;
63 | return this;
64 | }
65 |
66 | public DegreeBuilder withHolderName(String holderName) {
67 | this.holderName = holderName;
68 | return this;
69 | }
70 |
71 | public DegreeBuilder withGrade(int grade) {
72 | this.grade = grade;
73 | return this;
74 | }
75 |
76 | public Degree build() {
77 | // TODO some validation about the grades
78 | Validate.notNull(field, "Undefined degree field!");
79 | Validate.notEmpty(holderName, "Undefined degree holder's name!");
80 | return new Degree(this);
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/FactoryTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.testng.Assert;
6 | import org.testng.annotations.Test;
7 |
8 | import jps.tutorial.java8.test.TestSupport;
9 | import jsp.tutorial.java8.patterns.factory.Computer;
10 | import jsp.tutorial.java8.patterns.factory.Computer.ComputerModel;
11 | import jsp.tutorial.java8.patterns.factory.ComputerFactory;
12 | import jsp.tutorial.java8.patterns.factory.ComputerSpecs;
13 | import jsp.tutorial.java8.patterns.factory.ComputerSpecs.ComputerSpecsBuilder;
14 | import jsp.tutorial.java8.patterns.factory.HomeComputer;
15 | import jsp.tutorial.java8.patterns.factory.ServerComputer;
16 |
17 | /**
18 | * Factory pattern tests.
19 | *
20 | * @author John Psoroulas
21 | */
22 | @Test(enabled = true)
23 | public class FactoryTest extends TestSupport {
24 |
25 | private static final Logger LOG = LoggerFactory.getLogger(FactoryTest.class);
26 |
27 | /**
28 | * Tests the {@link HomeComputer} 'old-way' creation.
29 | */
30 | public void createHomeComputer() {
31 | /* Create a home computer with the built-in specs */
32 | Computer computer = ComputerFactory.create(ComputerModel.HOME);
33 | ComputerSpecs spec = computer.getSpecs();
34 | ComputerSpecs sdspec = ComputerSpecsBuilder.buildStandardSpec();
35 | /* Test the results */
36 | Assert.assertEquals(spec, sdspec,
37 | "Unexpected specs");
38 | }
39 |
40 | /**
41 | * Tests the {@link ServerComputer} 'lambda-way' creation.
42 | */
43 | public void createServerComputerLambda() {
44 | /* Create a server computer with the built-in specs */
45 | Computer computer = ComputerFactory.createLambda(ComputerModel.SERVER);
46 | ComputerSpecs spec = computer.getSpecs();
47 | ComputerSpecs exspec = ComputerSpecsBuilder.buildExtendedSpec();
48 | /* Test the results */
49 | Assert.assertEquals(spec, exspec,
50 | "Unexpected specs");
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/TemplateTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.testng.annotations.Test;
6 |
7 | import jsp.tutorial.java8.patterns.template.EngineerEvaluationProcess;
8 | import jsp.tutorial.java8.patterns.template.EvaluationProcessLambda;
9 | import jsp.tutorial.java8.patterns.template.ManagerEvaluationProcess;
10 | import jsp.tutorial.java8.patterns.template.ProcessEvaluationAlgorithms;
11 |
12 | /**
13 | * Template pattern tests.
14 | *
15 | * @author John Psoroulas
16 | */
17 | @Test(enabled = true)
18 | public class TemplateTest extends BaseTemplatePatternTest {
19 |
20 | private static final Logger LOG = LoggerFactory.getLogger(TemplateTest.class);
21 |
22 | /**
23 | * Tests the 'old-way' {@link EngineerEvaluationProcess}.
24 | */
25 | public void engineerEvaluationProcess() {
26 | /* Create the appropriate subclass of the template class */
27 | evaluationProcess(
28 | new EngineerEvaluationProcess(),
29 | ProcessEvaluationAlgorithms::engineer);
30 | }
31 |
32 | /**
33 | * Tests the 'old-way' {@link ManagerEvaluationProcess}.
34 | */
35 | public void managerEvaluationProcess() {
36 | /* Create the appropriate subclass of the template class */
37 | evaluationProcess(
38 | new ManagerEvaluationProcess(),
39 | ProcessEvaluationAlgorithms::management);
40 | }
41 |
42 | /**
43 | * Tests the 'lambda way' {@link EngineerEvaluationProcess}.
44 | */
45 | public void engineerEvaluationProcessLambda() {
46 | /* No subclass, move from polymorphism to composition by passing the appropriate lambda at the constructor */
47 | evaluationProcessLambda(
48 | new EvaluationProcessLambda(ProcessEvaluationAlgorithms::engineer));
49 | }
50 |
51 | /**
52 | * Tests the 'lambda way' {@link ManagerEvaluationProcess}.
53 | */
54 | public void managerEvaluationProcessLambda() {
55 | /* No subclass, move from polymorphism to composition by passing the appropriate lambda at the constructor */
56 | evaluationProcessLambda(
57 | new EvaluationProcessLambda(ProcessEvaluationAlgorithms::management));
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## Design Patterns with Java 8
2 |
3 | The purpose of these examples is to show how some of the traditional _Design Patterns_ can be applied using
4 | [Lambdas expressions](http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html).
5 | Each example is accompanied by a short description of the examined pattern and a brief discussion demonstrating
6 | the differences between the implementation with and without the Lambda expressions.
7 |
8 | Currently, the examined patterns are the following:
9 |
10 | ##### Creational Design Patterns
11 | * [Factory](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/factory)
12 |
13 | ##### Behavioral Design Patterns
14 | * [Strategy](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns//strategy)
15 | * [Template method](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/template)
16 | * [Observer](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/observer)
17 | * [Chain of Responsibility](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/chain)
18 | * [Command](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/command)
19 |
20 | ##### Structural Design Patterns
21 | * [Decorator](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns/decorator)
22 |
23 | ----
24 |
25 | The project is built and follows the standard structure of a [Maven](https://maven.apache.org/) project,
26 | so the domain model and the tests of the demonstrated examples can be found at
27 | [main/java](https://github.com/jpsoroulas/java8-patterns/tree/master/src/main/java/jsp/tutorial/java8/patterns) and
28 | [test/java](https://github.com/jpsoroulas/java8-patterns/tree/master/src/test/java/jps/tutorial/java8/test/patterns) respectively.
29 |
30 | You can compile and run the tests by typing the following command at the root project directory,
31 |
32 | ```
33 | mnv compile test
34 | ```
35 |
36 | ----
37 |
38 | Written by John Psoroulas, 2016.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | jps.tutorial
4 | java8-patterns
5 | 0.0.1-SNAPSHOT
6 | Design pattern revist with Java 8
7 | Design pattern revist with Java 8
8 |
9 |
10 | UTF-8
11 | 3.5.1
12 | 0.6.1
13 | 0.9.16
14 | 3.4
15 | 1.7.21
16 | 1.2.17
17 | 6.9.10
18 |
19 |
20 |
21 | org.apache.commons
22 | commons-lang3
23 | ${version.apache.commons.lang3}
24 |
25 |
26 | one.util
27 | streamex
28 | ${version.streamex}
29 |
30 |
31 | org.perf4j
32 | perf4j
33 | ${version.perf4j}
34 |
35 |
36 | org.slf4j
37 | slf4j-api
38 | ${version.slf4j-api}
39 | compile
40 |
41 |
42 | org.slf4j
43 | slf4j-log4j12
44 | ${version.slf4j-api}
45 | runtime
46 |
47 |
48 | log4j
49 | log4j
50 | ${version.log4j}
51 |
52 |
53 | org.testng
54 | testng
55 | ${version.testng}
56 |
57 |
58 |
59 |
60 |
61 | maven-compiler-plugin
62 | ${version.compiler.plugin}
63 |
64 | 1.8
65 | 1.8
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/ObserverTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import java.util.List;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.testng.annotations.Test;
8 |
9 | import jps.tutorial.java8.test.DataUtils;
10 | import jps.tutorial.java8.test.TestSupport;
11 | import jsp.tutorial.java8.patterns.JobInfo;
12 | import jsp.tutorial.java8.patterns.observer.CompanyJobNotificationSystem;
13 | import jsp.tutorial.java8.patterns.observer.EngineeringDepartment;
14 | import jsp.tutorial.java8.patterns.observer.JobNotificationSystem;
15 | import jsp.tutorial.java8.patterns.observer.JobPorcessingAlgorithms;
16 | import jsp.tutorial.java8.patterns.observer.LogisticsDepartment;
17 | import jsp.tutorial.java8.patterns.observer.SalesDepartment;
18 |
19 | /**
20 | * Observer pattern tests.
21 | *
22 | * @author John Psoroulas
23 | */
24 | @Test(enabled = true)
25 | public class ObserverTest extends TestSupport {
26 |
27 | private static final Logger LOG = LoggerFactory.getLogger(ObserverTest.class);
28 |
29 | /**
30 | * Tests job notification for the 'old-way' listeners.
31 | */
32 | public void sendNotifications() {
33 | /* Create company's job notification system and register the respective departments
34 | * to get notified for new jobs */
35 | JobNotificationSystem ns = new CompanyJobNotificationSystem();
36 | /* Register the 'old-way' department listeners */
37 | ns.addListener(new EngineeringDepartment());
38 | ns.addListener(new LogisticsDepartment());
39 | ns.addListener(new SalesDepartment());
40 | doSendJobNotifications(ns);
41 | }
42 |
43 | /**
44 | * Tests job notification for the 'lambda-way' listeners.
45 | */
46 | public void sendNotificationsLambda() {
47 | /* Create company's job notification system and register the respective departments
48 | * to get notified for new jobs */
49 | JobNotificationSystem ns = new CompanyJobNotificationSystem();
50 | /* Register the 'lambda-way' department listeners */
51 | ns.addListener(JobPorcessingAlgorithms::engineering);
52 | ns.addListener(JobPorcessingAlgorithms::logistics);
53 | ns.addListener(JobPorcessingAlgorithms::sales);
54 | doSendJobNotifications(ns);
55 | }
56 |
57 | /**
58 | * Sends the notifications.
59 | */
60 | private void doSendJobNotifications(JobNotificationSystem ns) {
61 | /* Build the jobs information */
62 | List jobsInfo = DataUtils.buildJobs(10);
63 | /* Notify the observers for the new jobs */
64 | jobsInfo.forEach(job -> ns.notify(job));
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/DecoratorTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import java.util.function.Function;
6 | import java.util.stream.Stream;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.testng.Assert;
11 | import org.testng.annotations.Test;
12 |
13 | import jps.tutorial.java8.test.TestSupport;
14 | import jsp.tutorial.java8.patterns.decorator.NewsSitesFilter;
15 | import jsp.tutorial.java8.patterns.decorator.SimpleFilter;
16 | import jsp.tutorial.java8.patterns.decorator.WebFilter;
17 | import jsp.tutorial.java8.patterns.decorator.WebFiltersAlgorithms;
18 | import jsp.tutorial.java8.patterns.decorator.WebMailsFilter;
19 |
20 | /**
21 | * Decorator pattern tests.
22 | *
23 | * @author John Psoroulas
24 | */
25 | @Test(enabled = true)
26 | public class DecoratorTest extends TestSupport {
27 |
28 | private static final Logger LOG = LoggerFactory.getLogger(DecoratorTest.class);
29 |
30 | private final List URLs = Arrays.asList(
31 | "https://www.facebook.com/",
32 | "https://twitter.com/",
33 | "https://mail.google.com",
34 | "http://www.bbc.com/news",
35 | "http://www.a.site/");
36 |
37 | /**
38 | * Tests the 'old-way' web filters decorators.
39 | */
40 | public void testWebFilters() {
41 | /* Create a simple filter with the 'old-way' */
42 | WebFilter filter = new SimpleFilter();
43 | /* Create a filter decorator with the 'old-way' */
44 | WebFilter filterDecorator = new NewsSitesFilter(new WebMailsFilter(filter));
45 | /* Test the results */
46 | Assert.assertEquals(filter.filter(URLs).size(), 3,
47 | "Only social media sites should be filtered");
48 | Assert.assertEquals(filterDecorator.filter(URLs).size(), 1,
49 | "Social media, web mail and news sites should be filtered");
50 | }
51 |
52 | /**
53 | * Tests the 'lambda-way' web filters decorators.
54 | */
55 | public void testWebFiltersLambda() {
56 | /* Create a simple filter with the 'lambda-way' */
57 | Function, List> filter = WebFiltersAlgorithms::removeSocialNetworks;
58 | /* Create a filter decorator with the 'lambda-way' */
59 | Function, List> filterDecorator = Stream., List>> of(
60 | WebFiltersAlgorithms::removeSocialNetworks,
61 | WebFiltersAlgorithms::removeWebMails,
62 | WebFiltersAlgorithms::removeNewsSites)
63 | .reduce((f, n) -> f.compose(n))
64 | .get();
65 | /* Test the results */
66 | Assert.assertEquals(filter.apply(URLs).size(), 3,
67 | "Only social media sites should be filtered");
68 | Assert.assertEquals(filterDecorator.apply(URLs).size(), 1,
69 | "Social media, web mail and news sites should be filtered");
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/streams/GroupTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.streams;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.stream.Collectors;
7 | import java.util.stream.IntStream;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.testng.Assert;
12 | import org.testng.annotations.Test;
13 |
14 | import jps.tutorial.java8.test.DataUtils;
15 | import jps.tutorial.java8.test.TestSupport;
16 | import jsp.tutorial.java8.patterns.Degree;
17 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
18 |
19 | /**
20 | *
21 | * @author John Psoroulas
22 | */
23 | @Test(enabled = true)
24 | public class GroupTest extends TestSupport {
25 |
26 | private static final Logger LOG = LoggerFactory.getLogger(GroupTest.class);
27 |
28 | /**
29 | * Simple group test
30 | * List --> group by collector
31 | * List --> group by candidate name
32 | */
33 | public void simpleGroup() {
34 | /* Build Degrees for various field and add them into a single list */
35 | List degrees = DataUtils.buildDegreesByField(DegreeField.ENGINEERING, 10);
36 | degrees.addAll(DataUtils.buildDegreesByField(DegreeField.CHEMISTRY, 15));
37 | degrees.addAll(DataUtils.buildDegreesByField(DegreeField.PHYSICS, 20));
38 | /* Group Degrees by field */
39 | Map> grouped = degrees.stream() /* Convert list to stream */
40 | .collect(Collectors.groupingBy(Degree::getField)); /* Group by Degree field */
41 | /* Check the groups size */
42 | Assert.assertEquals(grouped.get(DegreeField.ENGINEERING).size(), 10);
43 | Assert.assertEquals(grouped.get(DegreeField.CHEMISTRY).size(), 15);
44 | Assert.assertEquals(grouped.get(DegreeField.PHYSICS).size(), 20);
45 | }
46 |
47 | /**
48 | * Transform and group test
49 | * T --> transform to type R[] --> flatten arrays --> group by collector
50 | * int --> transform to type Degree[] --> flatten arrays --> group by degree field
51 | */
52 | public void transformAndGroup() {
53 | Map> res = IntStream.range(0, 5) /* Create an int stream */
54 | .mapToObj( /* For each int produce an array with two degrees */
55 | i -> {
56 | return new Degree[] {
57 | DataUtils.buildDegreeByField(DegreeField.ENGINEERING),
58 | DataUtils.buildDegreeByField(DegreeField.CHEMISTRY)
59 | };
60 | })
61 | .flatMap(Arrays::stream) /* Flatten the array */
62 | .collect(Collectors.groupingBy(Degree::getField)); /* Group by degree field */
63 | // .forEach((k, v) -> LOG.info("key {}, value {}", k, v));
64 | /* Check the groups size */
65 | Assert.assertEquals(res.get(DegreeField.ENGINEERING).size(), 5);
66 | Assert.assertEquals(res.get(DegreeField.CHEMISTRY).size(), 5);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/readme.md:
--------------------------------------------------------------------------------
1 | ## Factory pattern
2 |
3 | The Factory pattern is one of the creational design patterns. The factory design pattern lets you create objects without
4 | exposing the instantiation logic to the client. The created objects share a common interface used by the client to
5 | interact with them. That is, the _factory_ creates one of a _family of objects_ and the client is not aware of
6 | the object's specific subclass type. The instantiation of the appropriate object is based on the arguments
7 | passed at the factory's object creation method.
8 | One of the examples of this pattern in JDK, is the java.util.ResourceBundle class which returns the appropriate bundle
9 | through the getBundle() method.
10 |
11 | At the Factory pattern the following components are participating:
12 | * The _factory_, creates one of a family of objects without exposing the instantiation logic to the client.
13 | * The _family of objects_, the objects share a _common interface_ (interface or abstract class).
14 |
15 | ```
16 | family of objects
17 | +--------+
18 | /| type 1 |
19 | / +--------+
20 | / +--------+
21 | / .'| type 2 |
22 | +---------+ creates +---------------+ /.' +--------+
23 | | Factory +--------->| <> |<.-' .
24 | +---------+ +---------------+ \ .
25 | \ .
26 | `. +--------+
27 | \| type n |
28 | +--------+
29 | ```
30 |
31 | ### Test case
32 | ----
33 |
34 | In our scenario, suppose that there is a company that constructs two computer models, the 'home' and the 'server' model.
35 | The production of its model is based on the respective specifications that dictates what are the parts that the model consists of;
36 | for example, the number of cores. disks and the memory. The client can order a computer by simply specifying the model
37 | and not the specification details.
38 |
39 | Adapting our scenario to the Factory pattern results to the following components mapping:
40 | * Factory ---> ComputerFactory
41 | * Family objects' common abstraction ---> Computer
42 | * Family object 1 ---> HomeComputer
43 | * Family object 2 ---> ServerComputer
44 |
45 | ### Discussion
46 | ----
47 |
48 | The Factory pattern implementation using lambdas differs from the old approach that the family objects' builders
49 | (constructors, builder methods) can be lambdas that conforms to some interface and returns a new object.
50 | Note that the lambda approach doesn’t scale very well if the factory method needs to take multiple arguments
51 | to pass on to the object's 'builder'. In case of multiple arguments a custom functional interface should be
52 | created with the respective parameterized types.
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/chain/readme.md:
--------------------------------------------------------------------------------
1 | ## Chain of Responsibility pattern
2 |
3 | The Chain of Responsibility pattern is a behavior pattern in which a group of objects is chained together
4 | and is responsible to handle a request. The client pass the request to the chain of objects; If an object
5 | in the chain can process the particular request, it does so and returns the corresponding response.
6 | Otherwise, it delegates the request to the next object in the chain.
7 | This pattern is used to avoid coupling the sender of a request to its receiver by giving more than one object
8 | a chance to handle the request and It allows you to dynamically change the members or the order of the chain.
9 | Only one object in the chain handles a request, whereas some requests might not get handled. Those restrictions,
10 | of course, are for a classic CoR implementation. In practice, those rules are bent; for example, in JEE,
11 | the servlet filters are a CoR implementation that allows multiple filters to process an HTTP request
12 | (avax.servlet.Filter#doFilter()).
13 |
14 | At the Chain of Responsibility the following components are participating:
15 | * The _Handler_, an abstraction (interface/abstract) of the objects that may handle the requests.
16 | * The _ConcreteHandler(s)_, the Handler's implementation(s) for processing the request that is responsible for.
17 | * The _Client_, submits the request to the first handler on the chain.
18 |
19 | ```
20 | +--------+ creates +-------------+
21 | | Client |.........>|<>|
22 | +--------+ | Handler |
23 | +-------------+
24 | ^ ^
25 | .' `.
26 | .' `.
27 | +----------------+ +----------------+
28 | | <> | ... | <> |
29 | |ConcreteHandler1| |ConcreteHandlerN|
30 | +----------------+ +----------------+
31 | N Handlers
32 | ```
33 |
34 | ### Test case
35 | ----
36 |
37 | In our scenario, suppose that a software company offers three levels of help desk support with the following order:
38 | the first level, the second level and the third level support. A support level will try to handle the request, if
39 | all the lower-ordered support levels fail.
40 |
41 | Adapting our scenario to the Chain of Responsibility pattern results to the following components mapping:
42 | * Client ---> ChainTest -the unit test- (the client)
43 | * Handler ---> HDSupport
44 | * ConcreteHandler1 ---> FirstLevelHDSupport
45 | * ConcreteHandler2 ---> SecondLevelHDSupport
46 | * ConcreteHandler3 ---> ThirdLevelHDSupport
47 |
48 | ### Discussion
49 | ----
50 |
51 | The Chain of Responsibility implementation using lambdas differs from the old approach in how the various handlers
52 | are implemented and propagate the request to the next handler. At Java 8, the handlers implementation can be lambdas
53 | (Function), whereas the lambdas chaining is achieved natively via the default method andThen(...) of
54 | the Function interface.
55 |
56 | ```
57 | +--------+ creates +--------------+ <> ... +--------------+
58 | | Client |.........> |Function|.................|Function|
59 | +--------+ | lambda1 | | lambdaN |
60 | +--------------+ +--------------+
61 | N Handlers
62 | ```
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/Candidate.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Optional;
6 |
7 | import org.apache.commons.lang3.Validate;
8 |
9 | /**
10 | * Represents a job-position candidate.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public class Candidate {
15 |
16 | public enum Gender {
17 | MALE, FEMALE;
18 | }
19 |
20 | private String name;
21 |
22 | private int age;
23 |
24 | private Gender gender;
25 |
26 | private List degrees;
27 |
28 | // The candidate may not give a contact info
29 | private Optional contactInfo;
30 |
31 | /* Optionally, put here more info about the candidate.
32 | * Not all the info is mandatory. You can selectively set desire info using
33 | * the Builder pattern. */
34 |
35 | // ..., more candidate info
36 |
37 | private Candidate(CandidateBuilder builder) {
38 | this.name = builder.name;
39 | this.age = builder.age;
40 | this.gender = builder.gender;
41 | this.degrees = builder.degrees;
42 | this.contactInfo = builder.contactInfo;
43 | }
44 |
45 | public String getName() {
46 | return name;
47 | }
48 |
49 | public int getAge() {
50 | return age;
51 | }
52 |
53 | public Gender getGender() {
54 | return gender;
55 | }
56 |
57 | public List getDegrees() {
58 | return degrees;
59 | }
60 |
61 | public void addDegree(Degree degree) {
62 | this.degrees.add(degree);
63 | }
64 |
65 | public Optional getContactInfo() {
66 | return contactInfo;
67 | }
68 |
69 | @Override
70 | public String toString() {
71 | StringBuilder builder = new StringBuilder();
72 | builder
73 | .append("Candidate [name=")
74 | .append(name)
75 | .append(", age=")
76 | .append(age)
77 | .append(", gender=")
78 | .append(gender)
79 | .append(", degrees=")
80 | .append(degrees)
81 | .append(", contactInfo=")
82 | .append(contactInfo)
83 | .append("]");
84 | return builder.toString();
85 | }
86 |
87 | public static class CandidateBuilder {
88 |
89 | private String name;
90 |
91 | private int age;
92 |
93 | private Gender gender;
94 |
95 | private List degrees = new ArrayList<>();
96 |
97 | private Optional contactInfo;
98 |
99 | public CandidateBuilder withName(String name) {
100 | this.name = name;
101 | return this;
102 | }
103 |
104 | public CandidateBuilder withAge(Integer age) {
105 | this.age = age;
106 | return this;
107 | }
108 |
109 | public CandidateBuilder withGender(Gender gender) {
110 | this.gender = gender;
111 | return this;
112 | }
113 |
114 | public CandidateBuilder withDegree(Degree degree) {
115 | this.degrees.add(degree);
116 | return this;
117 | }
118 |
119 | public CandidateBuilder withDegrees(List degrees) {
120 | this.degrees.addAll(degrees);
121 | return this;
122 | }
123 |
124 | public CandidateBuilder withContactInfo(ContactInfo contactInfo) {
125 | this.contactInfo = Optional.ofNullable(contactInfo);
126 | return this;
127 | }
128 |
129 | public Candidate build() {
130 | // TODO some validation about the age
131 | Validate.notEmpty(name, "Undefined candidate name!");
132 | Validate.notNull(gender, "Undefined candidate gender!");
133 | return new Candidate(this);
134 | }
135 |
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/factory/ComputerSpecs.java:
--------------------------------------------------------------------------------
1 | package jsp.tutorial.java8.patterns.factory;
2 |
3 | import java.util.Optional;
4 | import java.util.stream.IntStream;
5 |
6 | import jsp.tutorial.java8.patterns.factory.Computer.ComputerModel;
7 |
8 | /**
9 | * Specifications for the available {@link ComputerModel computer models}.
10 | * Specifications are Immutable, once set, cannot be modified.
11 | *
12 | * @author John Psoroulas
13 | */
14 | public class ComputerSpecs {
15 |
16 | /**
17 | * The total number of cores.
18 | */
19 | private int cores;
20 |
21 | /**
22 | * The total number of disk drives.
23 | */
24 | private int disks;
25 |
26 | /**
27 | * The RAM in GB
28 | */
29 | private long memory;
30 |
31 | /* Optionally, put here more properties to define some extra computer's features.
32 | * Not all the features are mandatory. You can selectively set the desire features
33 | * using the Builder pattern. */
34 |
35 | // ..., some extra features
36 |
37 | private ComputerSpecs(ComputerSpecsBuilder builder) {
38 | this.cores = builder.cores;
39 | this.disks = builder.disks;
40 | this.memory = builder.memory;
41 | }
42 |
43 | public int getCores() {
44 | return cores;
45 | }
46 |
47 | public int getDisks() {
48 | return disks;
49 | }
50 |
51 | public long getMemory() {
52 | return memory;
53 | }
54 |
55 | @Override
56 | public int hashCode() {
57 | return IntStream.of(cores, disks, (int)(memory ^ (memory >>> 32)))
58 | .reduce(1, (hashcode, field) -> 37 * hashcode + field);
59 | }
60 |
61 | @Override
62 | public boolean equals(Object other) {
63 | return Optional.ofNullable(other)
64 | .filter(o -> this.getClass() == o.getClass())
65 | .map(Object::hashCode)
66 | .map(Integer.valueOf(hashCode())::equals)
67 | .orElse(Boolean.FALSE);
68 | }
69 |
70 | @Override
71 | public String toString() {
72 | StringBuilder builder = new StringBuilder();
73 | builder
74 | .append("ComputerSpecs [cores=")
75 | .append(cores)
76 | .append(", disks=")
77 | .append(disks)
78 | .append(", memory=")
79 | .append(memory)
80 | .append("]");
81 | return builder.toString();
82 | }
83 |
84 | /**
85 | * Builds {@link ComputerSpecs}.
86 | *
87 | * @author John Psoroulas
88 | */
89 | public static class ComputerSpecsBuilder {
90 |
91 | private int cores = 1;
92 |
93 | private int disks = 1;
94 |
95 | private long memory = 2;
96 |
97 | public ComputerSpecsBuilder withCores(int cores) {
98 | this.cores = cores;
99 | return this;
100 | }
101 |
102 | public ComputerSpecsBuilder withDisks(int disks) {
103 | this.disks = disks;
104 | return this;
105 | }
106 |
107 | public ComputerSpecsBuilder withMemory(long memory) {
108 | this.memory = memory;
109 | return this;
110 | }
111 |
112 | public ComputerSpecs build() {
113 | // TODO some validation
114 | return new ComputerSpecs(this);
115 | }
116 |
117 | public ComputerSpecsBuilder initFromSpec(ComputerSpecs specs) {
118 | this.cores = specs.getCores();
119 | this.disks = specs.getDisks();
120 | this.memory = specs.getMemory();
121 | return this;
122 | }
123 |
124 | /**
125 | * Builds the standard computer specification.
126 | *
127 | * @return the standard specification.
128 | */
129 | public static ComputerSpecs buildStandardSpec() {
130 | return new ComputerSpecsBuilder()
131 | .withCores(4)
132 | .withDisks(2)
133 | .withMemory(8)
134 | .build();
135 | }
136 |
137 | /**
138 | * Builds the extended computer specification.
139 | *
140 | * @return the extended specification.
141 | */
142 | public static ComputerSpecs buildExtendedSpec() {
143 | return new ComputerSpecsBuilder()
144 | .withCores(8)
145 | .withDisks(4)
146 | .withMemory(16)
147 | .build();
148 | }
149 |
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/template/readme.md:
--------------------------------------------------------------------------------
1 | ## Template Method pattern
2 |
3 | The Template Method Pattern is a behavior design patterns, it provides a template or a structure of an algorithm,
4 | and lets subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure.
5 | The Template pattern suggests keeping the outline of the algorithm in a separate method referred to as
6 | a _template method_ inside a class, which may be referred to as a _template class_, leaving out the specific
7 | implementations of the variant portions of the algorithm to different subclasses of this class.
8 | Examples of this pattern in JDK are the non-abstract methods of java.io.InputStream, java.io.OutputStream class.
9 |
10 | At the Template Method pattern the following components are participating:
11 | * The _Template class_, the class (abstract usually) that defines the template method.
12 | * The _ConcreteTemplate(s)_, the template class implementations that redefines some portions of the template method.
13 |
14 | ```
15 | +----------------------------------------+ +----------------------+
16 | | Template class | /| ConcreteTemplate1 |
17 | +----------------------------------------+ .' +----------------------+
18 | | templateMethod() { | /
19 | | step1(); | /
20 | | step2(); | .' .
21 | | aStep3(); // implementation needed |< . N implementations
22 | | step4(); |< .
23 | | aStep5(); // implementation needed | `.
24 | | } | \
25 | | | \
26 | | abstract aStep3(); | `. +----------------------+
27 | | abstract aStep5(); | \| ConcreteTemplateN |
28 | +----------------------------------------+ +----------------------+
29 |
30 | Note that the methods aStep3() and aStep5() are the steps that needs implementation.
31 | The template class can have some default implementations
32 |
33 | ```
34 |
35 | ### Test case
36 | ----
37 |
38 | In our scenario, assume that a company is going to hire some people for various job positions.
39 | The jobs are split into two types, the one that refers to software engineers and the other that refers to
40 | assistant managers. The evaluation process for candidates is a combination of two evaluation steps. The first step
41 | is common for both engineers and assistant managers job positions, whereas the second one depends on the job type.
42 | The total score of the evaluation steps determines which candidates are suitable for the job position.
43 |
44 | Adapting our scenario to the Strategy pattern results to the following components mapping:
45 | * Template class ---> EvaluationProcess (the evaluation process template)
46 | * ConcreteTemplate1 ---> EngineerEvaluationProcess (evaluation process for Engineers)
47 | * ConcreteTemplate2 ---> ManagerEvaluationProcess (evaluation process for Managers)
48 |
49 | ### Discussion
50 | ----
51 |
52 | The Template Method pattern implementation using lambdas differs from the old approach that no subclasses are used.
53 | The appropriate behavior with the form of lambda function, is injected to the constructor, moving this pattern from
54 | polymorphism to composition.
55 |
56 | ```
57 | +----------------------------------------+
58 | | Template class |
59 | +----------------------------------------+
60 | | Template(aStep3Fun, aStep5Fun) { |
61 | | ... |
62 | | } |
63 | | |
64 | | templateMethod() { |
65 | | step1(); |
66 | | step2(); |
67 | | // call aStep3Fun |
68 | | aStep3Fun.apply(...); |
69 | | step4(); |
70 | | // call aStep5Fun |
71 | | aStep5Fun.apply(...); |
72 | | } |
73 | +----------------------------------------+
74 |
75 | ```
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/strategy/readme.md:
--------------------------------------------------------------------------------
1 | ## Strategy Pattern
2 |
3 | The Strategy pattern is one of the behavioral design patterns. It is used to define a family of algorithms,
4 | encapsulate each one behind a common programming interface, and make them interchangeable dynamically.
5 | This pattern lets the algorithm vary independently from clients that use it. It converts the generalization
6 | of the template method to composition or aggregation.
7 | It allows the algorithm to vary frequently and is a very good alternate solution to sub-classing.
8 | One of the examples of this pattern in JDK, is Collections.sort() method that takes Comparator parameter with
9 | different implementations of Comparator interfaces.
10 |
11 | At the Strategy pattern the following components are participating:
12 | * The _Strategy_, an abstraction (interface) of the strategies via an interface or an abstract class.
13 | * The _ConcreteStrategy(ies)_, the strategy(ies) implementation(s) that contains the algorithm specific implementation.
14 | * The _Context_, the class which uses the Strategy.
15 |
16 | ```
17 | +--------------------+
18 | .' <> |
19 | +--------------------+ .-' | ConcreteStrategy1 |
20 | | <> | .' +--------------------+
21 | +--------------+ refers | Strategy |<-' .
22 | | Context |-------------@+--------------------+<. . N implementations
23 | +--------------+ | apply() | `. .
24 | +--------------------+ `-. +--------------------+
25 | `.| <> |
26 | | ConcreteStrategyN |
27 | +--------------------+
28 | ```
29 |
30 | ### Test case
31 | ----
32 |
33 | In our scenario, there is an employer that is going to hire some people, the candidates, for a job position.
34 | Each candidate holds some degrees which will be evaluated by the employer in order to select the appropriate
35 | candidate for the job. The selection algorithm may vary, for example could be the degree's relevance with the field
36 | of the offering job position, the grade of the degree, the number of the degrees or a mix of the above.
37 | In our case two hiring strategies will be used:
38 | * one that the algorithm is based on the 'relevant degree' and the
39 | * other is based on the grade.
40 |
41 | Adapting our scenario to the Strategy pattern results to the following components mapping:
42 | * Context ---> Employer (the employer)
43 | * Startegy ---> HiringStrategy (hiring strategy common API)
44 | * ConcreteStartegy1 ---> DegreeRelevantHiringStrategy ('relevant degree' hiring strategy)
45 | * ConcreteStartegy2 ---> GradeHiringStrategy ('grade based' hiring strategy)
46 |
47 | ### Discussion
48 | ----
49 |
50 | The Strategy pattern implementation using lambdas differs from the old approach in how the various strategies
51 | are implemented. At the old approach, the strategies are implemented via classes (or anonymous classes) whereas
52 | using lambdas the strategies are implemented with lambdas. Rather than creating a hierarchy of classes to
53 | support the strategy pattern we can now create a library of lambda functions or static methods (used as method
54 | references) to apply the respective strategy.
55 |
56 | ```
57 | +------------+
58 | .'| lambda1 |
59 | +--------------------+ .-' +------------+
60 | | <> | .' .
61 | +--------------+ refers | Strategy |<-' . N lambdas
62 | | Context |-------------@+--------------------+<. .
63 | +--------------+ | apply() | `.
64 | +--------------------+ `-. +------------+
65 | `.| lambdaN |
66 | +------------+
67 | ```
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/decorator/readme.md:
--------------------------------------------------------------------------------
1 | ## Decorator pattern
2 |
3 | The Decorator pattern is one of the structural design patterns. It is used to dynamically attach additional behavior
4 | to an object and provides a flexible alternative to subclassing for extending functionality.
5 | It is used to dynamically extend the functionality of an object without having to change the original class
6 | or using inheritance. This can be achieved by wrapping the original object, the _component_, in an object,
7 | the _decorator_ that interacts with the client on behalf of the wrapped component, acting as a proxy.
8 | The decorator comply with the component's interface. It receives all the requests from a client and forwards them
9 | to the underlying component after or before adding new behavior to. This ensures that the new behavior can be added
10 | to a given object externally at runtime without modifying its structure.
11 | Decorator pattern prevents the proliferation of subclasses. It is easy to add any combination of capabilities.
12 | The same capability can even be added many times. It is possible to have different decorator for a given component simultaneously.
13 | Many examples of this pattern in JDK can be found at Java I/O classes. Just note that all subclasses of java.io.InputStream,
14 | OutputStream, Reader and Writer have a constructor taking an instance of same type. The FileInputStream is an example of
15 | a component of being decorated whereas the BufferedInputStream is an example of a decorator implementation.
16 |
17 | At the Decorator pattern the following components are participating:
18 | * The _Component_, the abstraction (interface/abstract) for the objects that additional behavior can be added to them dynamically.
19 | * The _ConcreteComponent_, the component implementation. The object to which an additional behavior can be added.
20 | * The _Decorator_, the base class of the decorators. It complies with the component interface and holds a reference to a component implementation.
21 | * The _ConcreteDecorator(s)_, the decorator implementation(s) for adding behavior to the wrapped component.
22 |
23 | ```
24 | +-------------+
25 | |<>| refers
26 | | Component | @-----------.
27 | +-------------+ `--.
28 | .^ ^._ `.
29 | _.-' `-. :
30 | .-' `-._ |
31 | _.-' `-._ |
32 | +-------------------+ +-------------+ ;
33 | | <> | | <> | ,-'
34 | | ConcreteComponent | | Decorator |-'
35 | +-------------------+ +-------------+
36 | _.^ ^._
37 | _..-' `-.
38 | _..-' `-._
39 | ..-' `-._
40 | +-------------------+ +-------------------+
41 | | <> | | <> |
42 | | ConcreteDecorator1| ... | ConcreteDecoratorN|
43 | +-------------------+ +-------------------+
44 | N implementations
45 | ```
46 |
47 | ### Test case
48 | ----
49 |
50 | In our scenario, suppose that a company builds web filters to restrict the internet access of its employees.
51 | The applied web filters to an employee depends on the employee's rank. For example, the lower employee's rank
52 | the higher internet access restriction.
53 |
54 | Adapting our scenario to the Decorator pattern results to the following components mapping:
55 | * Component ---> WebFilter
56 | * ConcreteComponent ---> SimpleFilter
57 | * ConcreteDecorator1 ---> WebMailsFilter
58 | * ConcreteDecorator2 ---> NewsSitesFilter
59 |
60 | ### Discussion
61 | ----
62 |
63 | The Decorator pattern implementation using lambdas differs from the old approach in how the component and the decorators
64 | are implemented. At Java 8, the component/decorators implementation can be lambdas (Function), whereas the
65 | lambdas 'decoration' is achieved natively via the default method compose(...) of the Function interface.
66 |
67 | ```
68 | +--------+ creates +--------------+ <> ... +--------------+
69 | | Client |.........> |Function|.................|Function|
70 | +--------+ | lambda1 | | lambdaN |
71 | +--------------+ +--------------+
72 | N Decorators
73 | ```
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/observer/readme.md:
--------------------------------------------------------------------------------
1 | ## Observer pattern
2 |
3 | The Observer pattern is one of the behavioral design patterns. This pattern defines an one-to-many dependency
4 | between objects so that when the 'one' object changes state, all its dependents, the 'many', are get notified.
5 | The 'one' part of the dependency is the _subject_, whereas the 'many' part is the _observers_.
6 | This pattern promotes the power of the loose coupling. Objects are loosely coupled when they interact but
7 | they have very little knowledge of each other. That is, the only thing the subject knows about an
8 | observer is that it implements a certain interface.
9 | Examples of this pattern in JDK are all implementations of java.util.EventListener.
10 |
11 | At the Observer pattern the following components are participating:
12 | * The _Subject_, an abstraction (interface) for the subject implementations. This interface defines some
13 | commonly used methods for the subject such as (un)registering and notifying observers.
14 | * The _Observer_, an abstraction (interface) for the observers implementations. An interface with a
15 | single method called by the subject when it's state changes.
16 | * The _ConcreteSubject(s)_, the subject implementation(s), contains a list of observers to notify them when its state changes.
17 | * The _ConcreteObserver(s)_, the observer implementation(s). Each observer registers itself with a concrete
18 | subject to receive its updates.
19 |
20 | ```
21 | +-----------------------+
22 | | <> | +----------------+
23 | | Subject | | <> |
24 | +-----------------------+@ 0..1 0..* | Observer |
25 | | subscribe(observer) | \ @ +----------------+
26 | | unsubscribe(observer) | \ .' | notify(state) |
27 | | notifyObservers() | \refers / +----------------+
28 | +-----------------------+ \ / ^
29 | ^ \ .' |
30 | | \/ |
31 | | .'`. |
32 | | / \ |
33 | +---------------------+ / \ |
34 | | <> | .' \ +---------------------+
35 | | ConcreteSubject |/ \ | <> |
36 | +---------------------+ \| ConcreteObserver |
37 | | List of observers | +---------------------+
38 | +---------------------+
39 | ```
40 |
41 | ### Test case
42 | ----
43 |
44 | In our scenario, suppose that a company has a system notification about the new internal job opportunities.
45 | The system holds a list with company's departments which notified when a new internal job is available.
46 | Each department implements its own policy with respect to new job positions. For example, the engineering
47 | department consider only for jobs related to engineering.
48 |
49 | Adapting our scenario to the Observer pattern results to the following components mapping:
50 | * Subject ---> JobNotificationSystem (the abstraction for a company's job system notification)
51 | * Observer ---> JobListener (the abstraction for departments to get notified for the new internal jobs opportunities)
52 | * ConcreteSubject ---> CompanyJobNotificationSystem (company's job system notification)
53 | * ConcreteObserver1 ---> EngineeringDepartment (the company's engineering department)
54 | * ConcreteObserver2 ---> SalesDepartment (the company's sales department)
55 | * ConcreteObserver3 ---> LogisticsDepartment (the company's logistics department)
56 |
57 | ### Discussion
58 | ----
59 |
60 | The Observer pattern implementation using lambdas differs from the old approach in how the various observers
61 | are implemented. At the old approach, the observers are implemented via classes (or anonymous classes) whereas
62 | at Java 8 the observers can be lambdas. Note that lambdas can not always express an observer.
63 | The observers may be more complex, for example, they could have state and several methods.
64 |
65 | ```
66 | +-----------------------+
67 | | <> |
68 | | Subject |
69 | +-----------------------+
70 | | subscribe(observer) |
71 | | unsubscribe(observer) |
72 | | notifyObservers() |
73 | +-----------------------+
74 | ^
75 | |
76 | |
77 | |
78 | +---------------------+
79 | | <> |
80 | | ConcreteSubject | refers +--------------+
81 | +---------------------+---------------@| lambda |
82 | | List of observers | 0..* +--------------+
83 | +---------------------+
84 | ```
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/CommandTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import java.util.List;
4 | import java.util.concurrent.ExecutionException;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.TimeUnit;
8 | import java.util.stream.Collectors;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 | import org.testng.annotations.Test;
13 |
14 | import jps.tutorial.java8.test.DataUtils;
15 | import jps.tutorial.java8.test.TestSupport;
16 | import jsp.tutorial.java8.patterns.Candidate;
17 | import jsp.tutorial.java8.patterns.ContactInfo;
18 | import jsp.tutorial.java8.patterns.VariousUtils;
19 | import jsp.tutorial.java8.patterns.command.EmailDispatcher;
20 | import jsp.tutorial.java8.patterns.command.MessageData;
21 | import jsp.tutorial.java8.patterns.command.MessageDispather;
22 | import jsp.tutorial.java8.patterns.command.SMSDispatcher;
23 |
24 | /**
25 | * Command pattern tests.
26 | *
27 | * @author John Psoroulas
28 | */
29 | @Test(enabled = true)
30 | public class CommandTest extends TestSupport {
31 |
32 | private static final Logger LOG = LoggerFactory.getLogger(CommandTest.class);
33 |
34 | /**
35 | * Tests the message dispatching for job evaluation results.
36 | *
37 | * @throws InterruptedException
38 | * @throws ExecutionException
39 | */
40 | public void sendEvaluationResultsMessages() throws InterruptedException, ExecutionException {
41 | /*This is the client, creates the concrete commands and inject the receivers.
42 | * Creates the invoker and bind the appropriate commands */
43 |
44 | /* Lets assume that 10 is the number of secretaries */
45 | int secretaries = 10;
46 | /* Create the 'invoker', this is the secretarial services supervisor in our domain model.
47 | * The 'request' (send a message to inform the candidate about the evaluation result)
48 | * is passed to the 'command' via the invoker's submit method */
49 | ExecutorService executor = Executors.newFixedThreadPool(secretaries);
50 | /* Create the 'receivers'. Assume that is thread safe, so a single instance
51 | * can be used for the respective commands */
52 | MessageDispather emailDispatcher = new EmailDispatcher(); /* The 'receiver' for dispatching email messages*/
53 | MessageDispather smsDispatcher = new SMSDispatcher(); /* The 'receiver' for dispatching SMS messages*/
54 |
55 | /* The template message send at each candidate */
56 | String message = "Dear %s here is the results of the evaluation process blah, blah, blah, ...";
57 | /* Create the candidates, the same number as secretaries */
58 | List candiates = DataUtils.buildCandidates(secretaries);
59 | candiates.stream()
60 | .filter(candidate -> candidate.getContactInfo().isPresent()) /* Get the candidates that has set contact info */
61 | .map(candidate -> { /* Map each candidate with the future returned by the task submission to the executor */
62 | ContactInfo cinfo = candidate.getContactInfo().get();
63 | MessageData msgData = new MessageData(
64 | String.format(message, candidate.getName()),
65 | cinfo.getContact());
66 | /* Create the appropriate concrete command and inject the respective receivers */
67 | /* ------------ The old way ------------ */
68 | // Runnable command = cinfo.isEmail()
69 | // ? new EmailCommand(emailDispatcher, msgData)
70 | // : new SMSCommand(smsDispatcher, msgData);
71 | /* ------------ The lambda way ------------*/
72 | Runnable command = cinfo.isEmail()
73 | ? () -> emailDispatcher.send(msgData.getMessage(), msgData.getRecipients())
74 | : () -> smsDispatcher.send(msgData.getMessage(), msgData.getRecipients());
75 | /* Bind the commands to the invoker and forward the request to the command */
76 | return executor.submit(command);
77 | })
78 | /* Do not forget to collect all the futures first and then 'query' them to retrieve the result.
79 | * Otherwise the tasks are executed sequentially since each task submission
80 | * is followed by the query to the returned 'future' that blocks the whole stream processing
81 | * In case you want to query the future immediately after the task submission, you can have
82 | * a minor performance improvement if you use parallel stream. Using parallel stream,
83 | * the processed stream's elements in parallel are as many as the CPU cores of your desktop machine */
84 | .collect(Collectors.toList())
85 | .forEach(future -> {/* Check if the all commands are completed successfully, otherwise exceptions are raised */
86 | try {
87 | future.get(10, TimeUnit.SECONDS);
88 | }catch(Exception ex) {
89 | throw new RuntimeException(ex);
90 | }
91 | });
92 | /* Shutdown executor service */
93 | VariousUtils.shutdownExecutorService(executor, 10);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/BaseTemplatePatternTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import static java.util.Collections.reverseOrder;
4 | import static java.util.Comparator.comparing;
5 | import static org.testng.Assert.assertTrue;
6 |
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.function.Function;
10 | import java.util.function.ToIntFunction;
11 | import java.util.stream.Collectors;
12 |
13 | import org.apache.commons.lang3.tuple.Pair;
14 | import org.testng.Assert;
15 |
16 | import jps.tutorial.java8.test.DataUtils;
17 | import jps.tutorial.java8.test.TestSupport;
18 | import jsp.tutorial.java8.patterns.Candidate;
19 | import jsp.tutorial.java8.patterns.template.EvaluationProcess;
20 | import jsp.tutorial.java8.patterns.template.EvaluationProcessLambda;
21 | import jsp.tutorial.java8.patterns.template.IEvaluationProcess;
22 | import jsp.tutorial.java8.patterns.template.ProcessEvaluationAlgorithms;
23 |
24 | /**
25 | * Basic functionality for Template pattern tests.
26 | *
27 | * @author John Psoroulas
28 | */
29 | public class BaseTemplatePatternTest extends TestSupport {
30 |
31 | protected void evaluationProcessLambda(EvaluationProcessLambda process) {
32 | evaluationProcess(process, process.getSpecialEvaluation());
33 | }
34 |
35 | /**
36 | * Performs the evaluation process test for the specific {@link EvaluationProcess} type and the
37 | * respective special evaluation function
38 | * @param process the evaluation process
39 | * @param specialEvaluation the special evaluation function depended on the job type
40 | */
41 | /* PLEASE NOTE that the used algorithm is not the optimal and it may look naive on purpose,
42 | * since this code also targets to show java 8 lamda and streams features */
43 | protected void evaluationProcess(IEvaluationProcess process, ToIntFunction specialEvaluation) {
44 | /* Build the candidates */
45 | List candidates = DataUtils.buildCandidates(50);
46 | /* Calculate scores. Lets assume that this is an external service that returns a list
47 | * of Pair sorted descending by scores */
48 | List> scores = calculateScores(candidates, process);
49 | /* Test if the list is ordered by scores */
50 | scores.stream()
51 | .reduce((previous, next) -> {
52 | assertTrue(
53 | next.getRight().compareTo(previous.getRight()) <= 0,
54 | "Unexpected scores order");
55 | return next;
56 | });
57 | /* A mostly naive test to confirm that the specific evaluation algorithm is correctly applied.
58 | * just check if the total score is the sum of the common and the special evaluation algorithms */
59 | Map cmap = candidates.stream()
60 | /* Convert a candidate list to map for easy access. Assume that candidate name is unique */
61 | .collect(Collectors
62 | .toMap(
63 | Candidate::getName,
64 | Function.identity()));
65 | scores.stream()
66 | .forEach(score -> {
67 | Candidate candidate = cmap.get(score.getLeft()); /* Get the candidate by his name */
68 | /* If Function and not ToIntFunction is used you can try the andThen(...) and compose methods*/
69 | Integer expectedScore =
70 | ProcessEvaluationAlgorithms.common(candidate) + /* The common evaluation algorithm */
71 | specialEvaluation.applyAsInt(candidate); /* Apply the special evaluation algorithm */
72 | Assert.assertEquals(score.getRight(), expectedScore,
73 | "Unexpected score");
74 | });
75 | }
76 |
77 | /**
78 | * Calculates the candidates scores
79 | * @param candidates The candidates
80 | * @param process The evaluation process
81 | * @return the scores, a list of pairs (candidate name, score)
82 | */
83 | /* PLEASE NOTE that the used algorithm is not the optimal and it may look naive on purpose,
84 | * since this code also targets to show java 8 lamda and streams features
85 | * (e.g. in this example we could simply return a Map with candidates and scores
86 | * and not a sorted list of Pair) */
87 | protected List> calculateScores(List candidates, IEvaluationProcess process) {
88 | /* Create a list of pair(candidate name, score) sorted by the score */
89 | return candidates.stream()
90 | /* Map each candidate to a pair of candidate name and the score that results
91 | * from the evaluation process */
92 | .map(c -> {
93 | return Pair.of(
94 | c.getName(),
95 | process.evaluate(c));
96 | })
97 | /* In this case, the reversed() breaks the compiler's type inferencing mechanism,
98 | * use lambda expression providing explicitly the type parameters */
99 | // .sorted(Comparator.comparing(Pair::getRight).reversed())
100 | .sorted(
101 | reverseOrder(
102 | comparing((Pair p) -> p.getRight()))) /* sort the stream by score */
103 | .collect(Collectors.toList()); /* collect the stream into a list */
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/jsp/tutorial/java8/patterns/command/readme.md:
--------------------------------------------------------------------------------
1 | ## Command pattern
2 |
3 | The Command pattern is one of the behavioral design patterns and it is used to implement loose coupling
4 | between the _invoker_ and the _receiver_ of a request. The invoker is the object that issues a request
5 | on behalf of the client. The request is received by an implementation of the request
6 | processing abstraction, the _command_. The request processing abstraction is expressed by an interface
7 | with no-args method, usually named as _execute_. Each command implementation encapsulates the appropriate
8 | receiver which is the one that actually knows how to implement the request.
9 | The client program creates the command and probably binds it with the appropriate receiver
10 | (if it is not already encapsulated). After that creates the invoker and injects the command to perform an action.
11 | Note that the command may support undo. The command’s execute operation can store state for reversing its
12 | effects in the command itself. In this case, the command interface must have an undo operation that
13 | reverses the effects of the previous command execution.
14 | One of the example of this pattern in JDK, is the java.lang.Runnable and javax.swing.Action interfaces.
15 |
16 | At the Command pattern the following components are participating:
17 | * The _Command_, an abstraction (interface) for the command implementations, defines a common API for
18 | executing an operation.
19 | * The _ConcreteCommand(s)_, the command implementations. Defines a binding between a receiver and an action
20 | by invoking the receiver's appropriate method.
21 | * The _Invoker_, forwards the request from client to the command object.
22 | * The _Receiver_, knows how to process the request.
23 | * The _Client_, creates the concrete command and injects its receiver. Creates the invoker, inject
24 | the appropriate command and execute the invoker's method to issue the request at the command.
25 |
26 | ```
27 | +-------------+
28 | +------------+ +------------+ refers|<>|
29 | | Client | | Invoker |------@| Command |
30 | +------------+ +------------+ +-------------+
31 | : : | execute() |
32 | : : +-------------+
33 | : : creates ^
34 | : : |
35 | : : |
36 | : : +------------+ +-----------------+
37 | : :..........>| Receiver | calls | <> |
38 | : |------------|<<<<<<<| ConcreteCommand |
39 | : | action() | +-----------------+
40 | : +------------+ | execute() |
41 | :..................................>| |
42 | +-----------------+
43 | ```
44 |
45 | ### Test case
46 | ----
47 |
48 | In our scenario, suppose that a company has to inform the candidates of a job position about the evaluation
49 | results. A candidate is informed either via an email or a SMS message depending on candidate's preference.
50 | The message dispatching is performed by the company's secretaries which coordinated by the secretarial services
51 | supervisor.
52 |
53 | Adapting our scenario to the Command pattern results to the following components mapping:
54 | * Client ---> CommandTest -the unit test- (company/secretarial services)
55 | * Invoker ---> ExecutorService (secretarial services supervisor, each thread represents a secretary)
56 | * Command ---> java.lang.Runnable interface
57 | * ConcreteCommand1 ---> EmailCommand (command to inform candidate vi email)
58 | * ConcreteCommand2 ---> SMSCommand (command to inform candidate vi SMS)
59 | * Receiver1 ---> EmailDispatcher (the receiver for sending an email)
60 | * Receiver2 ---> SMSDispatcher (the receiver for sending a SMS)
61 |
62 | ### Discussion
63 | ----
64 |
65 | The Command pattern implementation using lambdas differs from the old approach in how the various commands
66 | are implemented. At the old approach, the commands are implemented via classes (or anonymous classes) whereas
67 | at Java 8 the commands can be lambdas (assuming the 'undo' functionality is not implemented since it requires
68 | to keep state).
69 |
70 | ```
71 | +------------+ +------------+
72 | | Client | | Invoker |`.
73 | +------------+ +------------+ \
74 | : : `.
75 | : : `. refers
76 | : : creates `.
77 | : : \
78 | : : `.
79 | : : +------------+ `@
80 | : :..........>| Receiver | calls +-------------+
81 | : |------------|<<<<<<<| |
82 | : | action() | | lambda |
83 | : +------------+ | |
84 | :..................................>| |
85 | +-------------+
86 | ```
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/DataUtils.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test;
2 |
3 | import java.util.List;
4 | import java.util.function.BiFunction;
5 | import java.util.function.IntSupplier;
6 | import java.util.function.Supplier;
7 | import java.util.stream.Collectors;
8 | import java.util.stream.IntStream;
9 |
10 | import org.apache.commons.lang3.RandomStringUtils;
11 | import org.apache.commons.lang3.RandomUtils;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import jsp.tutorial.java8.patterns.Candidate;
16 | import jsp.tutorial.java8.patterns.Candidate.Gender;
17 | import jsp.tutorial.java8.patterns.ContactInfo;
18 | import jsp.tutorial.java8.patterns.Degree;
19 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
20 | import jsp.tutorial.java8.patterns.JobInfo;
21 | import jsp.tutorial.java8.patterns.JobInfo.JobCategory;
22 | import one.util.streamex.StreamEx;
23 |
24 | /**
25 | * Data generation util
26 | * @author John Psoroulas
27 | */
28 | public final class DataUtils {
29 |
30 | private static final Logger LOG = LoggerFactory.getLogger(DataUtils.class);
31 |
32 | // some Suppliers
33 | public static final Supplier RAND_STRING_SUPPL =
34 | () -> RandomStringUtils.randomAlphabetic(15);
35 |
36 | public static final IntSupplier RAND_AGE_SUPPL =
37 | () -> RandomUtils.nextInt(20, 50);
38 |
39 | public static final Supplier RAND_GENDER_SUPPL =
40 | () -> Gender.values()[RandomUtils.nextInt(0, Gender.values().length)];
41 |
42 | public static final IntSupplier RAND_GRADE_SUPPL = () -> RandomUtils.nextInt(0, 10);
43 |
44 | public static final Supplier RAND_DEGREE_FIELD_SUPPL =
45 | () -> DegreeField.values()[RandomUtils.nextInt(0, DegreeField.values().length)];
46 |
47 | public static final Supplier RAND_JOB_CAT_SUPPL =
48 | () -> JobCategory.values()[RandomUtils.nextInt(0, JobCategory.values().length)];
49 |
50 | private DataUtils() {
51 | }
52 |
53 | public static Candidate buildCandidate(Supplier strSuppl, IntSupplier ageSuppl,
54 | Supplier genderSuppl) {
55 | return new Candidate.CandidateBuilder()
56 | .withName(strSuppl.get())
57 | .withAge(ageSuppl.getAsInt())
58 | .withGender(genderSuppl.get())
59 | .withDegree(buildDegree())
60 | .withContactInfo(new ContactInfo(strSuppl.get(), genRandomBoolean()))
61 | .build();
62 | }
63 |
64 | public static Candidate buildCandidate() {
65 | return buildCandidate(
66 | RAND_STRING_SUPPL,
67 | RAND_AGE_SUPPL,
68 | RAND_GENDER_SUPPL);
69 | }
70 |
71 | public static List buildCandidates(
72 | Supplier strSuppl,
73 | IntSupplier ageSuppl,
74 | Supplier genderSuppl,
75 | int numberOfCandidates) {
76 | return IntStream
77 | .range(0, numberOfCandidates) /* Generate a stream of int numbers */
78 | .mapToObj( /* Map numbers to 'Candidate' objects */
79 | i -> buildCandidate(strSuppl, ageSuppl, genderSuppl))
80 | .collect(Collectors.toList()); /* collect 'Candidate' objects to list */
81 | }
82 |
83 | public static List buildCandidates(int numberOfCandidates) {
84 | return buildCandidates(
85 | RAND_STRING_SUPPL,
86 | RAND_AGE_SUPPL,
87 | RAND_GENDER_SUPPL,
88 | numberOfCandidates);
89 | }
90 |
91 | public static List buildCandidatesByName(String name, int ns) {
92 | return buildCandidates(
93 | () -> name,
94 | RAND_AGE_SUPPL,
95 | RAND_GENDER_SUPPL,
96 | ns);
97 | }
98 |
99 | public static Degree buildDegree() {
100 | return new Degree.DegreeBuilder()
101 | .withField(RAND_DEGREE_FIELD_SUPPL.get())
102 | .withHolderName(RAND_STRING_SUPPL.get())
103 | .withGrade(RAND_GRADE_SUPPL.getAsInt())
104 | .build();
105 | }
106 |
107 | public static Degree buildDegreeByField(DegreeField field) {
108 | return new Degree.DegreeBuilder()
109 | .withField(field)
110 | .withHolderName(RAND_STRING_SUPPL.get())
111 | .withGrade(RAND_GRADE_SUPPL.getAsInt())
112 | .build();
113 | }
114 |
115 | public static List buildDegreesByField(DegreeField field, int numberOfDegrees) {
116 | return IntStream
117 | .range(0, numberOfDegrees)
118 | .mapToObj(i -> buildDegreeByField(field))
119 | .collect(Collectors.toList());
120 | }
121 |
122 | public static List buildJobs(int numberOfJobs) {
123 | return IntStream
124 | .range(0, numberOfJobs)
125 | .mapToObj(i -> new JobInfo(RAND_JOB_CAT_SUPPL.get(), RAND_STRING_SUPPL.get()))
126 | .collect(Collectors.toList());
127 | }
128 |
129 | public static boolean isSorted(List source, BiFunction unsc) {
130 | return StreamEx.of(source).pairMap(unsc).has(true);
131 | }
132 |
133 | public static void printList(List> list) {
134 | list.forEach((e) -> LOG.info(e.toString()));
135 | }
136 |
137 | public static boolean genRandomBoolean() {
138 | return Math.random() < 0.5;
139 | }
140 |
141 | // public static boolean isSorted(List source, BiFunction unsc) {
142 | // IntStream.range(1, arrayList.size())
143 | // .mapToObj(i -> new Pair(arrayList.get(i-1), arrayList.get(i)))
144 | // .forEach(System.out::println);
145 | // }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/StrategyTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import java.util.Collection;
4 | import java.util.List;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.testng.Assert;
9 | import org.testng.annotations.Test;
10 |
11 | import jps.tutorial.java8.test.DataUtils;
12 | import jps.tutorial.java8.test.TestSupport;
13 | import jsp.tutorial.java8.patterns.Candidate;
14 | import jsp.tutorial.java8.patterns.Degree;
15 | import jsp.tutorial.java8.patterns.Degree.DegreeField;
16 | import jsp.tutorial.java8.patterns.Employer;
17 | import jsp.tutorial.java8.patterns.strategy.DegreeRelevantHiringStrategy;
18 | import jsp.tutorial.java8.patterns.strategy.GradeHiringStrategy;
19 | import jsp.tutorial.java8.patterns.strategy.HiringStrategy;
20 | import jsp.tutorial.java8.patterns.strategy.StrategyAlgorithms;
21 |
22 | /**
23 | * Strategy pattern tests.
24 | *
25 | * @author John Psoroulas
26 | */
27 | @Test(enabled = true)
28 | public class StrategyTest extends TestSupport {
29 |
30 | private static final Logger LOG = LoggerFactory.getLogger(StrategyTest.class);
31 |
32 | private Employer employer = new Employer();
33 |
34 | /**
35 | * Just to check that employer can hire when the hiring strategy is set.
36 | * Check the 'Optional' usage at the {@link Employer#hire(Candidate)} method.
37 | */
38 | public void employerWithoutStrategy() {
39 | /* The employer is not authorized to hire */
40 | employer.setHiringStrategy(null);
41 | /* Build the candidates */
42 | List candidates = DataUtils.buildCandidates(100);
43 | // Find the number of hired candidates
44 | long hired = candidates.stream()
45 | .filter(employer::hire)
46 | .count();
47 | /* Test the results */
48 | Assert.assertEquals(hired, 0, "The employer has no authorization for hiring");
49 | }
50 |
51 | /**
52 | * Tests the 'old-way' {@link DegreeRelevantHiringStrategy}.
53 | */
54 | public void degreeRelevantHiringStrategy() {
55 | DegreeField field = DegreeField.ENGINEERING;
56 | /* Create an 'ENGINEERING degree relevant hiring strategy' with the 'old-way' */
57 | HiringStrategy strategy = new DegreeRelevantHiringStrategy(field);
58 | /* Do the test */
59 | doDegreeRelevantHiringStrategy(strategy, field);
60 | }
61 |
62 | /**
63 | * Tests the 'lambda-way' {@link DegreeRelevantHiringStrategy}.
64 | */
65 | public void degreeRelevantHiringStrategyLambda() {
66 | DegreeField field = DegreeField.ENGINEERING;
67 | /*
68 | * Create an 'ENGINEERING degree relevant hiring strategy' with the 'lambda-way'
69 | * The lambda calls the generic degree relevant hiring algorithm
70 | */
71 | HiringStrategy strategy = candidate -> StrategyAlgorithms.degree(candidate, field);
72 | /* Do the test */
73 | doDegreeRelevantHiringStrategy(strategy, field);
74 | }
75 |
76 | /**
77 | * The actual test for a {@link DegreeRelevantHiringStrategy}.
78 | */
79 | private void doDegreeRelevantHiringStrategy(HiringStrategy strategy, DegreeField field) {
80 | // Set the hiring strategy to the employer
81 | employer.setHiringStrategy(strategy);
82 | /* Build the candidates */
83 | List candidates = DataUtils.buildCandidates(100);
84 | /* Find the expected number of the hired candidates by counting the ones that
85 | * has ENGINEERING degree field */
86 | long expectedHired = candidates.stream()
87 | .map(Candidate::getDegrees)
88 | .flatMap(Collection::stream)
89 | .map(Degree::getField)
90 | .filter(field::equals)
91 | .count();
92 | /* Find the actual number of the hired candidates by counting the ones that
93 | * hired by applying the degree relevant hiring strategy */
94 | long actualHired = candidates.stream()
95 | .filter(employer::hire)
96 | .count();
97 | /* Test the results */
98 | Assert.assertEquals(actualHired, expectedHired, "Unexpected number of hired people");
99 | }
100 |
101 | /**
102 | * Tests the 'old-way' {@link GradeHiringStrategy}.
103 | */
104 | public void gradeHiringStrategy() {
105 | final int threshold = 8;
106 | /* Create a 'grade' hiring strategy with the 'old-way'*/
107 | HiringStrategy strategy = new GradeHiringStrategy(threshold);
108 | /* Do the test */
109 | doGradeHiringStrategy(strategy, threshold);
110 | }
111 |
112 | /**
113 | * Tests the 'lambda-way' {@link GradeHiringStrategy}.
114 | */
115 | public void gradeHiringStrategylambda() {
116 | final int threshold = 8;
117 | /* Create a 'grade' hiring strategy with the 'lambda-way'*/
118 | HiringStrategy strategy = candidate -> {
119 | // Call the generic grade hiring algorithm
120 | return StrategyAlgorithms.grade(candidate, threshold);
121 | };
122 | /* Do the test */
123 | doGradeHiringStrategy(strategy, threshold);
124 | }
125 |
126 | /**
127 | * The actual test for a {@link GradeHiringStrategy}.
128 | */
129 | private void doGradeHiringStrategy(HiringStrategy strategy, int threshold) {
130 | // Set the hiring strategy to the employer
131 | employer.setHiringStrategy(strategy);
132 | /* Build the candidates */
133 | List candidates = DataUtils.buildCandidates(100);
134 | /* Find the expected number of the hired candidates by counting the ones that
135 | * has grade greater or equals to the threshold */
136 | long expectedHired = candidates.stream()
137 | .map(Candidate::getDegrees)
138 | .flatMap(Collection::stream)
139 | .mapToInt(Degree::getGrade)
140 | .filter(grade -> grade >= threshold)
141 | .count();
142 | /* Find the actual number of the hired candidates by counting the ones that
143 | * hired by applying the grade hiring strategy */
144 | long actualHired = candidates.stream()
145 | .filter(employer::hire)
146 | .count();
147 | /* Test the results */
148 | Assert.assertEquals(actualHired, expectedHired, "Unexpected number of hired people");
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/test/java/jps/tutorial/java8/test/patterns/ChainTest.java:
--------------------------------------------------------------------------------
1 | package jps.tutorial.java8.test.patterns;
2 |
3 | import java.util.function.Function;
4 | import java.util.function.Supplier;
5 | import java.util.function.UnaryOperator;
6 | import java.util.stream.Stream;
7 |
8 | import org.apache.commons.lang3.Validate;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.testng.Assert;
12 | import org.testng.annotations.BeforeClass;
13 | import org.testng.annotations.Test;
14 |
15 | import jps.tutorial.java8.test.TestSupport;
16 | import jsp.tutorial.java8.patterns.chain.FirstLevelHDSupport;
17 | import jsp.tutorial.java8.patterns.chain.HDSupport;
18 | import jsp.tutorial.java8.patterns.chain.HDSupportHandlerAlgorithms;
19 | import jsp.tutorial.java8.patterns.chain.SecondLevelHDSupport;
20 | import jsp.tutorial.java8.patterns.chain.SupportRequest;
21 | import jsp.tutorial.java8.patterns.chain.SupportRequest.RequestType;
22 | import jsp.tutorial.java8.patterns.chain.ThirdLevelHDSupport;
23 | import jsp.tutorial.java8.patterns.chain.UnsupportedRequestException;
24 |
25 | /**
26 | * Chain pattern tests.
27 | *
28 | * @author John Psoroulas
29 | */
30 | @Test(enabled = true)
31 | public class ChainTest extends TestSupport {
32 |
33 | private static final Logger LOG = LoggerFactory.getLogger(ChainTest.class);
34 |
35 | /**
36 | * Holds the 'old-way' chain of handlers.
37 | */
38 | private HDSupport supportChain;
39 |
40 | /**
41 | * Holds the 'lambda-way' chain of handlers.
42 | */
43 | private Function supportChainLambda;
44 |
45 | @BeforeClass
46 | public void setup() {
47 | /* ------------ The 'old-way' ------------ */
48 | // /* Create the 'old way' support levels chain without streams */
49 | // supportChain = new FirstLevelHDSupport(
50 | // new SecondLevelHDSupport(
51 | // new ThirdLevelHDSupport()));
52 |
53 | /* Create the 'old-way' support levels chain using streams, creating the handlers references explicitly,
54 | * convenient for many handlers */
55 | // supportChain = Arrays
56 | // .stream(new HDSupport[] {new FirstLevelHDSupport(), new SecondLevelHDSupport(), new ThirdLevelHDSupport()})
57 | // .reduce((f, n) -> { /* reduce to the handler that chains all handlers together */
58 | // HDSupport target = f.getNextSupport();
59 | // if(null == target) {
60 | // target = f;
61 | // }
62 | // target.setNextSupport(n);
63 | // return f;
64 | // }).get();
65 |
66 | /* Another way to create the 'old-way' support levels chain using streams, using constructor references,
67 | * convenient for many handlers */
68 | supportChain = Stream.> of(
69 | FirstLevelHDSupport::new,
70 | SecondLevelHDSupport::new,
71 | ThirdLevelHDSupport::new)
72 | .map(c -> c.get()) /* Create the handler instance */
73 | .reduce((f, n) -> { /* Reduce to the handler that chains all handlers together */
74 | HDSupport target = f.getNextSupport();
75 | if(null == target) {
76 | target = f;
77 | }
78 | target.setNextSupport(n);
79 | return f;
80 | }).get();
81 |
82 | /* ------------ The 'lambda-way' ------------ */
83 | /* Handler wrapper function that decides whether the actual handler should be invoked or not */
84 | UnaryOperator> handlerWrapper =
85 | (f) -> {
86 | return (r) -> {
87 | Validate.notNull(r, "Support request is required!");
88 | if(r.isHandled()) { /* Do not invoke the handler if the support request is handled */
89 | return r; /* Just return the already handled support request */
90 | }
91 | return f.apply(r); /* Otherwise invoke the handler */
92 | };
93 | };
94 |
95 | /* Create a final handler that throws an UnsupportedRequestException
96 | * when the support request is not handled */
97 | Function hanldedChecker =
98 | (r) -> {
99 | /* If the support request is finally handled, just return the request */
100 | if(r.isHandled()) {
101 | return r;
102 | }/* Otherwise, throw an exception */
103 | throw new UnsupportedRequestException(r.getType().toString());
104 | };
105 |
106 | /* Create the support levels chain with the 'lambda-way'*/
107 | supportChainLambda = Stream.> of(
108 | handlerWrapper.apply(r -> HDSupportHandlerAlgorithms.firstLevelSupport(r)),
109 | handlerWrapper.apply(r -> HDSupportHandlerAlgorithms.secondLevelSupport(r)),
110 | handlerWrapper.apply(r -> HDSupportHandlerAlgorithms.thirdLevelSupport(r)),
111 | hanldedChecker)
112 | /* reduce to the handler that chains all handlers together */
113 | // .reduce((f, n) -> {
114 | // return f.andThen(n);
115 | // })
116 | // .orElseGet(() -> Function.identity());
117 | /* A more elegant way to reduce */
118 | .reduce(Function.identity(), Function::andThen);
119 |
120 | }
121 |
122 | /**
123 | * Tests a support request resolved by an 'old-way' support chain.
124 | */
125 | public void submitResolvedSupportRequest() {
126 | /* This is the client that submits the support requests to support levels chain */
127 | /* Create a request that the second level support can handle */
128 | SupportRequest request = new SupportRequest(RequestType.LEVEL2);
129 | /* Submit the request to the support levels chain */
130 | supportChain.handle(request);
131 | Assert.assertTrue(request.isHandled(), "The request should be handled");
132 | }
133 |
134 | /**
135 | * Tests a support request that can not resolved by an 'old-way' support chain.
136 | */
137 | @Test(expectedExceptions = {UnsupportedRequestException.class})
138 | public void submitUnresolvedSupportRequest() {
139 | /* This is the client that submits the support requests to support levels chain */
140 | /* Create a request that none of the support levels can handle */
141 | SupportRequest request = new SupportRequest(RequestType.LEVEL4);
142 | /* Submit the request to the support levels chain */
143 | supportChain.handle(request);
144 | }
145 |
146 | /**
147 | * Tests a support request resolved by a 'lambda-way' support chain.
148 | */
149 | public void submitResolvedSupportRequestLambda() {
150 | /* This is the client that submits the support requests to support levels chain*/
151 | /* Create a request that the second level support can handle */
152 | SupportRequest request = new SupportRequest(RequestType.LEVEL2);
153 | /* Submit the request to the support levels chain */
154 | supportChainLambda.apply(request);
155 | /* Test the results */
156 | Assert.assertTrue(request.isHandled(), "The request should be handled");
157 | }
158 |
159 | /**
160 | * Tests a support request that can not resolved by a 'lambda-way' support chain.
161 | */
162 | @Test(expectedExceptions = {UnsupportedRequestException.class})
163 | public void submitUnresolvedSupportRequestLambda() {
164 | /* This is the client that submits the support requests to support levels chain*/
165 | /* Create a request that none of the support levels can handle */
166 | SupportRequest request = new SupportRequest(RequestType.LEVEL4);
167 | /* Submit the request to the support levels chain */
168 | supportChainLambda.apply(request);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------