mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-08-18 08:24:59 +00:00
docs: add editorconfig + formatting
This commit is contained in:
+340
@@ -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
|
||||||
+68
-68
@@ -3,9 +3,9 @@ title: Abstract Document
|
|||||||
category: Structural
|
category: Structural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Abstraction
|
- Abstraction
|
||||||
- Extensibility
|
- Extensibility
|
||||||
- Decoupling
|
- Decoupling
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -43,43 +43,43 @@ map and any amount of child objects.
|
|||||||
```java
|
```java
|
||||||
public interface Document {
|
public interface Document {
|
||||||
|
|
||||||
Void put(String key, Object value);
|
Void put(String key, Object value);
|
||||||
|
|
||||||
Object get(String key);
|
Object get(String key);
|
||||||
|
|
||||||
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
|
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class AbstractDocument implements Document {
|
public abstract class AbstractDocument implements Document {
|
||||||
|
|
||||||
private final Map<String, Object> properties;
|
private final Map<String, Object> properties;
|
||||||
|
|
||||||
protected AbstractDocument(Map<String, Object> properties) {
|
protected AbstractDocument(Map<String, Object> properties) {
|
||||||
Objects.requireNonNull(properties, "properties map is required");
|
Objects.requireNonNull(properties, "properties map is required");
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void put(String key, Object value) {
|
public Void put(String key, Object value) {
|
||||||
properties.put(key, value);
|
properties.put(key, value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(String key) {
|
public Object get(String key) {
|
||||||
return properties.get(key);
|
return properties.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
|
||||||
return Stream.ofNullable(get(key))
|
return Stream.ofNullable(get(key))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(el -> (List<Map<String, Object>>) el)
|
.map(el -> (List<Map<String, Object>>) el)
|
||||||
.findAny()
|
.findAny()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.map(constructor);
|
.map(constructor);
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -90,35 +90,35 @@ static looking interface to our `Car` class.
|
|||||||
```java
|
```java
|
||||||
public enum Property {
|
public enum Property {
|
||||||
|
|
||||||
PARTS, TYPE, PRICE, MODEL
|
PARTS, TYPE, PRICE, MODEL
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HasType extends Document {
|
public interface HasType extends Document {
|
||||||
|
|
||||||
default Optional<String> getType() {
|
default Optional<String> getType() {
|
||||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HasPrice extends Document {
|
public interface HasPrice extends Document {
|
||||||
|
|
||||||
default Optional<Number> getPrice() {
|
default Optional<Number> getPrice() {
|
||||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HasModel extends Document {
|
public interface HasModel extends Document {
|
||||||
|
|
||||||
default Optional<String> getModel() {
|
default Optional<String> getModel() {
|
||||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HasParts extends Document {
|
public interface HasParts extends Document {
|
||||||
|
|
||||||
default Stream<Part> getParts() {
|
default Stream<Part> getParts() {
|
||||||
return children(Property.PARTS.toString(), Part::new);
|
return children(Property.PARTS.toString(), Part::new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -127,9 +127,9 @@ Now we are ready to introduce the `Car`.
|
|||||||
```java
|
```java
|
||||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||||
|
|
||||||
public Car(Map<String, Object> properties) {
|
public Car(Map<String, Object> properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,32 +138,32 @@ And finally here's how we construct and use the `Car` in a full example.
|
|||||||
```java
|
```java
|
||||||
LOGGER.info("Constructing parts and car");
|
LOGGER.info("Constructing parts and car");
|
||||||
|
|
||||||
var wheelProperties=Map.of(
|
var wheelProperties=Map.of(
|
||||||
Property.TYPE.toString(),"wheel",
|
Property.TYPE.toString(),"wheel",
|
||||||
Property.MODEL.toString(),"15C",
|
Property.MODEL.toString(),"15C",
|
||||||
Property.PRICE.toString(),100L);
|
Property.PRICE.toString(),100L);
|
||||||
|
|
||||||
var doorProperties=Map.of(
|
var doorProperties=Map.of(
|
||||||
Property.TYPE.toString(),"door",
|
Property.TYPE.toString(),"door",
|
||||||
Property.MODEL.toString(),"Lambo",
|
Property.MODEL.toString(),"Lambo",
|
||||||
Property.PRICE.toString(),300L);
|
Property.PRICE.toString(),300L);
|
||||||
|
|
||||||
var carProperties=Map.of(
|
var carProperties=Map.of(
|
||||||
Property.MODEL.toString(),"300SL",
|
Property.MODEL.toString(),"300SL",
|
||||||
Property.PRICE.toString(),10000L,
|
Property.PRICE.toString(),10000L,
|
||||||
Property.PARTS.toString(),List.of(wheelProperties,doorProperties));
|
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("Here is our car:");
|
||||||
LOGGER.info("-> model: {}",car.getModel().orElseThrow());
|
LOGGER.info("-> model: {}",car.getModel().orElseThrow());
|
||||||
LOGGER.info("-> price: {}",car.getPrice().orElseThrow());
|
LOGGER.info("-> price: {}",car.getPrice().orElseThrow());
|
||||||
LOGGER.info("-> parts: ");
|
LOGGER.info("-> parts: ");
|
||||||
car.getParts().forEach(p->LOGGER.info("\t{}/{}/{}",
|
car.getParts().forEach(p->LOGGER.info("\t{}/{}/{}",
|
||||||
p.getType().orElse(null),
|
p.getType().orElse(null),
|
||||||
p.getModel().orElse(null),
|
p.getModel().orElse(null),
|
||||||
p.getPrice().orElse(null))
|
p.getPrice().orElse(null))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Constructing parts and car
|
// Constructing parts and car
|
||||||
// Here is our car:
|
// Here is our car:
|
||||||
|
|||||||
+76
-76
@@ -3,9 +3,9 @@ title: Abstract Factory
|
|||||||
category: Creational
|
category: Creational
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Abstraction
|
- Abstraction
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -43,43 +43,43 @@ kingdom.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Castle {
|
public interface Castle {
|
||||||
String getDescription();
|
String getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface King {
|
public interface King {
|
||||||
String getDescription();
|
String getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Army {
|
public interface Army {
|
||||||
String getDescription();
|
String getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elven implementations ->
|
// Elven implementations ->
|
||||||
public class ElfCastle implements Castle {
|
public class ElfCastle implements Castle {
|
||||||
static final String DESCRIPTION = "This is the elven castle!";
|
static final String DESCRIPTION = "This is the elven castle!";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return DESCRIPTION;
|
return DESCRIPTION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ElfKing implements King {
|
public class ElfKing implements King {
|
||||||
static final String DESCRIPTION = "This is the elven king!";
|
static final String DESCRIPTION = "This is the elven king!";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return DESCRIPTION;
|
return DESCRIPTION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ElfArmy implements Army {
|
public class ElfArmy implements Army {
|
||||||
static final String DESCRIPTION = "This is the elven Army!";
|
static final String DESCRIPTION = "This is the elven Army!";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return DESCRIPTION;
|
return DESCRIPTION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orcish implementations similarly -> ...
|
// Orcish implementations similarly -> ...
|
||||||
@@ -90,47 +90,47 @@ Then we have the abstraction and implementations for the kingdom factory.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface KingdomFactory {
|
public interface KingdomFactory {
|
||||||
Castle createCastle();
|
Castle createCastle();
|
||||||
|
|
||||||
King createKing();
|
King createKing();
|
||||||
|
|
||||||
Army createArmy();
|
Army createArmy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ElfKingdomFactory implements KingdomFactory {
|
public class ElfKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Castle createCastle() {
|
public Castle createCastle() {
|
||||||
return new ElfCastle();
|
return new ElfCastle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public King createKing() {
|
public King createKing() {
|
||||||
return new ElfKing();
|
return new ElfKing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new ElfArmy();
|
return new ElfArmy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrcKingdomFactory implements KingdomFactory {
|
public class OrcKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Castle createCastle() {
|
public Castle createCastle() {
|
||||||
return new OrcCastle();
|
return new OrcCastle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public King createKing() {
|
public King createKing() {
|
||||||
return new OrcKing();
|
return new OrcKing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new OrcArmy();
|
return new OrcArmy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -139,21 +139,21 @@ castle, king and army, etc.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
var factory=new ElfKingdomFactory();
|
var factory=new ElfKingdomFactory();
|
||||||
var castle=factory.createCastle();
|
var castle=factory.createCastle();
|
||||||
var king=factory.createKing();
|
var king=factory.createKing();
|
||||||
var army=factory.createArmy();
|
var army=factory.createArmy();
|
||||||
|
|
||||||
castle.getDescription();
|
castle.getDescription();
|
||||||
king.getDescription();
|
king.getDescription();
|
||||||
army.getDescription();
|
army.getDescription();
|
||||||
```
|
```
|
||||||
|
|
||||||
Program output:
|
Program output:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
This is the elven castle!
|
This is the elven castle!
|
||||||
This is the elven king!
|
This is the elven king!
|
||||||
This is the elven Army!
|
This is the elven Army!
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`,
|
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
|
```java
|
||||||
public static class FactoryMaker {
|
public static class FactoryMaker {
|
||||||
|
|
||||||
public enum KingdomType {
|
public enum KingdomType {
|
||||||
ELF, ORC
|
ELF, ORC
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KingdomFactory makeFactory(KingdomType type) {
|
public static KingdomFactory makeFactory(KingdomType type) {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
case ELF -> new ElfKingdomFactory();
|
case ELF -> new ElfKingdomFactory();
|
||||||
case ORC -> new OrcKingdomFactory();
|
case ORC -> new OrcKingdomFactory();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var app = new App();
|
var app = new App();
|
||||||
|
|
||||||
LOGGER.info("Elf Kingdom");
|
LOGGER.info("Elf Kingdom");
|
||||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
|
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
|
||||||
LOGGER.info(app.getArmy().getDescription());
|
LOGGER.info(app.getArmy().getDescription());
|
||||||
LOGGER.info(app.getCastle().getDescription());
|
LOGGER.info(app.getCastle().getDescription());
|
||||||
LOGGER.info(app.getKing().getDescription());
|
LOGGER.info(app.getKing().getDescription());
|
||||||
|
|
||||||
LOGGER.info("Orc Kingdom");
|
LOGGER.info("Orc Kingdom");
|
||||||
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
|
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
|
||||||
--similar use of the orc factory
|
--similar use of the orc factory
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|||||||
+62
-62
@@ -3,7 +3,7 @@ title: Active Object
|
|||||||
category: Concurrency
|
category: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Performance
|
- Performance
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -29,57 +29,57 @@ itself, we can use the Active Object pattern.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public abstract class ActiveCreature {
|
public abstract class ActiveCreature {
|
||||||
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||||
|
|
||||||
private BlockingQueue<Runnable> requests;
|
private BlockingQueue<Runnable> requests;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
public ActiveCreature(String name) {
|
public ActiveCreature(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.requests = new LinkedBlockingQueue<Runnable>();
|
this.requests = new LinkedBlockingQueue<Runnable>();
|
||||||
thread = new Thread(new Runnable() {
|
thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
requests.take().run();
|
requests.take().run();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
logger.error(e.getMessage());
|
logger.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
|
thread.start();
|
||||||
}
|
}
|
||||||
);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void eat() throws InterruptedException {
|
public void eat() throws InterruptedException {
|
||||||
requests.put(new Runnable() {
|
requests.put(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.info("{} is eating!", name());
|
logger.info("{} is eating!", name());
|
||||||
logger.info("{} has finished eating!", name());
|
logger.info("{} has finished eating!", name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void roam() throws InterruptedException {
|
public void roam() throws InterruptedException {
|
||||||
requests.put(new Runnable() {
|
requests.put(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.info("{} has started to roam the wastelands.", name());
|
logger.info("{} has started to roam the wastelands.", name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
public String name() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -91,9 +91,9 @@ For example, the Orc class:
|
|||||||
```java
|
```java
|
||||||
public class Orc extends ActiveCreature {
|
public class Orc extends ActiveCreature {
|
||||||
|
|
||||||
public Orc(String name) {
|
public Orc(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -103,25 +103,25 @@ thread of control:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public static void main(String[]args){
|
public static void main(String[]args){
|
||||||
var app=new App();
|
var app=new App();
|
||||||
app.run();
|
app.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(){
|
public void run(){
|
||||||
ActiveCreature creature;
|
ActiveCreature creature;
|
||||||
try{
|
try{
|
||||||
for(int i=0;i<creatures;i++){
|
for(int i=0;i<creatures;i++){
|
||||||
creature=new Orc(Orc.class.getSimpleName().toString()+i);
|
creature=new Orc(Orc.class.getSimpleName().toString()+i);
|
||||||
creature.eat();
|
creature.eat();
|
||||||
creature.roam();
|
creature.roam();
|
||||||
}
|
}
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
}catch(InterruptedException e){
|
}catch(InterruptedException e){
|
||||||
logger.error(e.getMessage());
|
logger.error(e.getMessage());
|
||||||
}
|
}
|
||||||
Runtime.getRuntime().exit(1);
|
Runtime.getRuntime().exit(1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|||||||
+38
-38
@@ -3,8 +3,8 @@ title: Acyclic Visitor
|
|||||||
category: Behavioral
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Extensibility
|
- Extensibility
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -34,33 +34,33 @@ Here's the `Modem` hierarchy.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public abstract class Modem {
|
public abstract class Modem {
|
||||||
public abstract void accept(ModemVisitor modemVisitor);
|
public abstract void accept(ModemVisitor modemVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Zoom extends Modem {
|
public class Zoom extends Modem {
|
||||||
...
|
...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(ModemVisitor modemVisitor) {
|
public void accept(ModemVisitor modemVisitor) {
|
||||||
if (modemVisitor instanceof ZoomVisitor) {
|
if (modemVisitor instanceof ZoomVisitor) {
|
||||||
((ZoomVisitor) modemVisitor).visit(this);
|
((ZoomVisitor) modemVisitor).visit(this);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
|
LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Hayes extends Modem {
|
public class Hayes extends Modem {
|
||||||
...
|
...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(ModemVisitor modemVisitor) {
|
public void accept(ModemVisitor modemVisitor) {
|
||||||
if (modemVisitor instanceof HayesVisitor) {
|
if (modemVisitor instanceof HayesVisitor) {
|
||||||
((HayesVisitor) modemVisitor).visit(this);
|
((HayesVisitor) modemVisitor).visit(this);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -71,11 +71,11 @@ public interface ModemVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface HayesVisitor extends ModemVisitor {
|
public interface HayesVisitor extends ModemVisitor {
|
||||||
void visit(Hayes hayes);
|
void visit(Hayes hayes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ZoomVisitor extends ModemVisitor {
|
public interface ZoomVisitor extends ModemVisitor {
|
||||||
void visit(Zoom zoom);
|
void visit(Zoom zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
||||||
@@ -84,24 +84,24 @@ public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
|||||||
public class ConfigureForDosVisitor implements AllModemVisitor {
|
public class ConfigureForDosVisitor implements AllModemVisitor {
|
||||||
...
|
...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Hayes hayes) {
|
public void visit(Hayes hayes) {
|
||||||
LOGGER.info(hayes + " used with Dos configurator.");
|
LOGGER.info(hayes + " used with Dos configurator.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Zoom zoom) {
|
public void visit(Zoom zoom) {
|
||||||
LOGGER.info(zoom + " used with Dos configurator.");
|
LOGGER.info(zoom + " used with Dos configurator.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfigureForUnixVisitor implements ZoomVisitor {
|
public class ConfigureForUnixVisitor implements ZoomVisitor {
|
||||||
...
|
...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Zoom zoom) {
|
public void visit(Zoom zoom) {
|
||||||
LOGGER.info(zoom + " used with Unix configurator.");
|
LOGGER.info(zoom + " used with Unix configurator.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -109,13 +109,13 @@ Finally, here are the visitors in action.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
var conUnix=new ConfigureForUnixVisitor();
|
var conUnix=new ConfigureForUnixVisitor();
|
||||||
var conDos=new ConfigureForDosVisitor();
|
var conDos=new ConfigureForDosVisitor();
|
||||||
var zoom=new Zoom();
|
var zoom=new Zoom();
|
||||||
var hayes=new Hayes();
|
var hayes=new Hayes();
|
||||||
hayes.accept(conDos);
|
hayes.accept(conDos);
|
||||||
zoom.accept(conDos);
|
zoom.accept(conDos);
|
||||||
hayes.accept(conUnix);
|
hayes.accept(conUnix);
|
||||||
zoom.accept(conUnix);
|
zoom.accept(conUnix);
|
||||||
```
|
```
|
||||||
|
|
||||||
Program output:
|
Program output:
|
||||||
|
|||||||
+29
-29
@@ -3,9 +3,9 @@ title: Adapter
|
|||||||
category: Structural
|
category: Structural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Compatibility
|
- Compatibility
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
- Integration
|
- Integration
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -22,11 +22,11 @@ compatibility.
|
|||||||
Real-world example
|
Real-world example
|
||||||
|
|
||||||
> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer
|
> 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
|
> them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card
|
||||||
> your computer. In this case card reader is an adapter.
|
> to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a
|
||||||
> Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it
|
> three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible
|
||||||
> needs to use a power adapter that makes it compatible with the two-pronged outlets.
|
> with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to
|
||||||
> Yet another example would be a translator translating words spoken by one person to another
|
> another
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
@@ -46,14 +46,14 @@ First, we have interfaces `RowingBoat` and `FishingBoat`
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface RowingBoat {
|
public interface RowingBoat {
|
||||||
void row();
|
void row();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FishingBoat {
|
public class FishingBoat {
|
||||||
public void sail() {
|
public void sail() {
|
||||||
LOGGER.info("The fishing boat is sailing");
|
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
|
```java
|
||||||
public class Captain {
|
public class Captain {
|
||||||
|
|
||||||
private final RowingBoat rowingBoat;
|
private final RowingBoat rowingBoat;
|
||||||
|
|
||||||
// default constructor and setter for rowingBoat
|
// default constructor and setter for rowingBoat
|
||||||
public Captain(RowingBoat rowingBoat) {
|
public Captain(RowingBoat rowingBoat) {
|
||||||
this.rowingBoat = rowingBoat;
|
this.rowingBoat = rowingBoat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void row() {
|
public void row() {
|
||||||
rowingBoat.row();
|
rowingBoat.row();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,16 +83,16 @@ to create an adapter that allows the captain to operate the fishing boat with hi
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class FishingBoatAdapter implements RowingBoat {
|
public class FishingBoatAdapter implements RowingBoat {
|
||||||
|
|
||||||
private final FishingBoat boat;
|
private final FishingBoat boat;
|
||||||
|
|
||||||
public FishingBoatAdapter() {
|
public FishingBoatAdapter() {
|
||||||
boat = new FishingBoat();
|
boat = new FishingBoat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void row() {
|
public void row() {
|
||||||
boat.sail();
|
boat.sail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ And now the `Captain` can use the `FishingBoat` to escape the pirates.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
var captain=new Captain(new FishingBoatAdapter());
|
var captain=new Captain(new FishingBoatAdapter());
|
||||||
captain.row();
|
captain.row();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ title: Aggregator Microservices
|
|||||||
category: Architectural
|
category: Architectural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- API design
|
- API design
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Microservices
|
- Microservices
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -37,9 +37,9 @@ Let's start from the data model. Here's our `Product`.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class Product {
|
public class Product {
|
||||||
private String title;
|
private String title;
|
||||||
private int productInventories;
|
private int productInventories;
|
||||||
// Getters and setters omitted for brevity ->
|
// Getters and setters omitted for brevity ->
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -52,27 +52,27 @@ Next we can introduce our `Aggregator` microservice. It contains clients `Produc
|
|||||||
@RestController
|
@RestController
|
||||||
public class Aggregator {
|
public class Aggregator {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProductInformationClient informationClient;
|
private ProductInformationClient informationClient;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProductInventoryClient inventoryClient;
|
private ProductInventoryClient inventoryClient;
|
||||||
|
|
||||||
@RequestMapping(path = "/product", method = RequestMethod.GET)
|
@RequestMapping(path = "/product", method = RequestMethod.GET)
|
||||||
public Product getProduct() {
|
public Product getProduct() {
|
||||||
|
|
||||||
var product = new Product();
|
var product = new Product();
|
||||||
var productTitle = informationClient.getProductTitle();
|
var productTitle = informationClient.getProductTitle();
|
||||||
var productInventory = inventoryClient.getProductInventories();
|
var productInventory = inventoryClient.getProductInventories();
|
||||||
|
|
||||||
//Fallback to error message
|
//Fallback to error message
|
||||||
product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed"));
|
product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed"));
|
||||||
|
|
||||||
//Fallback to default error inventory
|
//Fallback to default error inventory
|
||||||
product.setProductInventories(requireNonNullElse(productInventory, -1));
|
product.setProductInventories(requireNonNullElse(productInventory, -1));
|
||||||
|
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,10 +83,10 @@ inventory counts.
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class InformationController {
|
public class InformationController {
|
||||||
@RequestMapping(value = "/information", method = RequestMethod.GET)
|
@RequestMapping(value = "/information", method = RequestMethod.GET)
|
||||||
public String getProductTitle() {
|
public String getProductTitle() {
|
||||||
return "The Product Title.";
|
return "The Product Title.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+78
-78
@@ -3,8 +3,8 @@ title: Ambassador
|
|||||||
category: Structural
|
category: Structural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -43,7 +43,7 @@ by the remote service as well as the ambassador service:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
interface RemoteServiceInterface {
|
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
|
@Slf4j
|
||||||
public class RemoteService implements RemoteServiceInterface {
|
public class RemoteService implements RemoteServiceInterface {
|
||||||
private static RemoteService service = null;
|
private static RemoteService service = null;
|
||||||
|
|
||||||
static synchronized RemoteService getRemoteService() {
|
static synchronized RemoteService getRemoteService() {
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
service = new RemoteService();
|
service = new RemoteService();
|
||||||
}
|
}
|
||||||
return service;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@Slf4j
|
||||||
public class ServiceAmbassador implements RemoteServiceInterface {
|
public class ServiceAmbassador implements RemoteServiceInterface {
|
||||||
private static final int RETRIES = 3;
|
private static final int RETRIES = 3;
|
||||||
private static final int DELAY_MS = 3000;
|
private static final int DELAY_MS = 3000;
|
||||||
|
|
||||||
ServiceAmbassador() {
|
ServiceAmbassador() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long doRemoteFunction(int value) {
|
public long doRemoteFunction(int value) {
|
||||||
return safeCall(value);
|
return safeCall(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long checkLatency(int value) {
|
private long checkLatency(int value) {
|
||||||
var startTime = System.currentTimeMillis();
|
var startTime = System.currentTimeMillis();
|
||||||
var result = RemoteService.getRemoteService().doRemoteFunction(value);
|
var result = RemoteService.getRemoteService().doRemoteFunction(value);
|
||||||
var timeTaken = System.currentTimeMillis() - startTime;
|
var timeTaken = System.currentTimeMillis() - startTime;
|
||||||
|
|
||||||
LOGGER.info("Time taken (ms): " + timeTaken);
|
LOGGER.info("Time taken (ms): " + timeTaken);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long safeCall(int value) {
|
private long safeCall(int value) {
|
||||||
var retries = 0;
|
var retries = 0;
|
||||||
var result = (long) FAILURE;
|
var result = (long) FAILURE;
|
||||||
|
|
||||||
for (int i = 0; i < RETRIES; i++) {
|
for (int i = 0; i < RETRIES; i++) {
|
||||||
if (retries >= RETRIES) {
|
if (retries >= RETRIES) {
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((result = checkLatency(value)) == FAILURE) {
|
if ((result = checkLatency(value)) == FAILURE) {
|
||||||
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
|
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
|
||||||
retries++;
|
retries++;
|
||||||
try {
|
try {
|
||||||
sleep(DELAY_MS);
|
sleep(DELAY_MS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOGGER.error("Thread sleep state interrupted", e);
|
LOGGER.error("Thread sleep state interrupted", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
return result;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,13 +138,13 @@ A client has a local service ambassador used to interact with the remote service
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Client {
|
public class Client {
|
||||||
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
|
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
|
||||||
|
|
||||||
long useService(int value) {
|
long useService(int value) {
|
||||||
var result = serviceAmbassador.doRemoteFunction(value);
|
var result = serviceAmbassador.doRemoteFunction(value);
|
||||||
LOGGER.info("Service result: " + result);
|
LOGGER.info("Service result: " + result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -152,12 +152,12 @@ Here are two clients using the service.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class App {
|
public class App {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var host1 = new Client();
|
var host1 = new Client();
|
||||||
var host2 = new Client();
|
var host2 = new Client();
|
||||||
host1.useService(12);
|
host1.useService(12);
|
||||||
host2.useService(73);
|
host2.useService(73);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -165,14 +165,14 @@ Here's the output for running the example:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
Time taken(ms):111
|
Time taken(ms):111
|
||||||
Service result:120
|
Service result:120
|
||||||
Time taken(ms):931
|
Time taken(ms):931
|
||||||
Failed to reach remote:(1)
|
Failed to reach remote:(1)
|
||||||
Time taken(ms):665
|
Time taken(ms):665
|
||||||
Failed to reach remote:(2)
|
Failed to reach remote:(2)
|
||||||
Time taken(ms):538
|
Time taken(ms):538
|
||||||
Failed to reach remote:(3)
|
Failed to reach remote:(3)
|
||||||
Service result:-1
|
Service result:-1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ title: Anti-corruption layer
|
|||||||
category: Integration
|
category: Integration
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Architecture
|
- Architecture
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Isolation
|
- Isolation
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -67,11 +67,11 @@ systems.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class LegacyOrder {
|
public class LegacyOrder {
|
||||||
private String id;
|
private String id;
|
||||||
private String customer;
|
private String customer;
|
||||||
private String item;
|
private String item;
|
||||||
private String qty;
|
private String qty;
|
||||||
private String price;
|
private String price;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -79,22 +79,22 @@ public class LegacyOrder {
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class ModernOrder {
|
public class ModernOrder {
|
||||||
private String id;
|
private String id;
|
||||||
private Customer customer;
|
private Customer customer;
|
||||||
|
|
||||||
private Shipment shipment;
|
private Shipment shipment;
|
||||||
|
|
||||||
private String extra;
|
private String extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Customer {
|
public class Customer {
|
||||||
private String address;
|
private String address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Shipment {
|
public class Shipment {
|
||||||
private String item;
|
private String item;
|
||||||
private String qty;
|
private String qty;
|
||||||
private String price;
|
private String price;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -103,19 +103,19 @@ public class Shipment {
|
|||||||
```java
|
```java
|
||||||
public class AntiCorruptionLayer {
|
public class AntiCorruptionLayer {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ModernShop modernShop;
|
private ModernShop modernShop;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LegacyShop legacyShop;
|
private LegacyShop legacyShop;
|
||||||
|
|
||||||
public Optional<LegacyOrder> findOrderInModernSystem(String id) {
|
public Optional<LegacyOrder> findOrderInModernSystem(String id) {
|
||||||
return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/);
|
return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ModernOrder> findOrderInLegacySystem(String id) {
|
public Optional<ModernOrder> findOrderInLegacySystem(String id) {
|
||||||
return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/);
|
return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -128,21 +128,21 @@ from the `Modern` system.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class LegacyShop {
|
public class LegacyShop {
|
||||||
@Autowired
|
@Autowired
|
||||||
private AntiCorruptionLayer acl;
|
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<LegacyOrder> orderInModernSystem = acl.findOrderInModernSystem(id);
|
Optional<LegacyOrder> orderInModernSystem = acl.findOrderInModernSystem(id);
|
||||||
|
|
||||||
if (orderInModernSystem.isPresent()) {
|
if (orderInModernSystem.isPresent()) {
|
||||||
// order is already in the modern system
|
// order is already in the modern system
|
||||||
} else {
|
} else {
|
||||||
// place order in the current system
|
// place order in the current system
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+53
-53
@@ -3,10 +3,10 @@ title: API Gateway
|
|||||||
category: Architectural
|
category: Architectural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- API design
|
- API design
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Microservices
|
- Microservices
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -68,27 +68,27 @@ Here's the Image microservice implementation.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface ImageClient {
|
public interface ImageClient {
|
||||||
String getImagePath();
|
String getImagePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImageClientImpl implements ImageClient {
|
public class ImageClientImpl implements ImageClient {
|
||||||
@Override
|
@Override
|
||||||
public String getImagePath() {
|
public String getImagePath() {
|
||||||
var httpClient = HttpClient.newHttpClient();
|
var httpClient = HttpClient.newHttpClient();
|
||||||
var httpGet = HttpRequest.newBuilder()
|
var httpGet = HttpRequest.newBuilder()
|
||||||
.GET()
|
.GET()
|
||||||
.uri(URI.create("http://localhost:50005/image-path"))
|
.uri(URI.create("http://localhost:50005/image-path"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
||||||
return httpResponse.body();
|
return httpResponse.body();
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -96,28 +96,28 @@ Here's the Price microservice implementation.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface PriceClient {
|
public interface PriceClient {
|
||||||
String getPrice();
|
String getPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PriceClientImpl implements PriceClient {
|
public class PriceClientImpl implements PriceClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPrice() {
|
public String getPrice() {
|
||||||
var httpClient = HttpClient.newHttpClient();
|
var httpClient = HttpClient.newHttpClient();
|
||||||
var httpGet = HttpRequest.newBuilder()
|
var httpGet = HttpRequest.newBuilder()
|
||||||
.GET()
|
.GET()
|
||||||
.uri(URI.create("http://localhost:50006/price"))
|
.uri(URI.create("http://localhost:50006/price"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
||||||
return httpResponse.body();
|
return httpResponse.body();
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -126,26 +126,26 @@ Here we can see how API Gateway maps the requests to the microservices.
|
|||||||
```java
|
```java
|
||||||
public class ApiGateway {
|
public class ApiGateway {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ImageClient imageClient;
|
private ImageClient imageClient;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private PriceClient priceClient;
|
private PriceClient priceClient;
|
||||||
|
|
||||||
@RequestMapping(path = "/desktop", method = RequestMethod.GET)
|
@RequestMapping(path = "/desktop", method = RequestMethod.GET)
|
||||||
public DesktopProduct getProductDesktop() {
|
public DesktopProduct getProductDesktop() {
|
||||||
var desktopProduct = new DesktopProduct();
|
var desktopProduct = new DesktopProduct();
|
||||||
desktopProduct.setImagePath(imageClient.getImagePath());
|
desktopProduct.setImagePath(imageClient.getImagePath());
|
||||||
desktopProduct.setPrice(priceClient.getPrice());
|
desktopProduct.setPrice(priceClient.getPrice());
|
||||||
return desktopProduct;
|
return desktopProduct;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/mobile", method = RequestMethod.GET)
|
@RequestMapping(path = "/mobile", method = RequestMethod.GET)
|
||||||
public MobileProduct getProductMobile() {
|
public MobileProduct getProductMobile() {
|
||||||
var mobileProduct = new MobileProduct();
|
var mobileProduct = new MobileProduct();
|
||||||
mobileProduct.setPrice(priceClient.getPrice());
|
mobileProduct.setPrice(priceClient.getPrice());
|
||||||
return mobileProduct;
|
return mobileProduct;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: Arrange/Act/Assert
|
|||||||
category: Testing
|
category: Testing
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Idiom
|
- Idiom
|
||||||
- Testing
|
- Testing
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -51,28 +51,28 @@ Let's first introduce our `Cash` class to be unit tested.
|
|||||||
```java
|
```java
|
||||||
public class Cash {
|
public class Cash {
|
||||||
|
|
||||||
private int amount;
|
private int amount;
|
||||||
|
|
||||||
Cash(int amount) {
|
Cash(int amount) {
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
}
|
|
||||||
|
|
||||||
void plus(int addend) {
|
|
||||||
amount += addend;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean minus(int subtrahend) {
|
|
||||||
if (amount >= subtrahend) {
|
|
||||||
amount -= subtrahend;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int count() {
|
void plus(int addend) {
|
||||||
return amount;
|
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
|
```java
|
||||||
class CashAAATest {
|
class CashAAATest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPlus() {
|
void testPlus() {
|
||||||
//Arrange
|
//Arrange
|
||||||
var cash = new Cash(3);
|
var cash = new Cash(3);
|
||||||
//Act
|
//Act
|
||||||
cash.plus(4);
|
cash.plus(4);
|
||||||
//Assert
|
//Assert
|
||||||
assertEquals(7, cash.count());
|
assertEquals(7, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMinus() {
|
void testMinus() {
|
||||||
//Arrange
|
//Arrange
|
||||||
var cash = new Cash(8);
|
var cash = new Cash(8);
|
||||||
//Act
|
//Act
|
||||||
var result = cash.minus(5);
|
var result = cash.minus(5);
|
||||||
//Assert
|
//Assert
|
||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
assertEquals(3, cash.count());
|
assertEquals(3, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInsufficientMinus() {
|
void testInsufficientMinus() {
|
||||||
//Arrange
|
//Arrange
|
||||||
var cash = new Cash(1);
|
var cash = new Cash(1);
|
||||||
//Act
|
//Act
|
||||||
var result = cash.minus(6);
|
var result = cash.minus(6);
|
||||||
//Assert
|
//Assert
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
assertEquals(1, cash.count());
|
assertEquals(1, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate() {
|
void testUpdate() {
|
||||||
//Arrange
|
//Arrange
|
||||||
var cash = new Cash(5);
|
var cash = new Cash(5);
|
||||||
//Act
|
//Act
|
||||||
cash.plus(6);
|
cash.plus(6);
|
||||||
var result = cash.minus(3);
|
var result = cash.minus(3);
|
||||||
//Assert
|
//Assert
|
||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
assertEquals(8, cash.count());
|
assertEquals(8, cash.count());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ title: Async Method Invocation
|
|||||||
category: Concurrency
|
category: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Asynchronous
|
- Asynchronous
|
||||||
- Reactive
|
- Reactive
|
||||||
- Scalability
|
- Scalability
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -48,29 +48,29 @@ manages the execution of the async tasks.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface AsyncResult<T> {
|
public interface AsyncResult<T> {
|
||||||
boolean isCompleted();
|
boolean isCompleted();
|
||||||
|
|
||||||
T getValue() throws ExecutionException;
|
T getValue() throws ExecutionException;
|
||||||
|
|
||||||
void await() throws InterruptedException;
|
void await() throws InterruptedException;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface AsyncCallback<T> {
|
public interface AsyncCallback<T> {
|
||||||
void onComplete(T value);
|
void onComplete(T value);
|
||||||
|
|
||||||
void onError(Exception ex);
|
void onError(Exception ex);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface AsyncExecutor {
|
public interface AsyncExecutor {
|
||||||
<T> AsyncResult<T> startProcess(Callable<T> task);
|
<T> AsyncResult<T> startProcess(Callable<T> task);
|
||||||
|
|
||||||
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
|
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
|
||||||
|
|
||||||
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -80,35 +80,35 @@ next.
|
|||||||
```java
|
```java
|
||||||
public class ThreadAsyncExecutor implements AsyncExecutor {
|
public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> AsyncResult<T> startProcess(Callable<T> task) {
|
public <T> AsyncResult<T> startProcess(Callable<T> task) {
|
||||||
return startProcess(task, null);
|
return startProcess(task, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
||||||
var result = new CompletableResult<>(callback);
|
var result = new CompletableResult<>(callback);
|
||||||
new Thread(
|
new Thread(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
result.setValue(task.call());
|
result.setValue(task.call());
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
result.setException(ex);
|
result.setException(ex);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"executor-" + idx.incrementAndGet())
|
"executor-" + idx.incrementAndGet())
|
||||||
.start();
|
.start();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T endProcess(AsyncResult<T> asyncResult)
|
public <T> T endProcess(AsyncResult<T> asyncResult)
|
||||||
throws ExecutionException, InterruptedException {
|
throws ExecutionException, InterruptedException {
|
||||||
if (!asyncResult.isCompleted()) {
|
if (!asyncResult.isCompleted()) {
|
||||||
asyncResult.await();
|
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
|
```java
|
||||||
public static void main(String[]args)throws Exception{
|
public static void main(String[]args)throws Exception{
|
||||||
// construct a new executor that will run async tasks
|
// construct a new executor that will run async tasks
|
||||||
var executor=new ThreadAsyncExecutor();
|
var executor=new ThreadAsyncExecutor();
|
||||||
|
|
||||||
// start few async tasks with varying processing times, two last with callback handlers
|
// start few async tasks with varying processing times, two last with callback handlers
|
||||||
final var asyncResult1=executor.startProcess(lazyval(10,500));
|
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 asyncResult3=executor.startProcess(lazyval(50L,700));
|
||||||
final var asyncResult4=executor.startProcess(lazyval(20,400),callback("Deploying lunar rover"));
|
final var asyncResult4=executor.startProcess(lazyval(20,400),callback("Deploying lunar rover"));
|
||||||
final var asyncResult5=
|
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
|
// 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
|
Thread.sleep(350); // Oh boy, we are working hard here
|
||||||
log("Mission command is sipping coffee");
|
log("Mission command is sipping coffee");
|
||||||
|
|
||||||
// wait for completion of the tasks
|
// wait for completion of the tasks
|
||||||
final var result1=executor.endProcess(asyncResult1);
|
final var result1=executor.endProcess(asyncResult1);
|
||||||
final var result2=executor.endProcess(asyncResult2);
|
final var result2=executor.endProcess(asyncResult2);
|
||||||
final var result3=executor.endProcess(asyncResult3);
|
final var result3=executor.endProcess(asyncResult3);
|
||||||
asyncResult4.await();
|
asyncResult4.await();
|
||||||
asyncResult5.await();
|
asyncResult5.await();
|
||||||
|
|
||||||
// log the results of the tasks, callbacks log immediately when complete
|
// log the results of the tasks, callbacks log immediately when complete
|
||||||
log("Space rocket <"+result1+"> launch complete");
|
log("Space rocket <"+result1+"> launch complete");
|
||||||
log("Space rocket <"+result2+"> launch complete");
|
log("Space rocket <"+result2+"> launch complete");
|
||||||
log("Space rocket <"+result3+"> launch complete");
|
log("Space rocket <"+result3+"> launch complete");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's the program console output.
|
Here's the program console output.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launched successfully
|
21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launched successfully
|
||||||
21:47:08.269[main]INFO com.iluwatar.async.method.invocation.App-Mission command is sipping coffee
|
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.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.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.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<callback> launched successfully
|
21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket<callback> launched successfully
|
||||||
21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<callback>
|
21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<callback>
|
||||||
21:47:08.616[executor-3]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launched successfully
|
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<10>launch complete
|
||||||
21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launch complete
|
21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launch complete
|
||||||
21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
|
21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
|
||||||
```
|
```
|
||||||
|
|
||||||
# Class diagram
|
# Class diagram
|
||||||
|
|||||||
+41
-41
@@ -3,7 +3,7 @@ title: Balking
|
|||||||
category: Concurrency
|
category: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -44,36 +44,36 @@ Here are the relevant parts of the `WashingMachine` class.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class WashingMachine {
|
public class WashingMachine {
|
||||||
|
|
||||||
private final DelayProvider delayProvider;
|
private final DelayProvider delayProvider;
|
||||||
private WashingMachineState washingMachineState;
|
private WashingMachineState washingMachineState;
|
||||||
|
|
||||||
public WashingMachine(DelayProvider delayProvider) {
|
public WashingMachine(DelayProvider delayProvider) {
|
||||||
this.delayProvider = delayProvider;
|
this.delayProvider = delayProvider;
|
||||||
this.washingMachineState = WashingMachineState.ENABLED;
|
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;
|
|
||||||
}
|
}
|
||||||
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
|
|
||||||
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void endOfWashing() {
|
public WashingMachineState getWashingMachineState() {
|
||||||
washingMachineState = WashingMachineState.ENABLED;
|
return washingMachineState;
|
||||||
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
|
}
|
||||||
}
|
|
||||||
|
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
|
```java
|
||||||
public interface DelayProvider {
|
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
|
```java
|
||||||
public static void main(String...args){
|
public static void main(String...args){
|
||||||
final var washingMachine=new WashingMachine();
|
final var washingMachine=new WashingMachine();
|
||||||
var executorService=Executors.newFixedThreadPool(3);
|
var executorService=Executors.newFixedThreadPool(3);
|
||||||
for(int i=0;i< 3;i++){
|
for(int i=0;i< 3;i++){
|
||||||
executorService.execute(washingMachine::wash);
|
executorService.execute(washingMachine::wash);
|
||||||
}
|
}
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
try{
|
try{
|
||||||
executorService.awaitTermination(10,TimeUnit.SECONDS);
|
executorService.awaitTermination(10,TimeUnit.SECONDS);
|
||||||
}catch(InterruptedException ie){
|
}catch(InterruptedException ie){
|
||||||
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here is the console output of the program.
|
Here is the console output of the program.
|
||||||
|
|||||||
+91
-91
@@ -3,9 +3,9 @@ title: Bridge
|
|||||||
category: Structural
|
category: Structural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Extensibility
|
- Extensibility
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -21,8 +21,8 @@ Decouple an abstraction from its implementation so that the two can vary indepen
|
|||||||
Real-world example
|
Real-world example
|
||||||
|
|
||||||
> Consider you have a weapon with different enchantments, and you are supposed to allow mixing different weapons with
|
> 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
|
> different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments
|
||||||
> would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the
|
> or would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the
|
||||||
> second.
|
> second.
|
||||||
|
|
||||||
In Plain Words
|
In Plain Words
|
||||||
@@ -41,77 +41,77 @@ Translating our weapon example from above. Here we have the `Weapon` hierarchy:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Weapon {
|
public interface Weapon {
|
||||||
void wield();
|
void wield();
|
||||||
|
|
||||||
void swing();
|
void swing();
|
||||||
|
|
||||||
void unwield();
|
void unwield();
|
||||||
|
|
||||||
Enchantment getEnchantment();
|
Enchantment getEnchantment();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Sword implements Weapon {
|
public class Sword implements Weapon {
|
||||||
|
|
||||||
private final Enchantment enchantment;
|
private final Enchantment enchantment;
|
||||||
|
|
||||||
public Sword(Enchantment enchantment) {
|
public Sword(Enchantment enchantment) {
|
||||||
this.enchantment = enchantment;
|
this.enchantment = enchantment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void wield() {
|
public void wield() {
|
||||||
LOGGER.info("The sword is wielded.");
|
LOGGER.info("The sword is wielded.");
|
||||||
enchantment.onActivate();
|
enchantment.onActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void swing() {
|
public void swing() {
|
||||||
LOGGER.info("The sword is swung.");
|
LOGGER.info("The sword is swung.");
|
||||||
enchantment.apply();
|
enchantment.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unwield() {
|
public void unwield() {
|
||||||
LOGGER.info("The sword is unwielded.");
|
LOGGER.info("The sword is unwielded.");
|
||||||
enchantment.onDeactivate();
|
enchantment.onDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enchantment getEnchantment() {
|
public Enchantment getEnchantment() {
|
||||||
return enchantment;
|
return enchantment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Hammer implements Weapon {
|
public class Hammer implements Weapon {
|
||||||
|
|
||||||
private final Enchantment enchantment;
|
private final Enchantment enchantment;
|
||||||
|
|
||||||
public Hammer(Enchantment enchantment) {
|
public Hammer(Enchantment enchantment) {
|
||||||
this.enchantment = enchantment;
|
this.enchantment = enchantment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void wield() {
|
public void wield() {
|
||||||
LOGGER.info("The hammer is wielded.");
|
LOGGER.info("The hammer is wielded.");
|
||||||
enchantment.onActivate();
|
enchantment.onActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void swing() {
|
public void swing() {
|
||||||
LOGGER.info("The hammer is swung.");
|
LOGGER.info("The hammer is swung.");
|
||||||
enchantment.apply();
|
enchantment.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unwield() {
|
public void unwield() {
|
||||||
LOGGER.info("The hammer is unwielded.");
|
LOGGER.info("The hammer is unwielded.");
|
||||||
enchantment.onDeactivate();
|
enchantment.onDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enchantment getEnchantment() {
|
public Enchantment getEnchantment() {
|
||||||
return enchantment;
|
return enchantment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -119,47 +119,47 @@ Here's the separate enchantment hierarchy:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Enchantment {
|
public interface Enchantment {
|
||||||
void onActivate();
|
void onActivate();
|
||||||
|
|
||||||
void apply();
|
void apply();
|
||||||
|
|
||||||
void onDeactivate();
|
void onDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FlyingEnchantment implements Enchantment {
|
public class FlyingEnchantment implements Enchantment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivate() {
|
public void onActivate() {
|
||||||
LOGGER.info("The item begins to glow faintly.");
|
LOGGER.info("The item begins to glow faintly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply() {
|
public void apply() {
|
||||||
LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
|
LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeactivate() {
|
public void onDeactivate() {
|
||||||
LOGGER.info("The item's glow fades.");
|
LOGGER.info("The item's glow fades.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SoulEatingEnchantment implements Enchantment {
|
public class SoulEatingEnchantment implements Enchantment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivate() {
|
public void onActivate() {
|
||||||
LOGGER.info("The item spreads bloodlust.");
|
LOGGER.info("The item spreads bloodlust.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply() {
|
public void apply() {
|
||||||
LOGGER.info("The item eats the soul of enemies.");
|
LOGGER.info("The item eats the soul of enemies.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeactivate() {
|
public void onDeactivate() {
|
||||||
LOGGER.info("Bloodlust slowly disappears.");
|
LOGGER.info("Bloodlust slowly disappears.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -167,16 +167,16 @@ Here are both hierarchies in action:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
LOGGER.info("The knight receives an enchanted sword.");
|
LOGGER.info("The knight receives an enchanted sword.");
|
||||||
var enchantedSword=new Sword(new SoulEatingEnchantment());
|
var enchantedSword=new Sword(new SoulEatingEnchantment());
|
||||||
enchantedSword.wield();
|
enchantedSword.wield();
|
||||||
enchantedSword.swing();
|
enchantedSword.swing();
|
||||||
enchantedSword.unwield();
|
enchantedSword.unwield();
|
||||||
|
|
||||||
LOGGER.info("The valkyrie receives an enchanted hammer.");
|
LOGGER.info("The valkyrie receives an enchanted hammer.");
|
||||||
var hammer=new Hammer(new FlyingEnchantment());
|
var hammer=new Hammer(new FlyingEnchantment());
|
||||||
hammer.wield();
|
hammer.wield();
|
||||||
hammer.swing();
|
hammer.swing();
|
||||||
hammer.unwield();
|
hammer.unwield();
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's the console output.
|
Here's the console output.
|
||||||
|
|||||||
+47
-47
@@ -3,7 +3,7 @@ title: Builder
|
|||||||
category: Creational
|
category: Creational
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -34,7 +34,7 @@ all seen a constructor like below:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public Hero(Profession profession,String name,HairType hairType,HairColor hairColor,Armor armor,Weapon weapon){
|
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
|
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
|
```java
|
||||||
public final class Hero {
|
public final class Hero {
|
||||||
private final Profession profession;
|
private final Profession profession;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final HairType hairType;
|
private final HairType hairType;
|
||||||
private final HairColor hairColor;
|
private final HairColor hairColor;
|
||||||
private final Armor armor;
|
private final Armor armor;
|
||||||
private final Weapon weapon;
|
private final Weapon weapon;
|
||||||
|
|
||||||
private Hero(Builder builder) {
|
private Hero(Builder builder) {
|
||||||
this.profession = builder.profession;
|
this.profession = builder.profession;
|
||||||
this.name = builder.name;
|
this.name = builder.name;
|
||||||
this.hairColor = builder.hairColor;
|
this.hairColor = builder.hairColor;
|
||||||
this.hairType = builder.hairType;
|
this.hairType = builder.hairType;
|
||||||
this.weapon = builder.weapon;
|
this.weapon = builder.weapon;
|
||||||
this.armor = builder.armor;
|
this.armor = builder.armor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -69,44 +69,44 @@ Then we have the builder:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final Profession profession;
|
private final Profession profession;
|
||||||
private final String name;
|
private final String name;
|
||||||
private HairType hairType;
|
private HairType hairType;
|
||||||
private HairColor hairColor;
|
private HairColor hairColor;
|
||||||
private Armor armor;
|
private Armor armor;
|
||||||
private Weapon weapon;
|
private Weapon weapon;
|
||||||
|
|
||||||
public Builder(Profession profession, String name) {
|
public Builder(Profession profession, String name) {
|
||||||
if (profession == null || name == null) {
|
if (profession == null || name == null) {
|
||||||
throw new IllegalArgumentException("profession and name can not be 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) {
|
public Builder withHairType(HairType hairType) {
|
||||||
this.hairType = hairType;
|
this.hairType = hairType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withHairColor(HairColor hairColor) {
|
public Builder withHairColor(HairColor hairColor) {
|
||||||
this.hairColor = hairColor;
|
this.hairColor = hairColor;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withArmor(Armor armor) {
|
public Builder withArmor(Armor armor) {
|
||||||
this.armor = armor;
|
this.armor = armor;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withWeapon(Weapon weapon) {
|
public Builder withWeapon(Weapon weapon) {
|
||||||
this.weapon = weapon;
|
this.weapon = weapon;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hero build() {
|
public Hero build() {
|
||||||
return new Hero(this);
|
return new Hero(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+41
-41
@@ -3,7 +3,7 @@ title: Business Delegate
|
|||||||
category: Structural
|
category: Structural
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -41,23 +41,23 @@ First, we have an abstraction for video streaming services and a couple of imple
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface VideoStreamingService {
|
public interface VideoStreamingService {
|
||||||
void doProcessing();
|
void doProcessing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class NetflixService implements VideoStreamingService {
|
public class NetflixService implements VideoStreamingService {
|
||||||
@Override
|
@Override
|
||||||
public void doProcessing() {
|
public void doProcessing() {
|
||||||
LOGGER.info("NetflixService is now processing");
|
LOGGER.info("NetflixService is now processing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class YouTubeService implements VideoStreamingService {
|
public class YouTubeService implements VideoStreamingService {
|
||||||
@Override
|
@Override
|
||||||
public void doProcessing() {
|
public void doProcessing() {
|
||||||
LOGGER.info("YouTubeService is now processing");
|
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
|
@Setter
|
||||||
public class BusinessLookup {
|
public class BusinessLookup {
|
||||||
|
|
||||||
private NetflixService netflixService;
|
private NetflixService netflixService;
|
||||||
private YouTubeService youTubeService;
|
private YouTubeService youTubeService;
|
||||||
|
|
||||||
public VideoStreamingService getBusinessService(String movie) {
|
public VideoStreamingService getBusinessService(String movie) {
|
||||||
if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
|
if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
|
||||||
return netflixService;
|
return netflixService;
|
||||||
} else {
|
} else {
|
||||||
return youTubeService;
|
return youTubeService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -89,12 +89,12 @@ video streaming service.
|
|||||||
@Setter
|
@Setter
|
||||||
public class BusinessDelegate {
|
public class BusinessDelegate {
|
||||||
|
|
||||||
private BusinessLookup lookupService;
|
private BusinessLookup lookupService;
|
||||||
|
|
||||||
public void playbackMovie(String movie) {
|
public void playbackMovie(String movie) {
|
||||||
VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
|
VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
|
||||||
videoStreamingService.doProcessing();
|
videoStreamingService.doProcessing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -103,15 +103,15 @@ The mobile client utilizes Business Delegate to call the business tier.
|
|||||||
```java
|
```java
|
||||||
public class MobileClient {
|
public class MobileClient {
|
||||||
|
|
||||||
private final BusinessDelegate businessDelegate;
|
private final BusinessDelegate businessDelegate;
|
||||||
|
|
||||||
public MobileClient(BusinessDelegate businessDelegate) {
|
public MobileClient(BusinessDelegate businessDelegate) {
|
||||||
this.businessDelegate = businessDelegate;
|
this.businessDelegate = businessDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playbackMovie(String movie) {
|
public void playbackMovie(String movie) {
|
||||||
businessDelegate.playbackMovie(movie);
|
businessDelegate.playbackMovie(movie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,18 +120,18 @@ Finally, we can demonstrate the complete example in action.
|
|||||||
```java
|
```java
|
||||||
public static void main(String[]args){
|
public static void main(String[]args){
|
||||||
|
|
||||||
// prepare the objects
|
// prepare the objects
|
||||||
var businessDelegate=new BusinessDelegate();
|
var businessDelegate=new BusinessDelegate();
|
||||||
var businessLookup=new BusinessLookup();
|
var businessLookup=new BusinessLookup();
|
||||||
businessLookup.setNetflixService(new NetflixService());
|
businessLookup.setNetflixService(new NetflixService());
|
||||||
businessLookup.setYouTubeService(new YouTubeService());
|
businessLookup.setYouTubeService(new YouTubeService());
|
||||||
businessDelegate.setLookupService(businessLookup);
|
businessDelegate.setLookupService(businessLookup);
|
||||||
|
|
||||||
// create the client and use the Business Delegate
|
// create the client and use the Business Delegate
|
||||||
var client=new MobileClient(businessDelegate);
|
var client=new MobileClient(businessDelegate);
|
||||||
client.playbackMovie("Die Hard 2");
|
client.playbackMovie("Die Hard 2");
|
||||||
client.playbackMovie("Maradona: The Greatest Ever");
|
client.playbackMovie("Maradona: The Greatest Ever");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here is the console output.
|
Here is the console output.
|
||||||
|
|||||||
+123
-123
@@ -3,7 +3,7 @@ title: Bytecode
|
|||||||
category: Behavioral
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Game programming
|
- Game programming
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -15,8 +15,8 @@ Allows encoding behavior as instructions for a virtual machine.
|
|||||||
Real world example
|
Real world example
|
||||||
|
|
||||||
> A team is working on a new game where wizards battle against each other. The wizard behavior needs to be carefully
|
> 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
|
> adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes
|
||||||
> time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual
|
> each time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual
|
||||||
> machine.
|
> machine.
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
@@ -42,21 +42,21 @@ One of the most important game objects is the `Wizard` class.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class Wizard {
|
public class Wizard {
|
||||||
|
|
||||||
private int health;
|
private int health;
|
||||||
private int agility;
|
private int agility;
|
||||||
private int wisdom;
|
private int wisdom;
|
||||||
private int numberOfPlayedSounds;
|
private int numberOfPlayedSounds;
|
||||||
private int numberOfSpawnedParticles;
|
private int numberOfSpawnedParticles;
|
||||||
|
|
||||||
public void playSound() {
|
public void playSound() {
|
||||||
LOGGER.info("Playing sound");
|
LOGGER.info("Playing sound");
|
||||||
numberOfPlayedSounds++;
|
numberOfPlayedSounds++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnParticles() {
|
public void spawnParticles() {
|
||||||
LOGGER.info("Spawning particles");
|
LOGGER.info("Spawning particles");
|
||||||
numberOfSpawnedParticles++;
|
numberOfSpawnedParticles++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -70,18 +70,18 @@ together and pushes the result to the stack.
|
|||||||
@Getter
|
@Getter
|
||||||
public enum Instruction {
|
public enum Instruction {
|
||||||
|
|
||||||
LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
|
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_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_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
|
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
|
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
|
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_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_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
|
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
|
ADD(10), // e.g. "ADD", pop 2 values, push their sum
|
||||||
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -94,89 +94,89 @@ the game object behavior.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class VirtualMachine {
|
public class VirtualMachine {
|
||||||
|
|
||||||
private final Stack<Integer> stack = new Stack<>();
|
private final Stack<Integer> stack = new Stack<>();
|
||||||
|
|
||||||
private final Wizard[] wizards = new Wizard[2];
|
private final Wizard[] wizards = new Wizard[2];
|
||||||
|
|
||||||
public VirtualMachine() {
|
public VirtualMachine() {
|
||||||
wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||||
0, 0);
|
0, 0);
|
||||||
wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||||
0, 0);
|
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 void setHealth(int wizard, int amount) {
|
public VirtualMachine(Wizard wizard1, Wizard wizard2) {
|
||||||
wizards[wizard].setHealth(amount);
|
wizards[0] = wizard1;
|
||||||
}
|
wizards[1] = wizard2;
|
||||||
// other setters ->
|
}
|
||||||
// ...
|
|
||||||
|
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
|
```java
|
||||||
public static void main(String[]args){
|
public static void main(String[]args){
|
||||||
|
|
||||||
var vm=new VirtualMachine(
|
var vm=new VirtualMachine(
|
||||||
new Wizard(45,7,11,0,0),
|
new Wizard(45,7,11,0,0),
|
||||||
new Wizard(36,18,8,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("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here is the console output.
|
Here is the console output.
|
||||||
|
|||||||
+177
-177
@@ -3,9 +3,9 @@ title: Caching
|
|||||||
category: Performance optimization
|
category: Performance optimization
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Caching
|
- Caching
|
||||||
- Performance
|
- Performance
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -24,9 +24,9 @@ again.
|
|||||||
Real world example
|
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
|
> 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
|
> after registering, but all the new posts require approval from one of the site moderators. The user accounts of the
|
||||||
> moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag each
|
> site moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag
|
||||||
> time a post is viewed becomes expensive, and it's a good idea to utilize caching here.
|
> each time a post is viewed becomes expensive, and it's a good idea to utilize caching here.
|
||||||
|
|
||||||
In plain words
|
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
|
> 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
|
> 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.
|
> elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it
|
||||||
> Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower
|
> cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading
|
||||||
> data store; thus, the more requests that can be served from the cache, the faster the system performs.
|
> from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
@@ -53,24 +53,24 @@ to/from database.
|
|||||||
@ToString
|
@ToString
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class UserAccount {
|
public class UserAccount {
|
||||||
private String userId;
|
private String userId;
|
||||||
private String userName;
|
private String userName;
|
||||||
private String additionalInfo;
|
private String additionalInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DbManager {
|
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
|
@Slf4j
|
||||||
public class LruCache {
|
public class LruCache {
|
||||||
|
|
||||||
static class Node {
|
static class Node {
|
||||||
String userId;
|
String userId;
|
||||||
UserAccount userAccount;
|
UserAccount userAccount;
|
||||||
Node previous;
|
Node previous;
|
||||||
Node next;
|
Node next;
|
||||||
|
|
||||||
public Node(String userId, UserAccount userAccount) {
|
public Node(String userId, UserAccount userAccount) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.userAccount = userAccount;
|
this.userAccount = userAccount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* ... omitted details ... */
|
/* ... omitted details ... */
|
||||||
|
|
||||||
public LruCache(int capacity) {
|
public LruCache(int capacity) {
|
||||||
this.capacity = 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;
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(String userId, UserAccount userAccount) {
|
public UserAccount get(String userId) {
|
||||||
if (cache.containsKey(userId)) {
|
if (cache.containsKey(userId)) {
|
||||||
var old = cache.get(userId);
|
var node = cache.get(userId);
|
||||||
old.userAccount = userAccount;
|
remove(node);
|
||||||
remove(old);
|
setHead(node);
|
||||||
setHead(old);
|
return node.userAccount;
|
||||||
} else {
|
}
|
||||||
var newNode = new Node(userId, userAccount);
|
return null;
|
||||||
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 boolean contains(String userId) {
|
public void set(String userId, UserAccount userAccount) {
|
||||||
return cache.containsKey(userId);
|
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<UserAccount> getCacheDataInListForm() { /* ... */ }
|
public void clear() { /* ... */ }
|
||||||
|
|
||||||
public void setCapacity(int newCapacity) { /* ... */ }
|
public List<UserAccount> 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
|
@Slf4j
|
||||||
public class CacheStore {
|
public class CacheStore {
|
||||||
|
|
||||||
private static final int CAPACITY = 3;
|
private static final int CAPACITY = 3;
|
||||||
private static LruCache cache;
|
private static LruCache cache;
|
||||||
private final DbManager dbManager;
|
private final DbManager dbManager;
|
||||||
|
|
||||||
/* ... details omitted ... */
|
/* ... details omitted ... */
|
||||||
|
|
||||||
public UserAccount readThrough(final String userId) {
|
public UserAccount readThrough(final String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Found in Cache!");
|
LOGGER.info("# Found in Cache!");
|
||||||
return cache.get(userId);
|
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) {
|
public void writeThrough(final UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
dbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
} else {
|
} else {
|
||||||
dbManager.writeToDb(userAccount);
|
dbManager.writeToDb(userAccount);
|
||||||
|
}
|
||||||
|
cache.set(userAccount.getUserId(), userAccount);
|
||||||
}
|
}
|
||||||
cache.set(userAccount.getUserId(), userAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeAround(final UserAccount userAccount) {
|
public void writeAround(final UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
dbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
// Cache data has been updated -- remove older
|
// Cache data has been updated -- remove older
|
||||||
cache.invalidate(userAccount.getUserId());
|
cache.invalidate(userAccount.getUserId());
|
||||||
// version from cache.
|
// version from cache.
|
||||||
} else {
|
} else {
|
||||||
dbManager.writeToDb(userAccount);
|
dbManager.writeToDb(userAccount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearCache() {
|
public static void clearCache() {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void flushCache() {
|
public static void flushCache() {
|
||||||
LOGGER.info("# flushCache...");
|
LOGGER.info("# flushCache...");
|
||||||
Optional.ofNullable(cache)
|
Optional.ofNullable(cache)
|
||||||
.map(LruCache::getCacheDataInListForm)
|
.map(LruCache::getCacheDataInListForm)
|
||||||
.orElse(List.of())
|
.orElse(List.of())
|
||||||
.forEach(DbManager::updateDb);
|
.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
|
@Slf4j
|
||||||
public final class AppManager {
|
public final class AppManager {
|
||||||
|
|
||||||
private static CachingPolicy cachingPolicy;
|
private static CachingPolicy cachingPolicy;
|
||||||
private final DbManager dbManager;
|
private final DbManager dbManager;
|
||||||
private final CacheStore cacheStore;
|
private final CacheStore cacheStore;
|
||||||
|
|
||||||
private AppManager() {
|
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);
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save(final UserAccount userAccount) {
|
public void initDb() { /* ... */ }
|
||||||
LOGGER.info("Save record!");
|
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
|
||||||
cacheStore.writeThrough(userAccount);
|
|
||||||
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
public static void initCacheCapacity(int capacity) { /* ... */ }
|
||||||
cacheStore.writeAround(userAccount);
|
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
public UserAccount find(final String userId) {
|
||||||
cacheStore.writeBehind(userAccount);
|
LOGGER.info("Trying to find {} in cache", userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
if (cachingPolicy == CachingPolicy.THROUGH
|
||||||
saveAside(userAccount);
|
|| 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() {
|
public void save(final UserAccount userAccount) {
|
||||||
return CacheStore.print();
|
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
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
boolean isDbMongo = isDbMongo(args);
|
boolean isDbMongo = isDbMongo(args);
|
||||||
if (isDbMongo) {
|
if (isDbMongo) {
|
||||||
LOGGER.info("Using the Mongo database engine to run the application.");
|
LOGGER.info("Using the Mongo database engine to run the application.");
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Using the 'in Memory' database to run the application.");
|
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() {
|
public void useReadAndWriteThroughStrategy() {
|
||||||
LOGGER.info("# CachingPolicy.THROUGH");
|
LOGGER.info("# CachingPolicy.THROUGH");
|
||||||
appManager.initCachingPolicy(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);
|
appManager.save(userAccount1);
|
||||||
LOGGER.info(appManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
appManager.find("001");
|
appManager.find("001");
|
||||||
appManager.find("001");
|
appManager.find("001");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
||||||
|
|
||||||
public void useReadThroughAndWriteBehindStrategy() { /* ... */ }
|
public void useReadThroughAndWriteBehindStrategy() { /* ... */ }
|
||||||
|
|
||||||
public void useCacheAsideStrategy() { /* ... */ }
|
public void useCacheAsideStrategy() { /* ... */ }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -3,10 +3,10 @@ title: Callback
|
|||||||
category: Functional
|
category: Functional
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Asynchronous
|
- Asynchronous
|
||||||
- Decoupling
|
- Decoupling
|
||||||
- Idiom
|
- Idiom
|
||||||
- Reactive
|
- Reactive
|
||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
@@ -42,7 +42,7 @@ Callback is a simple interface with single method.
|
|||||||
```java
|
```java
|
||||||
public interface Callback {
|
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
|
```java
|
||||||
public abstract class Task {
|
public abstract class Task {
|
||||||
|
|
||||||
final void executeWith(Callback callback) {
|
final void executeWith(Callback callback) {
|
||||||
execute();
|
execute();
|
||||||
Optional.ofNullable(callback).ifPresent(Callback::call);
|
Optional.ofNullable(callback).ifPresent(Callback::call);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void execute();
|
public abstract void execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public final class SimpleTask extends Task {
|
public final class SimpleTask extends Task {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
LOGGER.info("Perform some important activity and after call the callback method.");
|
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
|
```java
|
||||||
var task=new SimpleTask();
|
var task=new SimpleTask();
|
||||||
task.executeWith(()->LOGGER.info("I'm done now."));
|
task.executeWith(()->LOGGER.info("I'm done now."));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: Chain of responsibility
|
|||||||
category: Behavioral
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
- Decoupling
|
- Decoupling
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -48,27 +48,27 @@ import lombok.Getter;
|
|||||||
@Getter
|
@Getter
|
||||||
public class Request {
|
public class Request {
|
||||||
|
|
||||||
private final RequestType requestType;
|
private final RequestType requestType;
|
||||||
private final String requestDescription;
|
private final String requestDescription;
|
||||||
private boolean handled;
|
private boolean handled;
|
||||||
|
|
||||||
public Request(final RequestType requestType, final String requestDescription) {
|
public Request(final RequestType requestType, final String requestDescription) {
|
||||||
this.requestType = Objects.requireNonNull(requestType);
|
this.requestType = Objects.requireNonNull(requestType);
|
||||||
this.requestDescription = Objects.requireNonNull(requestDescription);
|
this.requestDescription = Objects.requireNonNull(requestDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markHandled() {
|
public void markHandled() {
|
||||||
this.handled = true;
|
this.handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getRequestDescription();
|
return getRequestDescription();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RequestType {
|
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
|
```java
|
||||||
public interface RequestHandler {
|
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
|
@Slf4j
|
||||||
public class OrcCommander implements RequestHandler {
|
public class OrcCommander implements RequestHandler {
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandleRequest(Request req) {
|
public boolean canHandleRequest(Request req) {
|
||||||
return req.getRequestType() == RequestType.DEFEND_CASTLE;
|
return req.getRequestType() == RequestType.DEFEND_CASTLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPriority() {
|
public int getPriority() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(Request req) {
|
public void handle(Request req) {
|
||||||
req.markHandled();
|
req.markHandled();
|
||||||
LOGGER.info("{} handling request \"{}\"", name(), req);
|
LOGGER.info("{} handling request \"{}\"", name(), req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return "Orc commander";
|
return "Orc commander";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander
|
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander
|
||||||
@@ -119,24 +119,24 @@ The Orc King gives the orders and forms the chain.
|
|||||||
```java
|
```java
|
||||||
public class OrcKing {
|
public class OrcKing {
|
||||||
|
|
||||||
private List<RequestHandler> handlers;
|
private List<RequestHandler> handlers;
|
||||||
|
|
||||||
public OrcKing() {
|
public OrcKing() {
|
||||||
buildChain();
|
buildChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildChain() {
|
private void buildChain() {
|
||||||
handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
|
handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeRequest(Request req) {
|
public void makeRequest(Request req) {
|
||||||
handlers
|
handlers
|
||||||
.stream()
|
.stream()
|
||||||
.sorted(Comparator.comparing(RequestHandler::getPriority))
|
.sorted(Comparator.comparing(RequestHandler::getPriority))
|
||||||
.filter(handler -> handler.canHandleRequest(req))
|
.filter(handler -> handler.canHandleRequest(req))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(handler -> handler.handle(req));
|
.ifPresent(handler -> handler.handle(req));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -144,9 +144,9 @@ The chain of responsibility in action.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
var king=new OrcKing();
|
var king=new OrcKing();
|
||||||
king.makeRequest(new Request(RequestType.DEFEND_CASTLE,"defend castle"));
|
king.makeRequest(new Request(RequestType.DEFEND_CASTLE,"defend castle"));
|
||||||
king.makeRequest(new Request(RequestType.TORTURE_PRISONER,"torture prisoner"));
|
king.makeRequest(new Request(RequestType.TORTURE_PRISONER,"torture prisoner"));
|
||||||
king.makeRequest(new Request(RequestType.COLLECT_TAX,"collect tax"));
|
king.makeRequest(new Request(RequestType.COLLECT_TAX,"collect tax"));
|
||||||
```
|
```
|
||||||
|
|
||||||
The console output.
|
The console output.
|
||||||
|
|||||||
+196
-197
@@ -3,9 +3,9 @@ title: Circuit Breaker
|
|||||||
category: Resilience
|
category: Resilience
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
- Fault tolerance
|
- Fault tolerance
|
||||||
- Microservices
|
- Microservices
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## 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
|
> 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
|
> 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
|
> [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application
|
||||||
> to crash. We should be able to detect
|
> to crash. We should be able to detect this situation and show the user an appropriate message so that he/she can
|
||||||
> this situation and show the user an appropriate message so that he/she can explore other parts of
|
> explore other parts of the app unaffected by the remote serv'ice failure. Meanwhile, the other services that are
|
||||||
> the app unaffected by the remote service failure. Meanwhile, the other services that are working
|
> working normally, should keep functioning unaffected by this failure.
|
||||||
> normally, should keep functioning unaffected by this failure.
|
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
@@ -62,59 +61,59 @@ In terms of code, the end user application is:
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
*
|
*
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
var serverStartTime = System.nanoTime();
|
var serverStartTime = System.nanoTime();
|
||||||
|
|
||||||
var delayedService = new DelayedRemoteService(serverStartTime, 5);
|
var delayedService = new DelayedRemoteService(serverStartTime, 5);
|
||||||
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
|
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
|
||||||
2000 * 1000 * 1000);
|
2000 * 1000 * 1000);
|
||||||
|
|
||||||
var quickService = new QuickRemoteService();
|
var quickService = new QuickRemoteService();
|
||||||
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
|
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
|
||||||
2000 * 1000 * 1000);
|
2000 * 1000 * 1000);
|
||||||
|
|
||||||
//Create an object of monitoring service which makes both local and remote calls
|
//Create an object of monitoring service which makes both local and remote calls
|
||||||
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
|
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
|
||||||
quickServiceCircuitBreaker);
|
quickServiceCircuitBreaker);
|
||||||
|
|
||||||
//Fetch response from local resource
|
//Fetch response from local resource
|
||||||
LOGGER.info(monitoringService.localResourceResponse());
|
LOGGER.info(monitoringService.localResourceResponse());
|
||||||
|
|
||||||
//Fetch response from delayed service 2 times, to meet the failure threshold
|
//Fetch response from delayed service 2 times, to meet the failure threshold
|
||||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||||
|
|
||||||
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
|
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
|
||||||
//which is OPEN now
|
//which is OPEN now
|
||||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||||
|
|
||||||
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
|
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
|
||||||
LOGGER.info(monitoringService.quickServiceResponse());
|
LOGGER.info(monitoringService.quickServiceResponse());
|
||||||
LOGGER.info(quickServiceCircuitBreaker.getState());
|
LOGGER.info(quickServiceCircuitBreaker.getState());
|
||||||
|
|
||||||
//Wait for the delayed service to become responsive
|
//Wait for the delayed service to become responsive
|
||||||
try {
|
try {
|
||||||
LOGGER.info("Waiting for delayed service to become responsive");
|
LOGGER.info("Waiting for delayed service to become responsive");
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOGGER.error("An error occurred: ", 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
|
```java
|
||||||
public class MonitoringService {
|
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) {
|
public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
|
||||||
this.delayedService = delayedService;
|
this.delayedService = delayedService;
|
||||||
this.quickService = quickService;
|
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();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
|
||||||
* Fetches response from a healthy service without any failure.
|
public String localResourceResponse() {
|
||||||
*
|
return "Local Service is working";
|
||||||
* @return response string
|
}
|
||||||
*/
|
|
||||||
public String quickServiceResponse() {
|
/**
|
||||||
try {
|
* Fetch response from the delayed service (with some simulated startup time).
|
||||||
return this.quickService.attemptRequest();
|
*
|
||||||
} catch (RemoteServiceException e) {
|
* @return response string
|
||||||
return e.getMessage();
|
*/
|
||||||
|
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
|
```java
|
||||||
public class DefaultCircuitBreaker implements CircuitBreaker {
|
public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||||
|
|
||||||
private final long timeout;
|
private final long timeout;
|
||||||
private final long retryTimePeriod;
|
private final long retryTimePeriod;
|
||||||
private final RemoteService service;
|
private final RemoteService service;
|
||||||
long lastFailureTime;
|
long lastFailureTime;
|
||||||
private String lastFailureResponse;
|
private String lastFailureResponse;
|
||||||
int failureCount;
|
int failureCount;
|
||||||
private final int failureThreshold;
|
private final int failureThreshold;
|
||||||
private State state;
|
private State state;
|
||||||
// Future time offset, in nanoseconds
|
// Future time offset, in nanoseconds
|
||||||
private final long futureTime = 1_000_000_000_000L;
|
private final long futureTime = 1_000_000_000_000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to create an instance of Circuit Breaker.
|
* Constructor to create an instance of Circuit Breaker.
|
||||||
*
|
*
|
||||||
* @param timeout Timeout for the API request. Not necessary for this simple example
|
* @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
|
* @param failureThreshold Number of failures we receive from the depended service before changing
|
||||||
* state to 'OPEN'
|
* state to 'OPEN'
|
||||||
* @param retryTimePeriod Time period after which a new request is made to remote service for
|
* @param retryTimePeriod Time period after which a new request is made to remote service for
|
||||||
* status check.
|
* status check.
|
||||||
*/
|
*/
|
||||||
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
|
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
|
||||||
long retryTimePeriod) {
|
long retryTimePeriod) {
|
||||||
this.service = serviceToCall;
|
this.service = serviceToCall;
|
||||||
// We start in a closed state hoping that everything is fine
|
// We start in a closed state hoping that everything is fine
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this.failureThreshold = failureThreshold;
|
this.failureThreshold = failureThreshold;
|
||||||
// Timeout for the API request.
|
// Timeout for the API request.
|
||||||
// Used to break the calls made to remote resource if it exceeds the limit
|
// Used to break the calls made to remote resource if it exceeds the limit
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
this.retryTimePeriod = retryTimePeriod;
|
this.retryTimePeriod = retryTimePeriod;
|
||||||
//An absurd amount of time in future which basically indicates the last failure never happened
|
//An absurd amount of time in future which basically indicates the last failure never happened
|
||||||
this.lastFailureTime = System.nanoTime() + futureTime;
|
this.lastFailureTime = System.nanoTime() + futureTime;
|
||||||
this.failureCount = 0;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Reset everything to defaults
|
||||||
public String getState() {
|
@Override
|
||||||
evaluateState();
|
public void recordSuccess() {
|
||||||
return state.name();
|
this.failureCount = 0;
|
||||||
}
|
this.lastFailureTime = System.nanoTime() + futureTime;
|
||||||
|
this.state = State.CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if
|
public void recordFailure(String response) {
|
||||||
* service comes online before expected.
|
failureCount = failureCount + 1;
|
||||||
*
|
|
||||||
* @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();
|
this.lastFailureTime = System.nanoTime();
|
||||||
}
|
// Cache the failure response for returning on open state
|
||||||
case HALF_OPEN -> {
|
this.lastFailureResponse = response;
|
||||||
this.failureCount = failureThreshold;
|
|
||||||
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
|
|
||||||
}
|
|
||||||
default -> this.failureCount = 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
|
||||||
* Executes service call.
|
protected void evaluateState() {
|
||||||
*
|
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
|
||||||
* @return Value from the remote resource, stale response or a custom exception
|
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
|
||||||
*/
|
//We have waited long enough and should try checking if service is up
|
||||||
@Override
|
state = State.HALF_OPEN;
|
||||||
public String attemptRequest() throws RemoteServiceException {
|
} else {
|
||||||
evaluateState();
|
//Service would still probably be down
|
||||||
if (state == State.OPEN) {
|
state = State.OPEN;
|
||||||
// return cached response if the circuit is in OPEN state
|
}
|
||||||
return this.lastFailureResponse;
|
} else {
|
||||||
} else {
|
//Everything is working fine
|
||||||
// Make the API request if the circuit is not OPEN
|
state = State.CLOSED;
|
||||||
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
|
@Override
|
||||||
var response = service.call();
|
public String getState() {
|
||||||
// Yay!! the API responded fine. Let's reset everything.
|
evaluateState();
|
||||||
recordSuccess();
|
return state.name();
|
||||||
return response;
|
}
|
||||||
} catch (RemoteServiceException ex) {
|
|
||||||
recordFailure(ex.getMessage());
|
/**
|
||||||
throw ex;
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+27
-27
@@ -3,8 +3,8 @@ title: Client Session
|
|||||||
category: Behavioral
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Session management
|
- Session management
|
||||||
- Web development
|
- Web development
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -21,17 +21,17 @@ application, ensuring a continuous and personalized user experience.
|
|||||||
Real-World Example
|
Real-World Example
|
||||||
|
|
||||||
> You're looking to create a data management app allowing users to send requests to the server to modify and make
|
> 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
|
> changes to data stored on their devices. These requests are small and the data is individual to each user, negating
|
||||||
> need for a large scale database implementation. Using the client session pattern, you are able to handle multiple
|
> 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
|
> 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
|
> also remove the need to store session IDs on the server side due to clients providing all the information that a
|
||||||
> needs to perform their process.
|
> server needs to perform their process.
|
||||||
|
|
||||||
In Plain words
|
In Plain words
|
||||||
|
|
||||||
> Instead of storing information about the current client and the information being accessed on the server, it is
|
> 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
|
> maintained client side only. Client has to send session data with each request to the server and has to send an
|
||||||
> state back to the client, which is stored on the clients machine. The server doesn't have to store the client
|
> 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))
|
> information. ([ref](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client))
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
@@ -46,30 +46,30 @@ session information in every request helps the server identify the client and pr
|
|||||||
```java
|
```java
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var server = new Server("localhost", 8080);
|
var server = new Server("localhost", 8080);
|
||||||
var session1 = server.getSession("Session1");
|
var session1 = server.getSession("Session1");
|
||||||
var session2 = server.getSession("Session2");
|
var session2 = server.getSession("Session2");
|
||||||
var request1 = new Request("Data1", session1);
|
var request1 = new Request("Data1", session1);
|
||||||
var request2 = new Request("Data2", session2);
|
var request2 = new Request("Data2", session2);
|
||||||
server.process(request1);
|
server.process(request1);
|
||||||
server.process(request2);
|
server.process(request2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Session {
|
public class Session {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session id.
|
* Session id.
|
||||||
*/
|
*/
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client name.
|
* Client name.
|
||||||
*/
|
*/
|
||||||
private String clientName;
|
private String clientName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,9 +77,9 @@ public class Session {
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Request {
|
public class Request {
|
||||||
|
|
||||||
private String data;
|
private String data;
|
||||||
|
|
||||||
private Session session;
|
private Session session;
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ title: Collecting Parameter
|
|||||||
category: Behavioral
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Accumulation
|
- Accumulation
|
||||||
- Generic
|
- Generic
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -55,33 +55,33 @@ import java.util.LinkedList;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
static PrinterQueue printerQueue = PrinterQueue.getInstance();
|
static PrinterQueue printerQueue = PrinterQueue.getInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
*
|
*
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
/*
|
/*
|
||||||
Initialising the printer queue with jobs
|
Initialising the printer queue with jobs
|
||||||
*/
|
*/
|
||||||
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 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.A3, 2, false, false));
|
||||||
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
|
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This variable is the collecting parameter.
|
This variable is the collecting parameter.
|
||||||
*/
|
*/
|
||||||
var result = new LinkedList<PrinterItem>();
|
var result = new LinkedList<PrinterItem>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Using numerous sub-methods to collaboratively add information to the result collecting parameter
|
* Using numerous sub-methods to collaboratively add information to the result collecting parameter
|
||||||
*/
|
*/
|
||||||
addA4Papers(result);
|
addA4Papers(result);
|
||||||
addA3Papers(result);
|
addA3Papers(result);
|
||||||
addA2Papers(result);
|
addA2Papers(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -90,71 +90,75 @@ appropriate print jobs as per the policy described previously. The three policie
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class App {
|
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
|
* Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client
|
||||||
* (the print center) wants.
|
* (the print center) wants.
|
||||||
*
|
*
|
||||||
* @param printerItemsCollection the collecting parameter
|
* @param printerItemsCollection the collecting parameter
|
||||||
*/
|
*/
|
||||||
public static void addA4Papers(Queue<PrinterItem> printerItemsCollection) {
|
public static void addA4Papers(Queue<PrinterItem> printerItemsCollection) {
|
||||||
/*
|
/*
|
||||||
Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter,
|
Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter,
|
||||||
which is 'printerItemsCollection' in this case.
|
which is 'printerItemsCollection' in this case.
|
||||||
*/
|
*/
|
||||||
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
||||||
if (nextItem.paperSize.equals(PaperSizes.A4)) {
|
if (nextItem.paperSize.equals(PaperSizes.A4)) {
|
||||||
var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided;
|
var isColouredAndSingleSided =
|
||||||
if (isColouredAndSingleSided) {
|
nextItem.isColour && !nextItem.isDoubleSided;
|
||||||
printerItemsCollection.add(nextItem);
|
if (isColouredAndSingleSided) {
|
||||||
} else if (!nextItem.isColour) {
|
printerItemsCollection.add(nextItem);
|
||||||
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
|
* 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 print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
|
||||||
* the wants of the client.
|
* the wants of the client.
|
||||||
*
|
*
|
||||||
* @param printerItemsCollection the collecting parameter
|
* @param printerItemsCollection the collecting parameter
|
||||||
*/
|
*/
|
||||||
public static void addA3Papers(Queue<PrinterItem> printerItemsCollection) {
|
public static void addA3Papers(Queue<PrinterItem> printerItemsCollection) {
|
||||||
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
||||||
if (nextItem.paperSize.equals(PaperSizes.A3)) {
|
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
|
// 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;
|
var isNotColouredAndSingleSided =
|
||||||
if (isNotColouredAndSingleSided) {
|
!nextItem.isColour && !nextItem.isDoubleSided;
|
||||||
printerItemsCollection.add(nextItem);
|
if (isNotColouredAndSingleSided) {
|
||||||
|
printerItemsCollection.add(nextItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds A2 document jobs to the collecting parameter according to some policy that can be whatever the client
|
* 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 print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
|
||||||
* the wants of the client.
|
* the wants of the client.
|
||||||
*
|
*
|
||||||
* @param printerItemsCollection the collecting parameter
|
* @param printerItemsCollection the collecting parameter
|
||||||
*/
|
*/
|
||||||
public static void addA2Papers(Queue<PrinterItem> printerItemsCollection) {
|
public static void addA2Papers(Queue<PrinterItem> printerItemsCollection) {
|
||||||
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
|
||||||
if (nextItem.paperSize.equals(PaperSizes.A2)) {
|
if (nextItem.paperSize.equals(PaperSizes.A2)) {
|
||||||
|
|
||||||
// Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured.
|
// 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
|
var isNotColouredSingleSidedAndOnePage =
|
||||||
&& !nextItem.isColour;
|
nextItem.pageCount == 1 &&
|
||||||
if (isNotColouredSingleSidedAndOnePage) {
|
!nextItem.isDoubleSided
|
||||||
printerItemsCollection.add(nextItem);
|
&& !nextItem.isColour;
|
||||||
|
if (isNotColouredSingleSidedAndOnePage) {
|
||||||
|
printerItemsCollection.add(nextItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user