diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..4095d32cb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,340 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = true +ij_smart_tabs = false +ij_visual_guides = +ij_wrap_on_typing = false + +[*.java] +indent_size = 2 +max_line_length = 100 +ij_continuation_indent_size = 4 +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_deconstruction_list_components = true +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = true +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = true +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_align_types_in_multi_catch = true +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = normal +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = normal +ij_java_assignment_wrap = normal +ij_java_binary_operation_sign_on_next_line = false +ij_java_binary_operation_wrap = normal +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 1 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_add_space = false +ij_java_block_comment_at_first_column = true +ij_java_builder_methods = +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = normal +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_class_names_in_javadoc = 1 +ij_java_deconstruction_list_wrap = normal +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_entity_dd_prefix = +ij_java_entity_dd_suffix = EJB +ij_java_entity_eb_prefix = +ij_java_entity_eb_suffix = Bean +ij_java_entity_hi_prefix = +ij_java_entity_hi_suffix = Home +ij_java_entity_lhi_prefix = Local +ij_java_entity_lhi_suffix = Home +ij_java_entity_li_prefix = Local +ij_java_entity_li_suffix = +ij_java_entity_pk_class = java.lang.String +ij_java_entity_ri_prefix = +ij_java_entity_ri_suffix = +ij_java_entity_vo_prefix = +ij_java_entity_vo_suffix = VO +ij_java_enum_constants_wrap = normal +ij_java_extends_keyword_wrap = normal +ij_java_extends_list_wrap = normal +ij_java_field_annotation_wrap = split_into_lines +ij_java_field_name_prefix = +ij_java_field_name_suffix = +ij_java_filter_class_prefix = +ij_java_filter_class_suffix = +ij_java_filter_dd_prefix = +ij_java_filter_dd_suffix = +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = normal +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_if_brace_force = always +ij_java_imports_layout = $*,|,* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_builder_methods_indents = false +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_add_space_on_reformat = false +ij_java_line_comment_at_first_column = true +ij_java_listener_class_prefix = +ij_java_listener_class_suffix = +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_message_dd_prefix = +ij_java_message_dd_suffix = EJB +ij_java_message_eb_prefix = +ij_java_message_eb_suffix = Bean +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = normal +ij_java_method_parameters_new_line_after_left_paren = false +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = normal +ij_java_modifier_list_wrap = false +ij_java_multi_catch_types_wrap = normal +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_new_line_after_lparen_in_annotation = false +ij_java_new_line_after_lparen_in_deconstruction_pattern = true +ij_java_new_line_after_lparen_in_record_header = false +ij_java_packages_to_use_import_on_demand = +ij_java_parameter_annotation_wrap = normal +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = normal +ij_java_repeat_annotations = +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = normal +ij_java_rparen_on_new_line_in_annotation = false +ij_java_rparen_on_new_line_in_deconstruction_pattern = true +ij_java_rparen_on_new_line_in_record_header = false +ij_java_servlet_class_prefix = +ij_java_servlet_class_suffix = +ij_java_servlet_dd_prefix = +ij_java_servlet_dd_suffix = +ij_java_session_dd_prefix = +ij_java_session_dd_suffix = EJB +ij_java_session_eb_prefix = +ij_java_session_eb_suffix = Bean +ij_java_session_hi_prefix = +ij_java_session_hi_suffix = Home +ij_java_session_lhi_prefix = Local +ij_java_session_lhi_suffix = Home +ij_java_session_li_prefix = Local +ij_java_session_li_suffix = +ij_java_session_ri_prefix = +ij_java_session_ri_suffix = +ij_java_session_si_prefix = +ij_java_session_si_suffix = Service +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = true +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_deconstruction_list = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_annotation_eq = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_deconstruction_list = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_record_header = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = false +ij_java_ternary_operation_wrap = normal +ij_java_test_name_prefix = +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = normal +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = normal +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = false +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true diff --git a/abstract-document/README.md b/abstract-document/README.md index f5bc50d99..a60004751 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -3,9 +3,9 @@ title: Abstract Document category: Structural language: en tag: - - Abstraction - - Extensibility - - Decoupling + - Abstraction + - Extensibility + - Decoupling --- ## Intent @@ -43,43 +43,43 @@ map and any amount of child objects. ```java public interface Document { - Void put(String key, Object value); + Void put(String key, Object value); - Object get(String key); + Object get(String key); - Stream children(String key, Function, T> constructor); + Stream children(String key, Function, T> constructor); } public abstract class AbstractDocument implements Document { - private final Map properties; + private final Map properties; - protected AbstractDocument(Map properties) { - Objects.requireNonNull(properties, "properties map is required"); - this.properties = properties; - } + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } - @Override - public Void put(String key, Object value) { - properties.put(key, value); - return null; - } + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } - @Override - public Object get(String key) { - return properties.get(key); - } + @Override + public Object get(String key) { + return properties.get(key); + } - @Override - public Stream children(String key, Function, T> constructor) { - return Stream.ofNullable(get(key)) - .filter(Objects::nonNull) - .map(el -> (List>) el) - .findAny() - .stream() - .flatMap(Collection::stream) - .map(constructor); - } + @Override + public Stream children(String key, Function, T> constructor) { + return Stream.ofNullable(get(key)) + .filter(Objects::nonNull) + .map(el -> (List>) el) + .findAny() + .stream() + .flatMap(Collection::stream) + .map(constructor); + } ... } ``` @@ -90,35 +90,35 @@ static looking interface to our `Car` class. ```java public enum Property { - PARTS, TYPE, PRICE, MODEL + PARTS, TYPE, PRICE, MODEL } public interface HasType extends Document { - default Optional getType() { - return Optional.ofNullable((String) get(Property.TYPE.toString())); - } + default Optional getType() { + return Optional.ofNullable((String) get(Property.TYPE.toString())); + } } public interface HasPrice extends Document { - default Optional getPrice() { - return Optional.ofNullable((Number) get(Property.PRICE.toString())); - } + default Optional getPrice() { + return Optional.ofNullable((Number) get(Property.PRICE.toString())); + } } public interface HasModel extends Document { - default Optional getModel() { - return Optional.ofNullable((String) get(Property.MODEL.toString())); - } + default Optional getModel() { + return Optional.ofNullable((String) get(Property.MODEL.toString())); + } } public interface HasParts extends Document { - default Stream getParts() { - return children(Property.PARTS.toString(), Part::new); - } + default Stream getParts() { + return children(Property.PARTS.toString(), Part::new); + } } ``` @@ -127,9 +127,9 @@ Now we are ready to introduce the `Car`. ```java public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { - public Car(Map properties) { - super(properties); - } + public Car(Map properties) { + super(properties); + } } ``` @@ -138,32 +138,32 @@ And finally here's how we construct and use the `Car` in a full example. ```java LOGGER.info("Constructing parts and car"); - var wheelProperties=Map.of( - Property.TYPE.toString(),"wheel", - Property.MODEL.toString(),"15C", - Property.PRICE.toString(),100L); + var wheelProperties=Map.of( + Property.TYPE.toString(),"wheel", + Property.MODEL.toString(),"15C", + Property.PRICE.toString(),100L); - var doorProperties=Map.of( - Property.TYPE.toString(),"door", - Property.MODEL.toString(),"Lambo", - Property.PRICE.toString(),300L); + var doorProperties=Map.of( + Property.TYPE.toString(),"door", + Property.MODEL.toString(),"Lambo", + Property.PRICE.toString(),300L); - var carProperties=Map.of( - Property.MODEL.toString(),"300SL", - Property.PRICE.toString(),10000L, - Property.PARTS.toString(),List.of(wheelProperties,doorProperties)); + var carProperties=Map.of( + Property.MODEL.toString(),"300SL", + Property.PRICE.toString(),10000L, + Property.PARTS.toString(),List.of(wheelProperties,doorProperties)); - var car=new Car(carProperties); + var car=new Car(carProperties); - LOGGER.info("Here is our car:"); - LOGGER.info("-> model: {}",car.getModel().orElseThrow()); - LOGGER.info("-> price: {}",car.getPrice().orElseThrow()); - LOGGER.info("-> parts: "); - car.getParts().forEach(p->LOGGER.info("\t{}/{}/{}", - p.getType().orElse(null), - p.getModel().orElse(null), - p.getPrice().orElse(null)) - ); + LOGGER.info("Here is our car:"); + LOGGER.info("-> model: {}",car.getModel().orElseThrow()); + LOGGER.info("-> price: {}",car.getPrice().orElseThrow()); + LOGGER.info("-> parts: "); + car.getParts().forEach(p->LOGGER.info("\t{}/{}/{}", + p.getType().orElse(null), + p.getModel().orElse(null), + p.getPrice().orElse(null)) + ); // Constructing parts and car // Here is our car: diff --git a/abstract-factory/README.md b/abstract-factory/README.md index c7db990bd..01c7f7b44 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -3,9 +3,9 @@ title: Abstract Factory category: Creational language: en tag: - - Abstraction - - Decoupling - - Gang of Four + - Abstraction + - Decoupling + - Gang of Four --- ## Also known as @@ -43,43 +43,43 @@ kingdom. ```java public interface Castle { - String getDescription(); + String getDescription(); } public interface King { - String getDescription(); + String getDescription(); } public interface Army { - String getDescription(); + String getDescription(); } // Elven implementations -> public class ElfCastle implements Castle { - static final String DESCRIPTION = "This is the elven castle!"; + static final String DESCRIPTION = "This is the elven castle!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } public class ElfKing implements King { - static final String DESCRIPTION = "This is the elven king!"; + static final String DESCRIPTION = "This is the elven king!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } public class ElfArmy implements Army { - static final String DESCRIPTION = "This is the elven Army!"; + static final String DESCRIPTION = "This is the elven Army!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } // Orcish implementations similarly -> ... @@ -90,47 +90,47 @@ Then we have the abstraction and implementations for the kingdom factory. ```java public interface KingdomFactory { - Castle createCastle(); + Castle createCastle(); - King createKing(); + King createKing(); - Army createArmy(); + Army createArmy(); } public class ElfKingdomFactory implements KingdomFactory { - @Override - public Castle createCastle() { - return new ElfCastle(); - } + @Override + public Castle createCastle() { + return new ElfCastle(); + } - @Override - public King createKing() { - return new ElfKing(); - } + @Override + public King createKing() { + return new ElfKing(); + } - @Override - public Army createArmy() { - return new ElfArmy(); - } + @Override + public Army createArmy() { + return new ElfArmy(); + } } public class OrcKingdomFactory implements KingdomFactory { - @Override - public Castle createCastle() { - return new OrcCastle(); - } + @Override + public Castle createCastle() { + return new OrcCastle(); + } - @Override - public King createKing() { - return new OrcKing(); - } + @Override + public King createKing() { + return new OrcKing(); + } - @Override - public Army createArmy() { - return new OrcArmy(); - } + @Override + public Army createArmy() { + return new OrcArmy(); + } } ``` @@ -139,21 +139,21 @@ castle, king and army, etc. ```java var factory=new ElfKingdomFactory(); - var castle=factory.createCastle(); - var king=factory.createKing(); - var army=factory.createArmy(); + var castle=factory.createCastle(); + var king=factory.createKing(); + var army=factory.createArmy(); - castle.getDescription(); - king.getDescription(); - army.getDescription(); + castle.getDescription(); + king.getDescription(); + army.getDescription(); ``` Program output: ```java This is the elven castle! - This is the elven king! - This is the elven Army! + This is the elven king! + This is the elven Army! ``` Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, @@ -165,31 +165,31 @@ factory the client will ask for. ```java public static class FactoryMaker { - public enum KingdomType { - ELF, ORC - } + public enum KingdomType { + ELF, ORC + } - public static KingdomFactory makeFactory(KingdomType type) { - return switch (type) { - case ELF -> new ElfKingdomFactory(); - case ORC -> new OrcKingdomFactory(); - }; - } + public static KingdomFactory makeFactory(KingdomType type) { + return switch (type) { + case ELF -> new ElfKingdomFactory(); + case ORC -> new OrcKingdomFactory(); + }; + } } - public static void main(String[] args) { - var app = new App(); + public static void main(String[] args) { + var app = new App(); - LOGGER.info("Elf Kingdom"); - app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF)); - LOGGER.info(app.getArmy().getDescription()); - LOGGER.info(app.getCastle().getDescription()); - LOGGER.info(app.getKing().getDescription()); + LOGGER.info("Elf Kingdom"); + app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF)); + LOGGER.info(app.getArmy().getDescription()); + LOGGER.info(app.getCastle().getDescription()); + LOGGER.info(app.getKing().getDescription()); - LOGGER.info("Orc Kingdom"); - app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC)); - --similar use of the orc factory - } + LOGGER.info("Orc Kingdom"); + app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC)); + --similar use of the orc factory + } ``` ## Class diagram diff --git a/active-object/README.md b/active-object/README.md index 6dc70d806..9935481c2 100644 --- a/active-object/README.md +++ b/active-object/README.md @@ -3,7 +3,7 @@ title: Active Object category: Concurrency language: en tag: - - Performance + - Performance --- ## Intent @@ -29,57 +29,57 @@ itself, we can use the Active Object pattern. ```java public abstract class ActiveCreature { - private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); - private BlockingQueue requests; + private BlockingQueue requests; - private String name; + private String name; - private Thread thread; + private Thread thread; - public ActiveCreature(String name) { - this.name = name; - this.requests = new LinkedBlockingQueue(); - thread = new Thread(new Runnable() { - @Override - public void run() { - while (true) { - try { - requests.take().run(); - } catch (InterruptedException e) { - logger.error(e.getMessage()); - } + public ActiveCreature(String name) { + this.name = name; + this.requests = new LinkedBlockingQueue(); + thread = new Thread(new Runnable() { + @Override + public void run() { + while (true) { + try { + requests.take().run(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + } + } } - } + ); + thread.start(); } - ); - thread.start(); - } - public void eat() throws InterruptedException { - requests.put(new Runnable() { - @Override - public void run() { - logger.info("{} is eating!", name()); - logger.info("{} has finished eating!", name()); - } - } - ); - } + public void eat() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} is eating!", name()); + logger.info("{} has finished eating!", name()); + } + } + ); + } - public void roam() throws InterruptedException { - requests.put(new Runnable() { - @Override - public void run() { - logger.info("{} has started to roam the wastelands.", name()); - } - } - ); - } + public void roam() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} has started to roam the wastelands.", name()); + } + } + ); + } - public String name() { - return this.name; - } + public String name() { + return this.name; + } } ``` @@ -91,9 +91,9 @@ For example, the Orc class: ```java public class Orc extends ActiveCreature { - public Orc(String name) { - super(name); - } + public Orc(String name) { + super(name); + } } ``` @@ -103,25 +103,25 @@ thread of control: ```java public static void main(String[]args){ - var app=new App(); - app.run(); - } + var app=new App(); + app.run(); + } @Override public void run(){ - ActiveCreature creature; - try{ - for(int i=0;i Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer -> them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to -> your computer. In this case card reader is an adapter. -> Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it -> needs to use a power adapter that makes it compatible with the two-pronged outlets. -> Yet another example would be a translator translating words spoken by one person to another +> them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card +> to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a +> three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible +> with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to +> another In plain words @@ -46,14 +46,14 @@ First, we have interfaces `RowingBoat` and `FishingBoat` ```java public interface RowingBoat { - void row(); + void row(); } @Slf4j public class FishingBoat { - public void sail() { - LOGGER.info("The fishing boat is sailing"); - } + public void sail() { + LOGGER.info("The fishing boat is sailing"); + } } ``` @@ -62,16 +62,16 @@ And captain expects an implementation of `RowingBoat` interface to be able to mo ```java public class Captain { - private final RowingBoat rowingBoat; + private final RowingBoat rowingBoat; - // default constructor and setter for rowingBoat - public Captain(RowingBoat rowingBoat) { - this.rowingBoat = rowingBoat; - } + // default constructor and setter for rowingBoat + public Captain(RowingBoat rowingBoat) { + this.rowingBoat = rowingBoat; + } - public void row() { - rowingBoat.row(); - } + public void row() { + rowingBoat.row(); + } } ``` @@ -83,16 +83,16 @@ to create an adapter that allows the captain to operate the fishing boat with hi @Slf4j public class FishingBoatAdapter implements RowingBoat { - private final FishingBoat boat; + private final FishingBoat boat; - public FishingBoatAdapter() { - boat = new FishingBoat(); - } + public FishingBoatAdapter() { + boat = new FishingBoat(); + } - @Override - public void row() { - boat.sail(); - } + @Override + public void row() { + boat.sail(); + } } ``` @@ -100,7 +100,7 @@ And now the `Captain` can use the `FishingBoat` to escape the pirates. ```java var captain=new Captain(new FishingBoatAdapter()); - captain.row(); + captain.row(); ``` ## Class diagram diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md index 2673862f2..a32005cef 100644 --- a/aggregator-microservices/README.md +++ b/aggregator-microservices/README.md @@ -3,10 +3,10 @@ title: Aggregator Microservices category: Architectural language: en tag: - - API design - - Cloud distributed - - Decoupling - - Microservices + - API design + - Cloud distributed + - Decoupling + - Microservices --- ## Intent @@ -37,9 +37,9 @@ Let's start from the data model. Here's our `Product`. ```java public class Product { - private String title; - private int productInventories; - // Getters and setters omitted for brevity -> + private String title; + private int productInventories; + // Getters and setters omitted for brevity -> ... } ``` @@ -52,27 +52,27 @@ Next we can introduce our `Aggregator` microservice. It contains clients `Produc @RestController public class Aggregator { - @Resource - private ProductInformationClient informationClient; + @Resource + private ProductInformationClient informationClient; - @Resource - private ProductInventoryClient inventoryClient; + @Resource + private ProductInventoryClient inventoryClient; - @RequestMapping(path = "/product", method = RequestMethod.GET) - public Product getProduct() { + @RequestMapping(path = "/product", method = RequestMethod.GET) + public Product getProduct() { - var product = new Product(); - var productTitle = informationClient.getProductTitle(); - var productInventory = inventoryClient.getProductInventories(); + var product = new Product(); + var productTitle = informationClient.getProductTitle(); + var productInventory = inventoryClient.getProductInventories(); - //Fallback to error message - product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed")); + //Fallback to error message + product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed")); - //Fallback to default error inventory - product.setProductInventories(requireNonNullElse(productInventory, -1)); + //Fallback to default error inventory + product.setProductInventories(requireNonNullElse(productInventory, -1)); - return product; - } + return product; + } } ``` @@ -83,10 +83,10 @@ inventory counts. @RestController public class InformationController { - @RequestMapping(value = "/information", method = RequestMethod.GET) - public String getProductTitle() { - return "The Product Title."; - } + @RequestMapping(value = "/information", method = RequestMethod.GET) + public String getProductTitle() { + return "The Product Title."; + } } ``` diff --git a/ambassador/README.md b/ambassador/README.md index 5bd535f07..172285dcd 100644 --- a/ambassador/README.md +++ b/ambassador/README.md @@ -3,8 +3,8 @@ title: Ambassador category: Structural language: en tag: - - Decoupling - - Cloud distributed + - Decoupling + - Cloud distributed --- ## Intent @@ -43,7 +43,7 @@ by the remote service as well as the ambassador service: ```java interface RemoteServiceInterface { - long doRemoteFunction(int value) throws Exception; + long doRemoteFunction(int value) throws Exception; } ``` @@ -53,30 +53,30 @@ A remote services represented as a singleton. @Slf4j public class RemoteService implements RemoteServiceInterface { - private static RemoteService service = null; + private static RemoteService service = null; - static synchronized RemoteService getRemoteService() { - if (service == null) { - service = new RemoteService(); - } - return service; - } - - private RemoteService() { - } - - @Override - public long doRemoteFunction(int value) { - long waitTime = (long) Math.floor(Math.random() * 1000); - - try { - sleep(waitTime); - } catch (InterruptedException e) { - LOGGER.error("Thread sleep interrupted", e); + static synchronized RemoteService getRemoteService() { + if (service == null) { + service = new RemoteService(); + } + return service; } - return waitTime >= 200 ? value * 10 : -1; - } + private RemoteService() { + } + + @Override + public long doRemoteFunction(int value) { + long waitTime = (long) Math.floor(Math.random() * 1000); + + try { + sleep(waitTime); + } catch (InterruptedException e) { + LOGGER.error("Thread sleep interrupted", e); + } + + return waitTime >= 200 ? value * 10 : -1; + } } ``` @@ -86,49 +86,49 @@ A service ambassador adding additional features such as logging, latency checks @Slf4j public class ServiceAmbassador implements RemoteServiceInterface { - private static final int RETRIES = 3; - private static final int DELAY_MS = 3000; + private static final int RETRIES = 3; + private static final int DELAY_MS = 3000; - ServiceAmbassador() { - } - - @Override - public long doRemoteFunction(int value) { - return safeCall(value); - } - - private long checkLatency(int value) { - var startTime = System.currentTimeMillis(); - var result = RemoteService.getRemoteService().doRemoteFunction(value); - var timeTaken = System.currentTimeMillis() - startTime; - - LOGGER.info("Time taken (ms): " + timeTaken); - return result; - } - - private long safeCall(int value) { - var retries = 0; - var result = (long) FAILURE; - - for (int i = 0; i < RETRIES; i++) { - if (retries >= RETRIES) { - return FAILURE; - } - - if ((result = checkLatency(value)) == FAILURE) { - LOGGER.info("Failed to reach remote: (" + (i + 1) + ")"); - retries++; - try { - sleep(DELAY_MS); - } catch (InterruptedException e) { - LOGGER.error("Thread sleep state interrupted", e); - } - } else { - break; - } + ServiceAmbassador() { + } + + @Override + public long doRemoteFunction(int value) { + return safeCall(value); + } + + private long checkLatency(int value) { + var startTime = System.currentTimeMillis(); + var result = RemoteService.getRemoteService().doRemoteFunction(value); + var timeTaken = System.currentTimeMillis() - startTime; + + LOGGER.info("Time taken (ms): " + timeTaken); + return result; + } + + private long safeCall(int value) { + var retries = 0; + var result = (long) FAILURE; + + for (int i = 0; i < RETRIES; i++) { + if (retries >= RETRIES) { + return FAILURE; + } + + if ((result = checkLatency(value)) == FAILURE) { + LOGGER.info("Failed to reach remote: (" + (i + 1) + ")"); + retries++; + try { + sleep(DELAY_MS); + } catch (InterruptedException e) { + LOGGER.error("Thread sleep state interrupted", e); + } + } else { + break; + } + } + return result; } - return result; - } } ``` @@ -138,13 +138,13 @@ A client has a local service ambassador used to interact with the remote service @Slf4j public class Client { - private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador(); + private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador(); - long useService(int value) { - var result = serviceAmbassador.doRemoteFunction(value); - LOGGER.info("Service result: " + result); - return result; - } + long useService(int value) { + var result = serviceAmbassador.doRemoteFunction(value); + LOGGER.info("Service result: " + result); + return result; + } } ``` @@ -152,12 +152,12 @@ Here are two clients using the service. ```java public class App { - public static void main(String[] args) { - var host1 = new Client(); - var host2 = new Client(); - host1.useService(12); - host2.useService(73); - } + public static void main(String[] args) { + var host1 = new Client(); + var host2 = new Client(); + host1.useService(12); + host2.useService(73); + } } ``` @@ -165,14 +165,14 @@ Here's the output for running the example: ```java Time taken(ms):111 - Service result:120 - Time taken(ms):931 - Failed to reach remote:(1) - Time taken(ms):665 - Failed to reach remote:(2) - Time taken(ms):538 - Failed to reach remote:(3) - Service result:-1 + Service result:120 + Time taken(ms):931 + Failed to reach remote:(1) + Time taken(ms):665 + Failed to reach remote:(2) + Time taken(ms):538 + Failed to reach remote:(3) + Service result:-1 ``` ## Class diagram diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md index 015dd0c45..3d44d7133 100644 --- a/anti-corruption-layer/README.md +++ b/anti-corruption-layer/README.md @@ -3,9 +3,9 @@ title: Anti-corruption layer category: Integration language: en tag: - - Architecture - - Decoupling - - Isolation + - Architecture + - Decoupling + - Isolation --- ## Also known as @@ -67,11 +67,11 @@ systems. ```java public class LegacyOrder { - private String id; - private String customer; - private String item; - private String qty; - private String price; + private String id; + private String customer; + private String item; + private String qty; + private String price; } ``` @@ -79,22 +79,22 @@ public class LegacyOrder { ```java public class ModernOrder { - private String id; - private Customer customer; + private String id; + private Customer customer; - private Shipment shipment; + private Shipment shipment; - private String extra; + private String extra; } public class Customer { - private String address; + private String address; } public class Shipment { - private String item; - private String qty; - private String price; + private String item; + private String qty; + private String price; } ``` @@ -103,19 +103,19 @@ public class Shipment { ```java public class AntiCorruptionLayer { - @Autowired - private ModernShop modernShop; + @Autowired + private ModernShop modernShop; - @Autowired - private LegacyShop legacyShop; + @Autowired + private LegacyShop legacyShop; - public Optional findOrderInModernSystem(String id) { - return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/); - } + public Optional findOrderInModernSystem(String id) { + return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/); + } - public Optional findOrderInLegacySystem(String id) { - return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/); - } + public Optional findOrderInLegacySystem(String id) { + return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/); + } } ``` @@ -128,21 +128,21 @@ from the `Modern` system. ```java public class LegacyShop { - @Autowired - private AntiCorruptionLayer acl; + @Autowired + private AntiCorruptionLayer acl; - public void placeOrder(LegacyOrder legacyOrder) throws ShopException { + public void placeOrder(LegacyOrder legacyOrder) throws ShopException { - String id = legacyOrder.getId(); + String id = legacyOrder.getId(); - Optional orderInModernSystem = acl.findOrderInModernSystem(id); + Optional orderInModernSystem = acl.findOrderInModernSystem(id); - if (orderInModernSystem.isPresent()) { - // order is already in the modern system - } else { - // place order in the current system + if (orderInModernSystem.isPresent()) { + // order is already in the modern system + } else { + // place order in the current system + } } - } } ``` diff --git a/api-gateway/README.md b/api-gateway/README.md index 9c60d54f6..edc4ca98c 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -3,10 +3,10 @@ title: API Gateway category: Architectural language: en tag: - - API design - - Cloud distributed - - Decoupling - - Microservices + - API design + - Cloud distributed + - Decoupling + - Microservices --- ## Intent @@ -68,27 +68,27 @@ Here's the Image microservice implementation. ```java public interface ImageClient { - String getImagePath(); + String getImagePath(); } public class ImageClientImpl implements ImageClient { - @Override - public String getImagePath() { - var httpClient = HttpClient.newHttpClient(); - var httpGet = HttpRequest.newBuilder() - .GET() - .uri(URI.create("http://localhost:50005/image-path")) - .build(); + @Override + public String getImagePath() { + var httpClient = HttpClient.newHttpClient(); + var httpGet = HttpRequest.newBuilder() + .GET() + .uri(URI.create("http://localhost:50005/image-path")) + .build(); - try { - var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); - return httpResponse.body(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); + try { + var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); + return httpResponse.body(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return null; } - - return null; - } } ``` @@ -96,28 +96,28 @@ Here's the Price microservice implementation. ```java public interface PriceClient { - String getPrice(); + String getPrice(); } public class PriceClientImpl implements PriceClient { - @Override - public String getPrice() { - var httpClient = HttpClient.newHttpClient(); - var httpGet = HttpRequest.newBuilder() - .GET() - .uri(URI.create("http://localhost:50006/price")) - .build(); + @Override + public String getPrice() { + var httpClient = HttpClient.newHttpClient(); + var httpGet = HttpRequest.newBuilder() + .GET() + .uri(URI.create("http://localhost:50006/price")) + .build(); - try { - var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); - return httpResponse.body(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); + try { + var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString()); + return httpResponse.body(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return null; } - - return null; - } } ``` @@ -126,26 +126,26 @@ Here we can see how API Gateway maps the requests to the microservices. ```java public class ApiGateway { - @Resource - private ImageClient imageClient; + @Resource + private ImageClient imageClient; - @Resource - private PriceClient priceClient; + @Resource + private PriceClient priceClient; - @RequestMapping(path = "/desktop", method = RequestMethod.GET) - public DesktopProduct getProductDesktop() { - var desktopProduct = new DesktopProduct(); - desktopProduct.setImagePath(imageClient.getImagePath()); - desktopProduct.setPrice(priceClient.getPrice()); - return desktopProduct; - } + @RequestMapping(path = "/desktop", method = RequestMethod.GET) + public DesktopProduct getProductDesktop() { + var desktopProduct = new DesktopProduct(); + desktopProduct.setImagePath(imageClient.getImagePath()); + desktopProduct.setPrice(priceClient.getPrice()); + return desktopProduct; + } - @RequestMapping(path = "/mobile", method = RequestMethod.GET) - public MobileProduct getProductMobile() { - var mobileProduct = new MobileProduct(); - mobileProduct.setPrice(priceClient.getPrice()); - return mobileProduct; - } + @RequestMapping(path = "/mobile", method = RequestMethod.GET) + public MobileProduct getProductMobile() { + var mobileProduct = new MobileProduct(); + mobileProduct.setPrice(priceClient.getPrice()); + return mobileProduct; + } } ``` diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md index ee2e72921..cc0c308ac 100644 --- a/arrange-act-assert/README.md +++ b/arrange-act-assert/README.md @@ -3,8 +3,8 @@ title: Arrange/Act/Assert category: Testing language: en tag: - - Idiom - - Testing + - Idiom + - Testing --- ## Also known as @@ -51,28 +51,28 @@ Let's first introduce our `Cash` class to be unit tested. ```java public class Cash { - private int amount; + private int amount; - Cash(int amount) { - this.amount = amount; - } - - void plus(int addend) { - amount += addend; - } - - boolean minus(int subtrahend) { - if (amount >= subtrahend) { - amount -= subtrahend; - return true; - } else { - return false; + Cash(int amount) { + this.amount = amount; } - } - int count() { - return amount; - } + void plus(int addend) { + amount += addend; + } + + boolean minus(int subtrahend) { + if (amount >= subtrahend) { + amount -= subtrahend; + return true; + } else { + return false; + } + } + + int count() { + return amount; + } } ``` @@ -82,49 +82,49 @@ separated steps for each unit test. ```java class CashAAATest { - @Test - void testPlus() { - //Arrange - var cash = new Cash(3); - //Act - cash.plus(4); - //Assert - assertEquals(7, cash.count()); - } + @Test + void testPlus() { + //Arrange + var cash = new Cash(3); + //Act + cash.plus(4); + //Assert + assertEquals(7, cash.count()); + } - @Test - void testMinus() { - //Arrange - var cash = new Cash(8); - //Act - var result = cash.minus(5); - //Assert - assertTrue(result); - assertEquals(3, cash.count()); - } + @Test + void testMinus() { + //Arrange + var cash = new Cash(8); + //Act + var result = cash.minus(5); + //Assert + assertTrue(result); + assertEquals(3, cash.count()); + } - @Test - void testInsufficientMinus() { - //Arrange - var cash = new Cash(1); - //Act - var result = cash.minus(6); - //Assert - assertFalse(result); - assertEquals(1, cash.count()); - } + @Test + void testInsufficientMinus() { + //Arrange + var cash = new Cash(1); + //Act + var result = cash.minus(6); + //Assert + assertFalse(result); + assertEquals(1, cash.count()); + } - @Test - void testUpdate() { - //Arrange - var cash = new Cash(5); - //Act - cash.plus(6); - var result = cash.minus(3); - //Assert - assertTrue(result); - assertEquals(8, cash.count()); - } + @Test + void testUpdate() { + //Arrange + var cash = new Cash(5); + //Act + cash.plus(6); + var result = cash.minus(3); + //Assert + assertTrue(result); + assertEquals(8, cash.count()); + } } ``` diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 60f4c7110..deb573c7b 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -3,9 +3,9 @@ title: Async Method Invocation category: Concurrency language: en tag: - - Asynchronous - - Reactive - - Scalability + - Asynchronous + - Reactive + - Scalability --- ## Intent @@ -48,29 +48,29 @@ manages the execution of the async tasks. ```java public interface AsyncResult { - boolean isCompleted(); + boolean isCompleted(); - T getValue() throws ExecutionException; + T getValue() throws ExecutionException; - void await() throws InterruptedException; + void await() throws InterruptedException; } ``` ```java public interface AsyncCallback { - void onComplete(T value); + void onComplete(T value); - void onError(Exception ex); + void onError(Exception ex); } ``` ```java public interface AsyncExecutor { - AsyncResult startProcess(Callable task); + AsyncResult startProcess(Callable task); - AsyncResult startProcess(Callable task, AsyncCallback callback); + AsyncResult startProcess(Callable task, AsyncCallback callback); - T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; + T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; } ``` @@ -80,35 +80,35 @@ next. ```java public class ThreadAsyncExecutor implements AsyncExecutor { - @Override - public AsyncResult startProcess(Callable task) { - return startProcess(task, null); - } - - @Override - public AsyncResult startProcess(Callable task, AsyncCallback callback) { - var result = new CompletableResult<>(callback); - new Thread( - () -> { - try { - result.setValue(task.call()); - } catch (Exception ex) { - result.setException(ex); - } - }, - "executor-" + idx.incrementAndGet()) - .start(); - return result; - } - - @Override - public T endProcess(AsyncResult asyncResult) - throws ExecutionException, InterruptedException { - if (!asyncResult.isCompleted()) { - asyncResult.await(); + @Override + public AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } + + @Override + public AsyncResult startProcess(Callable task, AsyncCallback callback) { + var result = new CompletableResult<>(callback); + new Thread( + () -> { + try { + result.setValue(task.call()); + } catch (Exception ex) { + result.setException(ex); + } + }, + "executor-" + idx.incrementAndGet()) + .start(); + return result; + } + + @Override + public T endProcess(AsyncResult asyncResult) + throws ExecutionException, InterruptedException { + if (!asyncResult.isCompleted()) { + asyncResult.await(); + } + return asyncResult.getValue(); } - return asyncResult.getValue(); - } } ``` @@ -116,8 +116,8 @@ Then we are ready to launch some rockets to see how everything works together. ```java public static void main(String[]args)throws Exception{ - // construct a new executor that will run async tasks - var executor=new ThreadAsyncExecutor(); + // construct a new executor that will run async tasks + var executor=new ThreadAsyncExecutor(); // start few async tasks with varying processing times, two last with callback handlers final var asyncResult1=executor.startProcess(lazyval(10,500)); @@ -125,40 +125,40 @@ final var asyncResult2=executor.startProcess(lazyval("test",300)); final var asyncResult3=executor.startProcess(lazyval(50L,700)); final var asyncResult4=executor.startProcess(lazyval(20,400),callback("Deploying lunar rover")); final var asyncResult5= - executor.startProcess(lazyval("callback",600),callback("Deploying lunar rover")); + executor.startProcess(lazyval("callback",600),callback("Deploying lunar rover")); - // emulate processing in the current thread while async tasks are running in their own threads - Thread.sleep(350); // Oh boy, we are working hard here - log("Mission command is sipping coffee"); + // emulate processing in the current thread while async tasks are running in their own threads + Thread.sleep(350); // Oh boy, we are working hard here + log("Mission command is sipping coffee"); // wait for completion of the tasks final var result1=executor.endProcess(asyncResult1); final var result2=executor.endProcess(asyncResult2); final var result3=executor.endProcess(asyncResult3); - asyncResult4.await(); - asyncResult5.await(); + asyncResult4.await(); + asyncResult5.await(); - // log the results of the tasks, callbacks log immediately when complete - log("Space rocket <"+result1+"> launch complete"); - log("Space rocket <"+result2+"> launch complete"); - log("Space rocket <"+result3+"> launch complete"); - } + // log the results of the tasks, callbacks log immediately when complete + log("Space rocket <"+result1+"> launch complete"); + log("Space rocket <"+result2+"> launch complete"); + log("Space rocket <"+result3+"> launch complete"); + } ``` Here's the program console output. ```java 21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully - 21:47:08.269[main]INFO com.iluwatar.async.method.invocation.App-Mission command is sipping coffee - 21:47:08.318[executor-4]INFO com.iluwatar.async.method.invocation.App-Space rocket<20>launched successfully - 21:47:08.335[executor-4]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<20> - 21:47:08.414[executor-1]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launched successfully - 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully - 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover + 21:47:08.269[main]INFO com.iluwatar.async.method.invocation.App-Mission command is sipping coffee + 21:47:08.318[executor-4]INFO com.iluwatar.async.method.invocation.App-Space rocket<20>launched successfully + 21:47:08.335[executor-4]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<20> + 21:47:08.414[executor-1]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launched successfully + 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully + 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover 21:47:08.616[executor-3]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launched successfully - 21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launch complete - 21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket launch complete - 21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete + 21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launch complete + 21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket launch complete + 21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete ``` # Class diagram diff --git a/balking/README.md b/balking/README.md index 0fa525396..f40c45d7d 100644 --- a/balking/README.md +++ b/balking/README.md @@ -3,7 +3,7 @@ title: Balking category: Concurrency language: en tag: - - Decoupling + - Decoupling --- ## Intent @@ -44,36 +44,36 @@ Here are the relevant parts of the `WashingMachine` class. @Slf4j public class WashingMachine { - private final DelayProvider delayProvider; - private WashingMachineState washingMachineState; + private final DelayProvider delayProvider; + private WashingMachineState washingMachineState; - public WashingMachine(DelayProvider delayProvider) { - this.delayProvider = delayProvider; - this.washingMachineState = WashingMachineState.ENABLED; - } - - public WashingMachineState getWashingMachineState() { - return washingMachineState; - } - - public void wash() { - synchronized (this) { - var machineState = getWashingMachineState(); - LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState); - if (this.washingMachineState == WashingMachineState.WASHING) { - LOGGER.error("Cannot wash if the machine has been already washing!"); - return; - } - this.washingMachineState = WashingMachineState.WASHING; + public WashingMachine(DelayProvider delayProvider) { + this.delayProvider = delayProvider; + this.washingMachineState = WashingMachineState.ENABLED; } - LOGGER.info("{}: Doing the washing", Thread.currentThread().getName()); - this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing); - } - public synchronized void endOfWashing() { - washingMachineState = WashingMachineState.ENABLED; - LOGGER.info("{}: Washing completed.", Thread.currentThread().getId()); - } + public WashingMachineState getWashingMachineState() { + return washingMachineState; + } + + public void wash() { + synchronized (this) { + var machineState = getWashingMachineState(); + LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState); + if (this.washingMachineState == WashingMachineState.WASHING) { + LOGGER.error("Cannot wash if the machine has been already washing!"); + return; + } + this.washingMachineState = WashingMachineState.WASHING; + } + LOGGER.info("{}: Doing the washing", Thread.currentThread().getName()); + this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing); + } + + public synchronized void endOfWashing() { + washingMachineState = WashingMachineState.ENABLED; + LOGGER.info("{}: Washing completed.", Thread.currentThread().getId()); + } } ``` @@ -81,7 +81,7 @@ Here's the simple `DelayProvider` interface used by the `WashingMachine`. ```java public interface DelayProvider { - void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task); + void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task); } ``` @@ -90,18 +90,18 @@ Now we introduce the application using the `WashingMachine`. ```java public static void main(String...args){ final var washingMachine=new WashingMachine(); - var executorService=Executors.newFixedThreadPool(3); - for(int i=0;i< 3;i++){ - executorService.execute(washingMachine::wash); - } - executorService.shutdown(); - try{ - executorService.awaitTermination(10,TimeUnit.SECONDS); - }catch(InterruptedException ie){ - LOGGER.error("ERROR: Waiting on executor service shutdown!"); - Thread.currentThread().interrupt(); - } - } + var executorService=Executors.newFixedThreadPool(3); + for(int i=0;i< 3;i++){ + executorService.execute(washingMachine::wash); + } + executorService.shutdown(); + try{ + executorService.awaitTermination(10,TimeUnit.SECONDS); + }catch(InterruptedException ie){ + LOGGER.error("ERROR: Waiting on executor service shutdown!"); + Thread.currentThread().interrupt(); + } + } ``` Here is the console output of the program. diff --git a/bridge/README.md b/bridge/README.md index 894688765..a5fac3a37 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -3,9 +3,9 @@ title: Bridge category: Structural language: en tag: - - Decoupling - - Extensibility - - Gang of Four + - Decoupling + - Extensibility + - Gang of Four --- ## Also known as @@ -21,8 +21,8 @@ Decouple an abstraction from its implementation so that the two can vary indepen Real-world example > Consider you have a weapon with different enchantments, and you are supposed to allow mixing different weapons with -> different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments or -> would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the +> different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments +> or would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the > second. In Plain Words @@ -41,77 +41,77 @@ Translating our weapon example from above. Here we have the `Weapon` hierarchy: ```java public interface Weapon { - void wield(); + void wield(); - void swing(); + void swing(); - void unwield(); + void unwield(); - Enchantment getEnchantment(); + Enchantment getEnchantment(); } public class Sword implements Weapon { - private final Enchantment enchantment; + private final Enchantment enchantment; - public Sword(Enchantment enchantment) { - this.enchantment = enchantment; - } + public Sword(Enchantment enchantment) { + this.enchantment = enchantment; + } - @Override - public void wield() { - LOGGER.info("The sword is wielded."); - enchantment.onActivate(); - } + @Override + public void wield() { + LOGGER.info("The sword is wielded."); + enchantment.onActivate(); + } - @Override - public void swing() { - LOGGER.info("The sword is swung."); - enchantment.apply(); - } + @Override + public void swing() { + LOGGER.info("The sword is swung."); + enchantment.apply(); + } - @Override - public void unwield() { - LOGGER.info("The sword is unwielded."); - enchantment.onDeactivate(); - } + @Override + public void unwield() { + LOGGER.info("The sword is unwielded."); + enchantment.onDeactivate(); + } - @Override - public Enchantment getEnchantment() { - return enchantment; - } + @Override + public Enchantment getEnchantment() { + return enchantment; + } } public class Hammer implements Weapon { - private final Enchantment enchantment; + private final Enchantment enchantment; - public Hammer(Enchantment enchantment) { - this.enchantment = enchantment; - } + public Hammer(Enchantment enchantment) { + this.enchantment = enchantment; + } - @Override - public void wield() { - LOGGER.info("The hammer is wielded."); - enchantment.onActivate(); - } + @Override + public void wield() { + LOGGER.info("The hammer is wielded."); + enchantment.onActivate(); + } - @Override - public void swing() { - LOGGER.info("The hammer is swung."); - enchantment.apply(); - } + @Override + public void swing() { + LOGGER.info("The hammer is swung."); + enchantment.apply(); + } - @Override - public void unwield() { - LOGGER.info("The hammer is unwielded."); - enchantment.onDeactivate(); - } + @Override + public void unwield() { + LOGGER.info("The hammer is unwielded."); + enchantment.onDeactivate(); + } - @Override - public Enchantment getEnchantment() { - return enchantment; - } + @Override + public Enchantment getEnchantment() { + return enchantment; + } } ``` @@ -119,47 +119,47 @@ Here's the separate enchantment hierarchy: ```java public interface Enchantment { - void onActivate(); + void onActivate(); - void apply(); + void apply(); - void onDeactivate(); + void onDeactivate(); } public class FlyingEnchantment implements Enchantment { - @Override - public void onActivate() { - LOGGER.info("The item begins to glow faintly."); - } + @Override + public void onActivate() { + LOGGER.info("The item begins to glow faintly."); + } - @Override - public void apply() { - LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand."); - } + @Override + public void apply() { + LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand."); + } - @Override - public void onDeactivate() { - LOGGER.info("The item's glow fades."); - } + @Override + public void onDeactivate() { + LOGGER.info("The item's glow fades."); + } } public class SoulEatingEnchantment implements Enchantment { - @Override - public void onActivate() { - LOGGER.info("The item spreads bloodlust."); - } + @Override + public void onActivate() { + LOGGER.info("The item spreads bloodlust."); + } - @Override - public void apply() { - LOGGER.info("The item eats the soul of enemies."); - } + @Override + public void apply() { + LOGGER.info("The item eats the soul of enemies."); + } - @Override - public void onDeactivate() { - LOGGER.info("Bloodlust slowly disappears."); - } + @Override + public void onDeactivate() { + LOGGER.info("Bloodlust slowly disappears."); + } } ``` @@ -167,16 +167,16 @@ Here are both hierarchies in action: ```java LOGGER.info("The knight receives an enchanted sword."); - var enchantedSword=new Sword(new SoulEatingEnchantment()); - enchantedSword.wield(); - enchantedSword.swing(); - enchantedSword.unwield(); + var enchantedSword=new Sword(new SoulEatingEnchantment()); + enchantedSword.wield(); + enchantedSword.swing(); + enchantedSword.unwield(); - LOGGER.info("The valkyrie receives an enchanted hammer."); - var hammer=new Hammer(new FlyingEnchantment()); - hammer.wield(); - hammer.swing(); - hammer.unwield(); + LOGGER.info("The valkyrie receives an enchanted hammer."); + var hammer=new Hammer(new FlyingEnchantment()); + hammer.wield(); + hammer.swing(); + hammer.unwield(); ``` Here's the console output. diff --git a/builder/README.md b/builder/README.md index c9f63f471..b42b0ede7 100644 --- a/builder/README.md +++ b/builder/README.md @@ -3,7 +3,7 @@ title: Builder category: Creational language: en tag: - - Gang of Four + - Gang of Four --- ## Intent @@ -34,7 +34,7 @@ all seen a constructor like below: ```java public Hero(Profession profession,String name,HairType hairType,HairColor hairColor,Armor armor,Weapon weapon){ - } + } ``` As you can see the number of constructor parameters can quickly get out of hand, and it may become difficult to @@ -47,21 +47,21 @@ The sane alternative is to use the Builder pattern. First of all, we have our he ```java public final class Hero { - private final Profession profession; - private final String name; - private final HairType hairType; - private final HairColor hairColor; - private final Armor armor; - private final Weapon weapon; + private final Profession profession; + private final String name; + private final HairType hairType; + private final HairColor hairColor; + private final Armor armor; + private final Weapon weapon; - private Hero(Builder builder) { - this.profession = builder.profession; - this.name = builder.name; - this.hairColor = builder.hairColor; - this.hairType = builder.hairType; - this.weapon = builder.weapon; - this.armor = builder.armor; - } + private Hero(Builder builder) { + this.profession = builder.profession; + this.name = builder.name; + this.hairColor = builder.hairColor; + this.hairType = builder.hairType; + this.weapon = builder.weapon; + this.armor = builder.armor; + } } ``` @@ -69,44 +69,44 @@ Then we have the builder: ```java public static class Builder { - private final Profession profession; - private final String name; - private HairType hairType; - private HairColor hairColor; - private Armor armor; - private Weapon weapon; + private final Profession profession; + private final String name; + private HairType hairType; + private HairColor hairColor; + private Armor armor; + private Weapon weapon; - public Builder(Profession profession, String name) { - if (profession == null || name == null) { - throw new IllegalArgumentException("profession and name can not be null"); + public Builder(Profession profession, String name) { + if (profession == null || name == null) { + throw new IllegalArgumentException("profession and name can not be null"); + } + this.profession = profession; + this.name = name; } - this.profession = profession; - this.name = name; - } - public Builder withHairType(HairType hairType) { - this.hairType = hairType; - return this; - } + public Builder withHairType(HairType hairType) { + this.hairType = hairType; + return this; + } - public Builder withHairColor(HairColor hairColor) { - this.hairColor = hairColor; - return this; - } + public Builder withHairColor(HairColor hairColor) { + this.hairColor = hairColor; + return this; + } - public Builder withArmor(Armor armor) { - this.armor = armor; - return this; - } + public Builder withArmor(Armor armor) { + this.armor = armor; + return this; + } - public Builder withWeapon(Weapon weapon) { - this.weapon = weapon; - return this; - } + public Builder withWeapon(Weapon weapon) { + this.weapon = weapon; + return this; + } - public Hero build() { - return new Hero(this); - } + public Hero build() { + return new Hero(this); + } } ``` diff --git a/business-delegate/README.md b/business-delegate/README.md index e35546dcb..086175d7f 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -3,7 +3,7 @@ title: Business Delegate category: Structural language: en tag: - - Decoupling + - Decoupling --- ## Intent @@ -41,23 +41,23 @@ First, we have an abstraction for video streaming services and a couple of imple ```java public interface VideoStreamingService { - void doProcessing(); + void doProcessing(); } @Slf4j public class NetflixService implements VideoStreamingService { - @Override - public void doProcessing() { - LOGGER.info("NetflixService is now processing"); - } + @Override + public void doProcessing() { + LOGGER.info("NetflixService is now processing"); + } } @Slf4j public class YouTubeService implements VideoStreamingService { - @Override - public void doProcessing() { - LOGGER.info("YouTubeService is now processing"); - } + @Override + public void doProcessing() { + LOGGER.info("YouTubeService is now processing"); + } } ``` @@ -68,16 +68,16 @@ Then, we have a lookup service that decides which video streaming service to use @Setter public class BusinessLookup { - private NetflixService netflixService; - private YouTubeService youTubeService; + private NetflixService netflixService; + private YouTubeService youTubeService; - public VideoStreamingService getBusinessService(String movie) { - if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { - return netflixService; - } else { - return youTubeService; + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; + } else { + return youTubeService; + } } - } } ``` @@ -89,12 +89,12 @@ video streaming service. @Setter public class BusinessDelegate { - private BusinessLookup lookupService; + private BusinessLookup lookupService; - public void playbackMovie(String movie) { - VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); - videoStreamingService.doProcessing(); - } + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.doProcessing(); + } } ``` @@ -103,15 +103,15 @@ The mobile client utilizes Business Delegate to call the business tier. ```java public class MobileClient { - private final BusinessDelegate businessDelegate; + private final BusinessDelegate businessDelegate; - public MobileClient(BusinessDelegate businessDelegate) { - this.businessDelegate = businessDelegate; - } + public MobileClient(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } - public void playbackMovie(String movie) { - businessDelegate.playbackMovie(movie); - } + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); + } } ``` @@ -120,18 +120,18 @@ Finally, we can demonstrate the complete example in action. ```java public static void main(String[]args){ - // prepare the objects - var businessDelegate=new BusinessDelegate(); - var businessLookup=new BusinessLookup(); - businessLookup.setNetflixService(new NetflixService()); - businessLookup.setYouTubeService(new YouTubeService()); - businessDelegate.setLookupService(businessLookup); + // prepare the objects + var businessDelegate=new BusinessDelegate(); + var businessLookup=new BusinessLookup(); + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); + businessDelegate.setLookupService(businessLookup); - // create the client and use the Business Delegate - var client=new MobileClient(businessDelegate); - client.playbackMovie("Die Hard 2"); - client.playbackMovie("Maradona: The Greatest Ever"); - } + // create the client and use the Business Delegate + var client=new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); + } ``` Here is the console output. diff --git a/bytecode/README.md b/bytecode/README.md index e4a459b58..919bf793e 100644 --- a/bytecode/README.md +++ b/bytecode/README.md @@ -3,7 +3,7 @@ title: Bytecode category: Behavioral language: en tag: - - Game programming + - Game programming --- ## Intent @@ -15,8 +15,8 @@ Allows encoding behavior as instructions for a virtual machine. Real world example > A team is working on a new game where wizards battle against each other. The wizard behavior needs to be carefully -> adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes each -> time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual +> adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes +> each time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual > machine. In plain words @@ -42,21 +42,21 @@ One of the most important game objects is the `Wizard` class. @Slf4j public class Wizard { - private int health; - private int agility; - private int wisdom; - private int numberOfPlayedSounds; - private int numberOfSpawnedParticles; + private int health; + private int agility; + private int wisdom; + private int numberOfPlayedSounds; + private int numberOfSpawnedParticles; - public void playSound() { - LOGGER.info("Playing sound"); - numberOfPlayedSounds++; - } + public void playSound() { + LOGGER.info("Playing sound"); + numberOfPlayedSounds++; + } - public void spawnParticles() { - LOGGER.info("Spawning particles"); - numberOfSpawnedParticles++; - } + public void spawnParticles() { + LOGGER.info("Spawning particles"); + numberOfSpawnedParticles++; + } } ``` @@ -70,18 +70,18 @@ together and pushes the result to the stack. @Getter public enum Instruction { - LITERAL(1), // e.g. "LITERAL 0", push 0 to stack - SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health - SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom - SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility - PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound - SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles - GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health - GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility - GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom - ADD(10), // e.g. "ADD", pop 2 values, push their sum - DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division - // ... + LITERAL(1), // e.g. "LITERAL 0", push 0 to stack + SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health + SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom + SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility + PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound + SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles + GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health + GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility + GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom + ADD(10), // e.g. "ADD", pop 2 values, push their sum + DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division + // ... } ``` @@ -94,89 +94,89 @@ the game object behavior. @Slf4j public class VirtualMachine { - private final Stack stack = new Stack<>(); + private final Stack stack = new Stack<>(); - private final Wizard[] wizards = new Wizard[2]; + private final Wizard[] wizards = new Wizard[2]; - public VirtualMachine() { - wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), - 0, 0); - wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), - 0, 0); - } - - public VirtualMachine(Wizard wizard1, Wizard wizard2) { - wizards[0] = wizard1; - wizards[1] = wizard2; - } - - public void execute(int[] bytecode) { - for (var i = 0; i < bytecode.length; i++) { - Instruction instruction = Instruction.getInstruction(bytecode[i]); - switch (instruction) { - case LITERAL: - // Read the next byte from the bytecode. - int value = bytecode[++i]; - // Push the next value to stack - stack.push(value); - break; - case SET_AGILITY: - var amount = stack.pop(); - var wizard = stack.pop(); - setAgility(wizard, amount); - break; - case SET_WISDOM: - amount = stack.pop(); - wizard = stack.pop(); - setWisdom(wizard, amount); - break; - case SET_HEALTH: - amount = stack.pop(); - wizard = stack.pop(); - setHealth(wizard, amount); - break; - case GET_HEALTH: - wizard = stack.pop(); - stack.push(getHealth(wizard)); - break; - case GET_AGILITY: - wizard = stack.pop(); - stack.push(getAgility(wizard)); - break; - case GET_WISDOM: - wizard = stack.pop(); - stack.push(getWisdom(wizard)); - break; - case ADD: - var a = stack.pop(); - var b = stack.pop(); - stack.push(a + b); - break; - case DIVIDE: - a = stack.pop(); - b = stack.pop(); - stack.push(b / a); - break; - case PLAY_SOUND: - wizard = stack.pop(); - getWizards()[wizard].playSound(); - break; - case SPAWN_PARTICLES: - wizard = stack.pop(); - getWizards()[wizard].spawnParticles(); - break; - default: - throw new IllegalArgumentException("Invalid instruction value"); - } - LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack()); + public VirtualMachine() { + wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); } - } - public void setHealth(int wizard, int amount) { - wizards[wizard].setHealth(amount); - } - // other setters -> - // ... + public VirtualMachine(Wizard wizard1, Wizard wizard2) { + wizards[0] = wizard1; + wizards[1] = wizard2; + } + + public void execute(int[] bytecode) { + for (var i = 0; i < bytecode.length; i++) { + Instruction instruction = Instruction.getInstruction(bytecode[i]); + switch (instruction) { + case LITERAL: + // Read the next byte from the bytecode. + int value = bytecode[++i]; + // Push the next value to stack + stack.push(value); + break; + case SET_AGILITY: + var amount = stack.pop(); + var wizard = stack.pop(); + setAgility(wizard, amount); + break; + case SET_WISDOM: + amount = stack.pop(); + wizard = stack.pop(); + setWisdom(wizard, amount); + break; + case SET_HEALTH: + amount = stack.pop(); + wizard = stack.pop(); + setHealth(wizard, amount); + break; + case GET_HEALTH: + wizard = stack.pop(); + stack.push(getHealth(wizard)); + break; + case GET_AGILITY: + wizard = stack.pop(); + stack.push(getAgility(wizard)); + break; + case GET_WISDOM: + wizard = stack.pop(); + stack.push(getWisdom(wizard)); + break; + case ADD: + var a = stack.pop(); + var b = stack.pop(); + stack.push(a + b); + break; + case DIVIDE: + a = stack.pop(); + b = stack.pop(); + stack.push(b / a); + break; + case PLAY_SOUND: + wizard = stack.pop(); + getWizards()[wizard].playSound(); + break; + case SPAWN_PARTICLES: + wizard = stack.pop(); + getWizards()[wizard].spawnParticles(); + break; + default: + throw new IllegalArgumentException("Invalid instruction value"); + } + LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack()); + } + } + + public void setHealth(int wizard, int amount) { + wizards[wizard].setHealth(amount); + } + // other setters -> + // ... } ``` @@ -185,23 +185,23 @@ Now we can show the full example utilizing the virtual machine. ```java public static void main(String[]args){ - var vm=new VirtualMachine( - new Wizard(45,7,11,0,0), - new Wizard(36,18,8,0,0)); + var vm=new VirtualMachine( + new Wizard(45,7,11,0,0), + new Wizard(36,18,8,0,0)); - vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); - vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); - vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); - vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); - vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); - vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); - vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); - vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); - vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); - vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); - vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); - vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); - } + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); + vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); + } ``` Here is the console output. diff --git a/caching/README.md b/caching/README.md index a3442a2b9..75e8b42c9 100644 --- a/caching/README.md +++ b/caching/README.md @@ -3,9 +3,9 @@ title: Caching category: Performance optimization language: en tag: - - Caching - - Performance - - Cloud distributed + - Caching + - Performance + - Cloud distributed --- ## Intent @@ -24,9 +24,9 @@ again. Real world example > A team is working on a website that provides new homes for abandoned cats. People can post their cats on the website -> after registering, but all the new posts require approval from one of the site moderators. The user accounts of the site -> moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag each -> time a post is viewed becomes expensive, and it's a good idea to utilize caching here. +> after registering, but all the new posts require approval from one of the site moderators. The user accounts of the +> site moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag +> each time a post is viewed becomes expensive, and it's a good idea to utilize caching here. In plain words @@ -36,9 +36,9 @@ Wikipedia says: > In computing, a cache is a hardware or software component that stores data so that future requests for that data can > be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored -> elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. -> Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower -> data store; thus, the more requests that can be served from the cache, the faster the system performs. +> elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it +> cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading +> from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs. **Programmatic Example** @@ -53,24 +53,24 @@ to/from database. @ToString @EqualsAndHashCode public class UserAccount { - private String userId; - private String userName; - private String additionalInfo; + private String userId; + private String userName; + private String additionalInfo; } public interface DbManager { - void connect(); + void connect(); - void disconnect(); + void disconnect(); - UserAccount readFromDb(String userId); + UserAccount readFromDb(String userId); - UserAccount writeToDb(UserAccount userAccount); + UserAccount writeToDb(UserAccount userAccount); - UserAccount updateDb(UserAccount userAccount); + UserAccount updateDb(UserAccount userAccount); - UserAccount upsertDb(UserAccount userAccount); + UserAccount upsertDb(UserAccount userAccount); } ``` @@ -96,73 +96,73 @@ always at the end of the list. @Slf4j public class LruCache { - static class Node { - String userId; - UserAccount userAccount; - Node previous; - Node next; + static class Node { + String userId; + UserAccount userAccount; + Node previous; + Node next; - public Node(String userId, UserAccount userAccount) { - this.userId = userId; - this.userAccount = userAccount; + public Node(String userId, UserAccount userAccount) { + this.userId = userId; + this.userAccount = userAccount; + } } - } - /* ... omitted details ... */ + /* ... omitted details ... */ - public LruCache(int capacity) { - this.capacity = capacity; - } - - public UserAccount get(String userId) { - if (cache.containsKey(userId)) { - var node = cache.get(userId); - remove(node); - setHead(node); - return node.userAccount; + public LruCache(int capacity) { + this.capacity = capacity; } - return null; - } - public void set(String userId, UserAccount userAccount) { - if (cache.containsKey(userId)) { - var old = cache.get(userId); - old.userAccount = userAccount; - remove(old); - setHead(old); - } else { - var newNode = new Node(userId, userAccount); - if (cache.size() >= capacity) { - LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId); - cache.remove(end.userId); // remove LRU data from cache. - remove(end); - setHead(newNode); - } else { - setHead(newNode); - } - cache.put(userId, newNode); + public UserAccount get(String userId) { + if (cache.containsKey(userId)) { + var node = cache.get(userId); + remove(node); + setHead(node); + return node.userAccount; + } + return null; } - } - public boolean contains(String userId) { - return cache.containsKey(userId); - } + public void set(String userId, UserAccount userAccount) { + if (cache.containsKey(userId)) { + var old = cache.get(userId); + old.userAccount = userAccount; + remove(old); + setHead(old); + } else { + var newNode = new Node(userId, userAccount); + if (cache.size() >= capacity) { + LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId); + cache.remove(end.userId); // remove LRU data from cache. + remove(end); + setHead(newNode); + } else { + setHead(newNode); + } + cache.put(userId, newNode); + } + } - public void remove(Node node) { /* ... */ } + public boolean contains(String userId) { + return cache.containsKey(userId); + } - public void setHead(Node node) { /* ... */ } + public void remove(Node node) { /* ... */ } - public void invalidate(String userId) { /* ... */ } + public void setHead(Node node) { /* ... */ } - public boolean isFull() { /* ... */ } + public void invalidate(String userId) { /* ... */ } - public UserAccount getLruData() { /* ... */ } + public boolean isFull() { /* ... */ } - public void clear() { /* ... */ } + public UserAccount getLruData() { /* ... */ } - public List getCacheDataInListForm() { /* ... */ } + public void clear() { /* ... */ } - public void setCapacity(int newCapacity) { /* ... */ } + public List getCacheDataInListForm() { /* ... */ } + + public void setCapacity(int newCapacity) { /* ... */ } } ``` @@ -173,58 +173,58 @@ The next layer we are going to look at is `CacheStore` which implements the diff @Slf4j public class CacheStore { - private static final int CAPACITY = 3; - private static LruCache cache; - private final DbManager dbManager; + private static final int CAPACITY = 3; + private static LruCache cache; + private final DbManager dbManager; - /* ... details omitted ... */ + /* ... details omitted ... */ - public UserAccount readThrough(final String userId) { - if (cache.contains(userId)) { - LOGGER.info("# Found in Cache!"); - return cache.get(userId); + public UserAccount readThrough(final String userId) { + if (cache.contains(userId)) { + LOGGER.info("# Found in Cache!"); + return cache.get(userId); + } + LOGGER.info("# Not found in cache! Go to DB!!"); + UserAccount userAccount = dbManager.readFromDb(userId); + cache.set(userId, userAccount); + return userAccount; } - LOGGER.info("# Not found in cache! Go to DB!!"); - UserAccount userAccount = dbManager.readFromDb(userId); - cache.set(userId, userAccount); - return userAccount; - } - public void writeThrough(final UserAccount userAccount) { - if (cache.contains(userAccount.getUserId())) { - dbManager.updateDb(userAccount); - } else { - dbManager.writeToDb(userAccount); + public void writeThrough(final UserAccount userAccount) { + if (cache.contains(userAccount.getUserId())) { + dbManager.updateDb(userAccount); + } else { + dbManager.writeToDb(userAccount); + } + cache.set(userAccount.getUserId(), userAccount); } - cache.set(userAccount.getUserId(), userAccount); - } - public void writeAround(final UserAccount userAccount) { - if (cache.contains(userAccount.getUserId())) { - dbManager.updateDb(userAccount); - // Cache data has been updated -- remove older - cache.invalidate(userAccount.getUserId()); - // version from cache. - } else { - dbManager.writeToDb(userAccount); + public void writeAround(final UserAccount userAccount) { + if (cache.contains(userAccount.getUserId())) { + dbManager.updateDb(userAccount); + // Cache data has been updated -- remove older + cache.invalidate(userAccount.getUserId()); + // version from cache. + } else { + dbManager.writeToDb(userAccount); + } } - } - public static void clearCache() { - if (cache != null) { - cache.clear(); + public static void clearCache() { + if (cache != null) { + cache.clear(); + } } - } - public static void flushCache() { - LOGGER.info("# flushCache..."); - Optional.ofNullable(cache) - .map(LruCache::getCacheDataInListForm) - .orElse(List.of()) - .forEach(DbManager::updateDb); - } + public static void flushCache() { + LOGGER.info("# flushCache..."); + Optional.ofNullable(cache) + .map(LruCache::getCacheDataInListForm) + .orElse(List.of()) + .forEach(DbManager::updateDb); + } - /* ... omitted the implementation of other caching strategies ... */ + /* ... omitted the implementation of other caching strategies ... */ } ``` @@ -239,50 +239,50 @@ the appropriate function in the `CacheStore` class. @Slf4j public final class AppManager { - private static CachingPolicy cachingPolicy; - private final DbManager dbManager; - private final CacheStore cacheStore; + private static CachingPolicy cachingPolicy; + private final DbManager dbManager; + private final CacheStore cacheStore; - private AppManager() { - } - - public void initDb() { /* ... */ } - - public static void initCachingPolicy(CachingPolicy policy) { /* ... */ } - - public static void initCacheCapacity(int capacity) { /* ... */ } - - public UserAccount find(final String userId) { - LOGGER.info("Trying to find {} in cache", userId); - if (cachingPolicy == CachingPolicy.THROUGH - || cachingPolicy == CachingPolicy.AROUND) { - return cacheStore.readThrough(userId); - } else if (cachingPolicy == CachingPolicy.BEHIND) { - return cacheStore.readThroughWithWriteBackPolicy(userId); - } else if (cachingPolicy == CachingPolicy.ASIDE) { - return findAside(userId); + private AppManager() { } - return null; - } - public void save(final UserAccount userAccount) { - LOGGER.info("Save record!"); - if (cachingPolicy == CachingPolicy.THROUGH) { - cacheStore.writeThrough(userAccount); - } else if (cachingPolicy == CachingPolicy.AROUND) { - cacheStore.writeAround(userAccount); - } else if (cachingPolicy == CachingPolicy.BEHIND) { - cacheStore.writeBehind(userAccount); - } else if (cachingPolicy == CachingPolicy.ASIDE) { - saveAside(userAccount); + public void initDb() { /* ... */ } + + public static void initCachingPolicy(CachingPolicy policy) { /* ... */ } + + public static void initCacheCapacity(int capacity) { /* ... */ } + + public UserAccount find(final String userId) { + LOGGER.info("Trying to find {} in cache", userId); + if (cachingPolicy == CachingPolicy.THROUGH + || cachingPolicy == CachingPolicy.AROUND) { + return cacheStore.readThrough(userId); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + return cacheStore.readThroughWithWriteBackPolicy(userId); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + return findAside(userId); + } + return null; } - } - public static String printCacheContent() { - return CacheStore.print(); - } + public void save(final UserAccount userAccount) { + LOGGER.info("Save record!"); + if (cachingPolicy == CachingPolicy.THROUGH) { + cacheStore.writeThrough(userAccount); + } else if (cachingPolicy == CachingPolicy.AROUND) { + cacheStore.writeAround(userAccount); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + cacheStore.writeBehind(userAccount); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + saveAside(userAccount); + } + } - /* ... details omitted ... */ + public static String printCacheContent() { + return CacheStore.print(); + } + + /* ... details omitted ... */ } ``` @@ -293,42 +293,42 @@ Here is what we do in the main class of the application. @Slf4j public class App { - public static void main(final String[] args) { - boolean isDbMongo = isDbMongo(args); - if (isDbMongo) { - LOGGER.info("Using the Mongo database engine to run the application."); - } else { - LOGGER.info("Using the 'in Memory' database to run the application."); + public static void main(final String[] args) { + boolean isDbMongo = isDbMongo(args); + if (isDbMongo) { + LOGGER.info("Using the Mongo database engine to run the application."); + } else { + LOGGER.info("Using the 'in Memory' database to run the application."); + } + App app = new App(isDbMongo); + app.useReadAndWriteThroughStrategy(); + String splitLine = "=============================================="; + LOGGER.info(splitLine); + app.useReadThroughAndWriteAroundStrategy(); + LOGGER.info(splitLine); + app.useReadThroughAndWriteBehindStrategy(); + LOGGER.info(splitLine); + app.useCacheAsideStategy(); + LOGGER.info(splitLine); } - App app = new App(isDbMongo); - app.useReadAndWriteThroughStrategy(); - String splitLine = "=============================================="; - LOGGER.info(splitLine); - app.useReadThroughAndWriteAroundStrategy(); - LOGGER.info(splitLine); - app.useReadThroughAndWriteBehindStrategy(); - LOGGER.info(splitLine); - app.useCacheAsideStategy(); - LOGGER.info(splitLine); - } - public void useReadAndWriteThroughStrategy() { - LOGGER.info("# CachingPolicy.THROUGH"); - appManager.initCachingPolicy(CachingPolicy.THROUGH); + public void useReadAndWriteThroughStrategy() { + LOGGER.info("# CachingPolicy.THROUGH"); + appManager.initCachingPolicy(CachingPolicy.THROUGH); - var userAccount1 = new UserAccount("001", "John", "He is a boy."); + var userAccount1 = new UserAccount("001", "John", "He is a boy."); - appManager.save(userAccount1); - LOGGER.info(appManager.printCacheContent()); - appManager.find("001"); - appManager.find("001"); - } + appManager.save(userAccount1); + LOGGER.info(appManager.printCacheContent()); + appManager.find("001"); + appManager.find("001"); + } - public void useReadThroughAndWriteAroundStrategy() { /* ... */ } + public void useReadThroughAndWriteAroundStrategy() { /* ... */ } - public void useReadThroughAndWriteBehindStrategy() { /* ... */ } + public void useReadThroughAndWriteBehindStrategy() { /* ... */ } - public void useCacheAsideStrategy() { /* ... */ } + public void useCacheAsideStrategy() { /* ... */ } } ``` diff --git a/callback/README.md b/callback/README.md index 3af2f2d60..886cecd80 100644 --- a/callback/README.md +++ b/callback/README.md @@ -3,10 +3,10 @@ title: Callback category: Functional language: en tag: - - Asynchronous - - Decoupling - - Idiom - - Reactive + - Asynchronous + - Decoupling + - Idiom + - Reactive --- ## Intent @@ -42,7 +42,7 @@ Callback is a simple interface with single method. ```java public interface Callback { - void call(); + void call(); } ``` @@ -51,21 +51,21 @@ Next we define a task that will execute the callback after the task execution ha ```java public abstract class Task { - final void executeWith(Callback callback) { - execute(); - Optional.ofNullable(callback).ifPresent(Callback::call); - } + final void executeWith(Callback callback) { + execute(); + Optional.ofNullable(callback).ifPresent(Callback::call); + } - public abstract void execute(); + public abstract void execute(); } @Slf4j public final class SimpleTask extends Task { - @Override - public void execute() { - LOGGER.info("Perform some important activity and after call the callback method."); - } + @Override + public void execute() { + LOGGER.info("Perform some important activity and after call the callback method."); + } } ``` @@ -73,7 +73,7 @@ Finally, here's how we execute a task and receive a callback when it's finished. ```java var task=new SimpleTask(); - task.executeWith(()->LOGGER.info("I'm done now.")); + task.executeWith(()->LOGGER.info("I'm done now.")); ``` ## Class diagram diff --git a/chain-of-responsibility/README.md b/chain-of-responsibility/README.md index e33a88cd5..147dcb0af 100644 --- a/chain-of-responsibility/README.md +++ b/chain-of-responsibility/README.md @@ -3,8 +3,8 @@ title: Chain of responsibility category: Behavioral language: en tag: - - Gang of Four - - Decoupling + - Gang of Four + - Decoupling --- ## Also known as @@ -48,27 +48,27 @@ import lombok.Getter; @Getter public class Request { - private final RequestType requestType; - private final String requestDescription; - private boolean handled; + private final RequestType requestType; + private final String requestDescription; + private boolean handled; - public Request(final RequestType requestType, final String requestDescription) { - this.requestType = Objects.requireNonNull(requestType); - this.requestDescription = Objects.requireNonNull(requestDescription); - } + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); + } - public void markHandled() { - this.handled = true; - } + public void markHandled() { + this.handled = true; + } - @Override - public String toString() { - return getRequestDescription(); - } + @Override + public String toString() { + return getRequestDescription(); + } } public enum RequestType { - DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX + DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX } ``` @@ -77,37 +77,37 @@ Next, we show the request handler hierarchy. ```java public interface RequestHandler { - boolean canHandleRequest(Request req); + boolean canHandleRequest(Request req); - int getPriority(); + int getPriority(); - void handle(Request req); + void handle(Request req); - String name(); + String name(); } @Slf4j public class OrcCommander implements RequestHandler { - @Override - public boolean canHandleRequest(Request req) { - return req.getRequestType() == RequestType.DEFEND_CASTLE; - } + @Override + public boolean canHandleRequest(Request req) { + return req.getRequestType() == RequestType.DEFEND_CASTLE; + } - @Override - public int getPriority() { - return 2; - } + @Override + public int getPriority() { + return 2; + } - @Override - public void handle(Request req) { - req.markHandled(); - LOGGER.info("{} handling request \"{}\"", name(), req); - } + @Override + public void handle(Request req) { + req.markHandled(); + LOGGER.info("{} handling request \"{}\"", name(), req); + } - @Override - public String name() { - return "Orc commander"; - } + @Override + public String name() { + return "Orc commander"; + } } // OrcOfficer and OrcSoldier are defined similarly as OrcCommander @@ -119,24 +119,24 @@ The Orc King gives the orders and forms the chain. ```java public class OrcKing { - private List handlers; + private List handlers; - public OrcKing() { - buildChain(); - } + public OrcKing() { + buildChain(); + } - private void buildChain() { - handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier()); - } + private void buildChain() { + handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier()); + } - public void makeRequest(Request req) { - handlers - .stream() - .sorted(Comparator.comparing(RequestHandler::getPriority)) - .filter(handler -> handler.canHandleRequest(req)) - .findFirst() - .ifPresent(handler -> handler.handle(req)); - } + public void makeRequest(Request req) { + handlers + .stream() + .sorted(Comparator.comparing(RequestHandler::getPriority)) + .filter(handler -> handler.canHandleRequest(req)) + .findFirst() + .ifPresent(handler -> handler.handle(req)); + } } ``` @@ -144,9 +144,9 @@ The chain of responsibility in action. ```java var king=new OrcKing(); - king.makeRequest(new Request(RequestType.DEFEND_CASTLE,"defend castle")); - king.makeRequest(new Request(RequestType.TORTURE_PRISONER,"torture prisoner")); - king.makeRequest(new Request(RequestType.COLLECT_TAX,"collect tax")); + king.makeRequest(new Request(RequestType.DEFEND_CASTLE,"defend castle")); + king.makeRequest(new Request(RequestType.TORTURE_PRISONER,"torture prisoner")); + king.makeRequest(new Request(RequestType.COLLECT_TAX,"collect tax")); ``` The console output. diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md index 394d5d1d5..58d4b644e 100644 --- a/circuit-breaker/README.md +++ b/circuit-breaker/README.md @@ -3,9 +3,9 @@ title: Circuit Breaker category: Resilience language: en tag: - - Cloud distributed - - Fault tolerance - - Microservices + - Cloud distributed + - Fault tolerance + - Microservices --- ## Also known as @@ -28,10 +28,9 @@ Real world example > services is slow or not responding successfully, our application will try to fetch response from > the remote service using multiple threads/processes, soon all of them will hang (also called > [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application -> to crash. We should be able to detect -> this situation and show the user an appropriate message so that he/she can explore other parts of -> the app unaffected by the remote service failure. Meanwhile, the other services that are working -> normally, should keep functioning unaffected by this failure. +> to crash. We should be able to detect this situation and show the user an appropriate message so that he/she can +> explore other parts of the app unaffected by the remote serv'ice failure. Meanwhile, the other services that are +> working normally, should keep functioning unaffected by this failure. In plain words @@ -62,59 +61,59 @@ In terms of code, the end user application is: @Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** - * Program entry point. - * - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { - var serverStartTime = System.nanoTime(); + var serverStartTime = System.nanoTime(); - var delayedService = new DelayedRemoteService(serverStartTime, 5); - var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, - 2000 * 1000 * 1000); + var delayedService = new DelayedRemoteService(serverStartTime, 5); + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, + 2000 * 1000 * 1000); - var quickService = new QuickRemoteService(); - var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, - 2000 * 1000 * 1000); + var quickService = new QuickRemoteService(); + var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, + 2000 * 1000 * 1000); - //Create an object of monitoring service which makes both local and remote calls - var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, - quickServiceCircuitBreaker); + //Create an object of monitoring service which makes both local and remote calls + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, + quickServiceCircuitBreaker); - //Fetch response from local resource - LOGGER.info(monitoringService.localResourceResponse()); + //Fetch response from local resource + LOGGER.info(monitoringService.localResourceResponse()); - //Fetch response from delayed service 2 times, to meet the failure threshold - LOGGER.info(monitoringService.delayedServiceResponse()); - LOGGER.info(monitoringService.delayedServiceResponse()); + //Fetch response from delayed service 2 times, to meet the failure threshold + LOGGER.info(monitoringService.delayedServiceResponse()); + LOGGER.info(monitoringService.delayedServiceResponse()); - //Fetch current state of delayed service circuit breaker after crossing failure threshold limit - //which is OPEN now - LOGGER.info(delayedServiceCircuitBreaker.getState()); + //Fetch current state of delayed service circuit breaker after crossing failure threshold limit + //which is OPEN now + LOGGER.info(delayedServiceCircuitBreaker.getState()); - //Meanwhile, the delayed service is down, fetch response from the healthy quick service - LOGGER.info(monitoringService.quickServiceResponse()); - LOGGER.info(quickServiceCircuitBreaker.getState()); + //Meanwhile, the delayed service is down, fetch response from the healthy quick service + LOGGER.info(monitoringService.quickServiceResponse()); + LOGGER.info(quickServiceCircuitBreaker.getState()); - //Wait for the delayed service to become responsive - try { - LOGGER.info("Waiting for delayed service to become responsive"); - Thread.sleep(5000); - } catch (InterruptedException e) { - LOGGER.error("An error occurred: ", e); + //Wait for the delayed service to become responsive + try { + LOGGER.info("Waiting for delayed service to become responsive"); + Thread.sleep(5000); + } catch (InterruptedException e) { + LOGGER.error("An error occurred: ", e); + } + //Check the state of delayed circuit breaker, should be HALF_OPEN + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + //Fetch response from delayed service, which should be healthy by now + LOGGER.info(monitoringService.delayedServiceResponse()); + //As successful response is fetched, it should be CLOSED again. + LOGGER.info(delayedServiceCircuitBreaker.getState()); } - //Check the state of delayed circuit breaker, should be HALF_OPEN - LOGGER.info(delayedServiceCircuitBreaker.getState()); - - //Fetch response from delayed service, which should be healthy by now - LOGGER.info(monitoringService.delayedServiceResponse()); - //As successful response is fetched, it should be CLOSED again. - LOGGER.info(delayedServiceCircuitBreaker.getState()); - } } ``` @@ -123,45 +122,45 @@ The monitoring service: ```java public class MonitoringService { - private final CircuitBreaker delayedService; + private final CircuitBreaker delayedService; - private final CircuitBreaker quickService; + private final CircuitBreaker quickService; - public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) { - this.delayedService = delayedService; - this.quickService = quickService; - } - - //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic - public String localResourceResponse() { - return "Local Service is working"; - } - - /** - * Fetch response from the delayed service (with some simulated startup time). - * - * @return response string - */ - public String delayedServiceResponse() { - try { - return this.delayedService.attemptRequest(); - } catch (RemoteServiceException e) { - return e.getMessage(); + public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) { + this.delayedService = delayedService; + this.quickService = quickService; } - } - /** - * Fetches response from a healthy service without any failure. - * - * @return response string - */ - public String quickServiceResponse() { - try { - return this.quickService.attemptRequest(); - } catch (RemoteServiceException e) { - return e.getMessage(); + //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic + public String localResourceResponse() { + return "Local Service is working"; + } + + /** + * Fetch response from the delayed service (with some simulated startup time). + * + * @return response string + */ + public String delayedServiceResponse() { + try { + return this.delayedService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } + } + + /** + * Fetches response from a healthy service without any failure. + * + * @return response string + */ + public String quickServiceResponse() { + try { + return this.quickService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } } - } } ``` @@ -171,128 +170,128 @@ remote (costly) service in a circuit breaker object, which prevents faults as fo ```java public class DefaultCircuitBreaker implements CircuitBreaker { - private final long timeout; - private final long retryTimePeriod; - private final RemoteService service; - long lastFailureTime; - private String lastFailureResponse; - int failureCount; - private final int failureThreshold; - private State state; - // Future time offset, in nanoseconds - private final long futureTime = 1_000_000_000_000L; + private final long timeout; + private final long retryTimePeriod; + private final RemoteService service; + long lastFailureTime; + private String lastFailureResponse; + int failureCount; + private final int failureThreshold; + private State state; + // Future time offset, in nanoseconds + private final long futureTime = 1_000_000_000_000L; - /** - * Constructor to create an instance of Circuit Breaker. - * - * @param timeout Timeout for the API request. Not necessary for this simple example - * @param failureThreshold Number of failures we receive from the depended service before changing - * state to 'OPEN' - * @param retryTimePeriod Time period after which a new request is made to remote service for - * status check. - */ - DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, - long retryTimePeriod) { - this.service = serviceToCall; - // We start in a closed state hoping that everything is fine - this.state = State.CLOSED; - this.failureThreshold = failureThreshold; - // Timeout for the API request. - // Used to break the calls made to remote resource if it exceeds the limit - this.timeout = timeout; - this.retryTimePeriod = retryTimePeriod; - //An absurd amount of time in future which basically indicates the last failure never happened - this.lastFailureTime = System.nanoTime() + futureTime; - this.failureCount = 0; - } - - // Reset everything to defaults - @Override - public void recordSuccess() { - this.failureCount = 0; - this.lastFailureTime = System.nanoTime() + futureTime; - this.state = State.CLOSED; - } - - @Override - public void recordFailure(String response) { - failureCount = failureCount + 1; - this.lastFailureTime = System.nanoTime(); - // Cache the failure response for returning on open state - this.lastFailureResponse = response; - } - - // Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. - protected void evaluateState() { - if (failureCount >= failureThreshold) { //Then something is wrong with remote service - if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { - //We have waited long enough and should try checking if service is up - state = State.HALF_OPEN; - } else { - //Service would still probably be down - state = State.OPEN; - } - } else { - //Everything is working fine - state = State.CLOSED; + /** + * Constructor to create an instance of Circuit Breaker. + * + * @param timeout Timeout for the API request. Not necessary for this simple example + * @param failureThreshold Number of failures we receive from the depended service before changing + * state to 'OPEN' + * @param retryTimePeriod Time period after which a new request is made to remote service for + * status check. + */ + DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, + long retryTimePeriod) { + this.service = serviceToCall; + // We start in a closed state hoping that everything is fine + this.state = State.CLOSED; + this.failureThreshold = failureThreshold; + // Timeout for the API request. + // Used to break the calls made to remote resource if it exceeds the limit + this.timeout = timeout; + this.retryTimePeriod = retryTimePeriod; + //An absurd amount of time in future which basically indicates the last failure never happened + this.lastFailureTime = System.nanoTime() + futureTime; + this.failureCount = 0; } - } - @Override - public String getState() { - evaluateState(); - return state.name(); - } + // Reset everything to defaults + @Override + public void recordSuccess() { + this.failureCount = 0; + this.lastFailureTime = System.nanoTime() + futureTime; + this.state = State.CLOSED; + } - /** - * Break the circuit beforehand if it is known service is down Or connect the circuit manually if - * service comes online before expected. - * - * @param state State at which circuit is in - */ - @Override - public void setState(State state) { - this.state = state; - switch (state) { - case OPEN -> { - this.failureCount = failureThreshold; + @Override + public void recordFailure(String response) { + failureCount = failureCount + 1; this.lastFailureTime = System.nanoTime(); - } - case HALF_OPEN -> { - this.failureCount = failureThreshold; - this.lastFailureTime = System.nanoTime() - retryTimePeriod; - } - default -> this.failureCount = 0; + // Cache the failure response for returning on open state + this.lastFailureResponse = response; } - } - /** - * Executes service call. - * - * @return Value from the remote resource, stale response or a custom exception - */ - @Override - public String attemptRequest() throws RemoteServiceException { - evaluateState(); - if (state == State.OPEN) { - // return cached response if the circuit is in OPEN state - return this.lastFailureResponse; - } else { - // Make the API request if the circuit is not OPEN - try { - //In a real application, this would be run in a thread and the timeout - //parameter of the circuit breaker would be utilized to know if service - //is working. Here, we simulate that based on server response itself - var response = service.call(); - // Yay!! the API responded fine. Let's reset everything. - recordSuccess(); - return response; - } catch (RemoteServiceException ex) { - recordFailure(ex.getMessage()); - throw ex; - } + // Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. + protected void evaluateState() { + if (failureCount >= failureThreshold) { //Then something is wrong with remote service + if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + //We have waited long enough and should try checking if service is up + state = State.HALF_OPEN; + } else { + //Service would still probably be down + state = State.OPEN; + } + } else { + //Everything is working fine + state = State.CLOSED; + } + } + + @Override + public String getState() { + evaluateState(); + return state.name(); + } + + /** + * Break the circuit beforehand if it is known service is down Or connect the circuit manually if + * service comes online before expected. + * + * @param state State at which circuit is in + */ + @Override + public void setState(State state) { + this.state = state; + switch (state) { + case OPEN -> { + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime(); + } + case HALF_OPEN -> { + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime() - retryTimePeriod; + } + default -> this.failureCount = 0; + } + } + + /** + * Executes service call. + * + * @return Value from the remote resource, stale response or a custom exception + */ + @Override + public String attemptRequest() throws RemoteServiceException { + evaluateState(); + if (state == State.OPEN) { + // return cached response if the circuit is in OPEN state + return this.lastFailureResponse; + } else { + // Make the API request if the circuit is not OPEN + try { + //In a real application, this would be run in a thread and the timeout + //parameter of the circuit breaker would be utilized to know if service + //is working. Here, we simulate that based on server response itself + var response = service.call(); + // Yay!! the API responded fine. Let's reset everything. + recordSuccess(); + return response; + } catch (RemoteServiceException ex) { + recordFailure(ex.getMessage()); + throw ex; + } + } } - } } ``` diff --git a/client-session/README.md b/client-session/README.md index bd1fbc9fe..a16eeca03 100644 --- a/client-session/README.md +++ b/client-session/README.md @@ -3,8 +3,8 @@ title: Client Session category: Behavioral language: en tags: - - Session management - - Web development + - Session management + - Web development --- ## Also known as @@ -21,17 +21,17 @@ application, ensuring a continuous and personalized user experience. Real-World Example > You're looking to create a data management app allowing users to send requests to the server to modify and make -> changes to data stored on their devices. These requests are small and the data is individual to each user, negating the -> need for a large scale database implementation. Using the client session pattern, you are able to handle multiple +> changes to data stored on their devices. These requests are small and the data is individual to each user, negating +> the need for a large scale database implementation. Using the client session pattern, you are able to handle multiple > concurrent requests, load balancing clients across different servers with ease due to servers remaining stateless. You -> also remove the need to store session IDs on the server side due to clients providing all the information that a server -> needs to perform their process. +> also remove the need to store session IDs on the server side due to clients providing all the information that a +> server needs to perform their process. In Plain words > Instead of storing information about the current client and the information being accessed on the server, it is -> maintained client side only. Client has to send session data with each request to the server and has to send an updated -> state back to the client, which is stored on the clients machine. The server doesn't have to store the client +> maintained client side only. Client has to send session data with each request to the server and has to send an +> updated state back to the client, which is stored on the clients machine. The server doesn't have to store the client > information. ([ref](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client)) **Programmatic Example** @@ -46,30 +46,30 @@ session information in every request helps the server identify the client and pr ```java public class App { - public static void main(String[] args) { - var server = new Server("localhost", 8080); - var session1 = server.getSession("Session1"); - var session2 = server.getSession("Session2"); - var request1 = new Request("Data1", session1); - var request2 = new Request("Data2", session2); - server.process(request1); - server.process(request2); - } + public static void main(String[] args) { + var server = new Server("localhost", 8080); + var session1 = server.getSession("Session1"); + var session2 = server.getSession("Session2"); + var request1 = new Request("Data1", session1); + var request2 = new Request("Data2", session2); + server.process(request1); + server.process(request2); + } } @Data @AllArgsConstructor public class Session { - /** - * Session id. - */ - private String id; + /** + * Session id. + */ + private String id; - /** - * Client name. - */ - private String clientName; + /** + * Client name. + */ + private String clientName; } @@ -77,9 +77,9 @@ public class Session { @AllArgsConstructor public class Request { - private String data; + private String data; - private Session session; + private Session session; } ``` diff --git a/collecting-parameter/README.md b/collecting-parameter/README.md index 34b8bf545..7b68c99d5 100644 --- a/collecting-parameter/README.md +++ b/collecting-parameter/README.md @@ -3,8 +3,8 @@ title: Collecting Parameter category: Behavioral language: en tag: - - Accumulation - - Generic + - Accumulation + - Generic --- ## Also known as @@ -55,33 +55,33 @@ import java.util.LinkedList; import java.util.Queue; public class App { - static PrinterQueue printerQueue = PrinterQueue.getInstance(); + static PrinterQueue printerQueue = PrinterQueue.getInstance(); - /** - * Program entry point. - * - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { /* Initialising the printer queue with jobs */ - printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false)); - printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false)); - printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false)); + printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false)); + printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false)); + printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false)); /* This variable is the collecting parameter. */ - var result = new LinkedList(); + var result = new LinkedList(); - /* - * Using numerous sub-methods to collaboratively add information to the result collecting parameter - */ - addA4Papers(result); - addA3Papers(result); - addA2Papers(result); - } + /* + * Using numerous sub-methods to collaboratively add information to the result collecting parameter + */ + addA4Papers(result); + addA3Papers(result); + addA2Papers(result); + } } ``` @@ -90,71 +90,75 @@ appropriate print jobs as per the policy described previously. The three policie ```java public class App { - static PrinterQueue printerQueue = PrinterQueue.getInstance(); + static PrinterQueue printerQueue = PrinterQueue.getInstance(); - /** - * Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client - * (the print center) wants. - * - * @param printerItemsCollection the collecting parameter - */ - public static void addA4Papers(Queue printerItemsCollection) { + /** + * Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client + * (the print center) wants. + * + * @param printerItemsCollection the collecting parameter + */ + public static void addA4Papers(Queue printerItemsCollection) { /* Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter, which is 'printerItemsCollection' in this case. */ - for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { - if (nextItem.paperSize.equals(PaperSizes.A4)) { - var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided; - if (isColouredAndSingleSided) { - printerItemsCollection.add(nextItem); - } else if (!nextItem.isColour) { - printerItemsCollection.add(nextItem); + for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { + if (nextItem.paperSize.equals(PaperSizes.A4)) { + var isColouredAndSingleSided = + nextItem.isColour && !nextItem.isDoubleSided; + if (isColouredAndSingleSided) { + printerItemsCollection.add(nextItem); + } else if (!nextItem.isColour) { + printerItemsCollection.add(nextItem); + } + } } - } } - } - /** - * Adds A3 document jobs to the collecting parameter according to some policy that can be whatever the client - * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate - * the wants of the client. - * - * @param printerItemsCollection the collecting parameter - */ - public static void addA3Papers(Queue printerItemsCollection) { - for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { - if (nextItem.paperSize.equals(PaperSizes.A3)) { + /** + * Adds A3 document jobs to the collecting parameter according to some policy that can be whatever the client + * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate + * the wants of the client. + * + * @param printerItemsCollection the collecting parameter + */ + public static void addA3Papers(Queue printerItemsCollection) { + for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { + if (nextItem.paperSize.equals(PaperSizes.A3)) { - // Encoding the policy into a Boolean: the A3 paper cannot be coloured and double-sided at the same time - var isNotColouredAndSingleSided = !nextItem.isColour && !nextItem.isDoubleSided; - if (isNotColouredAndSingleSided) { - printerItemsCollection.add(nextItem); + // Encoding the policy into a Boolean: the A3 paper cannot be coloured and double-sided at the same time + var isNotColouredAndSingleSided = + !nextItem.isColour && !nextItem.isDoubleSided; + if (isNotColouredAndSingleSided) { + printerItemsCollection.add(nextItem); + } + } } - } } - } - /** - * Adds A2 document jobs to the collecting parameter according to some policy that can be whatever the client - * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate - * the wants of the client. - * - * @param printerItemsCollection the collecting parameter - */ - public static void addA2Papers(Queue printerItemsCollection) { - for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { - if (nextItem.paperSize.equals(PaperSizes.A2)) { + /** + * Adds A2 document jobs to the collecting parameter according to some policy that can be whatever the client + * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate + * the wants of the client. + * + * @param printerItemsCollection the collecting parameter + */ + public static void addA2Papers(Queue printerItemsCollection) { + for (PrinterItem nextItem : printerQueue.getPrinterQueue()) { + if (nextItem.paperSize.equals(PaperSizes.A2)) { - // Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured. - var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided - && !nextItem.isColour; - if (isNotColouredSingleSidedAndOnePage) { - printerItemsCollection.add(nextItem); + // Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured. + var isNotColouredSingleSidedAndOnePage = + nextItem.pageCount == 1 && + !nextItem.isDoubleSided + && !nextItem.isColour; + if (isNotColouredSingleSidedAndOnePage) { + printerItemsCollection.add(nextItem); + } + } } - } } - } } ```