docs: add editorconfig + formatting

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