From 9f43b7017da045314ca919645815dc6b3c83cc17 Mon Sep 17 00:00:00 2001 From: Bacluc Date: Mon, 26 Feb 2024 12:17:44 +0000 Subject: [PATCH] topics: add software concepts topic It is an overview of some of the concepts one should have heard as a software developer. The topics are not discussed in depth. For the patterns part the explanations on https://github.com/iluwatar/java-design-patterns are used. Before the breaks we go through the code examples look for the learned patterns and concepts. Issue: #65 --- topics/CMakeLists.txt | 1 + topics/sw_concepts/CMakeLists.txt | 7 + topics/sw_concepts/code/abstractions.py | 120 +++++ .../images/button_factory/footer_c.png | Bin 0 -> 3236 bytes .../images/button_factory/footer_m.png | Bin 0 -> 15679 bytes .../no-dependency-inversion.png | Bin 0 -> 7910 bytes .../no-dependency-inversion.puml | 13 + .../with-dependency-inversion.png | Bin 0 -> 10688 bytes .../with-dependency-inversion.puml | 15 + .../java-interface-segregation.png | Bin 0 -> 60826 bytes .../java-interface-segregation.puml | 56 +++ .../sw_concepts/sw_concept_code_examples.md | 408 ++++++++++++++++ topics/sw_concepts/sw_concept_slides.md | 439 ++++++++++++++++++ 13 files changed, 1059 insertions(+) create mode 100644 topics/sw_concepts/CMakeLists.txt create mode 100644 topics/sw_concepts/code/abstractions.py create mode 100644 topics/sw_concepts/images/button_factory/footer_c.png create mode 100644 topics/sw_concepts/images/button_factory/footer_m.png create mode 100644 topics/sw_concepts/images/dependency-inversion/no-dependency-inversion.png create mode 100644 topics/sw_concepts/images/dependency-inversion/no-dependency-inversion.puml create mode 100644 topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.png create mode 100644 topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.puml create mode 100644 topics/sw_concepts/images/interface-segregation/java-interface-segregation.png create mode 100644 topics/sw_concepts/images/interface-segregation/java-interface-segregation.puml create mode 100644 topics/sw_concepts/sw_concept_code_examples.md create mode 100644 topics/sw_concepts/sw_concept_slides.md diff --git a/topics/CMakeLists.txt b/topics/CMakeLists.txt index 76d756f..396d885 100644 --- a/topics/CMakeLists.txt +++ b/topics/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(build_systems) add_subdirectory(docker) add_subdirectory(git) add_subdirectory(linux) +add_subdirectory(sw_concepts) add_subdirectory(testing) add_subdirectory(tools_and_varia) diff --git a/topics/sw_concepts/CMakeLists.txt b/topics/sw_concepts/CMakeLists.txt new file mode 100644 index 0000000..a8aa080 --- /dev/null +++ b/topics/sw_concepts/CMakeLists.txt @@ -0,0 +1,7 @@ +include(js_document) + +js_slides(sw_concept_slides sw_concept_slides.md) +js_slides(sw_concept_code_examples sw_concept_code_examples.md) + +file(GLOB_RECURSE code RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "code/*") +js_add_to_global_archive_file_list(${code}) diff --git a/topics/sw_concepts/code/abstractions.py b/topics/sw_concepts/code/abstractions.py new file mode 100644 index 0000000..ec590ca --- /dev/null +++ b/topics/sw_concepts/code/abstractions.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +import errno +import math +import os +import sys +import time +from optparse import OptionParser + +# Censored hardware prototype2 and series (tsl2550) value range: 0 - 4015, daylight: 544 +# Censored hardware v3 (opt3001) value range: 0 - 36157, daylight: 290 +INPUT_MIN = 0 +INPUT_MAX_TSL2550 = 544 +INPUT_MAX_OPT3001 = 290 +INPUT_THRESHOLD = 11 + +# Censored hardware prototype2 (Intel backlight) value range: 0 - 937 +# Censored hardware series Device (Intel backlight) value range: 0 - 7500 +OUTPUT_MIN_FACTOR = 0.2 +OUTPUT_CHANGE_MAX_FACTOR = 0.005 + +sensor_value_lux_approx_map = [ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 49, 53, 57, 61, 65, 69, 73, 77, + 81, 85, 89, 93, 97, 101, 105, 109, + 115, 123, 131, 139, 147, 155, 163, 171, + 179, 187, 195, 203, 211, 219, 227, 235, + 247, 263, 279, 295, 311, 327, 343, 359, + 375, 391, 407, 423, 439, 455, 471, 487, + 511, 543, 575, 607, 639, 671, 703, 735, + 767, 799, 831, 863, 895, 927, 959, 991, + 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487, + 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999, + 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991, + 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015 +] + + +def main(): + input_max = INPUT_MAX_TSL2550 + output_last_value = None + + parser = OptionParser() + _, args = parser.parse_args() + + if len(args) != 3: + raise Exception("wrong number of arguments") + + path = args[0] + input_path = args[1] + output_path = args[2] + opt3001 = "in_illuminance_input" in os.readlink(input_path) + + input_last_value = INPUT_MIN - INPUT_THRESHOLD + with open(path) as f: + output_max = int(f.readline().strip()) + output_min = int(math.ceil(output_max * OUTPUT_MIN_FACTOR)) + output_change_max = int(math.ceil(output_max * OUTPUT_CHANGE_MAX_FACTOR)) + + while True: + try: + if opt3001: + try: + with open(input_path) as f1: + input_value = float(f1.readline().strip()) + except IOError: + # This driver generates a read error if very little light is present + input_value = INPUT_MIN + else: + with open(input_path) as f: + input_value = int(f.readline().strip()) + if 0 <= input_value < len(sensor_value_lux_approx_map): + input_value = sensor_value_lux_approx_map[input_value] + else: + input_value = input_last_value + + input_value = min(input_value, input_max) + + # Ignore small input value changes + if abs(input_value - input_last_value) < INPUT_THRESHOLD: + input_value = input_last_value + + a = (input_value - INPUT_MIN) / float(input_max - INPUT_MIN) + value1 = int(a * float(output_max - output_min) + output_min) + output_value = min(value1, output_max) + + if output_last_value is None: + output_value = output_value + elif output_value >= output_last_value: + output_value = min(output_value, output_last_value + output_change_max) + else: + output_value = max(output_value, output_last_value - output_change_max) + dimmed_value = output_value + + if output_value != output_last_value: + print(f"input: %4i (%4.1f%%), output: %4i (%4.1f%%), dimmed: %4i (%4.1f%%)", + input_value, 100 * (input_value - INPUT_MIN) / float(input_max - INPUT_MIN), + output_value, 100 * (output_value - output_min) / float(output_max - output_min), + dimmed_value, 100 * (dimmed_value - output_min) / float(output_max - output_min)) + sys.stdout.flush() + + with open(output_path, "w") as f: + output_last_value = output_value + f.write(str(output_value)) + + input_last_value = input_value + except IOError as e: + # Ignore EGAIN errors which may happen if the I2C bus is busy + if e.errno != errno.EAGAIN: + raise e + + time.sleep(0.01) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/topics/sw_concepts/images/button_factory/footer_c.png b/topics/sw_concepts/images/button_factory/footer_c.png new file mode 100644 index 0000000000000000000000000000000000000000..2f4cae448198aedceeb7cd324c9623422a254e8f GIT binary patch literal 3236 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 D^?T*6 literal 0 HcmV?d00001 diff --git a/topics/sw_concepts/images/button_factory/footer_m.png b/topics/sw_concepts/images/button_factory/footer_m.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fca5be82286a61cfc9c489c6146dd5c3cb60e2 GIT binary patch literal 15679 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 GU;htkny?1| literal 0 HcmV?d00001 diff --git a/topics/sw_concepts/images/dependency-inversion/no-dependency-inversion.png b/topics/sw_concepts/images/dependency-inversion/no-dependency-inversion.png new file mode 100644 index 0000000000000000000000000000000000000000..20686d168fb7d8a6285c9dcc115ee79e9a9e2de3 GIT binary patch literal 7910 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 DBTransactionRepository + +@enduml diff --git a/topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.png b/topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.png new file mode 100644 index 0000000000000000000000000000000000000000..e10bcd93bd5fea89a32f703501f6ae2add8a1bb0 GIT binary patch literal 10688 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 literal 0 HcmV?d00001 diff --git a/topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.puml b/topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.puml new file mode 100644 index 0000000..286d683 --- /dev/null +++ b/topics/sw_concepts/images/dependency-inversion/with-dependency-inversion.puml @@ -0,0 +1,15 @@ +@startuml + +package domain { + class TransactionObserver + + interface TransactionRepository +} + +package persistence { + class DBTransactionRepository implements domain.TransactionRepository +} + +TransactionObserver --> TransactionRepository + +@enduml diff --git a/topics/sw_concepts/images/interface-segregation/java-interface-segregation.png b/topics/sw_concepts/images/interface-segregation/java-interface-segregation.png new file mode 100644 index 0000000000000000000000000000000000000000..1d6800cd646242a2cf91b8d37951faf5e05fef1e GIT binary patch literal 60826 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 literal 0 HcmV?d00001 diff --git a/topics/sw_concepts/images/interface-segregation/java-interface-segregation.puml b/topics/sw_concepts/images/interface-segregation/java-interface-segregation.puml new file mode 100644 index 0000000..2932030 --- /dev/null +++ b/topics/sw_concepts/images/interface-segregation/java-interface-segregation.puml @@ -0,0 +1,56 @@ +@startuml + +!theme plain +skinparam linetype ortho + +class ArrayList { +} +interface Cloneable << interface >> +interface Collection << interface >> { + + remove(Object): boolean + + clear(): void + + removeIf(Predicate): boolean + + isEmpty(): boolean + + addAll(Collection): boolean + + size(): int + + hashCode(): int + + contains(Object): boolean + + add(E): boolean +} +class LinkedHashSet { +} +interface List << interface >> { + + sort(Comparator): void + + isEmpty(): boolean + + contains(Object): boolean + + add(E): boolean + + set(int, E): E + + remove(Object): boolean + + lastIndexOf(Object): int + + size(): int + + remove(int): E + + get(int): E + + add(int, E): void + + clear(): void + + indexOf(Object): int + + containsAll(Collection): boolean +} +interface Serializable << interface >> +interface Set << interface >> { + + contains(Object): boolean + + add(E): boolean + + remove(Object): boolean + + isEmpty(): boolean + + clear(): void + + size(): int +} + +ArrayList -[#008200,dashed]--^ Cloneable +ArrayList -[#008200,dashed]--^ List +ArrayList -[#008200,dashed]--^ Serializable +LinkedHashSet -[#008200,dashed]--^ Cloneable +LinkedHashSet -[#008200,dashed]--^ Serializable +LinkedHashSet -[#008200,dashed]--^ Set +List -[#008200,plain]--^ Collection +Set -[#008200,plain]--^ Collection +@enduml diff --git a/topics/sw_concepts/sw_concept_code_examples.md b/topics/sw_concepts/sw_concept_code_examples.md new file mode 100644 index 0000000..df50fea --- /dev/null +++ b/topics/sw_concepts/sw_concept_code_examples.md @@ -0,0 +1,408 @@ +<#include meta/slides.md> + +--- +title: "SW Konzepte Code Beispiele" +date: \today +--- + +Code Beispiele +------- + +Überlege für die folgenden Code Beispiele folgendes: + +1. Hat das Beispiel etwas mit DRY, KISS, YAGNI oder NIH zu tun? +2. Hat es mit den SOLID Principles zu tun? +3. Welche Design Pattern siehst du? +4. Was könnte sonst noch verbessert werden? + +OrganisationSupplier +------- + +```python +class OrganisationSupplier: + def get_current_organisation(self): + pass + +class ConfigurationOrganisationSupplier(OrganisationSupplier): + def __init__(self, device_location_dao, konfiguration_dao, default_organisation): + self.konfiguration_dao = konfiguration_dao + self.default_organisation = default_organisation + + def get_current_organisation(self): + return self.konfiguration_dao.get_konfigurationKonfigurationDAO.ORGANISATION_ID, self.default_organisation) + +class DeviceLocationOrganisationSupplier(OrganisationSupplier): + def __init__(self, konfiguration_dao, device_location_dao): + self.konfiguration_dao = konfiguration_dao + self.device_location_dao = device_location_dao + + def get_current_organisation(self): + return Optional.ofNullable(self.konfiguration_dao.get_konfiguration(KonfigurationDAO.STANDORT_KENNUNG, None)) \ + .map(int) \ + .map(self.device_location_dao.get_device_location_by_id) \ + .map(lambda device_location_entity: device_location_entity.get_organisation()) +``` + +OrganisationSupplier Auswertung +------- + +1. DRY...: Property Keys werden geteilt (e.g. STANDORT_KENNUNG) +2. SOLID: nicht speziell +3. Pattern: Strategy +4. Verbesserung: - + +AtmelObservableFactory +------- + +```python +class AtmelObservableFactory: + @staticmethod + def create_power_state(host, port, scheduler): + return AtmelObservableFactory.create_power_state_with_interval(host, port, INTERVAL, scheduler) + + @staticmethod + def create_power_state_with_interval(host, port, interval, scheduler): + simple_request = SimpleRequest("inputGetIgnitionState") + ignition_state_request_executor = AtmelRequestExecutor(host, port, simple_request, IgnitionStateResult) + return PollingObservable.create(scheduler, lambda: ..., interval, scheduler) + + @staticmethod + def create_system_state(host, port, scheduler): + return AtmelObservableFactory.create_system_state_with_interval(host, port, INTERVAL, scheduler) + + @staticmethod + def create_system_state_with_interval(host, port, interval, scheduler): + simple_request = SimpleRequest("registerObject", ["InfovisionSystemState"]) + return RetryUntilSuccess.create(AtmelRequestExecutor(host, port, simple_request, SystemStateResult), lambda: interval, scheduler) +``` + +AtmelObservableFactory Auswertung +------- + +1. DRY...: YAGNI: muss das Interval konfigurierbar sein? +2. SOLID: Single Responsibility: muss beides hier erstellt werden? +3. Pattern: AbstractFactory, Factory method +4. Verbesserung: - + +BrightnessParserTest +------- + +```python +def test_parses_brightness_correctly(self): + windows_brightness_output = """ + Power Setting GUID: 3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e (Turn off display after) + Minimum Possible Setting: 0x00000000 + Maximum Possible Setting: 0xffffffff + Possible Settings increment: 0x00000001 + Current AC Power Setting Index: 0x00000000 + Current DC Power Setting Index: 0x00003840 + + Power Setting GUID: aded5e82-b909-4619-9949-f5d71dac0bcb (Display brightness) + Minimum Possible Setting: 0x00000000 + Maximum Possible Setting: 0x00000064 + Possible Settings increment: 0x00000001 + Possible Settings units: % + Current AC Power Setting Index: 0x0000005a + Current DC Power Setting Index: 0x0000005a +""" + self.assertEqual(parse(windows_brightness_output), "0000005a") +``` + +BrightnessParserTest Auswertung +------- + +1. DRY...: "0000005a" könnte geteilt und benamst werden +2. SOLID: - +3. Pattern: - +4. Verbesserung: - + +DoorState +------- + +```java +private synchronized void setDoorState(DoorState newDoorState, + DoorComponent doorComponent) { + doorContext.setDoorState(newDoorState, doorComponent); + if (doorContext.isInconsistent()) { + observers.forEach(DoorObserver::inconsistentSensors); + } else if (DoorState.OPEN.equals(newDoorState)) { + observers.forEach(DoorObserver::switchOpened); + } else if (DoorState.CLOSED.equals(newDoorState) && doorContext.isDoorCompletelyClosed()) { + observers.forEach(DoorObserver::doorClosed); + } + } +``` + +DoorState Auswertung +------- + +1. DRY...: - +2. SOLID: Open/Closed: Für alle neuen Events muss das if statement und das DoorObserver Interface geändert werden. +3. Pattern: Observer +4. Verbesserung: - + +TransactionRepository +------- + +```java +public interface TransactionRepository { + Transaction addTransactions(Collection transactions); + + void updateTransactions(Collection transactions); + + Optional findTransaction(UUID uuid); +} + +HibernateTransactionRepository implements TransactionRepository {} + +public class TransactionObserverTest { + private TransactionObserver transactionObserver; + private TransactionRepository transactionRepository; + @Before + public void givenTransactionObserver() { + transactionRepository = mock(TransactionRepository.class); + transactionObserver = new TransactionObserver(transactionRepository, createTerminalInfoExporter(), createLocationInfoExporter()); + } +``` + +TransactionRepository Auswertung +------- + +1. DRY...: KISS: 1 Mock kann auch einfach mit einer Klasse erreicht werden +2. SOLID: Dependency Inversion +3. Pattern: - +4. Verbesserung: - + +CreditCardAppIdMapperProvider +------- + +```java +public class CreditCardAppIdMapperProvider implements Provider { + private final CreditCardAppIdMapper creditCardAppIdMapper; + + @Inject + public CreditCardAppIdMapperProvider(CreditCardAppIdMappingConfiguration configuration, + CreditCardAppIdCategoryMappingParser creditCardAppIdCategoryMappingParser) { + if (configuration.isEnabled()) { + List mappingEntries = creditCardAppIdCategoryMappingParser.parse(new File(configuration.getMappingFilePath())); + Map map = new HashMap<>(); + mappingEntries.forEach(mappingEntry -> { + CreditCardAppIdMapper.CardAppIdCategory keyToAdd = new CreditCardAppIdMapper.CardAppIdCategory(mappingEntry.getAppIdCategory()); + map.put(keyToAdd, mappingEntry.getLabel()); + }); + creditCardAppIdMapper = new CreditCardAppIdMapper(map); + } else { + creditCardAppIdMapper = new CreditCardAppIdMapper(new HashMap<>()); + } + } + + @Override + public CreditCardAppIdMapper get() { + return creditCardAppIdMapper; + } +} +``` + +CreditCardAppIdMapperProvider Auswertung +------- + +1. DRY...: - +2. SOLID: - +3. Pattern: Factory, [Null Object](https://github.com/iluwatar/java-design-patterns/tree/master/null-object) +4. Verbesserung: Das parsen des Files (Input/Output oder I/O) im Konstruktor +kann das aufstarten Verzögern. Das könnte man wenn möglich asynchron machen. + +Board +------- + +```java +public class Board { + private final List boardObservers = new ArrayList<>(); + + public void executeMove(Move move) { + Command command = createCommand(move); + command.execute(); + boardObservers.forEach(boardObserver -> boardObserver.boardChanged(this)); + } + + public void registerObserver(BoardObserver boardObserver) { + boardObservers.add(boardObserver); + } +} +``` + +Board Auswertung +------- + +1. DRY...: - +2. SOLID: - +3. Pattern: Observer +4. Verbesserung: Keine Möglichkeit um Observer zu entfernen -> Memory Leak + +Board2 +------- + +```python +class SimpleMove(Command): + def execute(self): + self.store.remove_piece(self.start.get_key()) + self.store.add_piece(self.end.get_key(), self.end.get_value()) + + def undo(self): + self.store.add_piece(self.start.get_key(), self.start.get_value()) + self.store.remove_piece(self.end.get_key()) + +class Board: + def __init__(self): + self.executed_commands = [] + + def execute_move(self, move): + command = self.create_command(move) + command.execute() + self.executed_commands.append(command) + + def undo_last_turn(self): + if len(self.executed_commands) == 0: + raise NoPreviousMovesException() + + last_command = self.executed_commands[-1] + last_command.undo() + self.executed_commands.pop() +``` + +Board2 Auswertung +------- + +1. DRY...: - +2. SOLID: - +3. Pattern: Command +4. Verbesserung: - + +DoorEventHandler +------- + +```java +public class DoorEventHandler { + private static final Logger LOGGER = Logger.getLogger(DoorEventHandler.class); + + private final DoorContext doorContext; + + private final Set observers = new HashSet<>(); + + public DoorEventHandler(DoorContext doorContext) { + this.doorContext = doorContext; + } + + public void addObserver(DoorObserver observer) { + } + + @Handler + public void handle(DoorEvent event) { + } +} +``` + +DoorEventHandler Auswertung +------- + +1. DRY...: - +2. SOLID: - +3. Pattern: Observer +4. Verbesserung: + 1. Liste der Observer muss je nach dem Threadsafe sein + 2. Keine Möglichkeit Observer zu entfernen -> Memory Leak + +ButtonFactory +------- + +```python +class ButtonFactory: + def get_bwd_button(self, name, dim, text): + return BwdButton(name, dim, text) + + def get_circle_button(self, name, color, text): + return CircleButton(name, color, text) + + def get_fwd_button(self, name, dim, text): + return FwdButton(name, dim, text) + + def get_info_button(self, name, info_text): + return InfoButton(name, info_text) + +class ButtonFactoryC(ButtonFactory): + pass +``` + +ButtonFactory Auswertung +------- + +1. DRY...: - +2. SOLID: - +3. Pattern: AbstractFactory +4. Verbesserung: Inheritance hat in diesem Fall zu Problemen geführt. +da man nicht Voraussagen kann, wo welche Buttons gebraucht werden, +bietet sich hier Composition + Interface besser an. + +ReceiptFactory +------- + +```python +class ReceiptFactory: + def create_eft_receipt(self, receipt_lines): + pass + + def create_refund_receipt(self, kunden_session_nr, fehl_betrag, beleg_nr, mfk): + pass + +class Customer1ReceiptFactory(ReceiptFactory): + def __init__(self, printer_config): + self.printer_config = printer_config + + def create_eft_receipt(self, receipt_lines): + pass + + def create_refund_receipt(self, kunden_session_nr, fehl_betrag, beleg_nr, mfk): + print("No refund receipt implemented") + return None +``` + +ReceiptFactory Auswertung +------- + +1. DRY...: - +2. SOLID: create_refund_receipt verletzt Liskov's Substitution Principle +3. Pattern: AbstractFactory +4. Verbesserung: - + +OperatingPoint +----- + +```python +class OperatingPointNumberResolveStrategy: + """ + Returns the vending location (Verkaufspunkt) assigned to the operating point number (Betriebspunkt) + """ + def get_net_point(self, operating_point_number): + pass + +class DidokStrategy(OperatingPointNumberResolveStrategy): + def get_net_point(self, operating_point_number): + return self.assortment_provider.get_current_netpoint_by_didok_number( + operating_point_number + ) + +class SubstopNetpointIdStrategy(OperatingPointNumberResolveStrategy): + def get_net_point(self, operating_point_number): + return self.assortment_provider.get_current_netpoint_by_netpoint_id( + operating_point_number, + "SUBSTOP" + ) +``` + +OperatingPoint Auswertung +------- + +1. DRY...: KISS: Hätte auch ein IF statement gereicht? +2. SOLID: - +3. Pattern: Strategy +4. Verbesserung: - diff --git a/topics/sw_concepts/sw_concept_slides.md b/topics/sw_concepts/sw_concept_slides.md new file mode 100644 index 0000000..9f40439 --- /dev/null +++ b/topics/sw_concepts/sw_concept_slides.md @@ -0,0 +1,439 @@ +<#include meta/slides.md> + +--- +title: "SW Konzepte" +date: \today +--- + +Was ist hier falsch? +------- + +Code Beispiel: [abstractions.py](code/abstractions.py) + +abstractions.py Zusammenfassung +------- + +* Fast keine Strukturierung durch Methoden/Funktionen +* Code duplication + +Abstraktion: Nachteile +------- + +* erhöht Komplexität +* Änderung der Abstraktion haben Auswirkungen auf alle User +* Die Abstraktion am falschen Ort (Vertikal) +* Die Abstraktion versteckt wichtige Features + +Abstraktion am falschen Ort +------ + +```java +public void initialize() { + carouselDisposable.dispose(); + carouselDisposable = contactlessPMod.getCarouselProvider() + .addCarouselObserver( + imagePath -> JfxThreadingUtils.invokeOnFx(() -> setContactlessLogo(imagePath)) + ); +} + +private void setContactlessLogo(String imagePath) { + if (imagePath == null || getAcceptance() != ACCEPTED) { + final String imageFileName = getImageFileName() + final String imageUrl = IMAGES_PATH + imageFileName; + vibbekLogo.setImage(new Image(imageUrl)); + } else { + if (contactlessPMod.isCarouselEnabled()) { + vibbekLogo.setImage(new Image(imagePath)); + } else { + vibbekLogo.setImage(new Image(IMAGES_PATH + "static_carousel.png")); + } + } +} +``` + +Agenda +------- + +* Generelle Prinzipien (DRY, KISS...) +* Clean Code +* Design Patterns + +DRY +------- + +* **D**on't **R**epeat **Y**ourself +* Für **Verhalten** aber auch für **Information** +* Was wäre cool, wenn es sich bei allen Copy-and-paste Orten ändern würde +* Gibt dem Extrahierten auch einen Namen + +DRY Beispiel für Konstanten +------ + +```java +assertEquals(new DateTime(2013, 6, 5, 12, 54).toDate(), view.getServiceStartDate()); +assertEquals(new Long(4), view.getServiceSerialId()); +assertEquals(new Long(2), view.getServicePeriodSerialId()); +assertEquals("RGS_EMPTYING_TO_RETURNTRAY", view.getServiceType()); +``` + +KISS +------- + +* **K**eep **i**t **S**hort and **S**imple\ + (Keep it simple stupid) + +YAGNI +------- + +* **Y**ou **a**n't **g**onna **n**eed **i**t + +NIH +------- + +* **N**ot **i**nvented **h**ere +* Dinge selbst implementieren, die schon in der Sprache/in einer Library sind. + +NIH Beispiel 1 +------- + + + +Clean Code +------- + +* **S**ingle Responsibility +* **O**pen Closed Principle +* **L**iskov's Substition Principle +* **I**nterface Segregation +* **D**ependency Inversion + +Single Responsibility +------- + +* Eine Funktion, Klasse, Modul sollte nur eine "Responsibility" haben. +* Grenze ist nicht ganz klar +* Indikator: wenn bei der benennung ein "and" vorkommt, dann sollte man aufteilen. +* Andere Sichtweise: Eine Funktion, Klasse, Modul sollte nur einen Grund haben, sich zu ändern. + +Beispiel Single Responsibility verletzt +------ + +```java +public enum DirectionAndRazziaValue { + DIRECTION_1, + DIRECTION_2, + RAZZIA +} + +private static void updateCountersAndLogEjectedCoins(...) + +public SessionFactoryBuilder withUserAndPassword(String username, String password) { + this.username = username; + this.password = password; + return this; +} + +``` + +Beispiel Single Responsibility verletzt mit Kommentar +------ + +```java +// Werte in unabhängige Felder packen +public enum DirectionAndRazziaValue { + DIRECTION_1, + DIRECTION_2, + RAZZIA +} + +// 2 Concerns trennen +private static void updateCountersAndLogEjectedCoins(...) + +// in 2 methoden aufteilen: es ist ja schon ein Builder +public SessionFactoryBuilder withUserAndPassword(String username, String password) { + this.username = username; + this.password = password; + return this; +} + +``` + +Open Closed +------- + +* "Open for extension, closed for change" +* Neue Funktionalität -> nicht bestehenden code anpassen, sondern möglichst nur code hinzufügen + +Open Closed Beispiel +------- + +Regeln für Dame: + +* Eigenen Spielstein bewegen +* Ziel Feld muss leer sein +* Bauer darf sich 1 weit Feld bewegen, der König 2 +* Bauer darf nur vorwärts +* Bewegung muss diagonal sein +* Beim Springen muss gegnerische Figur dazwischen sein +* Falls man springen kann, muss man springen + + + +Open Closed Beispiel 2 +------- + +Wenn alle Regeln in separaten Klassen sind, macht das den Inhalt klarer. +Auch ist es jetzt einfacher, neue Regeln hinzuzufügen. + + +```python +class StartPieceValid: +class TargetFieldEmpty: +class MoveLength: +class MoveIsForwardIfNotKing: +class MoveIsDiagonal: +class OpponentPieceBetweenJump: +class NoOtherJumpMovePossible: +``` + +Open Closed Beispiel 3 +------- + +Und es ermöglicht composition: + +```python + moveValidators = [ + startPieceValid, + targetFieldEmpty, + moveLength, + moveIsForwardIfNotKing, + moveIsDiagonal, + opponentPieceBetweenJump, + noOtherMoveToJumpPossible + ] + winCondition = new WinCondition([ + startPieceValid, + targetFieldEmpty, + moveLength, + moveIsForwardIfNotKing, + moveIsDiagonal, + opponentPieceBetweenJump + ]) +``` + +Liskovs substitution principle +------- + +* Implementationen eines Interfaces müssen ausgetauscht werden können. +* Sie nehmen mindestens die gleichen Input werte an (dürfen auch mehr). +* Sie geben maximal die gleichen Rückgabewerte zurück. +* Werfen maximal die gleichen Exceptions (oder Subtypen der Exceptions). + +Beispiel Liskovs substitution principle Verletzung +------- + +```python +class Vehicle: + pass + +class Car(Vehicle): + pass + +class Bicycle(Vehicle): + pass + +class Garage: + def park(self, vehicle: Vehicle): + pass + +class BicycleGarage(Garage): + def park(self, vehicle: Vehicle): + if (not isinstance(vehicle, Bicycle)): + raise ValueError("BicycleGarage can only park bicycles") + +``` + +Interface Segregation +------- + +* Um die Kopplung zu reduzieren, kann man auch Interfaces aufteilen. +* Eine Klasse kann mehrere Interfaces implementieren +* Man will mit einer Klasse nicht benötigte Methoden nicht implementieren +* Man will nicht, dass jemand plötzlich eine Methode nutzt, die man gar nicht zur Verfügung stellen wollte. + +Interface Segregation Example +------ + +![Interface Segregation](images/interface-segregation/java-interface-segregation.png){height=95%} + +Dependency Inversion +------ + +* Um 2 Klassen zu entkoppeln kann ein Interface dazwischen geschalten werden. +* Das verhindert, dass eine High Level Komponente von einer Low Level Komponente abhängt. + +Dependency Inversion Beispiel 1 +----- + +![No dependency inversion](images/dependency-inversion/no-dependency-inversion.png) + +Dependency Inversion Beispiel 2 +----- + +![Dependency inversion](images/dependency-inversion/with-dependency-inversion.png){height=95%} + +Design Patterns +------- + + + +Factory (method) +------- + + + +Factory (method) Beispiel +------ + +```python +class CasiceConfigurationFactory: + def create(config): + if not config.is_casice_available(): + return CasiceConfiguration.not_available() + + if config.get_casice_host_override(): + return CasiceConfiguration.with_host(Observable.just(config.get_casice_host_override())) + + return CasiceConfiguration.with_host( + RetryUntilSuccess.create( + CasiceHostSupplier(config.get_casice_network_interface()), + Fibonacci.create(60).map(lambda i: 1000 * i) + ) + ) +``` + +AbstractFactory +------- + + + +AbstractFactory Beispiel 1 +------ + +```python +class ButtonFactory: + def get_bwd_button(self, name, dim, text): + return BwdButton(name, dim, text) + + def get_fwd_button(self, name, dim, text): + return FwdButton(name, dim, text) + + def get_info_button(self, name, info_text): + return InfoButton(name, info_text) + +class ButtonFactoryC(ButtonFactory): + pass + +class ButtonFactoryL(ButtonFactory): + pass +``` + +AbstractFactory Beispiel 2 +------ + +![footer_m](images/button_factory/footer_m.png) +![footer_c](images/button_factory/footer_c.png) + +Strategy +------- + + + +Strategy Example +------ + +```python +class RouteNumberResolveStrategy: + """ + Map the route number for lookup in assortment (menu structures, favorites, ...). + """ + def resolve(self, route_number): + pass + + +class IdentityRouteNumberStrategy(RouteNumberResolveStrategy): + def resolve(self, route_number): + return route_number + +class OrganisationPrefixRouteNumberStrategy(RouteNumberResolveStrategy): + def __init__(self, organisation_supplier): + self.organisation_supplier = organisation_supplier + + def resolve(self, route_number): +``` + +Observer +------- + + + +Observer Beispiel +------- + +```java +public class CarouselProvider { + + private final Set carouselObservers = Collections.synchronizedSet(new HashSet<>()); + private List files = new ArrayList<>(); + private int counter = 0; + + public void addCarouselObserver(CarouselObserver observer) { + carouselObservers.add(observer); + observer.fileChanged(getCurrentImage()); + } + + public void removeCarouselObserver(CarouselObserver observer) { + carouselObservers.remove(observer); + } +} +``` + +Command +------- + + + +Zusammenfassung Abstraktion +------ + +* Mit Abstraktionen + * \+ + * Strukturieren wir den Code + * Geben Konzepten (Abläufen, Konstanten) einen Namen + * Verstecken Details vor höheren Abstraktionsebenen + * Machen den Code DRY + * \- + * Erhöhen wir die Komplexität + * Verstecken Funktionalität + * Koppeln die User der Abstraktion + +Zusammenfassung +------ + +* High Level Abstraktionskonzepte + * DRY + * KISS + * YAGNI + * NIH +* SOLID Principles + * Single Responsibility Principle + * Open Closed Principle + * Liskov's Substition Principle + * Interface Segregation Principle + * Depdendency Inversion Principle +* Design Patterns + * Factory (method) + * AbstractFactory + * Strategy + * Observer + * Command