├── .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 | --------------------------------------------------------------------------------