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