├── src ├── main │ ├── db │ │ ├── scripts │ │ │ └── update_001.sql │ │ └── changeset.xml │ ├── java │ │ └── ru │ │ │ └── job4j │ │ │ ├── principle_004 │ │ │ ├── package-info.java │ │ │ ├── UnaryEx.java │ │ │ ├── ConEx.java │ │ │ ├── HibernateFactory.java │ │ │ ├── FunEx.java │ │ │ ├── BiConEx.java │ │ │ ├── TripleConEx.java │ │ │ ├── ExtResource.java │ │ │ ├── UsageLog4j2.java │ │ │ ├── Store.java │ │ │ ├── Usage.java │ │ │ ├── HibernateStore.java │ │ │ ├── User.java │ │ │ ├── DbStoreCopyPast.java │ │ │ └── DbStore.java │ │ │ ├── principle_005 │ │ │ ├── package-info.java │ │ │ └── Config.java │ │ │ ├── principle_001 │ │ │ ├── package-info.java │ │ │ ├── User.java │ │ │ ├── UserCompareGood.java │ │ │ ├── Compared.java │ │ │ ├── UserPojo.java │ │ │ ├── CompareByNull.java │ │ │ └── UserCompareBad.java │ │ │ ├── principle_007 │ │ │ ├── ContainsAll.java │ │ │ ├── schema.sql │ │ │ ├── ParseFile.java │ │ │ └── NotificationService.java │ │ │ ├── principle_003 │ │ │ ├── package-info.java │ │ │ ├── ValidateService.java │ │ │ ├── Validate.java │ │ │ ├── EvenIt.java │ │ │ ├── Credential.java │ │ │ ├── User.java │ │ │ └── UserServlet.java │ │ │ ├── principle_006 │ │ │ ├── NotFoundUserException.java │ │ │ ├── User.java │ │ │ ├── Account.java │ │ │ └── BankService.java │ │ │ └── principle_002 │ │ │ ├── package-info.java │ │ │ ├── Person.java │ │ │ ├── Message.java │ │ │ ├── MultiIf.java │ │ │ ├── SwitchAntiPattern.java │ │ │ ├── DispatchPattern.java │ │ │ └── DispatchDiapason.java │ ├── resources │ │ └── log4j2.xml │ └── kotlin │ │ └── ru │ │ └── job4j │ │ └── kt │ │ └── BankService.kt └── test │ ├── java │ └── ru │ │ └── job4j │ │ ├── principle_004 │ │ ├── package-info.java │ │ ├── PoolRollback.java │ │ ├── HibernateStoreTest.java │ │ ├── DbStoreTest.java │ │ └── ConnectionRollback.java │ │ ├── principle_005 │ │ ├── package-info.java │ │ └── ConfigTest.java │ │ ├── principle_003 │ │ ├── package-info.java │ │ └── EvenItTest.java │ │ ├── principle_001 │ │ ├── package-info.java │ │ └── UserComparedTest.java │ │ ├── principle_002 │ │ ├── package-info.java │ │ ├── MultiIfTest.java │ │ ├── SwitchAntiPatternTest.java │ │ ├── DispatchPatternTest.java │ │ └── DispatchDiapasonTest.java │ │ └── principle_007 │ │ └── ContainsAllTest.java │ └── resources │ └── hibernate.cfg.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .travis.yml ├── gradle.properties ├── .gitignore ├── settings.gradle ├── gradlew.bat ├── gradlew ├── config └── checkstyle │ └── checkstyle.xml ├── LICENSE └── README.md /src/main/db/scripts/update_001.sql: -------------------------------------------------------------------------------- 1 | create table users ( 2 | id serial primary key not null, 3 | login varchar(2000) 4 | ); -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterarsentev/code_quality_principles/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package describes the princile # 4. 3 | */ 4 | package ru.job4j.principle_004; -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_005/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package describes the princile # 5. 3 | */ 4 | package ru.job4j.principle_005; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk14 5 | 6 | before_install: 7 | - chmod +x gradlew 8 | 9 | script: 10 | - ./gradlew build -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * //TODO add comments. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_001; -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_007/ContainsAll.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_007; 2 | 3 | public class ContainsAll { 4 | public static Object check(String left, String right) { 5 | return null; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_004/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with principle #4. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_004; -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_005/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with principle #5. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_005; -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_003/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with if-else-throws. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_003; -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_001/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with multi return statements. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_001; -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with if-else-throw statements. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_003; -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_006/NotFoundUserException.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_006; 2 | 3 | public class NotFoundUserException extends Exception { 4 | public NotFoundUserException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with anti-pattern multi if and switch statements. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_002; -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_002/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains examples with anti-pattern multi if and switch statements. 3 | * 4 | * @author Petr Arsentev (parsentev@yandex.ru) 5 | * @version $Id$ 6 | * @since 0.1 7 | */ 8 | package ru.job4j.principle_002; -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | databaseQaUrl=jdbc:postgresql://127.0.0.1:5432/users 2 | databaseQaUsername=postgres 3 | databaseQaPassword=password 4 | databaseQaDriver=org.postgresql.Driver 5 | 6 | databaseTravisUrl=jdbc:postgresql://localhost/users 7 | databaseTravisUsername=postgres 8 | databaseTravisPassword= -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/UnaryEx.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * Unary functional interface with exception. 5 | */ 6 | public interface UnaryEx { 7 | /** 8 | * Action with exception. 9 | * @throws Exception possible exception. 10 | */ 11 | void action() throws Exception; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/ConEx.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * Function interface. It take a single param. 5 | * @param type. 6 | */ 7 | public interface ConEx { 8 | /** 9 | * Accept arg. 10 | * @param param arg 11 | * @throws Exception possible exception. 12 | */ 13 | void accept(T param) throws Exception; 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | .idea 25 | *.iml 26 | build 27 | 28 | out 29 | .gradle 30 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/User.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | /** 4 | * User model. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public interface User { 11 | /** 12 | * Name. 13 | * @return name of user. 14 | */ 15 | String name(); 16 | 17 | /** 18 | * Age. 19 | * @return age of user. 20 | */ 21 | int age(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/HibernateFactory.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.hibernate.SessionFactory; 4 | import org.hibernate.cfg.Configuration; 5 | 6 | /** 7 | * Contains pool. 8 | */ 9 | public class HibernateFactory { 10 | /** 11 | * Instance of factory. 12 | */ 13 | public static final SessionFactory FACTORY = new Configuration() 14 | .configure() 15 | .buildSessionFactory(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/FunEx.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * Funcation interface. It takes param and return data. 5 | * @param input arg. 6 | * @param return data. 7 | */ 8 | public interface FunEx { 9 | /** 10 | * Take param and return value. 11 | * @param param input param 12 | * @return value. 13 | * @throws Exception possible exception. 14 | */ 15 | R apply(T param) throws Exception; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_007/schema.sql: -------------------------------------------------------------------------------- 1 | --Есть две таблицы в базе. 2 | 3 | CREATE TABLE girl ( 4 | id serial PRIMARY KEY, 5 | name VARCHAR 6 | ); 7 | 8 | CREATE TABLE toy ( 9 | id serial PRIMARY KEY, 10 | name VARCHAR 11 | ); 12 | 13 | --1. Написать sql инструкцию. Инструкция должна добавить колонку toy_id в таблицу girl. 14 | 15 | --2. Написать sql запрос на получение имени девочки и ее игрушки. 16 | 17 | --3. Написать sql запрос на получение игрушек не привязанных к девочкам. -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_004/PoolRollback.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.apache.commons.dbcp.BasicDataSource; 4 | import java.sql.Connection; 5 | import java.sql.SQLException; 6 | 7 | /** 8 | * Pool with connection rollback. 9 | */ 10 | public class PoolRollback extends BasicDataSource { 11 | @Override 12 | public Connection getConnection() throws SQLException { 13 | return ConnectionRollback.create(super.getConnection()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/BiConEx.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * Functional interface, It take two params with possible exception. 5 | * @param first arg 6 | * @param second arg 7 | */ 8 | public interface BiConEx { 9 | /** 10 | * Accept two args. 11 | * @param left first. 12 | * @param right second. 13 | * @throws Exception possible exception. 14 | */ 15 | void accept(L left, R right) throws Exception; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/TripleConEx.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * It takes three args. 5 | * @param first. 6 | * @param second. 7 | * @param third. 8 | */ 9 | public interface TripleConEx { 10 | /** 11 | * It takes three args. 12 | * @param first first. 13 | * @param second second. 14 | * @param third third. 15 | * @throws Exception possible exception. 16 | */ 17 | void accept(F first, S second, T third) throws Exception; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/ExtResource.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * External resource. 5 | * @param type 6 | */ 7 | public interface ExtResource { 8 | 9 | /** 10 | * Read resource by name. 11 | * @param name resources' name 12 | * @return value. 13 | * @throws Exception possible exception. 14 | */ 15 | T read(String name) throws Exception; 16 | 17 | /** 18 | * Write resource. 19 | * @param value resource. 20 | * @throws Exception possible exception. 21 | */ 22 | void write(T value) throws Exception; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/db/changeset.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/UserCompareGood.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | import java.util.Comparator; 4 | 5 | /** 6 | * Good example of rewrite comparator. 7 | * 8 | * @author Petr Arsentev (parsentev@yandex.ru) 9 | * @version $Id$ 10 | * @since 0.1 11 | */ 12 | public class UserCompareGood implements Comparator { 13 | 14 | /** 15 | * ${@inheritDoc}. 16 | */ 17 | @Override 18 | public int compare(final User left, final User right) { 19 | final int rsl = left.name().compareTo(right.name()); 20 | return rsl != 0 ? rsl : Integer.compare(left.age(), right.age()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/ValidateService.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Validate service impl. 7 | */ 8 | public enum ValidateService implements Validate { 9 | /** 10 | * Impl singleton. 11 | */ 12 | INSTANCE; 13 | 14 | @Override 15 | public Map findAll() { 16 | return null; 17 | } 18 | 19 | @Override 20 | public void add(User user) { 21 | 22 | } 23 | 24 | @Override 25 | public void delete(Integer id) { 26 | 27 | } 28 | 29 | @Override 30 | public void update(Integer id, User user) { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * In a single project build this file can be empty or even removed. 6 | * 7 | * Detailed information about configuring a multi-project build in Gradle can be found 8 | * in the user guide at https://docs.gradle.org/3.3/userguide/multi_project_builds.html 9 | */ 10 | 11 | /* 12 | // To declare projects as part of a multi-project build use the 'include' method 13 | include 'shared' 14 | include 'api' 15 | include 'services:webservice' 16 | */ 17 | 18 | rootProject.name = 'code_quality_principles' 19 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/Person.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | /** 4 | * Person. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public interface Person { 11 | /** 12 | * Person's age. 13 | * @return age. 14 | */ 15 | int age(); 16 | 17 | /** 18 | * Access type. 19 | */ 20 | enum Access { 21 | /** 22 | * Forbidden. 23 | */ 24 | FORBIDDEN, 25 | /** 26 | * Limited. 27 | */ 28 | LIMIT, 29 | /** 30 | * Free access. 31 | */ 32 | FREE 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/Validate.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Validate data. 7 | */ 8 | public interface Validate { 9 | /** 10 | * All user. 11 | * @return map. 12 | */ 13 | Map findAll(); 14 | 15 | /** 16 | * Add user to store. 17 | * @param user user. 18 | */ 19 | void add(User user); 20 | 21 | /** 22 | * Delete user in store. 23 | * @param id user's id. 24 | */ 25 | void delete(Integer id); 26 | 27 | /** 28 | * Update user. 29 | * @param id id. 30 | * @param user user. 31 | */ 32 | void update(Integer id, User user); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/UsageLog4j2.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.apache.logging.log4j.Logger; 4 | import org.apache.logging.log4j.LogManager; 5 | 6 | /** 7 | * Usage of log4j 2. 8 | */ 9 | public class UsageLog4j2 { 10 | 11 | /** 12 | * Log. 13 | */ 14 | private static final Logger LOG = LogManager.getLogger(UsageLog4j2.class.getName()); 15 | 16 | /** 17 | * Main. 18 | * @param args main. 19 | */ 20 | public static void main(String[] args) { 21 | LOG.trace("trace message"); 22 | LOG.debug("debug message"); 23 | LOG.info("info message"); 24 | LOG.warn("warn message"); 25 | LOG.error("error message"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_007/ContainsAllTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_007; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.is; 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * Даны две строки. Нужно проверить, что вторая строка получилась методом перестановок символов в первой строке. 10 | */ 11 | public class ContainsAllTest { 12 | @Test 13 | public void whenEq() { 14 | assertThat(ContainsAll.check("Hello", "Hlloe"), is(true)); 15 | } 16 | 17 | @Test 18 | public void whenNotEq() { 19 | assertThat(ContainsAll.check("Hello", "Halle"), is(false)); 20 | } 21 | 22 | @Test 23 | public void whenNotMultiEq() { 24 | assertThat(ContainsAll.check("heloo", "hello"), is(false)); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/Message.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | /** 4 | * //TODO add comments. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public interface Message { 11 | 12 | /** 13 | * Sent to type. 14 | * @return type. 15 | */ 16 | Type type(); 17 | 18 | /** 19 | * Dist's type. 20 | */ 21 | enum Type { 22 | /** 23 | * Email. 24 | */ 25 | EMAIL, 26 | /** 27 | * Jabber. 28 | */ 29 | JABBER, 30 | /** 31 | * Twitter. 32 | */ 33 | TWITTER, 34 | /** 35 | * Etc. 36 | */ 37 | ETC, 38 | /** 39 | * Unknown. 40 | */ 41 | UNKNOWN 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/Store.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * It describes a store. 7 | * @param 8 | */ 9 | public interface Store { 10 | /** 11 | * Save model. 12 | * @param model Model. 13 | * @return stored model. 14 | */ 15 | T add(T model); 16 | 17 | /** 18 | * Update model. 19 | * @param model model. 20 | */ 21 | void update(T model); 22 | 23 | /** 24 | * Delete model from a store. 25 | * @param id model's id. 26 | */ 27 | void delete(int id); 28 | 29 | /** 30 | * Get all stored models. 31 | * @return list of models. 32 | */ 33 | List findAll(); 34 | 35 | /** 36 | * Get model by id. 37 | * @param id model's id. 38 | * @return model. 39 | */ 40 | T findById(int id); 41 | } 42 | -------------------------------------------------------------------------------- /src/test/resources/hibernate.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jdbc:postgresql://localhost:5432/users 8 | org.postgresql.Driver 9 | postgres 10 | password 11 | org.hibernate.dialect.PostgreSQL94Dialect 12 | true 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/Compared.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | /** 4 | * Simple example of multiple return statements. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public class Compared { 11 | /** 12 | * Return mex of left and right. 13 | * @param left left. 14 | * @param right right. 15 | * @return max of left, right. 16 | */ 17 | int maxBad(int left, int right) { 18 | if (left > right) { 19 | return left; 20 | } else { 21 | return right; 22 | } 23 | } 24 | 25 | /** 26 | * Return mex of left and right. 27 | * @param left left. 28 | * @param right right. 29 | * @return max of left, right. 30 | */ 31 | int maxGood(int left, int right) { 32 | return left > right ? left : right; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_003/EvenItTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | /** 9 | * @author Petr Arsentev (parsentev@yandex.ru) 10 | * @version $Id$ 11 | * @since 0.1 12 | */ 13 | public class EvenItTest { 14 | /** 15 | * Test for throwing. 16 | */ 17 | @Test(expected = IllegalStateException.class) 18 | public void whenItEmptyThenThrowException() { 19 | new EvenIt(new int[] {}).iterator().next(); 20 | } 21 | 22 | /** 23 | * Test for get even numbers. 24 | */ 25 | @Test 26 | public void whenItEvenAndNotEvenThenGetEvent() { 27 | assertThat( 28 | new EvenIt( 29 | new int[] {1, 2} 30 | ).iterator().next(), 31 | is(2) 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_002/MultiIfTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | /** 9 | * Tests for multi if statements. 10 | * 11 | * @author Petr Arsentev (parsentev@yandex.ru) 12 | * @version $Id$ 13 | * @since 0.1 14 | */ 15 | public class MultiIfTest { 16 | /** 17 | * Sent to unknown. 18 | */ 19 | @Test 20 | public void whenSentToUnknown() { 21 | assertThat( 22 | new MultiIf().sent( 23 | () -> Message.Type.UNKNOWN 24 | ), is(false) 25 | ); 26 | } 27 | 28 | /** 29 | * Sent to email. 30 | */ 31 | @Test 32 | public void whenSentToEmail() { 33 | assertThat( 34 | new MultiIf().sent( 35 | () -> Message.Type.EMAIL 36 | ), is(true) 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/UserPojo.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | /** 4 | * Pojo implementation of User. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public class UserPojo implements User { 11 | /** 12 | * User's name. 13 | */ 14 | private final String name; 15 | 16 | /** 17 | * User's age. 18 | */ 19 | private final int age; 20 | 21 | /** 22 | * Constructor. 23 | * @param name user's name. 24 | * @param age user's age. 25 | */ 26 | public UserPojo(final String name, final int age) { 27 | this.name = name; 28 | this.age = age; 29 | } 30 | 31 | /** 32 | * ${@inheritDoc}. 33 | */ 34 | @Override 35 | public String name() { 36 | return this.name; 37 | } 38 | 39 | /** 40 | * ${@inheritDoc}. 41 | */ 42 | @Override 43 | public int age() { 44 | return this.age; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_002/SwitchAntiPatternTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | /** 9 | * Test switch anti-pattern. 10 | * 11 | * @author Petr Arsentev (parsentev@yandex.ru) 12 | * @version $Id$ 13 | * @since 0.1 14 | */ 15 | public class SwitchAntiPatternTest { 16 | /** 17 | * Sent to unknown. 18 | */ 19 | @Test 20 | public void whenSentToUnknown() { 21 | assertThat( 22 | new SwitchAntiPattern().sent( 23 | () -> Message.Type.UNKNOWN 24 | ), is(false) 25 | ); 26 | } 27 | 28 | /** 29 | * Sent to email. 30 | */ 31 | @Test 32 | public void whenSentToEmail() { 33 | assertThat( 34 | new SwitchAntiPattern().sent( 35 | () -> Message.Type.EMAIL 36 | ), is(true) 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_002/DispatchPatternTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | /** 9 | * Tests for dispatch pattern. 10 | * 11 | * @author Petr Arsentev (parsentev@yandex.ru) 12 | * @version $Id$ 13 | * @since 0.1 14 | */ 15 | public class DispatchPatternTest { 16 | /** 17 | * Sent to unknown. 18 | */ 19 | @Test 20 | public void whenSentToUnknown() { 21 | assertThat( 22 | new DispatchPattern().init().sent( 23 | () -> Message.Type.UNKNOWN 24 | ), is(false) 25 | ); 26 | } 27 | 28 | /** 29 | * Sent to email. 30 | */ 31 | @Test 32 | public void whenSentToEmail() { 33 | assertThat( 34 | new DispatchPattern().init().sent( 35 | () -> Message.Type.EMAIL 36 | ), is(true) 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/MultiIf.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import ru.job4j.principle_002.Message.Type; 4 | 5 | /** 6 | * Anti-pattern - multi if statements. 7 | * 8 | * @author Petr Arsentev (parsentev@yandex.ru) 9 | * @version $Id$ 10 | * @since 0.1 11 | */ 12 | public class MultiIf { 13 | 14 | /** 15 | * Sent message to type. 16 | * @param msg message 17 | * @return true if it finds a type. 18 | */ 19 | public boolean sent(final Message msg) { 20 | boolean rsl = false; 21 | if (msg.type() == Type.EMAIL) { 22 | // sent to email. 23 | rsl = true; 24 | } else if (msg.type() == Type.JABBER) { 25 | // sent to jabber. 26 | rsl = true; 27 | } else if (msg.type() == Type.TWITTER) { 28 | // sent to twitter. 29 | rsl = true; 30 | } else if (msg.type() == Type.ETC) { 31 | // sent to etc. 32 | rsl = true; 33 | } 34 | return rsl; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/SwitchAntiPattern.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | /** 4 | * //TODO add comments. 5 | * 6 | * @author Petr Arsentev (parsentev@yandex.ru) 7 | * @version $Id$ 8 | * @since 0.1 9 | */ 10 | public class SwitchAntiPattern { 11 | 12 | /** 13 | * Sent message to type. 14 | * @param msg message 15 | * @return true if it finds a type. 16 | */ 17 | public boolean sent(final Message msg) { 18 | final boolean rsl; 19 | switch (msg.type()) { 20 | case EMAIL: 21 | // sent to email. 22 | rsl = true; 23 | break; 24 | case JABBER: 25 | // sent to jabber. 26 | rsl = true; 27 | break; 28 | case TWITTER: 29 | // sent to twitter. 30 | rsl = true; 31 | break; 32 | default: 33 | // sent to etc. 34 | rsl = false; 35 | break; 36 | } 37 | return rsl; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_001/UserComparedTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.core.Is.is; 6 | import static org.junit.Assert.assertThat; 7 | 8 | /** 9 | * Test's of compared users. 10 | * 11 | * @author Petr Arsentev (parsentev@yandex.ru) 12 | * @version $Id$ 13 | * @since 0.1 14 | */ 15 | public class UserComparedTest { 16 | /** 17 | * Test bad code. 18 | */ 19 | @Test 20 | public void whenLeftGreatThenMinus() { 21 | assertThat( 22 | new UserCompareBad().compare( 23 | new UserPojo("Nick", 20), 24 | new UserPojo("Nick", 30) 25 | ), is(-1)); 26 | } 27 | 28 | /** 29 | * Test good code. 30 | */ 31 | @Test 32 | public void whenRfcLeftGreatThenMinus() { 33 | assertThat( 34 | new UserCompareGood().compare( 35 | new UserPojo("Paul", 20), 36 | new UserPojo("Paul", 30) 37 | ), is(-1)); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_006/User.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_006; 2 | 3 | import java.util.Objects; 4 | 5 | public class User { 6 | private String passport; 7 | private String username; 8 | 9 | public User(String passport, String username) { 10 | this.passport = passport; 11 | this.username = username; 12 | } 13 | 14 | public String getPassport() { 15 | return passport; 16 | } 17 | 18 | public void setPassport(String passport) { 19 | this.passport = passport; 20 | } 21 | 22 | public String getUsername() { 23 | return username; 24 | } 25 | 26 | public void setUsername(String username) { 27 | this.username = username; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | User user = (User) o; 35 | return Objects.equals(passport, user.passport); 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return Objects.hash(passport); 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_006/Account.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_006; 2 | 3 | import java.util.Objects; 4 | 5 | public class Account { 6 | private String requisite; 7 | private double balance; 8 | 9 | public Account(String requisite, double balance) { 10 | this.requisite = requisite; 11 | this.balance = balance; 12 | } 13 | 14 | public String getRequisite() { 15 | return requisite; 16 | } 17 | 18 | public void setRequisite(String requisite) { 19 | this.requisite = requisite; 20 | } 21 | 22 | public double getBalance() { 23 | return balance; 24 | } 25 | 26 | public void setBalance(double balance) { 27 | this.balance = balance; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | Account account = (Account) o; 35 | return Objects.equals(requisite, account.requisite); 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return Objects.hash(requisite); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_007/ParseFile.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_007; 2 | 3 | import java.io.*; 4 | 5 | public class ParseFile { 6 | private File file; 7 | 8 | public synchronized void setFile(File f) { 9 | file = f; 10 | } 11 | 12 | public synchronized File getFile() { 13 | return file; 14 | } 15 | 16 | public String getContent() throws IOException { 17 | InputStream i = new FileInputStream(file); 18 | String output = ""; 19 | int data; 20 | while ((data = i.read()) > 0) { 21 | output += (char) data; 22 | } 23 | return output; 24 | } 25 | 26 | public String getContentWithoutUnicode() throws IOException { 27 | InputStream i = new FileInputStream(file); 28 | String output = ""; 29 | int data; 30 | while ((data = i.read()) > 0) { 31 | if (data < 0x80) { 32 | output += (char) data; 33 | } 34 | } 35 | return output; 36 | } 37 | 38 | public void saveContent(String content) throws IOException { 39 | OutputStream o = new FileOutputStream(file); 40 | for (int i = 0; i < content.length(); i += 1) { 41 | o.write(content.charAt(i)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/CompareByNull.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | import java.util.Comparator; 4 | import java.util.function.BiFunction; 5 | 6 | /** 7 | * Compare by null. 8 | * 9 | * @author Petr Arsentev (parsentev@yandex.ru) 10 | * @version $Id$ 11 | * @since 0.1 12 | */ 13 | public class CompareByNull implements Comparator { 14 | 15 | /** 16 | * ${@inheritDoc}. 17 | */ 18 | @Override 19 | public int compare(User left, User right) { 20 | return compareIfNull(left, right, (lft, rht) -> Integer.compare(lft.age(), rht.age())); 21 | } 22 | 23 | 24 | /** 25 | * Compare values by null. 26 | * @param left left value. 27 | * @param right right value. 28 | * @param compare if left and right not null then compare by function. 29 | * @return 1, 0, -1. 30 | */ 31 | public int compareIfNull(User left, User right, BiFunction compare) { 32 | int rsl; 33 | if (left != null && right != null) { 34 | rsl = compare.apply(left, right); 35 | } else if (left != null) { 36 | rsl = -1; 37 | } else if (right != null) { 38 | rsl = 1; 39 | } else { 40 | rsl = 0; 41 | } 42 | return rsl; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_001/UserCompareBad.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_001; 2 | 3 | import java.util.Comparator; 4 | 5 | /** 6 | * Bad example. 7 | * 8 | * List of problems 9 | * 1. Multi returns statements. 10 | * 2. Multi conditions statements (if-else-if). 11 | * 3. Unnecessary auto-boxing. 12 | * 4. Unnecessary calculating. 13 | * 5. Explicitly return result. 14 | * 15 | * @author Petr Arsentev (parsentev@yandex.ru) 16 | * @version $Id$ 17 | * @since 0.1 18 | */ 19 | public class UserCompareBad implements Comparator { 20 | 21 | /** 22 | * ${@inheritDoc}. 23 | */ 24 | @Override 25 | public int compare(User left, User right) { 26 | if (left.name().compareTo(right.name()) == 1) { 27 | return 1; 28 | } else if (left.name().compareTo(right.name()) == -1) { 29 | return -1; 30 | } else if (Integer.valueOf(left.age()) > Integer.valueOf(right.age()) 31 | && left.name().compareTo(right.name()) == 0) { 32 | return 1; 33 | } else if ( 34 | Integer.valueOf(left.age()) < Integer.valueOf(right.age()) 35 | && left.name().compareTo(right.name()) == 0 36 | ) { 37 | return -1; 38 | } else { 39 | return 0; 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_002/DispatchDiapasonTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import org.junit.Test; 4 | import static org.hamcrest.core.Is.is; 5 | import static org.junit.Assert.assertThat; 6 | 7 | /** 8 | * Test for person permission by age. 9 | * 10 | * @author Petr Arsentev (parsentev@yandex.ru) 11 | * @version $Id$ 12 | * @since 0.1 13 | */ 14 | public class DispatchDiapasonTest { 15 | 16 | /** 17 | * Between 14 and 18. 18 | */ 19 | @Test 20 | public void whenBetween14and18ThenLimited() { 21 | assertThat( 22 | new DispatchDiapason().init().access( 23 | () -> 16 24 | ), 25 | is(Person.Access.LIMIT) 26 | ); 27 | } 28 | 29 | /** 30 | * Up 18 age. 31 | */ 32 | @Test 33 | public void whenUp18AgeThenFree() { 34 | assertThat( 35 | new DispatchDiapason().init().access( 36 | () -> 21 37 | ), 38 | is(Person.Access.FREE) 39 | ); 40 | } 41 | 42 | /** 43 | * Under 14 age. 44 | */ 45 | @Test 46 | public void whenLess14ThenForbidden() { 47 | assertThat( 48 | new DispatchDiapason().init().access( 49 | () -> 10 50 | ), 51 | is(Person.Access.FORBIDDEN) 52 | ); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/ru/job4j/kt/BankService.kt: -------------------------------------------------------------------------------- 1 | package ru.job4j.kt 2 | 3 | import ru.job4j.principle_006.Account 4 | import ru.job4j.principle_006.User 5 | import java.util.* 6 | 7 | class BankService { 8 | private val users = HashMap>() 9 | 10 | fun addUser(user: User): Boolean = 11 | users.putIfAbsent(user, ArrayList()) == null 12 | 13 | fun findByPassport(passport: String): User? = 14 | users.keys.find { it.passport == passport } 15 | 16 | fun findAccounts(passport: String): ArrayList? { 17 | val user = findByPassport(passport) 18 | return user?.let { users[user] } 19 | } 20 | 21 | fun addAccount(passport: String, account: Account): Boolean { 22 | return findAccounts(passport)?.add(account) ?: false 23 | } 24 | 25 | fun findByRequisite(passport: String, requisite: String): Account? = 26 | findByPassport(passport)?.let { user -> 27 | users[user]?.find { it.requisite == requisite } 28 | } 29 | 30 | fun transferMoney(srcPassport: String, srcRequisite: String, 31 | destPassport: String, destRequisite: String, amount: Double): Boolean { 32 | val source = findByRequisite(srcPassport, srcRequisite) 33 | val dest = findByRequisite(destPassport, destRequisite) 34 | val rsl = source != null && dest != null && source.balance > amount; 35 | if (rsl) { 36 | source!!.balance -= amount 37 | dest!!.balance += amount 38 | } 39 | return rsl 40 | } 41 | } 42 | 43 | fun main() { 44 | val bank = BankService() 45 | bank.findByPassport("123") 46 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/Usage.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | /** 4 | * Usage of ExtResource. 5 | * @param value. 6 | */ 7 | public class Usage { 8 | 9 | /** 10 | * Execute actions with exception. 11 | * @param unary action. 12 | */ 13 | void ex(UnaryEx unary) { 14 | try { 15 | unary.action(); 16 | } catch (Exception e) { 17 | e.printStackTrace(); 18 | } 19 | } 20 | 21 | /** 22 | * Throw exception. 23 | * @param name name. 24 | * @param resource resources. 25 | * @throws Exception rethrow a possible exception. 26 | */ 27 | public void readToFile(String name, ExtResource resource) throws Exception { 28 | resource.read(name); 29 | } 30 | 31 | /** 32 | * Catch a possible exception. 33 | * @param value value. 34 | * @param resource resource. 35 | */ 36 | public void writeToFile(T value, ExtResource resource) { 37 | try { 38 | resource.write(value); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | /** 45 | * Read resource without exception. 46 | * @param name name. 47 | * @param resource resource. 48 | */ 49 | public void read(String name, ExtResource resource) { 50 | ex(() -> resource.read(name)); 51 | } 52 | 53 | /** 54 | * Write resource without exception. 55 | * @param value value. 56 | * @param resource resources. 57 | */ 58 | public void write(T value, ExtResource resource) { 59 | ex(() -> resource.write(value)); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/EvenIt.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * Iterator for even numbers. 7 | */ 8 | public class EvenIt implements Iterable { 9 | /** 10 | * Array of numbers. 11 | */ 12 | private final int[] data; 13 | 14 | /** 15 | * Default constructor. 16 | * @param data numbers. 17 | */ 18 | public EvenIt(final int[] data) { 19 | this.data = data; 20 | } 21 | 22 | /** 23 | * ${@inheritDoc}. 24 | */ 25 | @Override 26 | public Iterator iterator() { 27 | 28 | return new Iterator() { 29 | private int point; 30 | 31 | @Override 32 | public boolean hasNext() { 33 | return EvenIt.this.findEven(this.point) >= 0; 34 | } 35 | 36 | @Override 37 | public Integer next() { 38 | if (!this.hasNext()) { 39 | throw new IllegalStateException("No such element."); 40 | } 41 | this.point = EvenIt.this.findEven(this.point); 42 | return EvenIt.this.data[this.point++]; 43 | } 44 | }; 45 | } 46 | 47 | /** 48 | * Find event by positions. 49 | * @param start position of searching. 50 | * @return return the new position. 51 | */ 52 | private int findEven(final int start) { 53 | int rst = -1; 54 | for (int index = start; index != this.data.length; index++) { 55 | if (this.data[index] % 2 == 0) { 56 | rst = index; 57 | break; 58 | } 59 | } 60 | return rst; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_004/HibernateStoreTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.hibernate.Session; 4 | import org.hibernate.SessionFactory; 5 | import org.junit.AfterClass; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import static org.hamcrest.CoreMatchers.is; 10 | import static org.hamcrest.CoreMatchers.not; 11 | import static org.junit.Assert.assertThat; 12 | import static ru.job4j.principle_004.ConnectionRollback.create; 13 | 14 | /** 15 | * Tests for hibernate. 16 | */ 17 | @Ignore 18 | public class HibernateStoreTest { 19 | /** 20 | * Create a new user and check id. 21 | */ 22 | @Test 23 | public void whenCreateUser() { 24 | SessionFactory factory = create(HibernateFactory.FACTORY); 25 | HibernateStore store = new HibernateStore(factory); 26 | User user = store.add(new User(-1, "Petr Arsentev")); 27 | assertThat( 28 | user.getId(), 29 | not(-1) 30 | ); 31 | factory.close(); 32 | } 33 | 34 | /** 35 | * Create a new user and find it. 36 | */ 37 | @Test 38 | public void whenCreateAndFind() { 39 | SessionFactory factory = create(HibernateFactory.FACTORY); 40 | Session session = factory.openSession(); 41 | HibernateStore store = new HibernateStore(factory); 42 | User user = store.add(new User(-1, "Petr Arsentev")); 43 | assertThat( 44 | store.findById(user.getId()).getLogin(), 45 | is("Petr Arsentev") 46 | ); 47 | session.clear(); 48 | assertThat(store.findAll().isEmpty(), is(true)); 49 | factory.close(); 50 | } 51 | 52 | /** 53 | * Clean tests data. 54 | */ 55 | @AfterClass 56 | public static void close() { 57 | HibernateFactory.FACTORY.close(); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/DispatchPattern.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.function.Function; 6 | 7 | /** 8 | * Dispatch pattern. 9 | * 10 | * @author Petr Arsentev (parsentev@yandex.ru) 11 | * @version $Id$ 12 | * @since 0.1 13 | */ 14 | public class DispatchPattern { 15 | 16 | /** 17 | * Contains destinations. 18 | */ 19 | private final Map> dispatch = new HashMap<>(); 20 | 21 | /** 22 | * Handle to email. 23 | * @return handle. 24 | */ 25 | public Function toEmail() { 26 | return msg -> { 27 | // sent to email. 28 | return true; 29 | }; 30 | } 31 | 32 | /** 33 | * Handle to unknown. 34 | * @return handle. 35 | */ 36 | public Function toUnknown() { 37 | return msg -> { 38 | // sent to unknown. 39 | return false; 40 | }; 41 | } 42 | 43 | /** 44 | * Init's dispatch. 45 | * @return current object. 46 | */ 47 | public DispatchPattern init() { 48 | this.load(Message.Type.EMAIL, this.toEmail()); 49 | this.load(Message.Type.UNKNOWN, this.toUnknown()); 50 | return this; 51 | } 52 | 53 | /** 54 | * Load handlers for destinations. 55 | * @param type Message type. 56 | * @param handle Handler. 57 | */ 58 | public void load(Message.Type type, Function handle) { 59 | this.dispatch.put(type, handle); 60 | } 61 | 62 | /** 63 | * Sent message to dispatch. 64 | * @param msg message 65 | * @return true if it finds in a dispatch. 66 | */ 67 | public boolean sent(final Message msg) { 68 | return this.dispatch.get( 69 | msg.type() 70 | ).apply(msg); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_002/DispatchDiapason.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_002; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * Dispatch pattern for diapason key. 8 | * 9 | * @author Petr Arsentev (parsentev@yandex.ru) 10 | * @version $Id$ 11 | * @since 0.1 12 | */ 13 | public class DispatchDiapason { 14 | /** 15 | * Dispatch. 16 | */ 17 | private final LinkedHashMap, Function> dispatch = new LinkedHashMap<>(); 18 | 19 | /** 20 | * Load initial handlers. 21 | * @return current object. 22 | */ 23 | public DispatchDiapason init() { 24 | this.dispatch.put( 25 | person -> person.age() < 14, 26 | person -> Person.Access.FORBIDDEN 27 | ); 28 | this.dispatch.put( 29 | person -> person.age() >= 14 && person.age() < 18, 30 | person -> Person.Access.LIMIT 31 | ); 32 | this.dispatch.put( 33 | person -> person.age() >= 18, 34 | person -> Person.Access.FREE 35 | ); 36 | return this; 37 | } 38 | 39 | /** 40 | * Load handler and predict. 41 | * @param predict Predict. 42 | * @param handle Handle. 43 | */ 44 | public void load(Function predict, Function handle) { 45 | this.dispatch.put(predict, handle); 46 | } 47 | 48 | /** 49 | * Chech access for person by age. 50 | * @param person Person 51 | * @return true if access are allowed 52 | */ 53 | public Person.Access access(Person person) { 54 | for (Function predict : this.dispatch.keySet()) { 55 | if (predict.apply(person)) { 56 | return this.dispatch.get(predict).apply(person); 57 | } 58 | } 59 | throw new IllegalStateException("Could not found a handle for person"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_007/NotificationService.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_007; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.HashMap; 6 | import java.util.TimerTask; 7 | import java.util.Timer; 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | 13 | public class NotificationService { 14 | private final ExecutorService pool = Executors.newFixedThreadPool( 15 | Runtime.getRuntime().availableProcessors() 16 | ); 17 | private final Map> cache = new HashMap<>(); 18 | 19 | public NotificationService() { 20 | new Timer().schedule( 21 | new TimerTask() { 22 | @Override 23 | public void run() { 24 | fire(); 25 | } 26 | }, 27 | 5000L, 5000L 28 | ); 29 | } 30 | 31 | public void notify(String email, String text) { 32 | if (!cache.containsKey(email)) { 33 | cache.put(email, new ArrayList<>()); 34 | } 35 | cache.get(email).add(text); 36 | } 37 | 38 | private void fire() { 39 | for (String key : new HashSet<>(cache.keySet())) { 40 | List body = cache.remove(key); 41 | StringBuilder text = new StringBuilder(); 42 | body.forEach(text::append); 43 | send(key, text.toString()); 44 | } 45 | } 46 | 47 | private void send(String subject, String body) { 48 | pool.submit(() -> { 49 | /* Логика рассылки. */ 50 | }); 51 | } 52 | 53 | public void shutdown() { 54 | pool.shutdown(); 55 | while (!pool.isTerminated()) { 56 | try { 57 | Thread.sleep(100); 58 | } catch (InterruptedException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/Credential.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import ru.job4j.principle_001.User; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * Example of multiple if-else-throw statements. 11 | */ 12 | public class Credential { 13 | 14 | /** 15 | * Validations rules. 16 | */ 17 | private final List> validates = Arrays.asList( 18 | user -> { 19 | if (!this.checkName(user)) { 20 | throw new IllegalStateException("Wrong name."); 21 | } 22 | }, 23 | user -> { 24 | if (!this.checkSurname(user)) { 25 | throw new IllegalStateException("Wrong surname."); 26 | } 27 | }, 28 | user -> { 29 | if (!this.checkBalance(user)) { 30 | throw new IllegalStateException("Wrong balance."); 31 | } 32 | } 33 | ); 34 | 35 | /** 36 | * Check the access for user by validations rules. 37 | * @param user User. 38 | * @return true if user has the access. 39 | */ 40 | boolean hasAccess(final User user) { 41 | this.validates.forEach(action -> action.accept(user)); 42 | return true; 43 | } 44 | 45 | /** 46 | * Validate by users' balance. 47 | * @param user User 48 | * @return true if users' balance is valid 49 | */ 50 | private boolean checkBalance(User user) { 51 | return false; 52 | } 53 | 54 | /** 55 | * Validate by users' surname. 56 | * @param user User 57 | * @return true if users' surname is valid 58 | */ 59 | private boolean checkSurname(User user) { 60 | return false; 61 | } 62 | 63 | /** 64 | * Validate by users' name. 65 | * @param user User 66 | * @return true if users' name is valid 67 | */ 68 | private boolean checkName(User user) { 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/HibernateStore.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.hibernate.Session; 4 | import org.hibernate.SessionFactory; 5 | import org.hibernate.Transaction; 6 | 7 | import java.util.List; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Impl of store by hibernate. 12 | */ 13 | public class HibernateStore implements Store { 14 | /** 15 | * Session Factory. 16 | */ 17 | private final SessionFactory factory; 18 | 19 | /** 20 | * Constructor. 21 | * @param factory Session factory. 22 | */ 23 | public HibernateStore(SessionFactory factory) { 24 | this.factory = factory; 25 | } 26 | 27 | /** 28 | * Wrapper for transactions. 29 | * @param command actions. 30 | * @param model type. 31 | * @return result of actions. 32 | */ 33 | private T tx(final Function command) { 34 | final Session session = factory.openSession(); 35 | final Transaction tx = session.beginTransaction(); 36 | try { 37 | T rsl = command.apply(session); 38 | tx.commit(); 39 | return rsl; 40 | } catch (final Exception e) { 41 | session.getTransaction().rollback(); 42 | throw e; 43 | } finally { 44 | session.close(); 45 | } 46 | } 47 | 48 | @Override 49 | public User add(User model) { 50 | tx(session -> session.save(model)); 51 | return model; 52 | } 53 | 54 | @Override 55 | public void update(User model) { 56 | tx( 57 | session -> { 58 | session.update(model); 59 | return null; 60 | } 61 | ); 62 | } 63 | 64 | @Override 65 | public void delete(int id) { 66 | tx( 67 | session -> { 68 | session.delete(new User(id, null)); 69 | return null; 70 | } 71 | ); 72 | } 73 | 74 | @Override 75 | public List findAll() { 76 | return tx(session -> session.createQuery("from users").list()); 77 | } 78 | 79 | @Override 80 | public User findById(int id) { 81 | return tx(session -> session.find(User.class, id)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/User.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | import java.util.Objects; 8 | 9 | /** 10 | * User model. 11 | */ 12 | @Entity(name = "users") 13 | public class User { 14 | /** 15 | * Identification. 16 | */ 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private int id; 20 | /** 21 | * Login. 22 | */ 23 | private String login; 24 | 25 | /** 26 | * Default constructor. 27 | */ 28 | public User() { 29 | } 30 | 31 | /** 32 | * Takes id and login. 33 | * @param id id. 34 | * @param login login. 35 | */ 36 | public User(int id, String login) { 37 | this.id = id; 38 | this.login = login; 39 | } 40 | 41 | /** 42 | * Get id. 43 | * @return id. 44 | */ 45 | public int getId() { 46 | return id; 47 | } 48 | 49 | /** 50 | * Set id. 51 | * @param id id. 52 | */ 53 | public void setId(int id) { 54 | this.id = id; 55 | } 56 | 57 | /** 58 | * Get login. 59 | * @return login. 60 | */ 61 | public String getLogin() { 62 | return login; 63 | } 64 | 65 | /** 66 | * Set login. 67 | * @param login login. 68 | */ 69 | public void setLogin(String login) { 70 | this.login = login; 71 | } 72 | 73 | /** 74 | * ${@inheritDoc}. 75 | */ 76 | @Override 77 | public String toString() { 78 | return "User{" + "id=" + id + ", login='" + login + '\'' + '}'; 79 | } 80 | 81 | /** 82 | * ${@inheritDoc}. 83 | */ 84 | @Override 85 | public boolean equals(Object o) { 86 | if (this == o) { 87 | return true; 88 | } 89 | if (o == null || getClass() != o.getClass()) { 90 | return false; 91 | } 92 | User user1 = (User) o; 93 | return id == user1.id && Objects.equals(login, user1.login); 94 | } 95 | 96 | /** 97 | * ${@inheritDoc}. 98 | */ 99 | @Override 100 | public int hashCode() { 101 | return Objects.hash(id, login); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_004/DbStoreTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.apache.commons.dbcp.BasicDataSource; 4 | import org.junit.Test; 5 | 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.junit.Assert.assertThat; 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.InputStream; 11 | import java.sql.SQLException; 12 | import java.util.Properties; 13 | import static org.hamcrest.core.IsNot.not; 14 | 15 | /** 16 | * Integration tests. 17 | */ 18 | public class DbStoreTest { 19 | 20 | /** 21 | * Build db course. 22 | * @param con consumer. 23 | */ 24 | private void source(ConEx con) { 25 | BasicDataSource source = new PoolRollback(); 26 | try { 27 | Properties settings = new Properties(); 28 | try (InputStream in = new FileInputStream(new File("./gradle.properties"))) { 29 | settings.load(in); 30 | } 31 | source.setDriverClassName(settings.getProperty("databaseQaDriver")); 32 | source.setUrl(settings.getProperty("databaseQaUrl")); 33 | source.setUsername(settings.getProperty("databaseQaUsername")); 34 | source.setPassword(settings.getProperty("databaseQaPassword")); 35 | source.setMinIdle(5); 36 | source.setMaxIdle(10); 37 | source.setMaxOpenPreparedStatements(100); 38 | con.accept(source); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } finally { 42 | try { 43 | source.close(); 44 | } catch (SQLException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * Check that table is empty. 52 | */ 53 | @Test 54 | public void whenReadsThenTableEmpty() { 55 | this.source( 56 | source -> { 57 | DbStore store = new DbStore(source); 58 | assertThat( 59 | store.findAll().isEmpty(), 60 | is(true) 61 | ); 62 | } 63 | ); 64 | } 65 | 66 | /** 67 | * Test. Adding user. 68 | */ 69 | @Test 70 | public void whenAddUserThenGenerateId() { 71 | this.source( 72 | source -> { 73 | DbStore store = new DbStore(source); 74 | assertThat( 75 | store.add(new User(-1, "parsentev")), 76 | not(-1) 77 | ); 78 | } 79 | ); 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/User.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Model user. 7 | */ 8 | public class User { 9 | /** 10 | * Name. 11 | */ 12 | private String name; 13 | /** 14 | * Login. 15 | */ 16 | private String login; 17 | /** 18 | * Email. 19 | */ 20 | private String email; 21 | /** 22 | * Create's date. 23 | */ 24 | private String created; 25 | 26 | /** 27 | * Constructor. 28 | * @param name Name. 29 | * @param login Login. 30 | * @param email Email. 31 | * @param created Create's date. 32 | */ 33 | public User(String name, String login, String email, String created) { 34 | this.name = name; 35 | this.login = login; 36 | this.email = email; 37 | this.created = created; 38 | } 39 | 40 | /** 41 | * Get name. 42 | * @return name. 43 | */ 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | /** 49 | * Set name. 50 | * @param name name. 51 | */ 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | /** 57 | * Get login. 58 | * @return Login. 59 | */ 60 | public String getLogin() { 61 | return login; 62 | } 63 | 64 | /** 65 | * Set login. 66 | * @param login login. 67 | */ 68 | public void setLogin(String login) { 69 | this.login = login; 70 | } 71 | 72 | /** 73 | * Get email. 74 | * @return Email. 75 | */ 76 | public String getEmail() { 77 | return email; 78 | } 79 | 80 | /** 81 | * Set email. 82 | * @param email email. 83 | */ 84 | public void setEmail(String email) { 85 | this.email = email; 86 | } 87 | 88 | /** 89 | * Get create's date. 90 | * @return Date 91 | */ 92 | public String getCreated() { 93 | return created; 94 | } 95 | 96 | /** 97 | * Set create's date. 98 | * @param created created. 99 | */ 100 | public void setCreated(String created) { 101 | this.created = created; 102 | } 103 | 104 | /** 105 | * ${@inheritDoc}. 106 | */ 107 | @Override 108 | public boolean equals(Object o) { 109 | if (this == o) { 110 | return true; 111 | } 112 | if (o == null || getClass() != o.getClass()) { 113 | return false; 114 | } 115 | User user = (User) o; 116 | return Objects.equals(name, user.name) 117 | && Objects.equals(login, user.login) 118 | && Objects.equals(email, user.email) 119 | && Objects.equals(created, user.created); 120 | } 121 | 122 | /** 123 | * ${@inheritDoc}. 124 | */ 125 | @Override 126 | public int hashCode() { 127 | return Objects.hash(name, login, email, created); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_005/Config.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_005; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import java.util.LinkedHashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * This class works with file config (key=value format). 12 | * Example. 13 | * ##email config 14 | * email=parsentev@yandex.ru 15 | * name=Petr Arsentev 16 | * jdbc.url=... 17 | */ 18 | public class Config { 19 | /** 20 | * Separator of key=value. 21 | */ 22 | private static final String SP = "="; 23 | 24 | /** 25 | * File with key=value. 26 | */ 27 | private final String path; 28 | 29 | /** 30 | * Contains pair of key/value. 31 | */ 32 | private final Map settings = new LinkedHashMap<>(); 33 | 34 | /** 35 | * Contructor. 36 | * @param path file properties. 37 | */ 38 | public Config(final String path) { 39 | this.path = path; 40 | } 41 | 42 | /** 43 | * Load properties. 44 | * @return instance of config. 45 | */ 46 | public Config load() { 47 | this.settings.clear(); 48 | try (BufferedReader file = new BufferedReader(new FileReader(this.path))) { 49 | file.lines().forEach( 50 | line -> { 51 | if (line.contains(SP)) { 52 | int pos = line.indexOf(SP); 53 | settings.put(line.substring(0, pos), line.substring(pos + 1)); 54 | } else { 55 | settings.put(line, ""); 56 | } 57 | } 58 | ); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | return this; 63 | } 64 | 65 | /** 66 | * Get value by key. 67 | * @param key key. 68 | * @return value. 69 | */ 70 | public String value(String key) { 71 | return this.settings.get(key); 72 | } 73 | 74 | /** 75 | * Add or update the key and value. 76 | * @param key key. 77 | * @param value value. 78 | */ 79 | public void put(String key, String value) { 80 | this.settings.put(key, value); 81 | } 82 | 83 | /** 84 | * Delete pair by key. 85 | * @param key key. 86 | */ 87 | public void delete(String key) { 88 | this.settings.remove(key); 89 | } 90 | 91 | /** 92 | * Save of properties to file. 93 | */ 94 | public void save() { 95 | try (final PrintWriter file = new PrintWriter(this.path)) { 96 | this.settings.forEach( 97 | (key, value) -> { 98 | file.append(key); 99 | if (!value.isEmpty()) { 100 | file.append(SP).append(value); 101 | } 102 | file.println(); 103 | } 104 | ); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/DbStoreCopyPast.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.apache.commons.dbcp.BasicDataSource; 4 | 5 | import java.sql.PreparedStatement; 6 | import java.sql.Connection; 7 | import java.sql.Statement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Storage with copy past anti pattern. 15 | */ 16 | public class DbStoreCopyPast implements Store { 17 | /** 18 | * db source. 19 | */ 20 | private final BasicDataSource source; 21 | 22 | /** 23 | * Constructor. 24 | * 25 | * @param source db source. 26 | */ 27 | public DbStoreCopyPast(BasicDataSource source) { 28 | this.source = source; 29 | } 30 | 31 | @Override 32 | public User add(User user) { 33 | try (Connection connection = this.source.getConnection(); 34 | final PreparedStatement statement = connection 35 | .prepareStatement("insert into users (login) values (?)", 36 | Statement.RETURN_GENERATED_KEYS)) { 37 | statement.setString(1, user.getLogin()); 38 | statement.executeUpdate(); 39 | try (ResultSet generatedKeys = statement.getGeneratedKeys()) { 40 | if (generatedKeys.next()) { 41 | user.setId(generatedKeys.getInt(1)); 42 | return user; 43 | } 44 | } 45 | } catch (SQLException e) { 46 | e.printStackTrace(); 47 | } 48 | throw new IllegalStateException("Could not create new user"); 49 | } 50 | 51 | @Override 52 | public List findAll() { 53 | final List users = new ArrayList<>(); 54 | try (final PreparedStatement statement = this.source.getConnection() 55 | .prepareStatement("select * from users"); 56 | final ResultSet rs = statement.executeQuery()) { 57 | while (rs.next()) { 58 | users.add(new User(rs.getInt("id"), rs.getString("login"))); 59 | } 60 | } catch (SQLException e) { 61 | e.printStackTrace(); 62 | } 63 | return users; 64 | } 65 | 66 | @Override 67 | public void update(User user) { 68 | try (final PreparedStatement ps = this.source.getConnection() 69 | .prepareStatement("update users set login=? where id=?")) { 70 | ps.setString(1, user.getLogin()); 71 | ps.setString(2, user.getLogin()); 72 | ps.executeUpdate(); 73 | } catch (SQLException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | @Override 79 | public void delete(int id) { 80 | } 81 | 82 | @Override 83 | public User findById(int id) { 84 | User rsl = new User(); 85 | try (final PreparedStatement ps = this.source.getConnection() 86 | .prepareStatement("select * from users where id=?")) { 87 | ps.setInt(1, id); 88 | try (ResultSet rs = ps.executeQuery()) { 89 | if (rs.next()) { 90 | rsl = new User(rs.getInt("id"), rs.getString("login")); 91 | } 92 | } 93 | } catch (SQLException e) { 94 | e.printStackTrace(); 95 | } 96 | return rsl; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_006/BankService.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_006; 2 | import org.jetbrains.annotations.Nullable; 3 | 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.Optional; 7 | 8 | public class BankService { 9 | private final HashMap> users = new HashMap<>(); 10 | 11 | public void addUser(User user) { 12 | users.putIfAbsent(user, new ArrayList<>()); 13 | } 14 | 15 | public Account findByRequisite(String passport, String requisite) { 16 | User user = findByPassport(passport); 17 | if (user == null) { 18 | return null; 19 | } 20 | return users.get(user).stream() 21 | .filter(account -> account.getRequisite().equals(requisite)) 22 | .findFirst() 23 | .orElse(null); 24 | } 25 | 26 | public void addAccount(String passport, Account account) { 27 | User user = findByPassport(passport); 28 | if (user == null) { 29 | return; 30 | } 31 | users.get(user).add(account); 32 | } 33 | 34 | public User findByPassportIfNullThrow(String passport) throws NotFoundUserException { 35 | for (User user : users.keySet()) { 36 | if (user.getPassport().equals(passport)) { 37 | return user; 38 | } 39 | } 40 | throw new NotFoundUserException("User with passport " + passport + "not found"); 41 | } 42 | 43 | public User findByPassportIfNullRuntime(String passport) { 44 | for (User user : users.keySet()) { 45 | if (user.getPassport().equals(passport)) { 46 | return user; 47 | } 48 | } 49 | throw new NullPointerException("User with passport " + passport + "not found"); 50 | } 51 | 52 | @Nullable 53 | public User findByPassportCheckByCompile(String passport) { 54 | for (User user : users.keySet()) { 55 | if (user.getPassport().equals(passport)) { 56 | return user; 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | public Optional findByPassportIfNullOptional(String passport) { 63 | for (User user : users.keySet()) { 64 | if (user.getPassport().equals(passport)) { 65 | return Optional.of(user); 66 | } 67 | } 68 | return Optional.empty(); 69 | } 70 | 71 | public User findByPassport(String passport) { 72 | for (User user : users.keySet()) { 73 | if (user.getPassport().equals(passport)) { 74 | return user; 75 | } 76 | } 77 | return null; 78 | } 79 | 80 | public boolean transferMoney(String srcPassport, String srcRequisite, 81 | String destPassport, String descRequisite, double amount) { 82 | var source = findByRequisite(srcPassport, srcRequisite); 83 | var dest = findByRequisite(destPassport, descRequisite); 84 | boolean rsl = source != null && dest != null; 85 | if (rsl) { 86 | source.setBalance(source.getBalance() - amount); 87 | dest.setBalance(dest.getBalance() + amount); 88 | } 89 | return rsl; 90 | } 91 | 92 | public static void main(String[] args) { 93 | var bank = new BankService(); 94 | bank.addUser(new User("321", "Petr Arsentev")); 95 | try { 96 | var user = bank.findByPassportIfNullThrow("123"); 97 | System.out.println(user.getUsername()); 98 | } catch (NotFoundUserException e) { 99 | System.out.println("User not found."); 100 | } 101 | 102 | var user = bank.findByPassportCheckByCompile("123"); 103 | System.out.println(user.getUsername()); 104 | } 105 | } -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_004/ConnectionRollback.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.hibernate.Session; 4 | import org.hibernate.SessionFactory; 5 | import org.hibernate.Transaction; 6 | 7 | import java.lang.reflect.Proxy; 8 | import java.sql.Connection; 9 | import java.sql.SQLException; 10 | import static org.mockito.Mockito.mock; 11 | 12 | /** 13 | * Connection, which rollback all commits. 14 | * It is used for integration test. 15 | * @author Petr Arsentev (parsentev@yandex.ru) 16 | * @since 21.03.2019 17 | */ 18 | public class ConnectionRollback { 19 | 20 | /** 21 | * Create connection with autocommit=false mode and rollback call, when conneciton is closed. 22 | * @param connection connection. 23 | * @return Connection object. 24 | * @throws SQLException possible exception. 25 | */ 26 | public static Connection create(Connection connection) throws SQLException { 27 | connection.setAutoCommit(false); 28 | return (Connection) Proxy.newProxyInstance( 29 | ConnectionRollback.class.getClassLoader(), 30 | new Class[] { 31 | Connection.class 32 | }, 33 | (proxy, method, args) -> { 34 | Object rsl = null; 35 | if ("close".equals(method.getName())) { 36 | connection.rollback(); 37 | connection.close(); 38 | } else { 39 | rsl = method.invoke(connection, args); 40 | } 41 | return rsl; 42 | } 43 | ); 44 | } 45 | 46 | /** 47 | * Proxy for session factory. 48 | * @param factory session factory. 49 | * @return session factory. 50 | */ 51 | public static SessionFactory create(SessionFactory factory) { 52 | Session session = factory.openSession(); 53 | session.beginTransaction(); 54 | return (SessionFactory) Proxy.newProxyInstance( 55 | ConnectionRollback.class.getClassLoader(), 56 | new Class[] { 57 | SessionFactory.class 58 | }, 59 | (proxy, method, args) -> { 60 | Object rsl = null; 61 | if ("openSession".equals(method.getName())) { 62 | rsl = create(session); 63 | } else if ("close".equals(method.getName())) { 64 | session.getTransaction().rollback(); 65 | session.close(); 66 | } else { 67 | rsl = method.invoke(factory, args); 68 | } 69 | return rsl; 70 | } 71 | ); 72 | } 73 | 74 | /** 75 | * Proxy for session. 76 | * @param session session. 77 | * @return session. 78 | */ 79 | public static Session create(Session session) { 80 | return (Session) Proxy.newProxyInstance( 81 | ConnectionRollback.class.getClassLoader(), 82 | new Class[] { 83 | Session.class 84 | }, 85 | (proxy, method, args) -> { 86 | Object rsl; 87 | if ("beginTransaction".equals(method.getName())) { 88 | rsl = mock(Transaction.class); 89 | } else if ("close".equals(method.getName())) { 90 | rsl = null; 91 | } else if ("clear".equals(method.getName())) { 92 | session.getTransaction().rollback(); 93 | rsl = null; 94 | } else { 95 | rsl = method.invoke(session, args); 96 | } 97 | return rsl; 98 | } 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/ru/job4j/principle_005/ConfigTest.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_005; 2 | 3 | import org.junit.Test; 4 | import java.io.BufferedReader; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.io.File; 9 | import java.util.stream.Stream; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.hamcrest.CoreMatchers.nullValue; 13 | import static org.junit.Assert.assertThat; 14 | 15 | /** 16 | * Contains tests of config class. 17 | */ 18 | public class ConfigTest { 19 | 20 | /** 21 | * Check remove operations. 22 | * @throws IOException possible. 23 | */ 24 | @Test 25 | public void whenRemoveKeyConfig() throws IOException { 26 | File path = data( 27 | String.valueOf(System.currentTimeMillis()), 28 | "email=parsentev@yandex.ru" 29 | ); 30 | Config config = new Config(path.getAbsolutePath()).load(); 31 | config.delete("email"); 32 | config.save(); 33 | config.load(); 34 | assertThat(config.value("email"), is(nullValue())); 35 | path.deleteOnExit(); 36 | } 37 | 38 | /** 39 | * Check read operations. 40 | * @throws IOException possible. 41 | */ 42 | @Test 43 | public void whenReadConfig() throws IOException { 44 | File path = data( 45 | String.valueOf(System.currentTimeMillis()), 46 | "email=parsentev@yandex.ru" 47 | ); 48 | Config config = new Config(path.getAbsolutePath()).load(); 49 | assertThat(config.value("email"), is("parsentev@yandex.ru")); 50 | path.deleteOnExit(); 51 | } 52 | 53 | /** 54 | * Check write operations. 55 | * @throws IOException possible. 56 | */ 57 | @Test 58 | public void whenWriteConfig() throws IOException { 59 | File path = data( 60 | String.valueOf(System.currentTimeMillis()), 61 | "email=parsentev@yandex.ru" 62 | ); 63 | Config config = new Config(path.getAbsolutePath()).load(); 64 | config.put("email", "123@google.ru"); 65 | config.save(); 66 | config.load(); 67 | assertThat(config.value("email"), is("123@google.ru")); 68 | path.deleteOnExit(); 69 | } 70 | 71 | /** 72 | * When config has comments. 73 | * @throws IOException possible. 74 | */ 75 | @Test 76 | public void whenConfigHasComment() throws IOException { 77 | File path = data( 78 | String.valueOf(System.currentTimeMillis()), 79 | "### email for git", 80 | "email=parsentev@yandex.ru" 81 | ); 82 | Config config = new Config(path.getAbsolutePath()).load(); 83 | config.put("email", "123@google.ru"); 84 | config.save(); 85 | StringBuilder content = new StringBuilder(); 86 | try (BufferedReader file = new BufferedReader(new FileReader(path))) { 87 | file.lines().forEach( 88 | line -> content.append(line).append(System.lineSeparator()) 89 | ); 90 | } 91 | assertThat( 92 | content.toString(), 93 | is( 94 | String.join(System.lineSeparator(), 95 | "### email for git", 96 | "email=123@google.ru", "" 97 | ) 98 | ) 99 | ); 100 | path.deleteOnExit(); 101 | } 102 | 103 | /** 104 | * Fill input data. 105 | * @param file properties file. 106 | * @param properties data, key=value. 107 | * @return file. 108 | * @throws IOException possible. 109 | */ 110 | private File data(String file, String... properties) throws IOException { 111 | File path = new File( 112 | System.getProperty("java.io.tmpdir") 113 | + System.getProperty("file.separator") 114 | + file 115 | ); 116 | if (!path.createNewFile()) { 117 | throw new IllegalStateException(String.format("File could not created %s", path.getAbsoluteFile())); 118 | } 119 | try (final PrintWriter store = new PrintWriter(path)) { 120 | Stream.of(properties).forEach(store::println); 121 | } 122 | return path; 123 | } 124 | } -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_003/UserServlet.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_003; 2 | 3 | import javax.servlet.ServletException; 4 | import javax.servlet.http.HttpServlet; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.io.IOException; 8 | import java.io.PrintWriter; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.function.Consumer; 12 | 13 | /** 14 | * @author Vladimir Yamnikov (Androedge@gmail.com). 15 | * @version $1.0$. UserServlet. 16 | * @since 13.02.2019. 17 | */ 18 | public class UserServlet extends HttpServlet { 19 | /** 20 | * Check data. 21 | */ 22 | private final Validate store = ValidateService.INSTANCE; 23 | /** 24 | * Route the actions. 25 | */ 26 | private final Map> acts = new HashMap<>(); 27 | 28 | /** 29 | * Constructor. 30 | */ 31 | public UserServlet() { 32 | acts.put("add", this::add); 33 | acts.put("delete", this::delete); 34 | acts.put("update", this::update); 35 | } 36 | 37 | /** 38 | * Update user. 39 | * @param req request. 40 | */ 41 | private void update(HttpServletRequest req) { 42 | this.store.update( 43 | Integer.valueOf(req.getParameter("id")), 44 | new User( 45 | req.getParameter("name"), req.getParameter("login"), 46 | req.getParameter("email"), req.getParameter("create") 47 | ) 48 | ); 49 | } 50 | 51 | /** 52 | * Delete user. 53 | * @param req request. 54 | */ 55 | private void delete(HttpServletRequest req) { 56 | this.store.delete(Integer.valueOf(req.getParameter("id"))); 57 | } 58 | 59 | /** 60 | * Add user. 61 | * @param req request. 62 | */ 63 | private void add(HttpServletRequest req) { 64 | this.store.add( 65 | new User( 66 | req.getParameter("name"), req.getParameter("login"), 67 | req.getParameter("email"), req.getParameter("create") 68 | ) 69 | ); 70 | } 71 | 72 | 73 | /** 74 | * Draw the table with user. 75 | * @param req request. 76 | * @param resp response. 77 | * @throws ServletException possible. 78 | * @throws IOException possible. 79 | */ 80 | @Override 81 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 82 | resp.setContentType("text/html; charset=utf-8"); 83 | this.showList(this.store.findAll(), req, resp); 84 | } 85 | 86 | /** 87 | * Handle actions. 88 | * @param req request. 89 | * @param resp response. 90 | * @throws ServletException possible. 91 | * @throws IOException possible. 92 | */ 93 | @Override 94 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 95 | resp.setContentType("text/html; charset=utf-8"); 96 | String action = req.getParameter("action"); 97 | acts.getOrDefault( 98 | action, 99 | request -> { 100 | throw new UnsupportedOperationException(String.format("Action %s not found", action)); 101 | } 102 | ).accept(req); 103 | doGet(req, resp); 104 | } 105 | 106 | /** 107 | * Shows Users in list. 108 | * @param list list of users. 109 | * @param req request param. 110 | * @param resp response param. 111 | * @throws IOException possible. 112 | */ 113 | private void showList(Map list, HttpServletRequest req, HttpServletResponse resp) throws IOException { 114 | PrintWriter writer = new PrintWriter(resp.getOutputStream()); 115 | writer.append("List: \n"); 116 | list.forEach((integer, user) -> { 117 | writer.append("User id= ").append(String.valueOf(integer)).append("\n"); 118 | writer.append("Name= ").append(user.getName()).append("\n"); 119 | writer.append("Login= ").append(user.getLogin()).append("\n"); 120 | writer.append("Email= ").append(user.getEmail()).append("\n"); 121 | writer.append("Create Date= ").append(user.getCreated()).append("\n"); 122 | writer.append("\n"); 123 | }); 124 | writer.flush(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/ru/job4j/principle_004/DbStore.java: -------------------------------------------------------------------------------- 1 | package ru.job4j.principle_004; 2 | 3 | import org.apache.commons.dbcp.BasicDataSource; 4 | 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.Statement; 8 | import java.util.Map; 9 | import java.util.List; 10 | import java.util.HashMap; 11 | import java.util.ArrayList; 12 | import java.util.Optional; 13 | 14 | /** 15 | * Refactoring store. 16 | */ 17 | public class DbStore implements Store { 18 | /** 19 | * Db source. 20 | */ 21 | private final BasicDataSource source; 22 | 23 | /** 24 | * Dispatch param for prepared statement. 25 | */ 26 | private final Map, TripleConEx> dispatch = new HashMap<>(); 27 | 28 | /** 29 | * Constructor. 30 | * @param source DB source. 31 | */ 32 | public DbStore(BasicDataSource source) { 33 | this.source = source; 34 | this.dispatch.put(Integer.class, (index, ps, value) -> ps.setInt(index, (Integer) value)); 35 | this.dispatch.put(String.class, (index, ps, value) -> ps.setString(index, (String) value)); 36 | } 37 | 38 | /** 39 | * For-each statement with index. 40 | * @param list list. 41 | * @param consumer consumer. 42 | * @param type. 43 | * @throws Exception possible exception. 44 | */ 45 | private void forIndex(List list, BiConEx consumer) throws Exception { 46 | for (int index = 0; index != list.size(); index++) { 47 | consumer.accept(index, list.get(index)); 48 | } 49 | } 50 | 51 | /** 52 | * Wrapper with prepared statement. 53 | * @param sql query 54 | * @param params params 55 | * @param fun function with prepared statement. 56 | * @param key for generating key. 57 | * @param type 58 | * @return value. 59 | */ 60 | private Optional db(String sql, List params, FunEx fun, int key) { 61 | Optional rst = Optional.empty(); 62 | try (var connection = this.source.getConnection(); 63 | var pr = connection.prepareStatement(sql, key)) { 64 | this.forIndex( 65 | params, 66 | (index, value) -> dispatch.get(value.getClass()).accept(index + 1, pr, value) 67 | ); 68 | rst = Optional.of(fun.apply(pr)); 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | return rst; 73 | } 74 | 75 | /** 76 | * Overload without key. 77 | * @param sql query. 78 | * @param params params. 79 | * @param fun function with prepared statement. 80 | * @param type. 81 | * @return value. 82 | */ 83 | private Optional db(String sql, List params, FunEx fun) { 84 | return this.db(sql, params, fun, Statement.NO_GENERATED_KEYS); 85 | } 86 | 87 | /** 88 | * The same like DB only without return value. 89 | * @param sql query. 90 | * @param params params. 91 | * @param fun function with prepared statement. 92 | * @param key generated key. 93 | * @param type. 94 | */ 95 | private void db(String sql, List params, ConEx fun, int key) { 96 | this.db( 97 | sql, params, 98 | ps -> { 99 | fun.accept(ps); 100 | return Optional.empty(); 101 | }, key 102 | ); 103 | } 104 | 105 | /** 106 | * The same like DB only without return value. 107 | * @param sql query. 108 | * @param params params. 109 | * @param fun funcation with prepared statement. 110 | * @param type. 111 | */ 112 | private void db(String sql, List params, ConEx fun) { 113 | this.db(sql, params, fun, Statement.NO_GENERATED_KEYS); 114 | } 115 | 116 | @Override 117 | public User add(User user) { 118 | this.db( 119 | "insert into users (login) values (?)", List.of(user.getLogin()), 120 | ps -> { 121 | ps.executeUpdate(); 122 | try (ResultSet generatedKeys = ps.getGeneratedKeys()) { 123 | if (generatedKeys.next()) { 124 | user.setId(generatedKeys.getInt(1)); 125 | } 126 | } 127 | }, 128 | Statement.RETURN_GENERATED_KEYS 129 | ); 130 | return user; 131 | } 132 | 133 | @Override 134 | public List findAll() { 135 | final List users = new ArrayList<>(); 136 | this.db( 137 | "select * from users", List.of(), 138 | ps -> { 139 | try (final ResultSet rs = ps.executeQuery()) { 140 | while (rs.next()) { 141 | users.add(new User(rs.getInt("id"), rs.getString("login"))); 142 | } 143 | } 144 | } 145 | ); 146 | return users; 147 | } 148 | 149 | @Override 150 | public void update(User model) { 151 | this.db( 152 | "update users set login=? where id=?", 153 | List.of(model.getLogin(), model.getId()), 154 | ps -> { 155 | ps.executeUpdate(); 156 | } 157 | ); 158 | } 159 | 160 | @Override 161 | public void delete(int id) { 162 | this.db( 163 | "delete users where id=?", List.of(id), 164 | ps -> { 165 | ps.executeUpdate(); 166 | } 167 | ); 168 | } 169 | 170 | @Override 171 | public User findById(int id) { 172 | return this.db( 173 | "select * from users where id=?", List.of(id), 174 | ps -> { 175 | User rsl = new User(); 176 | try (ResultSet rs = ps.executeQuery()) { 177 | if (rs.next()) { 178 | rsl = new User(rs.getInt("id"), rs.getString("login")); 179 | } 180 | } 181 | return rsl; 182 | } 183 | ).orElse(new User()); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [travis-ci.org](http://www.travis-ci.org)
2 | [![Build Status](https://travis-ci.org/peterarsentev/code_quality_principles.svg?branch=master)](https://travis-ci.org/peterarsentev/code_quality_principles) 3 | 4 | 5 | 6 | Code quality principles. 7 | ======================== 8 | 9 | The project contains principles which could improve a code quality. 10 | 11 | Below you can find list of principles. Each principle has examples of bad and good code snippets with explanations. 12 | 13 | Contribute 14 | ---------- 15 | 16 | I will appreciate if you share challenges code snippets or add other useful principles with examples. 17 | If you have any questions, feel free to contact me. Skype : petrarsentev 18 | 19 | Content 20 | ------------------- 21 | 1. Multiple return statements 22 | 2. Multiple if-statements and switch anti-pattern 23 | 3. If-else-throw statements 24 | 4. Don't use exceptions. Exceptions make look your code ugly. 25 | 5. Check-then-act statements 26 | 27 | #### 1. Multiple return statements 28 | 29 | All methods must have only one return statement. It should be at the end of a method. 30 | 31 | Bad code. 32 | 33 | int max(int left, int right) { 34 | if (left > right) { 35 | return left; 36 | } else { 37 | return right; 38 | } 39 | } 40 | 41 | Good code. 42 | 43 | int max(int left, int right) { 44 | return left > right ? left : right; 45 | } 46 | 47 | #### 2. Dispatch pattern instead of multiple if-statements and switch anti-pattern. 48 | 49 | Each time you see a code like this below, replace it to dispatch pattern. 50 | 51 | Multiple if-statements. 52 | 53 | public boolean sent(final Message msg) { 54 | boolean rsl = false; 55 | if (msg.type() == Type.EMAIL) { 56 | // sent to email. 57 | rsl = true; 58 | } else if (msg.type() == Type.JABBER) { 59 | // sent to jabber. 60 | rsl = true; 61 | } else if (msg.type() == Type.TWITTER) { 62 | // sent to twitter. 63 | rsl = true; 64 | } else if (msg.type() == Type.ETC) { 65 | // sent to etc. 66 | rsl = true; 67 | } 68 | return rsl; 69 | } 70 | 71 | Switch cases statements. 72 | 73 | public boolean sent(final Message msg) { 74 | final boolean rsl; 75 | switch (msg.type()) { 76 | case EMAIL: 77 | // sent to email. 78 | rsl = true; 79 | break; 80 | case JABBER: 81 | // sent to jabber. 82 | rsl = true; 83 | break; 84 | case TWITTER: 85 | // sent to twitter. 86 | rsl = true; 87 | break; 88 | default: 89 | // sent to etc. 90 | rsl = false; 91 | break; 92 | } 93 | return rsl; 94 | } 95 | 96 | Dispatch pattern. 97 | 98 | import java.util.HashMap; 99 | import java.util.Map; 100 | import java.util.function.Function; 101 | 102 | /** 103 | * Dispatch pattern. 104 | * 105 | * @author Petr Arsentev (parsentev@yandex.ru) 106 | * @version $Id$ 107 | * @since 0.1 108 | */ 109 | public class DispatchPattern { 110 | 111 | /** 112 | * Contains destinations. 113 | */ 114 | private final Map> dispatch = new HashMap<>(); 115 | 116 | /** 117 | * Handle to email. 118 | * @return handle. 119 | */ 120 | public Function toEmail() { 121 | return msg -> { 122 | // sent to email. 123 | return true; 124 | }; 125 | } 126 | 127 | /** 128 | * Handle to unknown. 129 | * @return handle. 130 | */ 131 | public Function toUnknown() { 132 | return msg -> { 133 | // sent to unknown. 134 | return false; 135 | }; 136 | } 137 | 138 | /** 139 | * Init's dispatch. 140 | * @return current object. 141 | */ 142 | public DispatchPattern init() { 143 | this.load(Message.Type.EMAIL, this.toEmail()); 144 | this.load(Message.Type.UNKNOWN, this.toUnknown()); 145 | return this; 146 | } 147 | 148 | /** 149 | * Load handlers for destinations. 150 | * @param type Message type. 151 | * @param handle Handler. 152 | */ 153 | public void load(Message.Type type, Function handle) { 154 | this.dispatch.put(type, handle); 155 | } 156 | 157 | /** 158 | * Sent message to dispatch. 159 | * @param msg message 160 | * @return true if it finds in a dispatch. 161 | */ 162 | public boolean sent(final Message msg) { 163 | return this.dispatch.get( 164 | msg.type() 165 | ).apply(msg); 166 | } 167 | } 168 | 169 | The main benefit of dispatch pattern: 170 | 1. All code is split on independent small methods. 171 | 2. Flexible extension. 172 | 173 | An example. 174 | 175 | Let's consider a situation, we need to calculate permission for a person by age. 176 | 177 | Here, we have non-strict values, as it is on above example. It needs to check a predict in order to execute a handler. It can be done only by loop, so such method works by O(n), not like example above (O(1)). Actually, I don't know how to change this methods in order to make it work by O(1). I will be glad to hear a such solution from you. 178 | 179 | Permission table. 180 | 181 | < 14 - forbidden. 182 | >= 14 and < 18 - limit access. 183 | > 18 - free. 184 | 185 | Full code. 186 | 187 | import java.util.LinkedHashMap; 188 | import java.util.function.Function; 189 | 190 | /** 191 | * Dispatch pattern for diapason key. 192 | * 193 | * @author Petr Arsentev (parsentev@yandex.ru) 194 | * @version $Id$ 195 | * @since 0.1 196 | */ 197 | public class DispatchDiapason { 198 | /** 199 | * Dispatch. 200 | */ 201 | private final LinkedHashMap, Function> dispatch = new LinkedHashMap<>(); 202 | 203 | /** 204 | * Load initial handlers. 205 | * @return current object. 206 | */ 207 | public DispatchDiapason init() { 208 | this.dispatch.put( 209 | person -> person.age() < 14, 210 | person -> Person.Access.FORBIDDEN 211 | ); 212 | this.dispatch.put( 213 | person -> person.age() >= 14 && person.age() < 18, 214 | person -> Person.Access.LIMIT 215 | ); 216 | this.dispatch.put( 217 | person -> person.age() >= 18, 218 | person -> Person.Access.FREE 219 | ); 220 | return this; 221 | } 222 | 223 | /** 224 | * Load handler and predict. 225 | * @param predict Predict. 226 | * @param handle Handle. 227 | */ 228 | public void load(Function predict, Function handle) { 229 | this.dispatch.put(predict, handle); 230 | } 231 | 232 | /** 233 | * Check access for person by age. 234 | * @param person Person 235 | * @return true if access are allowed 236 | */ 237 | public Person.Access access(Person person) { 238 | for (Function predict : this.dispatch.keySet()) { 239 | if (predict.apply(person)) { 240 | return this.dispatch.get(predict).apply(person); 241 | } 242 | } 243 | throw new IllegalStateException("Could not found a handle for person"); 244 | } 245 | } 246 | 247 | import org.junit.Test; 248 | import static org.hamcrest.core.Is.is; 249 | import static org.junit.Assert.assertThat; 250 | 251 | /** 252 | * Test for person permission by age. 253 | * 254 | * @author Petr Arsentev (parsentev@yandex.ru) 255 | * @version $Id$ 256 | * @since 0.1 257 | */ 258 | public class DispatchDiapasonTest { 259 | 260 | /** 261 | * Between 14 and 18. 262 | */ 263 | @Test 264 | public void whenBetween14and18ThenLimited() { 265 | assertThat( 266 | new DispatchDiapason().init().access( 267 | () -> 16 268 | ), 269 | is(Person.Access.LIMIT) 270 | ); 271 | } 272 | 273 | /** 274 | * Up 18 age. 275 | */ 276 | @Test 277 | public void whenUp18AgeThenFree() { 278 | assertThat( 279 | new DispatchDiapason().init().access( 280 | () -> 21 281 | ), 282 | is(Person.Access.FREE) 283 | ); 284 | } 285 | 286 | /** 287 | * Under 14 age. 288 | */ 289 | @Test 290 | public void whenLess14ThenForbidden() { 291 | assertThat( 292 | new DispatchDiapason().init().access( 293 | () -> 10 294 | ), 295 | is(Person.Access.FORBIDDEN) 296 | ); 297 | } 298 | } 299 | 300 | ### 3. If-else-throw statements. 301 | 302 | Let's consider the following code. We need to implement an iterator for even numbers. 303 | 304 | package ru.job4j.principle_003; 305 | 306 | import java.util.Iterator; 307 | 308 | public class EvenIt implements Iterable { 309 | private final int[] data; 310 | 311 | public EvenIt(final int[] data) { 312 | this.data = data; 313 | } 314 | 315 | @Override 316 | public Iterator iterator() { 317 | 318 | return new Iterator() { 319 | private int point; 320 | 321 | @Override 322 | public boolean hasNext() { 323 | return EvenIt.this.findEven(this.point) >= 0; 324 | } 325 | 326 | @Override 327 | public Integer next() { 328 | if (this.hasNext()) { 329 | this.point = EvenIt.this.findEven(this.point); 330 | return EvenIt.this.data[this.point++]; 331 | } else { 332 | throw new IllegalStateException("No such element."); 333 | } 334 | } 335 | } 336 | 337 | private int findEven(final int start) { 338 | int rst = -1; 339 | for (int index = start; index != this.data.length; index++) { 340 | if (this.data[index] % 2 == 0) { 341 | rst = index; 342 | break; 343 | } 344 | } 345 | return rst; 346 | } 347 | } 348 | 349 | Let's focus on this snippet. 350 | 351 | @Override 352 | public Integer next() { 353 | if (this.hasNext()) { 354 | this.point = EvenIt.this.findEven(this.point); 355 | return EvenIt.this.data[this.point++]; 356 | } else { 357 | throw new IllegalStateException("No such element."); 358 | } 359 | } 360 | 361 | This short code looks good, but it can be improved by removing an else-statement. 362 | 363 | if (!this.hasNext()) { 364 | throw new IllegalStateException("No such element."); 365 | } 366 | this.point = EvenIt.this.findEven(this.point); 367 | return EvenIt.this.data[this.point++]; 368 | 369 | The main difference from these two snippets is to split the validation part and main logic part. Right now, we have two independent parts of code. 370 | 371 | Let's consider more complex situation. 372 | 373 | We need to validate the users input by few conditions. 374 | 375 | We can do like this: 376 | 377 | public class Credential { 378 | boolean hasAccess(final User login) { 379 | if (this.checkName(login)) { 380 | if (this.checkSurname(login)) { 381 | if (this.checkBalance(login)) { 382 | return true; 383 | } else { 384 | throw new IllegalStateException("Wrong balance."); 385 | } 386 | } else { 387 | throw new IllegalStateException("Wrong surname."); 388 | } 389 | } else { 390 | throw new IllegalStateException("Wrong name."); 391 | } 392 | } 393 | 394 | private boolean checkBalance(User login) { 395 | return false; 396 | } 397 | 398 | private boolean checkSurname(User login) { 399 | return false; 400 | } 401 | 402 | private boolean checkName(User login) { 403 | return false; 404 | } 405 | } 406 | 407 | If we need to add more conditions, we should add more inner if-else blocks. This code will be difficult to read and maintenance. 408 | 409 | Let's make the refactoring. 410 | 411 | First, we need to split the validation and logic parts. 412 | 413 | boolean hasAccess(final User login) { 414 | if (!this.checkName(login)) { 415 | throw new IllegalStateException("Wrong name."); 416 | } 417 | if (!this.checkSurname(login)) { 418 | throw new IllegalStateException("Wrong surname."); 419 | } 420 | if (!this.checkBalance(login)) { 421 | throw new IllegalStateException("Wrong balance."); 422 | } 423 | return true; 424 | 425 | Then, we need to replace multiple if-statements to dispatch pattern. 426 | 427 | private final List> validates = Arrays.asList( 428 | login -> { 429 | if (!this.checkName(login)) { 430 | throw new IllegalStateException("Wrong name."); 431 | } 432 | }, 433 | login -> { 434 | if (!this.checkSurname(login)) { 435 | throw new IllegalStateException("Wrong surname."); 436 | } 437 | }, 438 | login -> { 439 | if (!this.checkBalance(login)) { 440 | throw new IllegalStateException("Wrong balance."); 441 | } 442 | } 443 | ); 444 | 445 | boolean hasAccess(final User login) { 446 | this.validates.forEach(action -> action.accept(login)); 447 | return true; 448 | } 449 | 450 | ### 4. Don't use exceptions. Exceptions make look your code ugly. 451 | 452 | Frequently, my students ask me questions about exception. Why do we need it or how to use it? If you asked me such questions, before I wrote about this principle I would tell you follows: Exception is used when you need to notify clients about unusual situation in code. Generally, we just rethrow this exception or print it to console. We don't try to handle it. 453 | 454 | If we had had such behavior in real life it would be ridiculous. Let's consider you need a taxi. When the taxi comes, a driver tells you that you need to take repair tools, because a car may break on road and you have to fix it. It is the same situation, when you use code which throws exceptions. 455 | 456 | Now I would like to propagate a new idea about exception. 457 | 458 | Let's consider the following common interface. 459 | 460 | public interface ExtResource { 461 | 462 | T read(String name) throws Exception; 463 | 464 | void write(T value) throws Exception; 465 | } 466 | 467 | Such interface allows you to read and write a resource to external system. Each method may throw exception and you need to handle it. We use try-catch statements for handling exception. To my mind, such code looks ugly. 468 | 469 | public void writeToFile(T value, ExtResource resource) { 470 | try { 471 | resource.write(value); 472 | } catch (Exception e) { 473 | e.printStackTrace(); 474 | } 475 | } 476 | 477 | This is a primitive sample. Look at more real example: 478 | 479 | public void update(User user) { 480 | try (final PreparedStatement ps = this.source.getConnection() 481 | .prepareStatement("update users set login=? where id=?")) { 482 | ps.setString(1, user.getLogin()); 483 | ps.setInt(2, user.getId()); 484 | ps.executeUpdate(); 485 | } catch (SQLException e) { 486 | e.printStackTrace(); 487 | } 488 | } 489 | 490 | You start making you code in copy-past style. 491 | 492 | My idea is to get rid of any exceptions using lambla expression instead. 493 | 494 | Let's create function interface with an unary operation. 495 | 496 | public interface UnaryEx { 497 | 498 | void action() throws Exception; 499 | } 500 | 501 | Now we need to create a method which catches exception and prints it to console. 502 | 503 | void ex(UnaryEx unary) { 504 | try { 505 | unary.action(); 506 | } catch (Exception e) { 507 | e.printStackTrace(); 508 | } 509 | } 510 | 511 | Now, we can rewrite about code without try-catch statement. 512 | 513 | /** 514 | * Read resource without exception. 515 | * @param name name. 516 | * @param resource resource. 517 | */ 518 | public void read(String name, ExtResource resource) { 519 | ex(() -> resource.read(name)); 520 | } 521 | 522 | /** 523 | * Write resource without exception. 524 | * @param value value. 525 | * @param resource resources. 526 | */ 527 | public void write(T value, ExtResource resource) { 528 | ex(() -> resource.write(value)); 529 | } 530 | 531 | Now you can see, we don't have copy-past code. 532 | 533 | Look at the sample with JDBC method. 534 | 535 | public void update(User model) { 536 | this.db( 537 | "update users set login=? where id=?", 538 | List.of(model.getLogin(), model.getId()), 539 | ps -> { 540 | ps.executeUpdate(); 541 | } 542 | ); 543 | } 544 | 545 | This code is clean and clear. 546 | 547 | ### 5. Check-and-act statements. 548 | 549 | Let's consider situation, when we try to find a element in collection then do somethings with it. 550 | 551 | public void addUser(User user) { 552 | if (!users.containsKey(user)) { 553 | users.putIfAbsent(user, new ArrayList<>()); 554 | } 555 | } 556 | 557 | public Account findByRequisite(String passport, String requisite) { 558 | User user = findByPassport(passport); 559 | if (user != null) { 560 | for (Account account : users.get(user)) { 561 | if (account.getRequisite().equals(requisite)) { 562 | return account; 563 | } 564 | } 565 | } 566 | return null; 567 | } 568 | 569 | In this situation, we must check that an element is not null then do an action. 570 | I can describe by a template 571 | 572 | Element el = find(...); 573 | if (el != null) { 574 | // do an action with the element. 575 | } 576 | 577 | Any condition statements decrease readability of you code. 578 | 579 | I offer replace such construction check-then-act to two party: check-throw and act. 580 | 581 | Let's look how I refactored it. 582 | 583 | Situation with void method. 584 | 585 | public void addUser(User user) { 586 | if (users.containsKey(user)) { 587 | return; 588 | } 589 | users.put(user, new ArrayList<>()); 590 | } 591 | 592 | Situation with not-void method. 593 | 594 | public Account findByRequisite(String passport, String requisite) { 595 | User user = findByPassport(passport); 596 | if (user == null) { 597 | return null; 598 | } 599 | Account rsl = null; 600 | for (Account account : users.get(user)) { 601 | if (account.getRequisite().equals(requisite)) { 602 | rsl = account; 603 | break; 604 | } 605 | } 606 | return rsl; 607 | } 608 | 609 | ### How to handle null? 610 | 611 | 1. return null. 612 | ```java 613 | public User findByPassport(String passport) { 614 | for (User user : users.keySet()) { 615 | if (user.getPassport().equals(passport)) { 616 | return user; 617 | } 618 | } 619 | return null; 620 | } 621 | ``` 622 | Simple and obvious. Not safe for client. 623 | 624 | 2. Throw checked-exception 625 | ```java 626 | public User findByPassportIfNullThrow(String passport) throws NotFoundUserException { 627 | for (User user : users.keySet()) { 628 | if (user.getPassport().equals(passport)) { 629 | return user; 630 | } 631 | } 632 | throw new NotFoundUserException("User with passport " + passport + "not found"); 633 | } 634 | ``` 635 | Simple and obvious. Overcoding in client side. 636 | ```java 637 | try { 638 | var user = bank.findByPassportIfNullThrow("123"); 639 | System.out.println(user.getUsername()); 640 | } catch (NotFoundUserException e) { 641 | System.out.println("User not found."); 642 | } 643 | ``` 644 | 4. Throw Runtime-exception. 645 | ```java 646 | public User findByPassportIfNullRuntime(String passport) { 647 | for (User user : users.keySet()) { 648 | if (user.getPassport().equals(passport)) { 649 | return user; 650 | } 651 | } 652 | throw new NullPointerException("User with passport " + passport + "not found"); 653 | } 654 | ``` 655 | Simple and obvious. Not safe for client. 656 | 657 | 5. Use Optional. 658 | ```java 659 | public Optional findByPassportIfNullOptional(String passport) { 660 | for (User user : users.keySet()) { 661 | if (user.getPassport().equals(passport)) { 662 | return Optional.of(user); 663 | } 664 | } 665 | return Optional.empty(); 666 | } 667 | ``` 668 | Best choice. Lack in performance. 669 | 670 | 6. Annotation @Nullable 671 | ```java 672 | @Nullable 673 | public User findByPassportCheckByCompile(String passport) { 674 | for (User user : users.keySet()) { 675 | if (user.getPassport().equals(passport)) { 676 | return user; 677 | } 678 | } 679 | return null; 680 | } 681 | ``` 682 | Elegant. Overconfig needed. 683 | 684 | --------------------------------------------------------------------------------