Repository: xpinjection/design-patterns Branch: master Commit: c35bff7755d0 Files: 104 Total size: 56.5 KB Directory structure: gitextract_v3o3f7e_/ ├── LICENSE ├── README.md ├── libs/ │ └── lombok-1.16.10.jar └── src/ └── com/ └── xpinjection/ └── patterns/ ├── adapter/ │ ├── ModernAdapterClient.java │ └── canonical/ │ ├── AdapterClient.java │ ├── CharCounterTask.java │ └── ExecutorConsumerAdaptor.java ├── builder/ │ ├── FutureBuilderClient.java │ ├── FutureCluster.java │ ├── LombokBuilderClient.java │ ├── LombokCluster.java │ └── canonical/ │ ├── BuilderClient.java │ └── Cluster.java ├── chain/ │ ├── ModernAuthRequestHandler.java │ ├── ModernChainClient.java │ └── canonical/ │ ├── AbstractRequestHandler.java │ ├── AuthRequestHandler.java │ ├── ChainClient.java │ ├── LoggingRequestHandler.java │ ├── Request.java │ └── RequestHandler.java ├── command/ │ ├── ModernCommandClient.java │ ├── ModernMacro.java │ └── canonical/ │ ├── BoldCommand.java │ ├── Command.java │ ├── CommandClient.java │ ├── Document.java │ ├── Editor.java │ ├── ItalicCommand.java │ ├── Macro.java │ └── UnderlineCommand.java ├── decorator/ │ ├── ModernDecoratorClient.java │ └── canonical/ │ ├── BenchmarkingDigitCounter.java │ ├── DecoratorClient.java │ ├── DigitCounter.java │ └── NaiveDigitCounter.java ├── factorymethod/ │ ├── ModernFactoryMethodClient.java │ └── canonical/ │ ├── Document.java │ ├── DocumentFactory.java │ ├── FactoryMethodClient.java │ ├── JsonDocument.java │ └── JsonDocumentFactory.java ├── iterator/ │ ├── ModernIteratorClient.java │ ├── ModernMultilineText.java │ ├── ModernText.java │ └── canonical/ │ ├── IteratorClient.java │ ├── LineIterator.java │ ├── MultilineText.java │ └── Text.java ├── observer/ │ ├── ModernObserver.java │ ├── ModernObserverClient.java │ ├── ModernTimeServer.java │ └── canonical/ │ ├── Event.java │ ├── EventListener.java │ ├── LoggingEventListener.java │ ├── Observer.java │ ├── ObserverClient.java │ └── TimeServer.java ├── prototype/ │ ├── Stats.java │ ├── StatsProcessor.java │ ├── TrafficStats.java │ └── canonical/ │ ├── Circle.java │ ├── Editor.java │ ├── Figure.java │ ├── PrototypeClient.java │ └── Rectangle.java ├── proxy/ │ ├── ModernProxyClient.java │ ├── PermissionChecker.java │ ├── canonical/ │ │ ├── Order.java │ │ ├── OrderService.java │ │ ├── PermissionCheckingOrderServiceProxy.java │ │ ├── ProxyClient.java │ │ └── RealOrderService.java │ └── spring-context.xml ├── singleton/ │ ├── ModernServiceRegistry.java │ ├── ModernSingletonClient.java │ ├── canonical/ │ │ ├── NamedService.java │ │ ├── ServiceRegistry.java │ │ └── SingletonClient.java │ └── spring-context.xml ├── strategy/ │ ├── ModernStrategyClient.java │ ├── TrafficCalculationStrategies.java │ └── canonical/ │ ├── PageTrafficCalculator.java │ ├── Position.java │ ├── StrategyClient.java │ ├── TopTrafficCalculator.java │ └── TrafficCalculator.java ├── templatemethod/ │ ├── LoggingUserStorage.java │ ├── ModernTemplateMethodClient.java │ ├── ModernUserRestorer.java │ └── canonical/ │ ├── AbstractUserRestorer.java │ ├── LoggingUserRestorer.java │ ├── TemplateMethodClient.java │ └── User.java └── visitor/ ├── LambdaVisitor.java ├── ModernVisitorClient.java └── canonical/ ├── AreaVisitor.java ├── Circle.java ├── Element.java ├── PerimeterVisitor.java ├── Rectangle.java ├── Square.java ├── Visitor.java └── VisitorClient.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017-2025 XP Injection Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # design-patterns Modern view on classic design patterns implementation in Java [blog article](http://javaday.org.ua/modern-view-of-design-patterns-in-java/) ================================================ FILE: src/com/xpinjection/patterns/adapter/ModernAdapterClient.java ================================================ package com.xpinjection.patterns.adapter; import com.xpinjection.patterns.adapter.canonical.CharCounterTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; /** * @author Alimenkou Mikalai */ public class ModernAdapterClient { public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(3); AtomicInteger counter = new AtomicInteger(); Stream.of("af", "bdf", "c") .map(s -> new CharCounterTask(s, counter)) .forEach(executor::execute); executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); System.out.println("Total count: " + counter.get()); } } ================================================ FILE: src/com/xpinjection/patterns/adapter/canonical/AdapterClient.java ================================================ package com.xpinjection.patterns.adapter.canonical; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; /** * @author Alimenkou Mikalai */ public class AdapterClient { public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(3); AtomicInteger counter = new AtomicInteger(); Stream.of("af", "bdf", "c") .map(s -> new CharCounterTask(s, counter)) .forEach(new ExecutorConsumerAdaptor(executor)); executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); System.out.println("Total count: " + counter.get()); } } ================================================ FILE: src/com/xpinjection/patterns/adapter/canonical/CharCounterTask.java ================================================ package com.xpinjection.patterns.adapter.canonical; import java.util.concurrent.atomic.AtomicInteger; /** * @author Alimenkou Mikalai * @version 1.0 */ public class CharCounterTask implements Runnable { private final String str; private final AtomicInteger counter; public CharCounterTask(String str, AtomicInteger counter) { this.str = str; this.counter = counter; } @Override public void run() { counter.addAndGet(str.length()); } } ================================================ FILE: src/com/xpinjection/patterns/adapter/canonical/ExecutorConsumerAdaptor.java ================================================ package com.xpinjection.patterns.adapter.canonical; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * @author Alimenkou Mikalai */ public class ExecutorConsumerAdaptor implements Consumer { private final Executor executor; public ExecutorConsumerAdaptor(Executor executor) { this.executor = executor; } @Override public void accept(Runnable task) { executor.execute(task); } } ================================================ FILE: src/com/xpinjection/patterns/builder/FutureBuilderClient.java ================================================ package com.xpinjection.patterns.builder; /** * @author Alimenkou Mikalai */ public class FutureBuilderClient { /** With hope for better Java ;) * * public static void main(String[] args) { * FutureCluster cluster = new FutureCluster("localhost").setPort(3165) * .setRetryAttempts(3).setMetricsEnabled(false); * } */ } ================================================ FILE: src/com/xpinjection/patterns/builder/FutureCluster.java ================================================ package com.xpinjection.patterns.builder; /** * @author Alimenkou Mikalai */ public class FutureCluster { /** With hope for better Java ;) * * property String[] hosts; * property int port; * property int retryAttempts; * property boolean metricsEnabled; * */ } ================================================ FILE: src/com/xpinjection/patterns/builder/LombokBuilderClient.java ================================================ package com.xpinjection.patterns.builder; /** * @author Alimenkou Mikalai */ public class LombokBuilderClient { public static void main(String[] args) { LombokCluster cluster = LombokCluster.builder() .host("localhost").port(3165) .retryAttempts(3).metricsEnabled(false).build(); } } ================================================ FILE: src/com/xpinjection/patterns/builder/LombokCluster.java ================================================ package com.xpinjection.patterns.builder; import lombok.Builder; import lombok.Getter; import lombok.Singular; import java.util.List; /** * @author Alimenkou Mikalai */ @Builder @Getter public class LombokCluster { @Singular private List hosts; private int port; private int retryAttempts; private boolean metricsEnabled; } ================================================ FILE: src/com/xpinjection/patterns/builder/canonical/BuilderClient.java ================================================ package com.xpinjection.patterns.builder.canonical; /** * @author Alimenkou Mikalai */ public class BuilderClient { public static void main(String[] args) { Cluster cluster = Cluster.runtimeBuilder() .addContactPoints("localhost").withPort(3165) .withRetryAttempts(3) .withoutMetrics().build(); cluster = Cluster.storingBuilder() .addContactPoints("localhost").withPort(3165) .withRetryAttempts(3) .withoutMetrics().build(); } } ================================================ FILE: src/com/xpinjection/patterns/builder/canonical/Cluster.java ================================================ package com.xpinjection.patterns.builder.canonical; /** * @author Alimenkou Mikalai */ public class Cluster { private String[] hosts; private int port; private int retryAttempts; private boolean metricsEnabled; public static StoringBuilder storingBuilder() { return new StoringBuilder(); } public static RuntimeBuilder runtimeBuilder() { return new RuntimeBuilder(); } public Cluster(String[] hosts, int port) { this.hosts = hosts; this.port = port; } public Cluster() { } public void setHosts(String... hosts) { this.hosts = hosts; } public String[] getHosts() { return hosts; } public void setPort(int port) { this.port = port; } public int getPort() { return port; } public void setRetryAttempts(int retryAttempts) { this.retryAttempts = retryAttempts; } public int getRetryAttempts() { return retryAttempts; } public void setMetricsEnabled(boolean metricsEnabled) { this.metricsEnabled = metricsEnabled; } public boolean isMetricsEnabled() { return metricsEnabled; } public static class RuntimeBuilder { private Cluster cluster = new Cluster(); public RuntimeBuilder addContactPoints(String... hosts) { if (hosts == null || hosts.length == 0) { throw new IllegalArgumentException("Hosts must be set!"); } cluster.setHosts(hosts); return this; } public RuntimeBuilder withPort(int port) { cluster.setPort(port); return this; } public RuntimeBuilder withRetryAttempts(int retryAttempts) { cluster.setRetryAttempts(retryAttempts); return this; } public RuntimeBuilder withoutMetrics() { cluster.setMetricsEnabled(false); return this; } public Cluster build() { return cluster; } } public static class StoringBuilder { private String[] hosts; private int port; private int retryAttempts; private boolean metricsEnabled; public StoringBuilder addContactPoints(String... hosts) { if (hosts == null || hosts.length == 0) { throw new IllegalArgumentException("Hosts must be set!"); } this.hosts = hosts; return this; } public StoringBuilder withPort(int port) { this.port = port; return this; } public StoringBuilder withRetryAttempts(int retryAttempts) { this.retryAttempts = retryAttempts; return this; } public StoringBuilder withoutMetrics() { this.metricsEnabled = false; return this; } public Cluster build() { Cluster cluster = new Cluster(hosts, port); cluster.setMetricsEnabled(metricsEnabled); cluster.setRetryAttempts(retryAttempts); return cluster; } } } ================================================ FILE: src/com/xpinjection/patterns/chain/ModernAuthRequestHandler.java ================================================ package com.xpinjection.patterns.chain; import com.xpinjection.patterns.chain.canonical.Request; import java.util.function.Consumer; /** * @author Alimenkou Mikalai */ public class ModernAuthRequestHandler implements Consumer { private static final ThreadLocal currentUser = new ThreadLocal<>(); @Override public void accept(Request request) { currentUser.set(request.getUserId()); System.out.println("Current user is set to: " + request.getUserId()); request.addParam("AUTH_PASSED", "TRUE"); } public static Long getCurrentUser() { return currentUser.get(); } } ================================================ FILE: src/com/xpinjection/patterns/chain/ModernChainClient.java ================================================ package com.xpinjection.patterns.chain; import com.xpinjection.patterns.chain.canonical.Request; import java.util.function.Consumer; import java.util.stream.Stream; /** * @author Alimenkou Mikalai */ public class ModernChainClient { public static void main(String[] args) { Request request = new Request(5); chain(new ModernAuthRequestHandler(), ModernChainClient::log) .accept(request); } @SafeVarargs public static Consumer chain(Consumer... handlers) { return Stream.of(handlers) .reduce(Consumer::andThen) .get(); } private static void log(Request request) { System.out.println("Request arrived: " + request); } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/AbstractRequestHandler.java ================================================ package com.xpinjection.patterns.chain.canonical; /** * @author Alimenkou Mikalai */ public abstract class AbstractRequestHandler implements RequestHandler { private final RequestHandler next; public AbstractRequestHandler(RequestHandler next) { this.next = next; } protected void passToTheNext(Request request) { if (next != null) { next.handleRequest(request); } } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/AuthRequestHandler.java ================================================ package com.xpinjection.patterns.chain.canonical; /** * @author Alimenkou Mikalai */ public class AuthRequestHandler extends AbstractRequestHandler { private static final ThreadLocal currentUser = new ThreadLocal<>(); public AuthRequestHandler(RequestHandler next) { super(next); } @Override public void handleRequest(Request request) { currentUser.set(request.getUserId()); System.out.println("Current user is set to: " + request.getUserId()); request.addParam("AUTH_PASSED", "TRUE"); passToTheNext(request); } public static Long getCurrentUser() { return currentUser.get(); } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/ChainClient.java ================================================ package com.xpinjection.patterns.chain.canonical; /** * @author Alimenkou Mikalai */ public class ChainClient { public static void main(String[] args) { Request request = new Request(5); RequestHandler handler = new AuthRequestHandler(new LoggingRequestHandler(null)); handler.handleRequest(request); } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/LoggingRequestHandler.java ================================================ package com.xpinjection.patterns.chain.canonical; /** * @author Alimenkou Mikalai */ public class LoggingRequestHandler extends AbstractRequestHandler { public LoggingRequestHandler(RequestHandler next) { super(next); } @Override public void handleRequest(Request request) { System.out.println("Request arrived: " + request); passToTheNext(request); } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/Request.java ================================================ package com.xpinjection.patterns.chain.canonical; import java.util.HashMap; import java.util.Map; /** * @author Alimenkou Mikalai */ public class Request { private final long userId; private final Map params = new HashMap<>(); public Request(long userId) { this.userId = userId; } public Request addParam(String name, String value) { params.put(name, value); return this; } public long getUserId() { return userId; } public Map getParams() { return params; } @Override public String toString() { return "Request{userId=" + userId + ", params=" + params + "}"; } } ================================================ FILE: src/com/xpinjection/patterns/chain/canonical/RequestHandler.java ================================================ package com.xpinjection.patterns.chain.canonical; /** * @author Alimenkou Mikalai */ public interface RequestHandler { void handleRequest(Request request); } ================================================ FILE: src/com/xpinjection/patterns/command/ModernCommandClient.java ================================================ package com.xpinjection.patterns.command; import com.xpinjection.patterns.command.canonical.Document; /** * @author Alimenkou Mikalai */ public class ModernCommandClient { public static void main(String[] args) { Document editor = new Document(); ModernMacro macro = new ModernMacro(); macro.record(editor::bold) .record(editor::italic) .record(editor::underline) .run(); } } ================================================ FILE: src/com/xpinjection/patterns/command/ModernMacro.java ================================================ package com.xpinjection.patterns.command; import com.xpinjection.patterns.command.canonical.Command; import java.util.ArrayList; import java.util.List; /** * @author Alimenkou Mikalai */ public class ModernMacro { private final List commands = new ArrayList<>(); public ModernMacro record(Command action) { commands.add(action); return this; } public void run() { commands.forEach(Command::execute); } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/BoldCommand.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public class BoldCommand implements Command { private final Editor editor; public BoldCommand(Editor editor) { this.editor = editor; } @Override public void execute() { editor.bold(); } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/Command.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public interface Command { void execute(); } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/CommandClient.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public class CommandClient { public static void main(String[] args) { Document editor = new Document(); Macro macro = new Macro(); macro.record(new BoldCommand(editor)); macro.record(new ItalicCommand(editor)); macro.record(new UnderlineCommand(editor)); macro.run(); } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/Document.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public class Document implements Editor { @Override public void bold() { System.out.println("Bold text..."); } @Override public void italic() { System.out.println("Italic text..."); } @Override public void underline() { System.out.println("Underline text..."); } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/Editor.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public interface Editor { void bold(); void italic(); void underline(); } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/ItalicCommand.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public class ItalicCommand implements Command { private final Editor editor; public ItalicCommand(Editor editor) { this.editor = editor; } @Override public void execute() { editor.italic(); } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/Macro.java ================================================ package com.xpinjection.patterns.command.canonical; import java.util.ArrayList; import java.util.List; /** * @author Alimenkou Mikalai */ public class Macro { private final List commands = new ArrayList<>(); public void record(Command action) { commands.add(action); } public void run() { for (Command command : commands) { command.execute(); } } } ================================================ FILE: src/com/xpinjection/patterns/command/canonical/UnderlineCommand.java ================================================ package com.xpinjection.patterns.command.canonical; /** * @author Alimenkou Mikalai */ public class UnderlineCommand implements Command { private final Editor editor; public UnderlineCommand(Editor editor) { this.editor = editor; } @Override public void execute() { editor.underline(); } } ================================================ FILE: src/com/xpinjection/patterns/decorator/ModernDecoratorClient.java ================================================ package com.xpinjection.patterns.decorator; import com.xpinjection.patterns.decorator.canonical.DigitCounter; import com.xpinjection.patterns.decorator.canonical.NaiveDigitCounter; import java.util.function.Function; /** * @author Alimenkou Mikalai */ public class ModernDecoratorClient { public static void main(String[] args) { DigitCounter counter = wrap(new NaiveDigitCounter()); int digitsCount = counter.count("fd6j78fh19kj"); System.out.println(digitsCount + " digits found"); digitsCount = ((Function) String::trim) .andThen(new NaiveDigitCounter()::count) .apply(" \n 123 \t"); System.out.println(digitsCount + " digits found"); } public static DigitCounter wrap(DigitCounter counter) { return s -> { long startTime = System.currentTimeMillis(); int count = counter.count(s); long endTime = System.currentTimeMillis(); System.out.println("Counting took " + (endTime - startTime) + " ms"); return count; }; } } ================================================ FILE: src/com/xpinjection/patterns/decorator/canonical/BenchmarkingDigitCounter.java ================================================ package com.xpinjection.patterns.decorator.canonical; /** * @author Alimenkou Mikalai */ public class BenchmarkingDigitCounter implements DigitCounter { private final DigitCounter delegate; public BenchmarkingDigitCounter(DigitCounter delegate) { this.delegate = delegate; } @Override public int count(String str) { long startTime = System.currentTimeMillis(); int count = delegate.count(str); long endTime = System.currentTimeMillis(); System.out.println("Counting took " + (endTime - startTime) + " ms"); return count; } } ================================================ FILE: src/com/xpinjection/patterns/decorator/canonical/DecoratorClient.java ================================================ package com.xpinjection.patterns.decorator.canonical; /** * @author Alimenkou Mikalai */ public class DecoratorClient { public static void main(String[] args) { DigitCounter counter = new BenchmarkingDigitCounter(new NaiveDigitCounter()); int digitsCount = counter.count("fd6j78fh19kj"); System.out.println(digitsCount + " digits found"); } } ================================================ FILE: src/com/xpinjection/patterns/decorator/canonical/DigitCounter.java ================================================ package com.xpinjection.patterns.decorator.canonical; /** * @author Alimenkou Mikalai */ public interface DigitCounter { int count(String str); } ================================================ FILE: src/com/xpinjection/patterns/decorator/canonical/NaiveDigitCounter.java ================================================ package com.xpinjection.patterns.decorator.canonical; /** * @author Alimenkou Mikalai */ public class NaiveDigitCounter implements DigitCounter { @Override public int count(String str) { int count = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (Character.isDigit(c)) { count++; } } return count; } } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/ModernFactoryMethodClient.java ================================================ package com.xpinjection.patterns.factorymethod; import com.xpinjection.patterns.factorymethod.canonical.Document; import com.xpinjection.patterns.factorymethod.canonical.DocumentFactory; import com.xpinjection.patterns.factorymethod.canonical.JsonDocument; import java.util.function.Function; /** * @author Alimenkou Mikalai */ public class ModernFactoryMethodClient { public static void main(String[] args) { DocumentFactory factory = JsonDocument::new; printUserDetails(factory.create("USER")); Function plainFactory = JsonDocument::new; printUserDetails(plainFactory.apply("USER")); } private static void printUserDetails(Document document) { document.addField("name", "Mikalai"); document.addField("surname", "Alimenkou"); System.out.println(document); } } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/canonical/Document.java ================================================ package com.xpinjection.patterns.factorymethod.canonical; /** * @author Alimenkou Mikalai */ public interface Document { String getName(); void addField(String name, String value); String toString(); } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/canonical/DocumentFactory.java ================================================ package com.xpinjection.patterns.factorymethod.canonical; /** * @author Alimenkou Mikalai */ public interface DocumentFactory { Document create(String name); } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/canonical/FactoryMethodClient.java ================================================ package com.xpinjection.patterns.factorymethod.canonical; /** * @author Alimenkou Mikalai */ public class FactoryMethodClient { public static void main(String[] args) { DocumentFactory factory = new JsonDocumentFactory(); Document document = factory.create("USER"); document.addField("name", "Mikalai"); document.addField("surname", "Alimenkou"); System.out.println(document); } } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/canonical/JsonDocument.java ================================================ package com.xpinjection.patterns.factorymethod.canonical; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; /** * @author Alimenkou Mikalai */ public class JsonDocument implements Document { private final String name; private final Map fields = new LinkedHashMap<>(); public JsonDocument(String name) { this.name = name; } @Override public String getName() { return name; } @Override public void addField(String name, String value) { fields.put(name, value); } @Override public String toString() { return fields.entrySet().stream() .map(e -> e.getKey() + ": " + e.getValue()) .collect(Collectors.joining(",\n", "{\n", "\n}")); } } ================================================ FILE: src/com/xpinjection/patterns/factorymethod/canonical/JsonDocumentFactory.java ================================================ package com.xpinjection.patterns.factorymethod.canonical; /** * @author Alimenkou Mikalai */ public class JsonDocumentFactory implements DocumentFactory { @Override public Document create(String name) { return new JsonDocument(name); } } ================================================ FILE: src/com/xpinjection/patterns/iterator/ModernIteratorClient.java ================================================ package com.xpinjection.patterns.iterator; import java.util.Arrays; import java.util.stream.Collectors; /** * @author Alimenkou Mikalai */ public class ModernIteratorClient { public static void main(String[] args) { ModernText text = new ModernMultilineText("This is just \n" + " a simple multiline \n" + "\n" + " text\n" + "\n", "\n"); text.linesStream().forEach(System.out::println); String sentence = "JEEConf is the oldest Java conference in Ukraine"; System.out.println(capitalize(sentence)); } private static String capitalize(String sentence) { return Arrays.stream(sentence.split(" ")) .filter(word -> word.length() > 4) .map(String::toUpperCase) .collect(Collectors.joining(" ")); } } ================================================ FILE: src/com/xpinjection/patterns/iterator/ModernMultilineText.java ================================================ package com.xpinjection.patterns.iterator; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * @author Alimenkou Mikalai */ public class ModernMultilineText implements ModernText { private final String text; private final String lineSeparator; public ModernMultilineText(String text, String lineSeparator) { this.text = text; this.lineSeparator = lineSeparator; } @Override public Stream linesStream() { return StreamSupport.stream(new LineSupplier(), false); } private class LineSupplier extends Spliterators.AbstractSpliterator { private int lineStartIndex = 0; public LineSupplier() { super(Long.MAX_VALUE, Spliterator.NONNULL | Spliterator.SIZED); } @Override public boolean tryAdvance(Consumer action) { if (lineStartIndex >= text.length()) { return false; } int separatorIndex = text.indexOf(lineSeparator, lineStartIndex + 1); if (separatorIndex < 0) { separatorIndex = text.length() - 1; } String line = text.substring(lineStartIndex, separatorIndex); action.accept(line); lineStartIndex = separatorIndex + 1; return true; } } } ================================================ FILE: src/com/xpinjection/patterns/iterator/ModernText.java ================================================ package com.xpinjection.patterns.iterator; import java.util.stream.Stream; /** * @author Alimenkou Mikalai */ public interface ModernText { Stream linesStream(); } ================================================ FILE: src/com/xpinjection/patterns/iterator/canonical/IteratorClient.java ================================================ package com.xpinjection.patterns.iterator.canonical; /** * @author Alimenkou Mikalai */ public class IteratorClient { public static void main(String[] args) { Text text = new MultilineText("This is just \n" + " a simple multiline \n" + "\n" + " text\n" + "\n", "\n"); LineIterator lineIterator = text.lineIterator(); String line = lineIterator.nextLine(); while (line != null) { System.out.println(line); line = lineIterator.nextLine(); } String sentence = "JEEConf is the oldest Java conference in Ukraine"; System.out.println(capitalize(sentence)); } private static String capitalize(String sentence) { String[] words = sentence.split(" "); StringBuilder caps = new StringBuilder(); for (String word : words) { if (word.length() > 4) { caps.append(word.toUpperCase()).append(' '); } } return caps.toString().trim(); } } ================================================ FILE: src/com/xpinjection/patterns/iterator/canonical/LineIterator.java ================================================ package com.xpinjection.patterns.iterator.canonical; /** * @author Alimenkou Mikalai */ public interface LineIterator { String nextLine(); } ================================================ FILE: src/com/xpinjection/patterns/iterator/canonical/MultilineText.java ================================================ package com.xpinjection.patterns.iterator.canonical; /** * @author Alimenkou Mikalai */ public class MultilineText implements Text { private final String text; private final String lineSeparator; public MultilineText(String text, String lineSeparator) { this.text = text; this.lineSeparator = lineSeparator; } @Override public LineIterator lineIterator() { return new SeparatorBasedLineIterator(); } private class SeparatorBasedLineIterator implements LineIterator { private int lineStartIndex = 0; @Override public String nextLine() { if (lineStartIndex >= text.length()) { return null; } int separatorIndex = text.indexOf(lineSeparator, lineStartIndex + 1); if (separatorIndex < 0) { separatorIndex = text.length() - 1; } String line = text.substring(lineStartIndex, separatorIndex); lineStartIndex = separatorIndex + 1; return line; } } } ================================================ FILE: src/com/xpinjection/patterns/iterator/canonical/Text.java ================================================ package com.xpinjection.patterns.iterator.canonical; /** * @author Alimenkou Mikalai */ public interface Text { LineIterator lineIterator(); } ================================================ FILE: src/com/xpinjection/patterns/observer/ModernObserver.java ================================================ package com.xpinjection.patterns.observer; import com.xpinjection.patterns.observer.canonical.Event; import java.util.function.Consumer; /** * @author Alimenkou Mikalai */ public interface ModernObserver { void addListener(Consumer listener); } ================================================ FILE: src/com/xpinjection/patterns/observer/ModernObserverClient.java ================================================ package com.xpinjection.patterns.observer; import com.xpinjection.patterns.observer.canonical.Event; /** * @author Alimenkou Mikalai */ public class ModernObserverClient { public static void main(String[] args) { ModernTimeServer server = new ModernTimeServer(); server.addListener(ModernObserverClient::log); server.addListener(e -> System.out.println("ANOTHER EVENT: " + e.getName())); server.reportTime(); server.reportTime(); } public static void log(Event event) { System.out.println("EVENT: " + event.getName()); } } ================================================ FILE: src/com/xpinjection/patterns/observer/ModernTimeServer.java ================================================ package com.xpinjection.patterns.observer; import com.xpinjection.patterns.observer.canonical.Event; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; /** * @author Alimenkou Mikalai */ public class ModernTimeServer implements ModernObserver { private static final String TIME_FORMAT = "HH:mm:ss"; private final Queue> listeners = new ConcurrentLinkedQueue<>(); @Override public void addListener(Consumer listener) { listeners.add(listener); } public void reportTime() { Event event = new Event(new SimpleDateFormat(TIME_FORMAT).format(new Date())); listeners.forEach(listener -> listener.accept(event)); } } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/Event.java ================================================ package com.xpinjection.patterns.observer.canonical; /** * @author Alimenkou Mikalai */ public class Event { private final String name; public Event(String name) { this.name = name; } public String getName() { return name; } } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/EventListener.java ================================================ package com.xpinjection.patterns.observer.canonical; /** * @author Alimenkou Mikalai */ public interface EventListener { void onEvent(Event event); } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/LoggingEventListener.java ================================================ package com.xpinjection.patterns.observer.canonical; /** * @author Alimenkou Mikalai */ public class LoggingEventListener implements EventListener { @Override public void onEvent(Event event) { System.out.println("EVENT: " + event.getName()); } } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/Observer.java ================================================ package com.xpinjection.patterns.observer.canonical; /** * @author Alimenkou Mikalai */ public interface Observer { void addListener(EventListener listener); } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/ObserverClient.java ================================================ package com.xpinjection.patterns.observer.canonical; /** * @author Alimenkou Mikalai */ public class ObserverClient { public static void main(String[] args) { TimeServer server = new TimeServer(); server.addListener(new LoggingEventListener()); server.reportTime(); server.reportTime(); } } ================================================ FILE: src/com/xpinjection/patterns/observer/canonical/TimeServer.java ================================================ package com.xpinjection.patterns.observer.canonical; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author Alimenkou Mikalai */ public class TimeServer implements Observer { private static final String TIME_FORMAT = "HH:mm:ss"; private final List listeners = new ArrayList<>(); @Override public void addListener(EventListener listener) { listeners.add(listener); } public void reportTime() { Event event = new Event(new SimpleDateFormat(TIME_FORMAT).format(new Date())); for (EventListener listener : listeners) { listener.onEvent(event); } } } ================================================ FILE: src/com/xpinjection/patterns/prototype/Stats.java ================================================ package com.xpinjection.patterns.prototype; import java.util.Date; /** * @author Alimenkou Mikalai */ public interface Stats { S add(S delta); S invert(Date forDate); } ================================================ FILE: src/com/xpinjection/patterns/prototype/StatsProcessor.java ================================================ package com.xpinjection.patterns.prototype; import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * @author Alimenkou Mikalai */ public class StatsProcessor { public List process(long domainId, List stats) { Date now = new Date(); return stats.stream() .filter(stat -> stat.getDomainId() == domainId) .map(stat -> stat.invert(now)) .collect(Collectors.toList()); } } ================================================ FILE: src/com/xpinjection/patterns/prototype/TrafficStats.java ================================================ package com.xpinjection.patterns.prototype; import java.util.Date; /** * @author Alimenkou Mikalai */ public class TrafficStats implements Stats { private final long domainId; private final Date rankDate; private long estimatedTraffic; public TrafficStats(long domainId, Date rankDate) { this.domainId = domainId; this.rankDate = rankDate; } private TrafficStats(long domainId, Date rankDate, long traffic) { this(domainId, rankDate); this.estimatedTraffic = traffic; } public long getDomainId() { return domainId; } public Date getRankDate() { return rankDate; } public void setEstimatedTraffic(long estimatedTraffic) { this.estimatedTraffic = estimatedTraffic; } public long getEstimatedTraffic() { return estimatedTraffic; } @Override public TrafficStats add(TrafficStats delta) { return new TrafficStats(domainId, rankDate, estimatedTraffic + delta.estimatedTraffic); } @Override public TrafficStats invert(Date forDate) { return new TrafficStats(domainId, forDate, -estimatedTraffic); } @Override public String toString() { return "TrafficStats{domainId=" + domainId + ", rankDate=" + rankDate + ", estimatedTraffic=" + estimatedTraffic + "}"; } } ================================================ FILE: src/com/xpinjection/patterns/prototype/canonical/Circle.java ================================================ package com.xpinjection.patterns.prototype.canonical; /** * @author Alimenkou Mikalai */ public class Circle implements Figure { private int radix; public Circle(int radix) { this.radix = radix; } public void changeRadix(int delta) { radix += delta; } @Override public double getSquare() { return Math.PI * radix * radix; } @Override public Circle clone() { return new Circle(radix); } } ================================================ FILE: src/com/xpinjection/patterns/prototype/canonical/Editor.java ================================================ package com.xpinjection.patterns.prototype.canonical; import java.util.ArrayList; import java.util.List; /** * @author Alimenkou Mikalai */ public class Editor { private final List
figures = new ArrayList<>(); private final Circle defaultCircle = new Circle(5); private final Rectangle defaultRectangle = new Rectangle(2, 4); public Circle addCircle() { Circle circle = defaultCircle.clone(); figures.add(circle); return circle; } public Rectangle addRectangle() { Rectangle rectangle = defaultRectangle.clone(); figures.add(rectangle); return rectangle; } } ================================================ FILE: src/com/xpinjection/patterns/prototype/canonical/Figure.java ================================================ package com.xpinjection.patterns.prototype.canonical; /** * @author Alimenkou Mikalai */ public interface Figure { double getSquare(); Figure clone(); } ================================================ FILE: src/com/xpinjection/patterns/prototype/canonical/PrototypeClient.java ================================================ package com.xpinjection.patterns.prototype.canonical; /** * @author Alimenkou Mikalai */ public class PrototypeClient { public static void main(String[] args) { Editor editor = new Editor(); Circle circle = editor.addCircle(); circle.changeRadix(5); Rectangle rectangle = editor.addRectangle(); rectangle.changeSize(1, -1); } } ================================================ FILE: src/com/xpinjection/patterns/prototype/canonical/Rectangle.java ================================================ package com.xpinjection.patterns.prototype.canonical; /** * @author Alimenkou Mikalai */ public class Rectangle implements Figure { private int width; private int height; public Rectangle(int width, int height) { this.width = width; this.height = height; } public void changeSize(int widthDelta, int heightDelta) { width += widthDelta; height += heightDelta; } @Override public double getSquare() { return width * height; } @Override public Rectangle clone() { return new Rectangle(width, height); } } ================================================ FILE: src/com/xpinjection/patterns/proxy/ModernProxyClient.java ================================================ package com.xpinjection.patterns.proxy; import com.xpinjection.patterns.proxy.canonical.Order; import com.xpinjection.patterns.proxy.canonical.RealOrderService; import java.util.function.Consumer; /** * @author Alimenkou Mikalai */ public class ModernProxyClient { public static void main(String[] args) { Order order = new Order(5L); order.putItem("XP", 2); RealOrderService service = new RealOrderService(); PermissionChecker checker = new PermissionChecker(); Consumer processing = ((Consumer) checker::checkPermission) .andThen(service::processOrder); checker.setCurrentUser(5); processing.accept(order); checker.setCurrentUser(2); processing.accept(order); } } ================================================ FILE: src/com/xpinjection/patterns/proxy/PermissionChecker.java ================================================ package com.xpinjection.patterns.proxy; import com.xpinjection.patterns.proxy.canonical.Order; /** * @author Alimenkou Mikalai */ public class PermissionChecker { private final ThreadLocal currentUser = new ThreadLocal<>(); public void setCurrentUser(long userId) { currentUser.set(userId); } public void checkPermission(Order order) { if (order.getUserId() != currentUser.get()) { throw new IllegalStateException("Order for another user can't be processed: " + order); } } } ================================================ FILE: src/com/xpinjection/patterns/proxy/canonical/Order.java ================================================ package com.xpinjection.patterns.proxy.canonical; import java.util.HashMap; import java.util.Map; /** * @author Alimenkou Mikalai */ public class Order { private final long userId; private final Map items = new HashMap<>(); public Order(long userId) { this.userId = userId; } public void putItem(String code, int count) { items.put(code, count); } public long getUserId() { return userId; } public Map getItems() { return items; } @Override public String toString() { return "Order{userId=" + userId + ", items=" + items + "}"; } } ================================================ FILE: src/com/xpinjection/patterns/proxy/canonical/OrderService.java ================================================ package com.xpinjection.patterns.proxy.canonical; /** * @author Alimenkou Mikalai */ public interface OrderService { void processOrder(Order order); } ================================================ FILE: src/com/xpinjection/patterns/proxy/canonical/PermissionCheckingOrderServiceProxy.java ================================================ package com.xpinjection.patterns.proxy.canonical; /** * @author Alimenkou Mikalai */ public class PermissionCheckingOrderServiceProxy implements OrderService { private final ThreadLocal currentUser = new ThreadLocal<>(); private final OrderService delegate; public PermissionCheckingOrderServiceProxy(OrderService delegate) { this.delegate = delegate; } public void setCurrentUser(long userId) { currentUser.set(userId); } @Override public void processOrder(Order order) { if (order.getUserId() != currentUser.get()) { throw new IllegalStateException("Order for another user can't be processed: " + order); } delegate.processOrder(order); } } ================================================ FILE: src/com/xpinjection/patterns/proxy/canonical/ProxyClient.java ================================================ package com.xpinjection.patterns.proxy.canonical; /** * @author Alimenkou Mikalai */ public class ProxyClient { public static void main(String[] args) { Order order = new Order(5L); order.putItem("XP", 2); RealOrderService service = new RealOrderService(); service.processOrder(order); PermissionCheckingOrderServiceProxy proxy = new PermissionCheckingOrderServiceProxy(service); proxy.setCurrentUser(5); proxy.processOrder(order); proxy.setCurrentUser(2); proxy.processOrder(order); } } ================================================ FILE: src/com/xpinjection/patterns/proxy/canonical/RealOrderService.java ================================================ package com.xpinjection.patterns.proxy.canonical; /** * @author Alimenkou Mikalai */ public class RealOrderService implements OrderService { @Override public void processOrder(Order order) { System.out.println("Order processed: " + order); } } ================================================ FILE: src/com/xpinjection/patterns/proxy/spring-context.xml ================================================ ================================================ FILE: src/com/xpinjection/patterns/singleton/ModernServiceRegistry.java ================================================ package com.xpinjection.patterns.singleton; import com.xpinjection.patterns.singleton.canonical.NamedService; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author Alimenkou Mikalai */ public class ModernServiceRegistry { private final ConcurrentMap services = new ConcurrentHashMap<>(); public void register(NamedService service) { String name = service.getName(); System.out.println("Register service with name: " + name); services.put(name, service); } public NamedService getService(String name) { return services.get(name); } } ================================================ FILE: src/com/xpinjection/patterns/singleton/ModernSingletonClient.java ================================================ package com.xpinjection.patterns.singleton; import com.xpinjection.patterns.singleton.canonical.NamedService; /** * @author Alimenkou Mikalai */ public class ModernSingletonClient implements NamedService { private final ModernServiceRegistry registry; public ModernSingletonClient(ModernServiceRegistry registry) { this.registry = registry; } public static void main(String[] args) { ModernServiceRegistry registry = new ModernServiceRegistry(); new ModernSingletonClient(registry).start(); } public void start() { registry.register(this); } @Override public String getName() { return "MAIN"; } } ================================================ FILE: src/com/xpinjection/patterns/singleton/canonical/NamedService.java ================================================ package com.xpinjection.patterns.singleton.canonical; /** * @author Alimenkou Mikalai */ public interface NamedService { String getName(); } ================================================ FILE: src/com/xpinjection/patterns/singleton/canonical/ServiceRegistry.java ================================================ package com.xpinjection.patterns.singleton.canonical; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author Alimenkou Mikalai */ public final class ServiceRegistry { private static final ServiceRegistry INSTANCE = new ServiceRegistry(); public static ServiceRegistry getInstance() { return INSTANCE; } private final ConcurrentMap services = new ConcurrentHashMap<>(); private ServiceRegistry() {} public void register(NamedService service) { String name = service.getName(); System.out.println("Register service with name: " + name); services.put(name, service); } public NamedService getService(String name) { return services.get(name); } } ================================================ FILE: src/com/xpinjection/patterns/singleton/canonical/SingletonClient.java ================================================ package com.xpinjection.patterns.singleton.canonical; /** * @author Alimenkou Mikalai */ public class SingletonClient implements NamedService { public static void main(String[] args) { new SingletonClient().start(); } public void start() { ServiceRegistry.getInstance().register(this); } @Override public String getName() { return "MAIN"; } } ================================================ FILE: src/com/xpinjection/patterns/singleton/spring-context.xml ================================================ ================================================ FILE: src/com/xpinjection/patterns/strategy/ModernStrategyClient.java ================================================ package com.xpinjection.patterns.strategy; import com.xpinjection.patterns.strategy.canonical.Position; import java.util.function.ToIntFunction; /** * @author Alimenkou Mikalai */ public class ModernStrategyClient { private final ToIntFunction calculator; public ModernStrategyClient(ToIntFunction calculator) { this.calculator = calculator; } public static void main(String[] args) { Position position = new Position(3, 1000); new ModernStrategyClient(TrafficCalculationStrategies::calculateTopTrafficOnly).process(position); new ModernStrategyClient(TrafficCalculationStrategies::calculateTrafficForPage).process(position); // for unit tests new ModernStrategyClient(pos -> 15).process(position); } public void process(Position position) { int traffic = calculator.applyAsInt(position); System.out.println("Traffic is " + traffic + " for position " + position); } } ================================================ FILE: src/com/xpinjection/patterns/strategy/TrafficCalculationStrategies.java ================================================ package com.xpinjection.patterns.strategy; import com.xpinjection.patterns.strategy.canonical.Position; /** * @author Alimenkou Mikalai */ public final class TrafficCalculationStrategies { private static final int PAGE_SIZE = 10; private static final double[] TOP_RANKS = {0.5, 0.3, 0.1, 0.05, 0.05}; private TrafficCalculationStrategies() {} public static int calculateTrafficForPage(Position position) { int rank = position.getRank(); if (rank < 0) { return 0; } int page = rank / PAGE_SIZE; return position.getMediaValue() / (PAGE_SIZE * (page + 1)); } public static int calculateTopTrafficOnly(Position position) { int rank = position.getRank(); if (rank < 0 || rank >= TOP_RANKS.length) { return 0; } return (int) (TOP_RANKS[rank] * position.getMediaValue()); } } ================================================ FILE: src/com/xpinjection/patterns/strategy/canonical/PageTrafficCalculator.java ================================================ package com.xpinjection.patterns.strategy.canonical; /** * @author Alimenkou Mikalai */ public class PageTrafficCalculator implements TrafficCalculator { private static final int PAGE_SIZE = 10; @Override public int calculate(Position position) { int rank = position.getRank(); if (rank < 0) { return 0; } int page = rank / PAGE_SIZE; return position.getMediaValue() / (PAGE_SIZE * (page + 1)); } } ================================================ FILE: src/com/xpinjection/patterns/strategy/canonical/Position.java ================================================ package com.xpinjection.patterns.strategy.canonical; /** * @author Alimenkou Mikalai */ public class Position { private int rank; private int mediaValue; public Position(int rank, int mediaValue) { this.rank = rank; this.mediaValue = mediaValue; } public int getRank() { return rank; } public void setRank(int rank) { this.rank = rank; } public int getMediaValue() { return mediaValue; } public void setMediaValue(int mediaValue) { this.mediaValue = mediaValue; } @Override public String toString() { return "Position{rank=" + rank + ", mediaValue=" + mediaValue + "}"; } } ================================================ FILE: src/com/xpinjection/patterns/strategy/canonical/StrategyClient.java ================================================ package com.xpinjection.patterns.strategy.canonical; /** * @author Alimenkou Mikalai */ public class StrategyClient { private final TrafficCalculator calculator; public StrategyClient(TrafficCalculator calculator) { this.calculator = calculator; } public static void main(String[] args) { Position position = new Position(3, 1000); new StrategyClient(new TopTrafficCalculator()).process(position); new StrategyClient(new PageTrafficCalculator()).process(position); } public void process(Position position) { int traffic = calculator.calculate(position); System.out.println("Traffic is " + traffic + " for position " + position); } } ================================================ FILE: src/com/xpinjection/patterns/strategy/canonical/TopTrafficCalculator.java ================================================ package com.xpinjection.patterns.strategy.canonical; /** * @author Alimenkou Mikalai */ public class TopTrafficCalculator implements TrafficCalculator { private static final double[] TOP_RANKS = {0.5, 0.3, 0.1, 0.05, 0.05}; @Override public int calculate(Position position) { int rank = position.getRank(); if (rank < 0 || rank >= TOP_RANKS.length) { return 0; } return (int) (TOP_RANKS[rank] * position.getMediaValue()); } } ================================================ FILE: src/com/xpinjection/patterns/strategy/canonical/TrafficCalculator.java ================================================ package com.xpinjection.patterns.strategy.canonical; /** * @author Alimenkou Mikalai */ public interface TrafficCalculator { int calculate(Position position); } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/LoggingUserStorage.java ================================================ package com.xpinjection.patterns.templatemethod; import com.xpinjection.patterns.templatemethod.canonical.User; /** * @author Alimenkou Mikalai */ public class LoggingUserStorage { public long store(User user) { System.out.println("User is stored: " + user); return System.currentTimeMillis(); } } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/ModernTemplateMethodClient.java ================================================ package com.xpinjection.patterns.templatemethod; import com.xpinjection.patterns.templatemethod.canonical.User; /** * @author Alimenkou Mikalai */ public class ModernTemplateMethodClient { public static void main(String[] args) { LoggingUserStorage storage = new LoggingUserStorage(); User mikalai = new ModernUserRestorer(storage::store).restore("Mikalai:32"); System.out.println("Mikalai is restored: " + mikalai); // for unit tests mikalai = new ModernUserRestorer(user -> 5L).restore("Mikalai:32"); System.out.println("Mikalai is restored: " + mikalai); } } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/ModernUserRestorer.java ================================================ package com.xpinjection.patterns.templatemethod; import com.xpinjection.patterns.templatemethod.canonical.User; import java.util.StringTokenizer; import java.util.function.ToLongFunction; /** * @author Alimenkou Mikalai */ public class ModernUserRestorer { private static final String DELIMITER = ":"; private final ToLongFunction storage; public ModernUserRestorer(ToLongFunction storage) { this.storage = storage; } public User restore(String serialized) { StringTokenizer tokenizer = new StringTokenizer(serialized, DELIMITER); validate(tokenizer); User user = parse(tokenizer); long id = storage.applyAsLong(user); user.setId(id); return user; } private User parse(StringTokenizer tokenizer) { String name = tokenizer.nextToken(); int age = Integer.parseInt(tokenizer.nextToken()); return new User(name, age); } private void validate(StringTokenizer tokenizer) { if (tokenizer.countTokens() != 2) { throw new IllegalArgumentException("Invalid data format!"); } } } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/canonical/AbstractUserRestorer.java ================================================ package com.xpinjection.patterns.templatemethod.canonical; import java.util.StringTokenizer; /** * @author Alimenkou Mikalai */ public abstract class AbstractUserRestorer { private static final String DELIMITER = ":"; public final User restore(String serialized) { StringTokenizer tokenizer = new StringTokenizer(serialized, DELIMITER); validate(tokenizer); User user = parse(tokenizer); long id = store(user); user.setId(id); return user; } private User parse(StringTokenizer tokenizer) { String name = tokenizer.nextToken(); int age = Integer.parseInt(tokenizer.nextToken()); return new User(name, age); } private void validate(StringTokenizer tokenizer) { if (tokenizer.countTokens() != 2) { throw new IllegalArgumentException("Invalid data format!"); } } protected abstract long store(User user); } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/canonical/LoggingUserRestorer.java ================================================ package com.xpinjection.patterns.templatemethod.canonical; /** * @author Alimenkou Mikalai */ public class LoggingUserRestorer extends AbstractUserRestorer { @Override protected long store(User user) { System.out.println("User is stored: " + user); return System.currentTimeMillis(); } } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/canonical/TemplateMethodClient.java ================================================ package com.xpinjection.patterns.templatemethod.canonical; /** * @author Alimenkou Mikalai */ public class TemplateMethodClient { public static void main(String[] args) { User mikalai = new LoggingUserRestorer().restore("Mikalai:32"); System.out.println("Mikalai is restored: " + mikalai); } } ================================================ FILE: src/com/xpinjection/patterns/templatemethod/canonical/User.java ================================================ package com.xpinjection.patterns.templatemethod.canonical; /** * @author Alimenkou Mikalai */ public class User { private Long id; private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{id=" + id + ", name='" + name + "', age=" + age + "}"; } } ================================================ FILE: src/com/xpinjection/patterns/visitor/LambdaVisitor.java ================================================ package com.xpinjection.patterns.visitor; import java.util.HashMap; import java.util.Map; import java.util.function.Function; /** * @author Alimenkou Mikalai */ public class LambdaVisitor implements Function { private final Map, Function> functions = new HashMap<>(); public Acceptor on(Class clazz) { return new Acceptor<>(this, clazz); } @Override public A apply(Object o) { return functions.get(o.getClass()).apply(o); } public static class Acceptor { private final LambdaVisitor visitor; private final Class clazz; public Acceptor(LambdaVisitor visitor, Class clazz) { this.visitor = visitor; this.clazz = clazz; } public LambdaVisitor then(Function f) { visitor.functions.put(clazz, f); return visitor; } } } ================================================ FILE: src/com/xpinjection/patterns/visitor/ModernVisitorClient.java ================================================ package com.xpinjection.patterns.visitor; import com.xpinjection.patterns.visitor.canonical.Circle; import com.xpinjection.patterns.visitor.canonical.Rectangle; import com.xpinjection.patterns.visitor.canonical.Square; import java.util.Arrays; import java.util.List; import java.util.function.Function; /** * @author Alimenkou Mikalai */ public class ModernVisitorClient { private final static Function AREA_CALCULATOR = new LambdaVisitor() .on(Square.class).then(s -> s.getSide() * s.getSide()) .on(Circle.class).then(c -> Math.PI * c.getRadius() * c.getRadius()) .on(Rectangle.class).then(r -> r.getHeight() * r.getWidth()); private final static Function PERIMETER_CALCULATOR = new LambdaVisitor() .on(Square.class).then(s -> 4 * s.getSide()) .on(Circle.class).then(c -> 2 * Math.PI * c.getRadius()) .on(Rectangle.class).then(r -> 2 * r.getHeight() + 2 * r.getWidth()); public static void main(String[] args) { List figures = Arrays.asList(new Circle(4), new Square(5), new Rectangle(6, 7)); double totalArea = figures.stream() .map(AREA_CALCULATOR) .reduce(0.0, Double::sum); System.out.println("Total area = " + totalArea); double totalPerimeter = figures.stream() .map(PERIMETER_CALCULATOR) .reduce(0.0, Double::sum); System.out.println("Total perimeter = " + totalPerimeter); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/AreaVisitor.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public class AreaVisitor implements Visitor { @Override public Double visit(Square element) { return element.getSide() * element.getSide(); } @Override public Double visit(Circle element) { return Math.PI * element.getRadius() * element.getRadius(); } @Override public Double visit(Rectangle element) { return element.getHeight() * element.getWidth(); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/Circle.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public class Circle implements Element { private final double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } @Override public T accept(Visitor visitor) { return visitor.visit(this); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/Element.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public interface Element { T accept(Visitor visitor); } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/PerimeterVisitor.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public class PerimeterVisitor implements Visitor { @Override public Double visit(Square element) { return 4 * element.getSide(); } @Override public Double visit(Circle element) { return 2 * Math.PI * element.getRadius(); } @Override public Double visit(Rectangle element) { return (2 * element.getHeight() + 2 * element.getWidth()); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/Rectangle.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public class Rectangle implements Element { private final double width; private final double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } public double getWidth() { return width; } public double getHeight() { return height; } @Override public T accept(Visitor visitor) { return visitor.visit(this); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/Square.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public class Square implements Element { private final double side; public Square(double side) { this.side = side; } public double getSide() { return side; } @Override public T accept(Visitor visitor) { return visitor.visit(this); } } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/Visitor.java ================================================ package com.xpinjection.patterns.visitor.canonical; /** * @author Alimenkou Mikalai */ public interface Visitor { T visit(Square element); T visit(Circle element); T visit(Rectangle element); } ================================================ FILE: src/com/xpinjection/patterns/visitor/canonical/VisitorClient.java ================================================ package com.xpinjection.patterns.visitor.canonical; import java.util.Arrays; import java.util.List; /** * @author Alimenkou Mikalai */ public class VisitorClient { public static void main(String[] args) { List figures = Arrays.asList(new Circle(4), new Square(5), new Rectangle(6, 7)); double totalArea = 0.0; Visitor areaVisitor = new AreaVisitor(); for (Element figure : figures) { totalArea += figure.accept(areaVisitor); } System.out.println("Total area = " + totalArea); double totalPerimeter = 0.0; Visitor perimeterVisitor = new PerimeterVisitor(); for (Element figure : figures) { totalPerimeter += figure.accept(perimeterVisitor); } System.out.println("Total perimeter = " + totalPerimeter); } }