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