Introduction
Dieses Repository beinhaltet diverse Zusammenfassungen des Studiengangs 2021 in Informatik an der ZHAW.
Helpers
Hex Helpers
Dec to Hex
decimal = 1 print (f"Hex: {hex(decimal)}") print (f"Octal: {oct(decimal)}")
Hex to Int
hexadecimal = 0xFF print(f"Dec: {int(hexadecimal)}") print(f"Octal: {oct(hexadecimal)}")
Readme
Begriff | Bedeutung | Notwendigkeit |
---|---|---|
MMU | Memory Management Unit | Laufen lassen von mehreren Anwendungen |
BSP | Board Support Package | Benötigt, um auf das Board zuzugreifen |
GPL | Gnu Public License | |
MBR | Master Boot Record | Minimale Boot-Informationen, benötigt zum booten |
Embedded Linux
- Smallest possible linux
- Some open source addons
Key Advantages
- Ability to re-use componentes
- Many components exist already
- No-one should re-develop yet another operation system kernel, TCP/IP stack, USB stack or another graphical tooklit value
-> Focus on the added value of the product
Build system
- Adds dependencies to a minimal kernel
- Automatic creation of linux systems
- Repeatability
Yocto
- Build system
- Allows creation custom linux embedded distributions
Layers
bblayers.conf
BBLAYERS ?= " \
/home/mc2/poky-mickledore/meta \
/home/mc2/poky-mickledore/meta-poky \
/home/mc2/poky-mickledore/meta-yocto-bsp \
/home/mc2/rpi64/meta-openembedded/meta-oe \
/home/mc2/rpi64/meta-raspberrypi \
/home/mc2/rpi64/meta-mc2 \
/home/mc2/rpi64/meta-student \
"
Symbol | Description |
---|---|
= | when using the variable, value is expand |
:= | immediately expand the value |
?= | assign if no other value was previously assigned |
??= | same as previous, with a lower precedence |
SWEN2
exTreme Programming (XP)
Risks in development projects
The role of XP is to give us principles and practives in oder to deal with these risks!
- Schedule slips
- Project canceled
- System does not evolve gracefully (defect rate increses)
- Defect rate
- Business misunderstood
- Business changes
- False feature rich
- Staff turnover (knowhow gets lost by employees leaving)
Variables
-
Time
-
Resources
-
Quality
-
Scope
-
External forces (customers, managers) can set the values for 3 of the variables
-
The development team sets the value for the 4th one
-
It is also possible, to develop cheap software, where automated tests are not of importance
Values
Communication
- Everyone is part of the team and we communicate face to face daily.
- We will work together on everything from requirements to code.
- We will create the best solution to our problem that we can together.
Simplicity
- We will do what is needed and asked for, but no more.
- This will maximize the value created for the investment made to date.
- We will take small simple steps to our goal and mitigate failures as they happen.
- We will create something we are proud of and maintain it long term for reasonable costs.
Feedback
- We will take every iteration commitment seriously by delivering working software.
- We demonstrate our software early and often then listen carefully and make any changes needed.
- We will talk about the project and adapt our process to it, not the other way around.
Courage
- We will tell the truth about progress and estimates.
- We don't document excuses for failure because we plan to succeed.
- We don't fear anything because no one ever works alone.
- We will adapt to changes when ever they happen
Respect
- Everyone gives and feels the respect they deserve as a valued team member.
- Everyone contributes value even if it's simply enthusiasm.
- Developers respect the expertise of the customers and vice versa.
- Management respects our right to accept responsibility and receive authority over our own work.
Cost of Change
Traditional View
The XP View
Basic Principles
Fundamental principles
- Rapid feedback
- Assume simplicity
- Incremental change
- Embracing change
- Quality work
Other principles (contd)
- Teach learning
- Small inital investment
- Play to win
- Concrete experiments
- Open, honest communication
- Work with people's instincts, not against them
- Accepted responsibility
- Local adaption
- Travel light
- Honest mesurement
Practices
Planing Game
- Balance between busines and technical considerations
- Business people decide about:
- Scope
- Priority
- Composition of releases
- Dates of releases
- Technical people decide about:
- Estimates
- Consequences
- Process
- Detailed Scheduling
Small Releases
- Every release should be as small as possible, containing the most valuable business requirements
- The release has to make sense as a whole (no half-working features).
- Better to release once a month than twice a year.
Metaphor
- Everybody on the team needs to have a “common understanding” for the system.
- Everybody on the team needs to have a “shared vocabulary”.
- This applies to technical and non-technical people.
- What are the basic elements of the system and what are their relationships?
Simple Design
- The right design for a software system is one that:
- Runs all tests.
- Has no duplicated logic.
- Has the fewest possible classes and methods.
- “Put in what need when you need it”.
- Emergent, growing design; no big design upfront (through refactoring)
Testing
- Any program feature without an automated test simply doesn’t exist.
- The tests become part of the system.
- The tests allow the system to accept change.
- Development cycle:
- Listen (requirements)
- Test (write first)
- Code (simplest)
- Design (refactor)
Refactoring
- When implementing a feature, ask yourself if there is a way to improve the existing source code, so that implementing the feature is easier.
- Automated tests provide a safety-net for refactoring without fear.
Pair Programming
- All production code is written by two people looking at one screen, with one keyboard and one mouse.
- Two roles. The programmer on the keyboard focuses on the current method. The other programmer thinks about the broader context (refactoring, etc.)
- Pairs change frequently.
Collective Ownership
- Anybody who sees an opportunity to add value to any portion of the code is required to do so at any time. TTL.
- Everybody takes responsibility for the whole of the system. Not everyone knows every part equally well, but everyone knows something about every part.
Continous integration
- Code is integrated and tested at least once a day (sometimes more).
- Build process must me automated, on a dedicated machine.
- Automated tests are run and make it possible to identify problems early.
40 hours week
- Sustainable development. Effort should be spread out evenly.
- Extended periods of overtime have a negative impact on productivity.
- Goal: be fresh every morning, be tired and satisfied every evening.
- Not being in front of a computer does not mean forgetting about the system... taking a step back often leads to “Aha!” moments.
On-site customer
- A real customer must be physically with the team, available to answer their questions.
- Real customer = user who will use the system.
- The real customer does not work on the project 100% of his time, but needs to be “there” to answer questions rapidly.
- The real customer also help with prioritization.
Codign standards
- Collective ownership + constant refactoring means that coding practices must be unified in the team.
Primary Practices
- Sit together
- whole team
- informative workspace
- energized work
- pair programming
- stories
- weekly cycle
- quarterly cycle
- slack
- ten-minute build
- continous integration
- test-first programming
- incremental design
Corollary practices (logische praktiken)
- Real customer involment
- incremental deployment
- team continuity
- shrinking teams
- root-cause analysis
- shared code
- code adn tests
- single code base
- daily deployment
- negotiated scope contract
- pay-per-use
CFD
- Displays stability over time
- Tracks and accumulates each task with their corresponding stage
Stages
- Requested
- In Progress
- Done
SWEN2 Prüfungszusammenfassung
Tidy First? (Buch von Kent Beck)
Wichtigste Ideen und Konzepte:
- Ordnung vor Funktionalität: Die Grundidee ist, dass es einfacher ist, Änderungen in sauberem Code vorzunehmen.
- Kontinuierliches Aufräumen: Code sollte regelmäßig aufgeräumt werden, um technische Schulden zu vermeiden.
- Empirisches Softwaredesign: Entscheidungen sollten auf Grundlage von Erfahrungen und Experimenten getroffen werden.
Einführung Softwareentwicklung
SWEBOK:
- Definition: Software Engineering Body of Knowledge.
- Inhalt: Umfasst grundlegende Prinzipien, Praktiken und Techniken des Software Engineerings.
Plangetriebene Methoden:
- Vorteile: Vorhersagbarkeit, klare Struktur und Dokumentation, gut für große, komplexe Projekte.
- Nachteile: Weniger Flexibilität, schwierig bei sich ändernden Anforderungen.
- Einsatzgebiete: Große, stabile Projekte mit klaren Anforderungen (z.B. Regierungsprojekte).
Gesetze der Softwareentwicklung
Drei Hauptgesetze:
- Lehman's Laws: Software altert und muss kontinuierlich weiterentwickelt werden.
Weitere Gesetze:
- Conway’s Law: Systemdesign spiegelt die Kommunikationsstruktur der Organisation wider.
- Brooks’s Law: Hinzufügen von mehr Personal zu einem verspäteten Projekt verzögert es weiter.
- Parkinson’s Law: Arbeit dehnt sich aus, um die verfügbare Zeit zu füllen.
- Pareto’s Fallacy: 80% der Effekte kommen von 20% der Ursachen.
- Sturgeon’s Revelation: 90% von allem sind Schrott.
- The Iceberg Fallacy: Der größte Teil eines Problems ist unsichtbar.
- The Peter Principle: In einer Hierarchie steigt jeder bis zu seiner Inkompetenz auf.
- Eagleson’s Law: Jeder Programmierer verdoppelt den Speicherbedarf des Programms.
- Greenspun’s 10th Rule: Jede komplexe Software enthält ein schlechtes, in sich verborgenes Lisp.
Agile Manifesto
Values:
- Individuen und Interaktionen über Prozesse und Werkzeuge.
- Funktionierende Software über umfassende Dokumentation.
- Zusammenarbeit mit dem Kunden über Vertragsverhandlungen.
- Reagieren auf Veränderung über das Befolgen eines Plans.
12 Principles:
- Frühe und kontinuierliche Auslieferung wertvoller Software.
- Begrüßen von sich ändernden Anforderungen.
- Lieferung funktionierender Software häufig.
- Tägliche Zusammenarbeit zwischen Geschäftsleuten und Entwicklern.
- Projekte um motivierte Individuen herum aufgebaut.
- Effektive Kommunikation von Angesicht zu Angesicht.
- Funktionierende Software als wichtigstes Fortschrittsmaß.
- Nachhaltige Entwicklung.
- Technische Exzellenz und gutes Design fördern Agilität.
- Einfachheit.
- Selbstorganisierte Teams.
- Regelmäßige Reflexion und Anpassung.
Agile
Begriffe:
- Risk: Potenzielle Probleme, die den Projekterfolg beeinträchtigen könnten.
- Cost of Change: Der Aufwand, der nötig ist, um Änderungen am Projekt vorzunehmen.
- Iron Triangle: Die drei Variablen Scope, Time, und Cost beeinflussen sich gegenseitig.
eXtreme Programming (XP)
Core Values:
- Kommunikation, Einfachheit, Feedback, Mut, Respekt.
Principles:
- Menschliche Werte, Wirtschaftlichkeit, gegenseitiger Nutzen, Selbstähnlichkeit, kontinuierliche Verbesserung.
Practices:
- Planungsspiel, kleine Releases, Metapher, einfaches Design, Tests, Refactoring, Pair Programming, kollektive Code-Eigentümerschaft, kontinuierliche Integration, 40-Stunden-Woche, Vor-Ort-Kunde, Coding-Standards, TDD, Slack, Spike, inkrementelles Design, selbstorganisiertes Team.
Software Craft
Notwendigkeit und Prinzipien:
- Notwendigkeit: Reaktion auf mangelhafte Softwarequalität und -praktiken.
- Formate: Coding Dojo, Code Katas zur Übung und Verbesserung der Programmierfähigkeiten.
- Community-Prinzipien: Kontinuierliches Lernen und Teilen von Wissen.
Pyramid of Agile Competencies
Drei Ebenen:
- Technische Praktiken: Grundlagen wie Testen und Refactoring.
- Team Praktiken: Zusammenarbeit und Kommunikation.
- Business Praktiken: Kundenorientierung und Wertschöpfung.
User Stories
Ziele und Anwendung:
- Ziele: Verständnis der Nutzerbedürfnisse.
- Anwendung: Agile Planung und Priorisierung.
- Format: "Als [Rolle] möchte ich [Funktion], um [Nutzen]."
- Zusammenhang: Epics (große Stories), Themes (thematische Gruppierung), Stories (kleinere, umsetzbare Einheiten).
- Merkmale: Unabhängig, verhandelbar, wertvoll, schätzbar, klein, testbar.
Estimation and Planning
Begriffe:
- User Stories, User Roles, Epics, Themes, Story Points, Velocity, Planning Poker, Conditions of Satisfaction, Levels of Planning, Product Backlog, Priorisierung.
- Schätzungstechniken: Relativ, Planning Poker.
- Planung für Wert: Kosten, finanzieller Wert, Risiko, neues Wissen, Kano-Modell der Kundenzufriedenheit.
Build Automation, CI, CD, DevOps
Arten der Automatisierung:
- On-demand, scheduled, triggered.
Pipeline:
- Schritte von Code-Commit bis Deployment.
Scrum
Charakteristika und Begriffe:
- Scrum-Rollen: Scrum Master, Product Owner, Team.
- Scrum-Events: Sprint, Daily Scrum, Review, Retrospektive.
- Scrum-Artefakte: Product Backlog, Sprint Backlog, Increment.
- Scrum-Werte: Mut, Fokus, Offenheit, Respekt, Commitment.
Begriffe:
- Sprint, Sprintziel, Retrospektive, Task Board, Burndown Chart, Definition of Done, Definition of Ready, Daily Scrum, Increment.
Architektur Patterns (Gruppenpuzzle)
Patterns:
- CQRS: Trennung von Lese- und Schreiboperationen.
- Event Sourcing: Speicherung aller Zustandsänderungen als Ereignisse.
- Strangler Pattern: Schrittweise Ersetzung eines alten Systems durch ein neues.
- Online-Migration: Live-Migration von Daten.
- Circuit Breaker: Schutz vor Ausfällen durch Überlastung.
- Bulkhead: Isolation von Systemkomponenten.
- Retry: Wiederholungsmechanismen bei Fehlern.
- Serverless: Betrieb von Anwendungen ohne Verwaltung von Servern.
- Microservices: Modularisierung von Anwendungen in kleine, unabhängige Dienste.
- Self-contained Systems: Autonome Systeme mit eigener Datenhaltung.
- Monolith (Modulith): Großer, zusammenhängender Codeblock, der in Module aufgeteilt werden kann.
Software Architecture
Grundbegriffe:
- Software-Architektur: Struktur und Organisation eines Softwaresystems.
- Software-Architekt: Verantwortlich für das Design und die Integrität der Architektur.
- Enterprise Architecture vs. Application Architecture: Unternehmensweite vs. anwendungsspezifische Architektur.
- Schwierigkeiten: Komplexität, Konformität, Änderbarkeit, Unsichtbarkeit.
Architectural Drivers:
- Faktoren, die die Architektur beeinflussen (Geschäftsanforderungen, technische Anforderungen).
Standards, Stile, Patterns:
- Standardisierte Lösungen für wiederkehrende Probleme.
- Unterschiede zwischen Layer und Tier.
Architekturstile:
- Vor- und Nachteile von Stilen wie Monolith, Microservices.
Patterns:
- Transaction Script: Verarbeiten von Transaktionen durch einfache Skripte.
- Domain Model: Modellierung der Geschäftslogik.
- Table Module: Datenbanktabellen als zentrale Logikstrukturen.
C4-Modell:
- Kontextsensitive Architekturmodellierung.
Architecture Canvas:
- Werkzeug zur Visualisierung und Planung von Architekturen.
Cynefin Framework / Codefin
Begriffe:
- Cynefin (Komplexitätsframework), Exaptation (Nutzung bestehender Strukturen für neue Zwecke), 4 + 1 Domains (Einfach, Kompliziert, Komplex, Chaotisch, Unordnung), Causality, Correlation, Constraint.
Anwendung:
- Nutzung des Cynefin-Frameworks zur Entscheidungsfindung und Problemlösung in der Softwareentwicklung.
Kanban / Lean Software Development
Zusammenhang und Herkunft:
- Agile, Lean, Scrum, XP und Kanban stammen aus der Lean-Philosophie.
Kanban-Prinzipien:
- Visualisieren des Workflows, Limitierung von WIP (Work in Progress), Management des Flusses.
Kanban Board:
- Werkzeug zur Visualisierung des Workflows.
Cumulative Flow Diagram (CFD):
- Darstellung des Arbeitsflusses zur Identifikation von Engpässen.
Einführung in die Softwareentwicklung
Die Softwareentwicklung ist ein komplexer Prozess, der verschiedene Phasen durchläuft, darunter Planung, Design, Implementierung, Testen, Bereitstellung und Wartung. Ein systematischer Ansatz ist erforderlich, um hochwertige Softwareprodukte zu liefern, die den Anforderungen der Benutzer entsprechen und innerhalb der geplanten Zeit und des Budgets fertiggestellt werden.
SWEBOK: Software Engineering Body of Knowledge
SWEBOK steht für "Software Engineering Body of Knowledge" und ist eine Sammlung von grundlegenden Prinzipien und Praktiken, die für das Software Engineering als Disziplin anerkannt sind. SWEBOK deckt verschiedene Wissensgebiete ab, darunter Anforderungen, Design, Konstruktion, Testen, Wartung, Projektmanagement, Qualitätssicherung und mehr. Es dient als Leitfaden für Software-Ingenieure und hilft dabei, Best Practices zu definieren und zu standardisieren.
Plangetriebene Methoden in der Softwareentwicklung
Plangetriebene Methoden, auch als traditionelle oder sequentielle Methoden bekannt, beinhalten eine umfassende Planung und Dokumentation vor Beginn der eigentlichen Entwicklungsarbeit. Ein typisches Beispiel für eine plangetriebene Methode ist das Wasserfallmodell, bei dem jede Phase des Entwicklungsprozesses in einer festen Reihenfolge durchgeführt wird.
Vorteile der plangetriebenen Methoden
-
Klare Struktur und Reihenfolge:
- Jede Phase hat klar definierte Ziele und Outputs.
- Einfacher zu verfolgen und zu dokumentieren.
-
Vollständige Dokumentation:
- Umfassende Dokumentation ist in jeder Phase erforderlich.
- Hilfreich für zukünftige Wartung und Überprüfung.
-
Vorhersagbarkeit:
- Einfachere Planung von Ressourcen und Zeitplänen.
- Risiken können frühzeitig identifiziert und gemanagt werden.
-
Einheitliche Anforderungen:
- Anforderungen werden zu Beginn des Projekts klar definiert und dokumentiert.
- Geringeres Risiko von Anforderungsänderungen im späteren Verlauf.
Nachteile der plangetriebenen Methoden
-
Unflexibilität:
- Änderungen sind schwer und teuer umzusetzen, sobald das Projekt begonnen hat.
- Schwierigkeiten beim Umgang mit sich ändernden Anforderungen.
-
Lange Vorlaufzeit:
- Lange Phasen der Planung und Dokumentation können den Projektstart verzögern.
- Markt- oder Geschäftsanforderungen könnten sich ändern, bevor die Software fertiggestellt ist.
-
Späte Lieferung:
- Funktionalitäten werden erst spät im Entwicklungsprozess geliefert und getestet.
- Kundenfeedback kommt oft zu spät, um in das aktuelle Projekt einfließen zu können.
-
Hoher Verwaltungsaufwand:
- Erheblicher Zeit- und Ressourcenaufwand für die Erstellung und Pflege der Dokumentation.
- Potenzial für Bürokratie und ineffiziente Prozesse.
Geeignete Projekte für plangetriebene Methoden
Plangetriebene Methoden eignen sich besonders für:
-
Große und komplexe Projekte:
- Wo eine detaillierte Planung und umfassende Dokumentation unerlässlich sind.
- Beispiele: Bau von Betriebssystemen, sicherheitskritische Systeme (z. B. in der Luftfahrt).
-
Projekte mit stabilen Anforderungen:
- Wo die Anforderungen zu Beginn des Projekts gut verstanden und unwahrscheinlich sind, sich zu ändern.
- Beispiele: Langfristige, behördlich regulierte Projekte, wie z. B. medizinische Software.
Projekte, für die plangetriebene Methoden weniger geeignet sind
Plangetriebene Methoden sind weniger geeignet für:
-
Dynamische und sich schnell ändernde Projekte:
- Wo die Anforderungen und Prioritäten häufig wechseln.
- Beispiele: Startups, Web- und Mobile-Anwendungen, die auf Nutzerfeedback angewiesen sind.
-
Kleinere, agile Projekte:
- Wo schnelle Iterationen und regelmäßige Releases erforderlich sind.
- Beispiele: Agile Entwicklungsteams, die Scrum oder Kanban verwenden.
Fazit
Plangetriebene Methoden bieten klare Vorteile in Bezug auf Struktur, Dokumentation und Vorhersagbarkeit, sind jedoch weniger flexibel und anpassungsfähig an sich ändernde Anforderungen. Die Wahl der Methode sollte basierend auf den spezifischen Projektanforderungen und -bedingungen erfolgen. In dynamischen und sich schnell ändernden Umgebungen sind agile Methoden oft besser geeignet, während plangetriebene Ansätze in stabileren und gut definierten Projekten Vorteile bieten können.
Einführung in die Softwareentwicklung
Die Softwareentwicklung ist ein komplexer Prozess, der verschiedene Phasen durchläuft, darunter Planung, Design, Implementierung, Testen, Bereitstellung und Wartung. Ein systematischer Ansatz ist erforderlich, um hochwertige Softwareprodukte zu liefern, die den Anforderungen der Benutzer entsprechen und innerhalb der geplanten Zeit und des Budgets fertiggestellt werden.
SWEBOK: Software Engineering Body of Knowledge
SWEBOK steht für "Software Engineering Body of Knowledge" und ist eine Sammlung von grundlegenden Prinzipien und Praktiken, die für das Software Engineering als Disziplin anerkannt sind. SWEBOK deckt verschiedene Wissensgebiete ab, darunter Anforderungen, Design, Konstruktion, Testen, Wartung, Projektmanagement, Qualitätssicherung und mehr. Es dient als Leitfaden für Software-Ingenieure und hilft dabei, Best Practices zu definieren und zu standardisieren.
Plangetriebene Methoden in der Softwareentwicklung
Plangetriebene Methoden, auch als traditionelle oder sequentielle Methoden bekannt, beinhalten eine umfassende Planung und Dokumentation vor Beginn der eigentlichen Entwicklungsarbeit. Ein typisches Beispiel für eine plangetriebene Methode ist das Wasserfallmodell, bei dem jede Phase des Entwicklungsprozesses in einer festen Reihenfolge durchgeführt wird.
Vorteile der plangetriebenen Methoden
-
Klare Struktur und Reihenfolge:
- Jede Phase hat klar definierte Ziele und Outputs.
- Einfacher zu verfolgen und zu dokumentieren.
-
Vollständige Dokumentation:
- Umfassende Dokumentation ist in jeder Phase erforderlich.
- Hilfreich für zukünftige Wartung und Überprüfung.
-
Vorhersagbarkeit:
- Einfachere Planung von Ressourcen und Zeitplänen.
- Risiken können frühzeitig identifiziert und gemanagt werden.
-
Einheitliche Anforderungen:
- Anforderungen werden zu Beginn des Projekts klar definiert und dokumentiert.
- Geringeres Risiko von Anforderungsänderungen im späteren Verlauf.
Nachteile der plangetriebenen Methoden
-
Unflexibilität:
- Änderungen sind schwer und teuer umzusetzen, sobald das Projekt begonnen hat.
- Schwierigkeiten beim Umgang mit sich ändernden Anforderungen.
-
Lange Vorlaufzeit:
- Lange Phasen der Planung und Dokumentation können den Projektstart verzögern.
- Markt- oder Geschäftsanforderungen könnten sich ändern, bevor die Software fertiggestellt ist.
-
Späte Lieferung:
- Funktionalitäten werden erst spät im Entwicklungsprozess geliefert und getestet.
- Kundenfeedback kommt oft zu spät, um in das aktuelle Projekt einfließen zu können.
-
Hoher Verwaltungsaufwand:
- Erheblicher Zeit- und Ressourcenaufwand für die Erstellung und Pflege der Dokumentation.
- Potenzial für Bürokratie und ineffiziente Prozesse.
Geeignete Projekte für plangetriebene Methoden
Plangetriebene Methoden eignen sich besonders für:
-
Große und komplexe Projekte:
- Wo eine detaillierte Planung und umfassende Dokumentation unerlässlich sind.
- Beispiele: Bau von Betriebssystemen, sicherheitskritische Systeme (z. B. in der Luftfahrt).
-
Projekte mit stabilen Anforderungen:
- Wo die Anforderungen zu Beginn des Projekts gut verstanden und unwahrscheinlich sind, sich zu ändern.
- Beispiele: Langfristige, behördlich regulierte Projekte, wie z. B. medizinische Software.
Projekte, für die plangetriebene Methoden weniger geeignet sind
Plangetriebene Methoden sind weniger geeignet für:
-
Dynamische und sich schnell ändernde Projekte:
- Wo die Anforderungen und Prioritäten häufig wechseln.
- Beispiele: Startups, Web- und Mobile-Anwendungen, die auf Nutzerfeedback angewiesen sind.
-
Kleinere, agile Projekte:
- Wo schnelle Iterationen und regelmäßige Releases erforderlich sind.
- Beispiele: Agile Entwicklungsteams, die Scrum oder Kanban verwenden.
Fazit
Plangetriebene Methoden bieten klare Vorteile in Bezug auf Struktur, Dokumentation und Vorhersagbarkeit, sind jedoch weniger flexibel und anpassungsfähig an sich ändernde Anforderungen. Die Wahl der Methode sollte basierend auf den spezifischen Projektanforderungen und -bedingungen erfolgen. In dynamischen und sich schnell ändernden Umgebungen sind agile Methoden oft besser geeignet, während plangetriebene Ansätze in stabileren und gut definierten Projekten Vorteile bieten können.
Values des Agile Manifesto
Das Agile Manifesto wurde im Jahr 2001 von einer Gruppe von Softwareentwicklern veröffentlicht, um eine neue Herangehensweise an die Softwareentwicklung zu fördern, die flexibler und anpassungsfähiger ist. Es basiert auf vier zentralen Werten:
-
Individuen und Interaktionen über Prozesse und Werkzeuge
- Bedeutung: Der Schwerpunkt liegt auf der Zusammenarbeit und Kommunikation der Menschen, die das Projekt durchführen. Werkzeuge und Prozesse sind wichtig, aber sie sollten nicht die menschlichen Interaktionen ersetzen.
-
Funktionierende Software über umfassende Dokumentation
- Bedeutung: Das Hauptziel ist es, funktionierende Software zu liefern, die den Bedürfnissen der Kunden entspricht. Dokumentation ist wichtig, aber sie sollte nicht die Entwicklung funktionierender Software behindern.
-
Zusammenarbeit mit dem Kunden über Vertragsverhandlungen
- Bedeutung: Eine enge Zusammenarbeit mit dem Kunden wird gegenüber starren Vertragsverhandlungen bevorzugt. Es geht darum, gemeinsam an Lösungen zu arbeiten, anstatt nur vertragliche Verpflichtungen zu erfüllen.
-
Reagieren auf Veränderung über das Befolgen eines Plans
- Bedeutung: Agile Methoden betonen die Fähigkeit, auf Veränderungen flexibel zu reagieren, anstatt strikt einem vorgefassten Plan zu folgen. Anpassungsfähigkeit ist entscheidend für den Erfolg in einer sich ständig ändernden Umgebung.
12 Principles des Agile Manifesto
Hinter den Werten des Agile Manifesto stehen 12 Prinzipien, die genauer beschreiben, wie diese Werte in der Praxis umgesetzt werden können:
-
Unsere höchste Priorität ist es, den Kunden durch frühzeitige und kontinuierliche Auslieferung wertvoller Software zufrieden zu stellen.
- Bedeutung: Der Fokus liegt darauf, dem Kunden schnell funktionierende Software zu liefern und kontinuierlich zu verbessern.
-
Willkommen ändern Anforderungen, auch spät in der Entwicklung. Agile Prozesse nutzen Veränderungen zum Wettbewerbsvorteil des Kunden.
- Bedeutung: Änderungen werden begrüßt und als Möglichkeit gesehen, das Produkt zu verbessern und den Wettbewerbsvorteil zu erhöhen.
-
Liefern Sie häufig funktionierende Software, im Abstand von wenigen Wochen bis zu wenigen Monaten, mit Präferenz für die kürzere Zeitspanne.
- Bedeutung: Regelmäßige, kleine Releases helfen dabei, schnell Feedback zu erhalten und das Produkt iterativ zu verbessern.
-
Fachleute und Entwickler müssen während des Projekts täglich zusammenarbeiten.
- Bedeutung: Eine enge Zusammenarbeit zwischen Fachexperten und Entwicklern ist entscheidend für den Projekterfolg.
-
Bauen Sie Projekte um motivierte Individuen. Geben Sie ihnen das Umfeld und die Unterstützung, die sie brauchen, und vertrauen Sie ihnen, die Arbeit zu erledigen.
- Bedeutung: Motivierte und unterstützte Teammitglieder sind der Schlüssel zu erfolgreichen Projekten.
-
Der effizienteste und effektivste Weg, Informationen an und innerhalb eines Entwicklungsteams zu übermitteln, ist das Gespräch von Angesicht zu Angesicht.
- Bedeutung: Direkte Kommunikation ist der beste Weg, um Missverständnisse zu vermeiden und klare Informationen zu übermitteln.
-
Funktionierende Software ist das wichtigste Fortschrittsmaß.
- Bedeutung: Der Erfolg eines Projekts wird daran gemessen, ob funktionierende Software geliefert wird, nicht an der Erfüllung von Plänen oder der Erstellung von Dokumentation.
-
Agile Prozesse fördern nachhaltige Entwicklung. Die Auftraggeber, Entwickler und Benutzer sollten in einem gleichmäßigen Tempo unbegrenzt zusammenarbeiten können.
- Bedeutung: Ein nachhaltiges Arbeitstempo sorgt dafür, dass Teams langfristig produktiv bleiben.
-
Ständiges Augenmerk auf technische Exzellenz und gutes Design verbessert die Agilität.
- Bedeutung: Hohe technische Qualität und gutes Design sind entscheidend für die Fähigkeit, sich schnell an Veränderungen anzupassen.
-
Einfachheit – die Kunst, die Menge nicht getaner Arbeit zu maximieren – ist essenziell.
- Bedeutung: Der Fokus liegt auf dem Wesentlichen, um unnötige Arbeit zu vermeiden.
-
Die besten Architekturen, Anforderungen und Entwürfe entstehen durch selbstorganisierte Teams.
- Bedeutung: Teams, die selbstorganisiert arbeiten, sind oft kreativer und produktiver.
-
In regelmäßigen Abständen reflektiert das Team, wie es effektiver werden kann, und passt sein Verhalten entsprechend an.
- Bedeutung: Kontinuierliche Verbesserung durch regelmäßige Selbstreflexion und Anpassung der Arbeitsweisen ist entscheidend.
Bedeutung des Agile Manifesto für die Softwareentwicklung
Das Agile Manifesto hat die Softwareentwicklung grundlegend verändert, indem es den Fokus auf Flexibilität, Zusammenarbeit und Kundenzufriedenheit gelegt hat. Agile Methoden wie Scrum, Kanban und Extreme Programming (XP) sind direkt aus den Prinzipien des Agile Manifesto entstanden und haben sich weit verbreitet.
Wesentliche Vorteile der agilen Ansätze sind:
- Schnellere Lieferung von funktionierender Software: Durch häufige Releases können Teams schneller auf Kundenfeedback reagieren und die Software kontinuierlich verbessern.
- Bessere Anpassungsfähigkeit: Agile Teams können schnell auf Veränderungen reagieren und die Richtung des Projekts entsprechend anpassen.
- Verbesserte Zusammenarbeit und Kommunikation: Engere Zusammenarbeit zwischen Entwicklern und Kunden führt zu besseren Ergebnissen und zufriedeneren Kunden.
- Erhöhte Motivation und Produktivität: Selbstorganisierte Teams, die in einem unterstützenden Umfeld arbeiten, sind motivierter und produktiver.
Insgesamt hat das Agile Manifesto dazu beigetragen, die Softwareentwicklung effizienter und effektiver zu gestalten, indem es die Bedürfnisse der Kunden in den Mittelpunkt stellt und eine Kultur der kontinuierlichen Verbesserung fördert.
Begriffe in der agilen Softwareentwicklung
Risk (Risiko)
In der Softwareentwicklung bezeichnet Risiko die Möglichkeit, dass ein Projektziel nicht erreicht wird oder unerwünschte Ereignisse eintreten, die den Projekterfolg gefährden könnten. Risiken können technischer, personeller, organisatorischer oder externer Natur sein und umfassen beispielsweise:
- Technische Risiken: Fehler in der Technologie, unzureichende Leistung, Integrationsprobleme.
- Personelle Risiken: Mangel an Fachkräften, Ausfall wichtiger Teammitglieder.
- Organisatorische Risiken: Änderungen in der Unternehmensstruktur, mangelnde Unterstützung durch das Management.
- Externe Risiken: Gesetzesänderungen, wirtschaftliche Schwankungen, Konkurrenzprodukte.
Agile Methoden versuchen, Risiken durch iterative und inkrementelle Ansätze zu minimieren, indem sie häufige Feedbackschleifen und regelmäßige Anpassungen fördern.
Cost of Change (Kosten der Veränderung)
Die Kosten der Veränderung beziehen sich auf die Ressourcen (Zeit, Geld, Aufwand), die erforderlich sind, um Änderungen am Projekt oder Produkt vorzunehmen. In traditionellen, plangetriebenen Entwicklungsansätzen steigen die Kosten der Veränderung im Laufe des Projekts exponentiell an. Dies liegt daran, dass späte Änderungen oft tiefgreifende Auswirkungen haben und umfangreiche Anpassungen in Design, Code und Tests erforderlich machen.
In agilen Methoden werden die Kosten der Veränderung durch regelmäßige, iterative Entwicklungszyklen und kontinuierliches Feedback reduziert. Änderungen werden frühzeitig und häufig vorgenommen, was bedeutet, dass Anpassungen weniger kostspielig und einfacher umzusetzen sind.
Variablen der Softwareentwicklung (Iron Triangle) und deren Kräfte
Das Iron Triangle (Eisernes Dreieck) der Projektmanagement beschreibt die drei Hauptvariablen, die jedes Projekt beeinflussen: Umfang (Scope), Zeit (Time) und Kosten (Cost). Diese drei Faktoren sind eng miteinander verbunden und beeinflussen sich gegenseitig.
-
Umfang (Scope):
- Dies umfasst die Gesamtheit der Arbeiten, die erforderlich sind, um ein Projekt abzuschließen, einschließlich der Anforderungen und Spezifikationen. Änderungen im Umfang können die Zeit und Kosten beeinflussen.
-
Zeit (Time):
- Der Zeitrahmen, innerhalb dessen das Projekt abgeschlossen werden muss. Eine Verkürzung der Zeitrahmen kann die Kosten erhöhen und/oder den Umfang einschränken.
-
Kosten (Cost):
- Das Budget, das für das Projekt zur Verfügung steht. Erhöhte Kosten können den Umfang erweitern und/oder die Zeit verkürzen.
Kräfte im Iron Triangle:
- Qualität: Eine vierte Variable, die oft im Zentrum des Dreiecks dargestellt wird. Qualität kann durch Kompromisse zwischen Umfang, Zeit und Kosten beeinflusst werden. Agile Methoden zielen darauf ab, Qualität durch regelmäßige Tests, Refactoring und kontinuierliche Integration zu erhalten.
Beziehungen und Kompromisse:
- Wenn eine Variable angepasst wird, müssen die anderen entsprechend angepasst werden, um das Gleichgewicht zu halten. Beispielsweise:
- Mehr Umfang: Erfordert entweder mehr Zeit, mehr Kosten oder beides.
- Weniger Zeit: Erfordert entweder eine Reduzierung des Umfangs, eine Erhöhung der Kosten oder eine Kombination aus beidem.
- Geringere Kosten: Erfordert entweder eine Reduzierung des Umfangs, eine Verlängerung der Zeit oder eine Kombination aus beidem.
Agile Ansätze und das Iron Triangle:
- Agile Methoden bieten Flexibilität bei der Anpassung dieser Variablen durch iterative Entwicklungszyklen und kontinuierliche Priorisierung der Anforderungen. Dies ermöglicht es Teams, schneller auf Änderungen zu reagieren und das Gleichgewicht zwischen Umfang, Zeit und Kosten dynamisch anzupassen, um die bestmögliche Qualität zu liefern.
Zusammenfassung
Die Begriffe Risiko, Kosten der Veränderung und die Variablen des Iron Triangle sind zentrale Konzepte in der Softwareentwicklung, insbesondere im agilen Kontext. Agile Methoden helfen dabei, Risiken zu minimieren und die Kosten der Veränderung zu senken, indem sie auf iterative Entwicklungszyklen, kontinuierliches Feedback und flexible Anpassung der Projektvariablen setzen. Dies fördert eine höhere Qualität und bessere Anpassungsfähigkeit an sich ändernde Anforderungen und Bedingungen.
eXtreme Programming (XP)
eXtreme Programming (XP) ist eine agile Softwareentwicklungsmethode, die darauf abzielt, die Flexibilität und Qualität der Software durch intensive Zusammenarbeit, ständige Kommunikation und iterative Entwicklungsprozesse zu maximieren. XP wurde in den späten 1990er Jahren von Kent Beck entwickelt und konzentriert sich auf Kundenzufriedenheit und die Lieferung von wertvoller Software in kurzen Entwicklungszyklen.
Core Values
-
Communication (Kommunikation):
- Offene und häufige Kommunikation zwischen allen Beteiligten ist entscheidend, um Missverständnisse zu vermeiden und schnell auf Änderungen reagieren zu können.
-
Simplicity (Einfachheit):
- Das Design und der Code sollten so einfach wie möglich gehalten werden. Überflüssige Komplexität sollte vermieden werden, um die Wartbarkeit und Erweiterbarkeit zu erhöhen.
-
Feedback (Rückmeldung):
- Regelmäßiges Feedback von Kunden und Teammitgliedern hilft, die Entwicklung kontinuierlich zu verbessern und sicherzustellen, dass die Software den Anforderungen entspricht.
-
Courage (Mut):
- Teammitglieder sollten den Mut haben, Änderungen vorzunehmen, Fehler zuzugeben und Risiken einzugehen, um die beste Lösung zu finden.
-
Respect (Respekt):
- Alle Teammitglieder sollten einander respektieren und die Meinungen und Beiträge jedes Einzelnen schätzen.
Principles
- Rapid Feedback: Schnelles und häufiges Feedback von Kunden und Tests.
- Assume Simplicity: Annahme der einfachsten Lösung, die funktionieren könnte.
- Incremental Change: Schrittweise und kleine Änderungen, die leicht zu testen und zu überprüfen sind.
- Embrace Change: Akzeptanz und Begrüßung von Änderungen als Chance zur Verbesserung.
- Quality Work: Hohe Qualität der Arbeit wird durch ständige Verbesserung und Tests gewährleistet.
Practices
The Planning Game
- Bedeutung: Kunden und Entwickler planen gemeinsam die nächsten Iterationen, indem sie die Anforderungen priorisieren und die Umsetzung schätzen.
Small Releases
- Bedeutung: Häufige, kleine Releases liefern kontinuierlich funktionierende Software an den Kunden, um Feedback zu ermöglichen und Risiken zu minimieren.
Metaphor
- Bedeutung: Eine gemeinsame Metapher, die das System beschreibt und allen Beteiligten hilft, das Gesamtsystem und seine Architektur zu verstehen.
Simple Design
- Bedeutung: Das Design sollte einfach und direkt sein, um nur die aktuell benötigte Funktionalität zu unterstützen.
Unit-Testing
- Bedeutung: Automatisierte Unit-Tests werden geschrieben, um sicherzustellen, dass jede Komponente des Systems korrekt funktioniert.
Refactoring
- Bedeutung: Der Code wird regelmäßig umstrukturiert, um die interne Struktur zu verbessern, ohne das externe Verhalten zu ändern.
Pair-Programming
- Bedeutung: Zwei Entwickler arbeiten gemeinsam an einem Computer, um die Qualität des Codes zu verbessern und Wissen zu teilen.
Collective Code Ownership
- Bedeutung: Jeder im Team kann Änderungen an jedem Teil des Codes vornehmen, um Engpässe zu vermeiden und die Verantwortlichkeit zu teilen.
Continuous Integration
- Bedeutung: Der Code wird häufig integriert und getestet, um Fehler frühzeitig zu erkennen und zu beheben.
40 Hours Week
- Bedeutung: Eine normale Arbeitswoche (40 Stunden) wird angestrebt, um Überarbeitung zu vermeiden und die Produktivität langfristig zu erhalten.
On-site Customer
- Bedeutung: Ein Kunde ist ständig vor Ort, um Fragen zu beantworten und Anforderungen zu klären.
Coding Standards
- Bedeutung: Einhaltung einheitlicher Kodierungsstandards, um die Lesbarkeit und Wartbarkeit des Codes zu gewährleisten.
Test-Driven Development (TDD)
- Bedeutung: Tests werden vor dem eigentlichen Code geschrieben, um sicherzustellen, dass der Code die Anforderungen erfüllt.
Slack
- Bedeutung: Pufferzeit in den Zeitplänen, um unerwartete Probleme bewältigen zu können, ohne den gesamten Zeitplan zu gefährden.
Spike
- Bedeutung: Kurzfristige, explorative Entwicklung, um technische Probleme zu lösen oder die Machbarkeit einer Idee zu testen.
Incremental Design
- Bedeutung: Design und Architektur werden schrittweise entwickelt und verbessert, um flexibel auf Änderungen reagieren zu können.
Self-Organized Team
- Bedeutung: Teams organisieren sich selbst, treffen Entscheidungen gemeinsam und sind für ihre Arbeit verantwortlich.
Cost of Change
In XP wird angenommen, dass die Kosten für Änderungen durch kontinuierliche Tests, Refactoring und enge Zusammenarbeit mit dem Kunden minimiert werden können. Dies steht im Gegensatz zu traditionellen Ansätzen, bei denen die Kosten für Änderungen im Verlauf des Projekts exponentiell steigen.
Business Value
Der Fokus von XP liegt auf der Lieferung von Software, die für das Geschäft des Kunden wertvoll ist. Durch kontinuierliche Zusammenarbeit und regelmäßiges Feedback wird sichergestellt, dass die entwickelten Features tatsächlich den Bedürfnissen und Prioritäten des Kunden entsprechen.
Zusammenfassung
eXtreme Programming ist eine agile Methode, die durch intensive Zusammenarbeit, iterative Prozesse und ständige Verbesserung eine hohe Softwarequalität und Kundenzufriedenheit anstrebt. Die praktischen Ansätze und Prinzipien von XP helfen, flexibel auf Änderungen zu reagieren, Risiken zu minimieren und wertvolle Software effizient zu liefern.
Software Craft
Gründe für das Software Craft Manifesto
Das Software Craft Manifesto wurde notwendig, weil viele in der Softwareentwicklung tätige Personen die Notwendigkeit sahen, über die Prinzipien und Praktiken der agilen Methodik hinauszugehen, um eine Kultur der handwerklichen Qualität zu fördern. Hauptgründe waren:
- Mangelnde Qualität: Trotz agiler Methoden wurde oft minderwertige Software produziert, die schwer wartbar war.
- Kontinuierliche Verbesserung: Es bestand ein Bedürfnis nach einem ständigen Streben nach Exzellenz und Verbesserung der Fähigkeiten.
- Gemeinschaft und Lernen: Der Austausch von Wissen und Erfahrung zwischen Entwicklern sollte gefördert werden.
- Berufsstolz: Es wurde betont, dass Softwareentwicklung ein Handwerk ist, auf das man stolz sein sollte, ähnlich wie traditionelle Handwerksberufe.
Das Manifesto for Software Craftsmanship ergänzt das Agile Manifesto mit einem Fokus auf Exzellenz in der Softwareentwicklung:
- Nicht nur funktionierende Software, sondern auch gut gestaltete Software
- Nicht nur auf Änderungen reagieren, sondern auch stetig Mehrwert liefern
- Nicht nur Individuen und Interaktionen, sondern auch eine Gemeinschaft professioneller Entwickler
- Nicht nur Kundenkollaboration, sondern auch produktive Partnerschaften
Formate der Software Craft Community
- Coding Dojos: Regelmäßige Treffen, bei denen Entwickler zusammenkommen, um Programmieraufgaben in einer kollaborativen Umgebung zu lösen und voneinander zu lernen.
- Code Katas: Übungen, bei denen Entwickler wiederholt bestimmte Programmierprobleme lösen, um ihre Fähigkeiten zu verfeinern.
- Pair Programming: Zwei Entwickler arbeiten gemeinsam an einem Computer, um Wissen zu teilen und die Codequalität zu verbessern.
- Code Retreats: Ganztägige Veranstaltungen, bei denen Entwickler durch intensive Praxis ihre Fähigkeiten verbessern und neue Techniken erlernen.
- Tech Talks und Meetups: Präsentationen und Diskussionen zu spezifischen Themen der Softwareentwicklung.
- Online Communities und Foren: Plattformen wie Stack Overflow, GitHub und andere, wo Entwickler Fragen stellen, Antworten geben und ihr Wissen teilen können.
Prinzipien der Software Craft Community
- Meisterschaft: Streben nach Exzellenz in der Softwareentwicklung.
- Kollaboration: Zusammenarbeit und gemeinsames Lernen sind zentral.
- Kontinuierliches Lernen: Ständige Weiterbildung und Verbesserung der Fähigkeiten.
- Praktische Übung: Regelmäßige, praxisnahe Übung zur Vertiefung und Verfeinerung der Fähigkeiten.
- Verantwortung: Verantwortung für die Qualität des Codes und dessen Auswirkungen auf das Team und die Benutzer übernehmen.
Üben als Softwareentwickler:in
Softwareentwickler können ihre Fähigkeiten durch verschiedene Methoden verbessern:
-
Coding Dojo: Ein Coding Dojo ist eine kollaborative Programmierübung, bei der Entwickler zusammenkommen, um an einer gemeinsamen Aufgabe zu arbeiten. Es fördert das Lernen durch Zusammenarbeit und ermöglicht es den Teilnehmern, voneinander zu lernen. Typischerweise gibt es verschiedene Formate:
- Randori: Eine Live-Coding-Session, bei der ein Paar von Entwicklern (Driver und Navigator) den Code schreibt, während der Rest des Teams zuschaut und Feedback gibt.
- Kata: Einzelne oder Paare arbeiten an einer Aufgabe und präsentieren später ihre Lösungen.
-
Code Katas: Eine Code Kata ist eine kurze, wiederholbare Übung, die ein bestimmtes Problem oder eine Technik behandelt. Sie helfen Entwicklern, ihre Fähigkeiten durch ständige Wiederholung und Verbesserung zu verfeinern. Beispiele sind:
- FizzBuzz: Eine einfache Übung zur Überprüfung grundlegender Programmierkenntnisse.
- Roman Numerals: Eine Übung zur Konvertierung von Zahlen in römische Ziffern und umgekehrt.
-
Pair Programming: Zwei Entwickler arbeiten gemeinsam an einem Computer. Einer schreibt den Code (Driver), während der andere den Code überprüft und navigiert (Navigator). Dies fördert den Wissensaustausch und verbessert die Codequalität.
-
Refactoring: Regelmäßiges Überarbeiten von Code, um dessen Struktur zu verbessern, ohne die Funktionalität zu ändern. Dies hilft, technischen Schulden vorzubeugen und die Wartbarkeit zu erhöhen.
-
Test-Driven Development (TDD): Eine Praxis, bei der Tests geschrieben werden, bevor der eigentliche Code implementiert wird. Dies fördert die Erstellung von zuverlässigem und gut getestetem Code.
Zusammenfassung
Das Software Craft Manifesto und die zugehörigen Praktiken und Prinzipien betonen die Bedeutung von handwerklicher Qualität, kontinuierlicher Verbesserung und kollaborativem Lernen in der Softwareentwicklung. Durch Formate wie Coding Dojos, Code Katas und Pair Programming können Entwickler ihre Fähigkeiten kontinuierlich verbessern und eine Kultur der Exzellenz fördern.
Pyramid of Agile Competencies
Die Pyramid of Agile Competencies ist ein Modell, das die Fähigkeiten und Kompetenzen beschreibt, die erforderlich sind, um in einer agilen Umgebung erfolgreich zu arbeiten. Sie besteht aus drei Ebenen, die aufeinander aufbauen und unterschiedliche Aspekte der agilen Arbeitsweise abdecken: Technische Kompetenzen, Kollaborative Kompetenzen und Geschäftsbezogene Kompetenzen.
1. Technische Kompetenzen (Technical Competencies)
Diese bilden die Basis der Pyramide und umfassen die grundlegenden technischen Fähigkeiten und Kenntnisse, die für die agile Softwareentwicklung erforderlich sind. Dazu gehören:
- Coding Skills: Beherrschung der Programmiersprachen und Technologien, die im Projekt verwendet werden.
- Automatisiertes Testen: Fähigkeit, automatisierte Tests zu schreiben und zu pflegen, um die Qualität des Codes sicherzustellen.
- Continuous Integration (CI) und Continuous Deployment (CD): Kenntnis und Anwendung von CI/CD-Praktiken, um den Code kontinuierlich zu integrieren und zu deployen.
- Refactoring: Fähigkeit, den Code kontinuierlich zu verbessern, um ihn sauber, lesbar und wartbar zu halten.
- Test-Driven Development (TDD): Praxis, Tests zu schreiben, bevor der eigentliche Code implementiert wird.
Diese technischen Fähigkeiten sind notwendig, um qualitativ hochwertige Software schnell und effizient zu entwickeln.
2. Kollaborative Kompetenzen (Collaborative Competencies)
Diese Ebene betont die Bedeutung der Zusammenarbeit und Kommunikation innerhalb des Teams und mit Stakeholdern. Dazu gehören:
- Teamarbeit: Fähigkeit, effektiv mit anderen Teammitgliedern zusammenzuarbeiten und gemeinsam Lösungen zu entwickeln.
- Kommunikation: Klare und effektive Kommunikation innerhalb des Teams und mit externen Stakeholdern.
- Pair Programming: Praxis, bei der zwei Entwickler gemeinsam an einem Computer arbeiten, um die Qualität des Codes zu verbessern und Wissen zu teilen.
- Feedback-Kultur: Fähigkeit, konstruktives Feedback zu geben und zu empfangen, um kontinuierlich zu lernen und sich zu verbessern.
- Retrospektiven: Regelmäßige Meetings, bei denen das Team seine Arbeitsweise reflektiert und Verbesserungsmöglichkeiten identifiziert.
Diese kollaborativen Fähigkeiten fördern ein harmonisches und produktives Arbeitsumfeld, in dem das Team als Einheit agiert.
3. Geschäftsbezogene Kompetenzen (Business Competencies)
Die oberste Ebene der Pyramide konzentriert sich auf das Verständnis der geschäftlichen Anforderungen und den Mehrwert, den die Software liefern soll. Dazu gehören:
- Produktverständnis: Kenntnis der Produktvision und der Geschäftsziele, die das Team erreichen soll.
- Kundenzentrierung: Fokussierung auf die Bedürfnisse und Anforderungen der Kunden, um wertvolle und nützliche Software zu entwickeln.
- Stakeholder-Management: Fähigkeit, effektiv mit Stakeholdern zu kommunizieren und ihre Erwartungen zu managen.
- Priorisierung: Fähigkeit, Anforderungen und Aufgaben basierend auf ihrem Geschäftswert und ihrer Dringlichkeit zu priorisieren.
- Agile Planning: Praxis der iterativen Planung und kontinuierlichen Anpassung des Projektplans basierend auf Feedback und Veränderungen.
Diese geschäftsbezogenen Fähigkeiten stellen sicher, dass das Team nicht nur technisch exzellente, sondern auch geschäftlich relevante und wertvolle Lösungen liefert.
Zusammenfassung
Die Pyramid of Agile Competencies zeigt, dass erfolgreiche agile Teams eine Kombination aus technischen, kollaborativen und geschäftsbezogenen Fähigkeiten benötigen. Die Basis der Pyramide bildet die technische Kompetenz, gefolgt von kollaborativen Fähigkeiten und schließlich den geschäftsbezogenen Kompetenzen an der Spitze. Dieses Modell betont die Notwendigkeit einer ausgewogenen Entwicklung in allen drei Bereichen, um in einer agilen Umgebung erfolgreich zu sein.
User Stories
Ziele von User Stories
User Stories sind ein zentrales Element in der agilen Softwareentwicklung. Sie dienen dazu, die Anforderungen aus Sicht des Endbenutzers zu beschreiben und den Fokus auf den Mehrwert für den Benutzer zu legen. Die Hauptziele von User Stories sind:
- Kundenzentrierung: Sicherstellung, dass die Entwicklung den tatsächlichen Bedürfnissen der Benutzer entspricht.
- Kommunikation: Förderung des Dialogs zwischen Entwicklern, Kunden und anderen Stakeholdern.
- Priorisierung: Unterstützung bei der Priorisierung von Aufgaben basierend auf dem Geschäftswert und den Benutzeranforderungen.
- Flexibilität: Erleichterung der Anpassung an sich ändernde Anforderungen und Umstände.
- Transparenz: Verbesserung der Nachvollziehbarkeit und Klarheit der Anforderungen.
Anwendung von User Stories
User Stories werden während des gesamten Entwicklungsprozesses verwendet, um Anforderungen zu definieren, zu diskutieren und umzusetzen. Sie sind besonders nützlich in agilen Methoden wie Scrum und Kanban. Der typische Workflow umfasst:
- Erstellung: Stakeholder und Produktbesitzer schreiben User Stories, um die Anforderungen zu definieren.
- Diskussion: Das Team bespricht die Stories, um ein gemeinsames Verständnis zu entwickeln.
- Schätzung: Das Team schätzt den Aufwand für die Umsetzung der Stories.
- Priorisierung: Stories werden nach ihrem Geschäftswert und ihrer Dringlichkeit priorisiert.
- Umsetzung: Das Entwicklungsteam implementiert die Stories während der Sprints oder Iterationen.
- Überprüfung: Am Ende des Sprints wird die umgesetzte Story mit dem Kunden oder Produktbesitzer überprüft und abgenommen.
Format einer User Story
Eine User Story folgt einem einfachen und klaren Format, das typischerweise aus drei Teilen besteht:
- Rolle (Role): Wer ist der Benutzer oder die Benutzergruppe, für die die Anforderung gilt?
- Ziel (Goal): Was möchte der Benutzer erreichen?
- Nutzen (Benefit): Warum ist dieses Ziel für den Benutzer wichtig?
Ein gängiges Template für User Stories lautet:
Als [Rolle] möchte ich [Ziel], damit [Nutzen].
Beispiel:
Als Online-Shopper möchte ich Produkte in meinem Warenkorb speichern, damit ich meinen Einkauf später fortsetzen kann.
Zusammenhang zwischen Epics, Themes und Stories
- Epics: Große, grob umrissene User Stories, die in kleinere Stories unterteilt werden können. Epics sind oft zu groß, um in einem einzigen Sprint abgeschlossen zu werden.
- Themes: Eine Sammlung von User Stories, die zu einem gemeinsamen übergeordneten Ziel oder Thema gehören. Themes helfen dabei, zusammenhängende Anforderungen zu organisieren und zu verwalten.
- User Stories: Kleinere, spezifische Anforderungen, die in einem Sprint umgesetzt werden können. Sie sind detaillierter und fokussierter als Epics und Themes.
Beispiel:
- Theme: Benutzerverwaltung
- Epic: Benutzerregistrierung
- User Story: Als neuer Benutzer möchte ich mich registrieren können, damit ich den Service nutzen kann.
- User Story: Als Benutzer möchte ich mein Passwort zurücksetzen können, damit ich wieder Zugang zu meinem Konto erhalte.
- Epic: Benutzerregistrierung
Merkmale einer guten User Story
Eine gute User Story erfüllt die INVEST-Kriterien:
- Independent (Unabhängig): Die Story sollte so weit wie möglich unabhängig von anderen Stories sein.
- Negotiable (Verhandelbar): Die Details der Story sollten verhandelbar und nicht in Stein gemeißelt sein.
- Valuable (Wertvoll): Die Story sollte für den Endbenutzer oder den Kunden einen klaren Mehrwert bieten.
- Estimable (Schätzbar): Das Team sollte in der Lage sein, den Aufwand für die Umsetzung der Story zu schätzen.
- Small (Klein): Die Story sollte klein genug sein, um in einem Sprint umgesetzt werden zu können.
- Testable (Testbar): Es sollte klar sein, wie die erfolgreiche Umsetzung der Story überprüft werden kann.
Beispiel für eine gute User Story:
Als registrierter Benutzer möchte ich eine Liste meiner letzten Bestellungen sehen, damit ich schnell auf meine vergangenen Käufe zugreifen kann.
- Independent: Kann unabhängig von anderen Features implementiert werden.
- Negotiable: Details können diskutiert und angepasst werden.
- Valuable: Bietet dem Benutzer einen klaren Nutzen.
- Estimable: Aufwand kann geschätzt werden.
- Small: Kann innerhalb eines Sprints abgeschlossen werden.
- Testable: Erfolgskriterien sind klar definierbar.
Zusammenfassung
User Stories sind ein wesentlicher Bestandteil der agilen Softwareentwicklung, um die Anforderungen klar, fokussiert und benutzerorientiert zu definieren. Durch die Strukturierung in Epics, Themes und Stories können komplexe Anforderungen organisiert und verwaltet werden. Gute User Stories zeichnen sich durch die Erfüllung der INVEST-Kriterien aus, was zu einer effektiven und effizienten Umsetzung führt.
Estimation and Planning in Agile
User Stories
User Stories sind kurze, einfache Beschreibungen einer Funktionalität aus der Perspektive des Endbenutzers. Sie folgen typischerweise dem Format: "Als [Rolle] möchte ich [Ziel], damit [Nutzen]."
User Roles
User Roles definieren verschiedene Benutzertypen, die mit dem System interagieren werden. Diese Rollen helfen, die Anforderungen und Erwartungen unterschiedlicher Benutzergruppen zu berücksichtigen.
Epics
Epics sind große User Stories, die zu umfangreich sind, um in einem einzigen Sprint umgesetzt zu werden. Sie werden in kleinere, handhabbare User Stories aufgeteilt.
Themes
Themes sind Sammlungen von User Stories, die einem gemeinsamen übergeordneten Ziel oder Thema dienen. Sie helfen bei der Organisation und Verwaltung zusammenhängender Anforderungen.
Story Points
Story Points sind eine Einheit zur Schätzung des relativen Aufwands zur Umsetzung einer User Story. Sie basieren auf der Komplexität, dem Risiko und der Unsicherheit der Aufgabe.
Velocity
Velocity ist die Geschwindigkeit, mit der ein Team User Stories in einem Sprint abschließt. Sie wird typischerweise als die Summe der Story Points der abgeschlossenen Stories gemessen.
Planning Poker
Planning Poker ist eine Methode zur Schätzung von User Stories, bei der Teammitglieder Karten mit verschiedenen Schätzungen (in Story Points) verdeckt auswählen und dann gleichzeitig aufdecken. Diskussionen folgen, um eine gemeinsame Schätzung zu erreichen.
Conditions of Satisfaction
Conditions of Satisfaction sind Kriterien, die definieren, wann eine User Story als vollständig und erfolgreich umgesetzt gilt. Sie helfen, die Erwartungen klar zu definieren und sicherzustellen, dass die Anforderungen erfüllt werden.
Levels of Planning
Die Planung in agilen Projekten erfolgt auf mehreren Ebenen:
- Vision und Roadmap: Langfristige Ziele und strategische Ausrichtung des Produkts.
- Release Planning: Planung von Releases mit mehreren Sprints, um spezifische Ziele zu erreichen.
- Sprint Planning: Detaillierte Planung für einen einzelnen Sprint, basierend auf der Priorität der User Stories und der verfügbaren Velocity.
Product Backlog
Der Product Backlog ist eine priorisierte Liste von Anforderungen (User Stories, Epics, etc.), die für das Produkt entwickelt werden sollen. Der Product Owner ist verantwortlich für die Pflege und Priorisierung des Backlogs.
Priorisierung (der User Stories)
User Stories werden basierend auf ihrem Geschäftswert, Risiko, Aufwand und anderen Faktoren priorisiert. Methoden zur Priorisierung umfassen MoSCoW (Must have, Should have, Could have, Won't have), Kano-Modell und Wert-Risiko-Matrix.
Techniques for Estimating
Zu den Schätztechniken gehören:
- Planning Poker: Kartenbasierte Schätzmethode.
- T-Shirt Sizes: Grobe Schätzungen in Kategorien wie S, M, L, XL.
- Affinity Estimation: Gruppieren von User Stories nach ähnlichem Aufwand.
Relative Schätzung
Relative Schätzung bedeutet, den Aufwand von User Stories im Verhältnis zueinander zu bewerten, anstatt absolute Zeitangaben zu machen. Dies erleichtert die Schätzung und Anpassung bei Änderungen.
Planning for Value, Cost, Financial Value, Risk, New Knowledge
Planning for Value
Planung, um den größtmöglichen Geschäftswert zu liefern, indem man sich auf die wichtigsten und wertvollsten Features konzentriert.
Cost
Kosten umfassen alle Ressourcen, die für die Entwicklung und Lieferung der Software erforderlich sind, einschließlich Zeit, Personal und Infrastruktur.
Financial Value
Der finanzielle Wert misst den monetären Nutzen, den die Software für das Unternehmen generiert, z.B. durch Einnahmensteigerungen oder Kosteneinsparungen.
Risk
Risiko bezieht sich auf die Unsicherheiten und potenziellen Probleme, die den Projekterfolg gefährden könnten. Agiles Risikomanagement versucht, Risiken frühzeitig zu identifizieren und zu mindern.
New Knowledge
Neue Erkenntnisse oder Entdeckungen, die während des Projekts gemacht werden und die Planung und Priorisierung beeinflussen können.
Kano-Model of Customer Satisfaction
Das Kano-Modell klassifiziert Kundenanforderungen in drei Kategorien:
- Basismerkmale: Muss-Kriterien, die selbstverständlich erwartet werden.
- Leistungsmerkmale: Kriterien, die direkt mit der Kundenzufriedenheit korrelieren.
- Begeisterungsmerkmale: Unerwartete, aber erfreuliche Features, die Kunden begeistern.
Planungsebenen im agilen Kontext und Zuständigkeiten
-
Vision und Roadmap
- Verantwortlich: Produktbesitzer und Führungskräfte.
- Beschreibung: Langfristige strategische Planung und Zielsetzung des Produkts.
-
Release Planning
- Verantwortlich: Produktbesitzer, Scrum Master und Team.
- Beschreibung: Planung von Releases über mehrere Sprints, um spezifische Meilensteine zu erreichen.
-
Sprint Planning
- Verantwortlich: Scrum Master und Entwicklungsteam.
- Beschreibung: Detaillierte Planung eines einzelnen Sprints, basierend auf priorisierten User Stories und der Team-Velocity.
Insgesamt ermöglicht die agile Planung eine flexible und iterative Herangehensweise, die darauf abzielt, kontinuierlich wertvolle und qualitativ hochwertige Software zu liefern, die den sich ändernden Anforderungen der Benutzer entspricht.
Build Automation, CI, CD, DevOps
Arten der Software Automation
Software Automation umfasst verschiedene Arten, die den Entwicklungsprozess effizienter gestalten:
-
On-Demand Automation:
- Beschreibung: Aufgaben werden manuell vom Entwickler oder einem Teammitglied ausgelöst.
- Beispiel: Manuelles Ausführen eines Builds oder Tests, wenn ein Entwickler dies für notwendig hält.
-
Scheduled Automation:
- Beschreibung: Aufgaben werden zu festgelegten Zeiten oder Intervallen automatisch ausgeführt.
- Beispiel: Nächtliche Builds oder wöchentliche Integrations-Tests.
-
Triggered Automation:
- Beschreibung: Aufgaben werden automatisch durch bestimmte Ereignisse oder Bedingungen ausgelöst.
- Beispiel: Automatischer Build und Testlauf bei jedem Code-Commit oder Merge in das Haupt-Repository.
Ziele der Software Automation
- Effizienzsteigerung: Reduzierung manueller, repetitiver Aufgaben, um Zeit und Ressourcen zu sparen.
- Fehlerreduktion: Minimierung menschlicher Fehler durch konsistente und reproduzierbare Prozesse.
- Schnellere Feedback-Zyklen: Schnellere Erkennung und Behebung von Fehlern durch kontinuierliche Integration und Tests.
- Kontinuierliche Bereitstellung: Ermöglichen häufigerer und zuverlässigerer Software-Releases.
- Verbesserte Qualität: Durchgängige Tests und Überprüfungen gewährleisten eine höhere Codequalität.
Arten der Automation
- Build Automation: Automatisierung des Kompilierens und Erstellens der Software, einschließlich Abhängigkeitsmanagement und Versionskontrolle.
- Test Automation: Automatisierung von Tests, um sicherzustellen, dass neue Änderungen die bestehende Funktionalität nicht beeinträchtigen.
- Deployment Automation: Automatisierung des Bereitstellens der Software auf verschiedenen Umgebungen (z.B. Test, Staging, Produktion).
- Monitoring Automation: Automatisierung der Überwachung der Software und Infrastruktur, um Fehler und Leistungsprobleme frühzeitig zu erkennen.
Software Automation Pipeline
Die Software Automation Pipeline, oft auch CI/CD-Pipeline genannt, umfasst mehrere Schritte, die darauf abzielen, den Entwicklungsprozess effizient und zuverlässig zu gestalten:
-
Code:
- Beschreibung: Entwickeln und Verwalten des Quellcodes in einem Versionskontrollsystem wie Git.
- Tools: Git, Bitbucket, GitHub.
-
Build:
- Beschreibung: Kompilieren des Codes, Verwalten von Abhängigkeiten und Erstellen von ausführbaren Artefakten.
- Tools: Maven, Gradle, Ant.
-
Test:
- Beschreibung: Automatisierte Tests, einschließlich Unit-Tests, Integrationstests und Akzeptanztests.
- Tools: JUnit, Selenium, TestNG.
-
Package:
- Beschreibung: Verpacken der ausführbaren Artefakte in Formaten wie JAR, WAR, Docker-Images.
- Tools: Docker, Kubernetes.
-
Release:
- Beschreibung: Verwalten und Veröffentlichen der fertigen Artefakte in Repositories oder Bereitstellungsplattformen.
- Tools: Nexus, Artifactory.
-
Deploy:
- Beschreibung: Bereitstellen der Software in verschiedenen Umgebungen wie Test, Staging und Produktion.
- Tools: Jenkins, Ansible, Chef, Puppet.
-
Operate:
- Beschreibung: Betrieb und Überwachung der Software in der Produktionsumgebung, einschließlich Skalierung und Wartung.
- Tools: Nagios, Prometheus, Grafana.
-
Monitor:
- Beschreibung: Kontinuierliche Überwachung der Softwareleistung und Verfügbarkeit, um Probleme frühzeitig zu erkennen und zu beheben.
- Tools: ELK Stack (Elasticsearch, Logstash, Kibana), Splunk.
Unterschiede in den Schritten der Pipeline
- Build vs. Package: Der Build-Schritt konzentriert sich auf das Kompilieren des Codes und das Erstellen von Binärdateien, während der Package-Schritt die Erstellung von Deployment-Artefakten umfasst, die in verschiedenen Umgebungen verwendet werden können.
- Deploy vs. Release: Deployment bezieht sich auf das physische Bereitstellen der Software in einer Umgebung, während Release den Prozess der Freigabe einer Version für die Produktion beschreibt.
- Test: Kann mehrere Phasen haben, wie Unit-Tests, Integrationstests, Systemtests und Akzeptanztests, die jeweils unterschiedliche Aspekte der Software überprüfen.
Zusammenfassung
Build Automation, Continuous Integration (CI), Continuous Deployment (CD) und DevOps zielen darauf ab, den Softwareentwicklungsprozess durch Automatisierung effizienter und zuverlässiger zu gestalten. Die Pipeline der Software Automation umfasst mehrere Schritte, die von der Code-Entwicklung über das Testen und Paketieren bis hin zum Deployment und Monitoring reichen. Diese Automatisierung hilft, die Qualität der Software zu verbessern, die Entwicklungszyklen zu verkürzen und schneller auf Änderungen und Anforderungen zu reagieren.
Scrum
Scrum ist ein Rahmenwerk für agile Projektmanagement und Softwareentwicklung, das iterative und inkrementelle Ansätze nutzt, um komplexe Projekte zu bewältigen. Hier sind die Hauptcharakteristiken von Scrum, einschließlich Rollen, Zeremonien, Artefakten und Werten:
Scrum Rollen
-
Product Owner:
- Verantwortlich für das Produkt-Backlog und die Maximierung des Werts des Produkts.
- Stellt sicher, dass das Team die richtigen Aufgaben priorisiert und erledigt.
- Verbindet die Interessen der Stakeholder mit dem Entwicklungsteam.
-
Scrum Master:
- Verantwortlich für die Einhaltung der Scrum-Prinzipien und -Praktiken.
- Unterstützt das Team, Hindernisse zu beseitigen und effizient zu arbeiten.
- Moderiert die Zeremonien und schützt das Team vor äußeren Störungen.
-
Entwicklungsteam:
- Selbstorganisierende und funktionsübergreifende Gruppe von Fachleuten.
- Verantwortlich für die Umsetzung der Sprint-Ziele und die Lieferung inkrementeller Produkte.
- Besteht normalerweise aus 3-9 Mitgliedern.
Scrum Zeremonien
-
Sprint Planning:
- Findet zu Beginn jedes Sprints statt.
- Ziel: Planung der Arbeit für den kommenden Sprint und Festlegung des Sprint-Ziels.
- Das Entwicklungsteam wählt Items aus dem Product Backlog, die es innerhalb des Sprints abschließen kann.
-
Daily Scrum (Stand-up):
- Tägliches 15-minütiges Meeting.
- Ziel: Teammitglieder berichten, was sie seit dem letzten Meeting getan haben, was sie als nächstes tun werden und welche Hindernisse es gibt.
- Fördert die Zusammenarbeit und Transparenz im Team.
-
Sprint Review:
- Am Ende des Sprints.
- Ziel: Vorstellung der während des Sprints abgeschlossenen Arbeit.
- Feedback von Stakeholdern einholen und das Produkt-Backlog entsprechend anpassen.
-
Sprint Retrospective:
- Nach dem Sprint Review.
- Ziel: Reflexion über den vergangenen Sprint und Identifikation von Verbesserungsmöglichkeiten.
- Das Team diskutiert, was gut gelaufen ist, was verbessert werden kann und wie die Verbesserungen umgesetzt werden sollen.
Scrum Artefakte
-
Product Backlog:
- Eine priorisierte Liste von Anforderungen (User Stories, Aufgaben), die für das Produkt notwendig sind.
- Wird vom Product Owner gepflegt und regelmäßig aktualisiert.
-
Sprint Backlog:
- Eine Liste von Aufgaben, die das Team während des Sprints umsetzen möchte.
- Wird während des Sprint Planning Meetings erstellt.
-
Increment:
- Das kumulierte Ergebnis aller abgeschlossenen Product Backlog Items eines Sprints.
- Muss den Definition of Done (DoD) entsprechen und in einem nutzbaren Zustand sein.
Scrum Werte
-
Commitment (Verpflichtung):
- Das Team verpflichtet sich, die Ziele des Sprints zu erreichen.
-
Courage (Mut):
- Teammitglieder haben den Mut, schwierige Entscheidungen zu treffen und Herausforderungen anzunehmen.
-
Focus (Fokus):
- Das Team konzentriert sich auf die Arbeit im Sprint und die Erreichung des Sprint-Ziels.
-
Openness (Offenheit):
- Es herrscht eine offene Kommunikation über Fortschritte, Hindernisse und Verbesserungsmöglichkeiten.
-
Respect (Respekt):
- Teammitglieder respektieren einander und schätzen die Fähigkeiten und Beiträge jedes Einzelnen.
Begriffe in Scrum
-
Sprint:
- Eine zeitlich festgelegte Iteration von typischerweise 1-4 Wochen, in der ein inkrementelles Produkt geliefert wird.
-
Sprint Goal:
- Ein spezifisches Ziel, das während des Sprints erreicht werden soll, und das dem Team einen gemeinsamen Fokus gibt.
-
Retrospective:
- Eine Zeremonie am Ende eines Sprints, bei der das Team reflektiert und Verbesserungsmöglichkeiten identifiziert.
-
Task Board:
- Ein visuelles Tool zur Verfolgung des Fortschritts der Aufgaben im Sprint. Es zeigt typischerweise Spalten wie "To Do", "In Progress" und "Done".
-
Burndown Chart:
- Ein Diagramm, das den verbleibenden Arbeitsaufwand im Sprint im Zeitverlauf darstellt und zeigt, ob das Team auf dem richtigen Weg ist, um die Sprint-Ziele zu erreichen.
-
Definition of Done (DoD):
- Eine klare Liste von Kriterien, die erfüllt sein müssen, damit eine User Story als vollständig betrachtet wird.
-
Definition of Ready (DoR):
- Eine Liste von Kriterien, die erfüllt sein müssen, damit eine User Story als bereit für die Bearbeitung im Sprint betrachtet wird.
-
Daily Scrum:
- Ein tägliches, 15-minütiges Meeting, in dem Teammitglieder ihren Fortschritt besprechen, bevorstehende Aufgaben planen und Hindernisse identifizieren.
-
Increment:
- Das Ergebnis eines Sprints, das den Definition of Done-Kriterien entspricht und ein nutzbares, potentiell auslieferbares Produkt darstellt.
Zusammenfassung
Scrum ist ein agiles Framework, das auf kollaborative, iterative und inkrementelle Ansätze setzt, um komplexe Projekte zu bewältigen. Es fördert Transparenz, Inspektion und Anpassung durch regelmäßige Zeremonien und klare Rollen. Die Werte von Scrum legen den Grundstein für eine respektvolle, fokussierte und mutige Arbeitsweise, die darauf abzielt, kontinuierlich wertvolle und qualitativ hochwertige Produkte zu liefern.
Architektur Patterns
CQRS (Command Query Responsibility Segregation)
CQRS ist ein Architekturpattern, das die Lese- und Schreiboperationen eines Systems trennt. Dies bedeutet, dass es unterschiedliche Modelle für Abfragen (Queries) und Befehle (Commands) gibt.
-
Vorteile:
- Optimierte Lese- und Schreibmodelle.
- Bessere Skalierbarkeit und Leistung.
- Erleichtert die Implementierung von Event Sourcing.
-
Nachteile:
- Komplexität der Implementierung.
- Dateninkonsistenz zwischen Lese- und Schreibmodell möglich.
Event Sourcing
Event Sourcing ist ein Architekturpattern, bei dem der Zustand eines Systems durch eine Abfolge von Ereignissen (Events) gespeichert wird, anstatt den aktuellen Zustand direkt zu speichern.
-
Vorteile:
- Volle Nachvollziehbarkeit und Reproduzierbarkeit von Änderungen.
- Unterstützung für Audit- und Wiederherstellungsprozesse.
- Erleichtert die Implementierung von CQRS.
-
Nachteile:
- Komplexität der Event-Nachverfolgung und -Wartung.
- Erfordert robuste Event-Handling-Mechanismen.
Strangler Pattern
Das Strangler Pattern ist eine Methode zur schrittweisen Migration von Altsystemen zu neuen Systemen. Neue Funktionen werden in ein neues System geschrieben, während das alte System schrittweise zurückgebaut wird.
-
Vorteile:
- Minimierung der Risiken bei der Migration.
- Kontinuierliche Verbesserungen und Refactoring möglich.
- Keine Downtime erforderlich.
-
Nachteile:
- Komplexität der Koexistenz von Altsystem und neuem System.
- Zusätzlicher Integrationsaufwand.
Online-Migration
Online-Migration ist der Prozess der Datenmigration, bei dem das System während der Migration online und verfügbar bleibt.
-
Vorteile:
- Minimale Ausfallzeiten.
- Bessere Benutzererfahrung durch kontinuierliche Verfügbarkeit.
-
Nachteile:
- Erhöhte Komplexität und Risiko der Dateninkonsistenz.
- Erfordert sorgfältige Planung und Testing.
Circuit Breaker
Das Circuit Breaker Pattern schützt eine Anwendung vor Kaskadeneffekten von Fehlern und Überlastungen in abhängigen Diensten. Es unterbricht Aufrufe zu fehlerhaften Diensten und stellt nach einer bestimmten Zeitspanne Verbindungen wieder her.
-
Vorteile:
- Verbesserung der Systemstabilität und Fehlertoleranz.
- Verhindert Ressourcenerschöpfung durch wiederholte Fehlversuche.
-
Nachteile:
- Komplexität der Implementierung und Konfiguration.
- Mögliche Verzögerungen durch falsche Auslösungen.
Bulkhead (Schottwand)
Das Bulkhead Pattern teilt ein System in unabhängige Bereiche (Bulkheads), um den Einfluss eines Fehlers zu begrenzen und die Gesamtsystemstabilität zu erhöhen.
-
Vorteile:
- Verbesserung der Fehlertoleranz und Isolation.
- Reduzierung der Auswirkungen eines Teilausfalls.
-
Nachteile:
- Zusätzlicher Verwaltungsaufwand für die Konfiguration und Wartung.
- Mögliche Ressourcenunterauslastung.
Retry
Das Retry Pattern wiederholt fehlgeschlagene Operationen nach einer definierten Anzahl von Versuchen und Zeitintervallen, um vorübergehende Fehler zu überwinden.
-
Vorteile:
- Erhöhung der Erfolgsrate von Operationen.
- Vermeidung unnötiger Fehlermeldungen durch vorübergehende Probleme.
-
Nachteile:
- Erhöhung der Latenzzeit bei wiederholten Versuchen.
- Risiko von Überlastungen durch wiederholte Versuche.
Serverless
Serverless ist ein Architekturmuster, bei dem Anwendungen auf verwalteten Cloud-Diensten laufen, die automatisch skalieren und Ressourcen basierend auf der tatsächlichen Nutzung bereitstellen.
-
Vorteile:
- Keine Serververwaltung erforderlich.
- Automatische Skalierung und hohe Verfügbarkeit.
- Bezahlung nach Nutzung.
-
Nachteile:
- Begrenzte Kontrolle über die Infrastruktur.
- Potenzielle Abhängigkeit von Cloud-Anbietern.
Microservices
Microservices ist ein Architekturmuster, bei dem eine Anwendung aus einer Sammlung kleiner, unabhängiger Dienste besteht, die jeweils einen spezifischen Geschäftsfähigkeitsbereich abdecken.
-
Vorteile:
- Unabhängige Entwicklung, Bereitstellung und Skalierung von Diensten.
- Erhöhte Flexibilität und Agilität.
- Bessere Fehlertoleranz und Isolation.
-
Nachteile:
- Komplexität des Service-Managements und der Kommunikation.
- Herausforderungen bei der Datenkonsistenz und -integration.
Self-contained Systems
Self-contained Systems (SCS) ist ein Architekturansatz, bei dem eine Anwendung in unabhängige Systeme unterteilt wird, die jeweils eine spezifische Domäne abdecken und sowohl Frontend- als auch Backend-Funktionen enthalten.
-
Vorteile:
- Unabhängige Entwicklung und Bereitstellung.
- Reduzierung der Abhängigkeiten zwischen Teams und Systemen.
- Bessere Skalierbarkeit und Wartbarkeit.
-
Nachteile:
- Erhöhte Komplexität der Systemintegration.
- Herausforderungen bei der Konsistenz und gemeinsamen Nutzung von Daten.
Monolith (Modulith)
Ein Monolith ist eine traditionelle Architekturmuster, bei dem eine Anwendung als eine einzige, zusammenhängende Einheit entwickelt und bereitgestellt wird. Ein Modulith ist ein gut strukturierter Monolith, der in gut definierte Module unterteilt ist.
-
Vorteile:
- Einfache Entwicklung und Bereitstellung.
- Keine Netzwerk-Latenz zwischen Modulen.
- Einfacheres Debugging und Testing.
-
Nachteile:
- Schwierigkeiten bei der Skalierung und Wartung großer Codebasen.
- Abhängigkeiten zwischen Modulen können Änderungen erschweren.
- Begrenzte Flexibilität und Agilität.
Zusammenfassung
Architekturpatterns bieten verschiedene Ansätze zur Strukturierung und Verwaltung von Softwareanwendungen, um spezifische Probleme und Herausforderungen zu lösen. Die Wahl des richtigen Patterns hängt von den spezifischen Anforderungen, Zielen und Kontexten der Anwendung ab.
Software Architecture
Begriffe und Erklärungen
Software-Architektur Die Software-Architektur beschreibt die grundlegenden Strukturen einer Softwareanwendung, einschließlich der Komponenten und deren Beziehungen zueinander. Sie umfasst die Entwurfsentscheidungen, die getroffen werden, um die funktionalen und nicht-funktionalen Anforderungen zu erfüllen.
Software-Architekt Ein Software-Architekt ist verantwortlich für die Gestaltung und das Design der Software-Architektur. Dies beinhaltet die Entscheidung über die Struktur der Software, die Auswahl der Technologien und die Definition von Standards und Prinzipien, die das Entwicklungsteam befolgen soll.
Unterschied zwischen Enterprise Architecture und Application Architecture
- Enterprise Architecture: Umfasst die gesamte IT-Infrastruktur einer Organisation, einschließlich der Strategien, Prinzipien und Richtlinien, die zur Verwaltung der IT-Ressourcen verwendet werden. Sie zielt darauf ab, die IT-Umgebung mit den Geschäftszielen der Organisation in Einklang zu bringen.
- Application Architecture: Bezieht sich auf das Design und die Struktur einer einzelnen Softwareanwendung. Sie konzentriert sich auf die internen Komponenten der Anwendung und deren Beziehungen sowie auf die Integration mit anderen Systemen.
Schwierigkeiten des Software Designs
-
Complexity (Komplexität):
- Software-Systeme sind oft sehr komplex, was das Verständnis, die Entwicklung und die Wartung erschwert.
-
Conformity (Konformität):
- Software muss sich an externe Standards, Schnittstellen und Systeme anpassen, was die Flexibilität einschränkt und die Komplexität erhöht.
-
Changeability (Änderbarkeit):
- Anforderungen und Technologien ändern sich ständig, was die kontinuierliche Anpassung der Software erforderlich macht.
-
Invisibility (Unsichtbarkeit):
- Software ist immateriell und ihre Struktur ist oft schwer zu visualisieren, was die Kommunikation und das Verständnis erschwert.
Architectural Drivers
-
Functional Requirements (Funktionale Anforderungen):
- Beschreiben, was das System tun soll. Diese Anforderungen bestimmen die Funktionen und Features, die das System bieten muss.
-
Non-Functional Requirements (Nicht-funktionale Anforderungen):
- Beziehen sich auf die Qualitätseigenschaften des Systems, wie Leistung, Sicherheit, Zuverlässigkeit und Wartbarkeit. Diese Anforderungen beeinflussen die Architekturentscheidungen maßgeblich.
Begriffe und Unterschiede
Standard:
- Ein definierter und anerkannter Weg zur Durchführung einer Aufgabe oder zur Gestaltung eines Systems. Standards bieten Konsistenz und Interoperabilität.
Style (Architekturstil):
- Ein Satz von Prinzipien, der zur Strukturierung und Organisation einer Softwarearchitektur verwendet wird. Beispiele sind Layered Architecture, Microservices und Event-Driven Architecture.
Pattern (Architekturpattern):
- Wiederverwendbare Lösung für ein häufig auftretendes Problem in einem bestimmten Kontext. Beispiele sind Singleton, Factory und Observer.
Layer vs. Tier:
- Layer (Schicht): Eine logische Trennung von Verantwortlichkeiten innerhalb einer Softwareanwendung. Beispiel: Präsentationsschicht, Geschäftsschicht, Datenschicht.
- Tier (Ebene): Eine physische Trennung von Komponenten, die auf unterschiedlichen Maschinen oder Servern ausgeführt werden. Beispiel: Client-Server-Architektur, bei der der Client auf einer Maschine und der Server auf einer anderen Maschine läuft.
Architekturstile
Layered Architecture
- Vorteile: Einfach zu verstehen, gut strukturiert, ermöglicht die Trennung von Verantwortlichkeiten.
- Nachteile: Kann zu Leistungseinbußen führen, schwer zu ändern, wenn Schichten stark gekoppelt sind.
Microservices Architecture
- Vorteile: Skalierbarkeit, Flexibilität, Unabhängigkeit der Dienste, einfache Wartbarkeit.
- Nachteile: Komplexität der Verwaltung, erhöhtes Risiko der Netzwerkkommunikation, Datenkonsistenzprobleme.
Event-Driven Architecture
- Vorteile: Entkopplung von Komponenten, Skalierbarkeit, Echtzeitverarbeitung.
- Nachteile: Komplexität der Ereignisverwaltung, schwierige Fehlerbehandlung, Potenzial für inkonsistente Datenzustände.
Monolithic Architecture
- Vorteile: Einfacher zu entwickeln und bereitzustellen, weniger Überkopfkosten durch weniger Netzwerkkommunikation.
- Nachteile: Schwer skalierbar, schwierig zu warten und zu erweitern, potenziell längere Entwicklungszeiten bei Änderungen.
Drei große Patterns
-
Transaction Script:
- Einsatzgebiet: Einfachere Anwendungen, bei denen Geschäftslogik direkt in Prozeduren oder Skripten implementiert wird.
- Vorteile: Einfachheit, schnelle Entwicklung.
- Nachteile: Schwer wartbar und erweiterbar bei wachsender Komplexität.
-
Domain Model:
- Einsatzgebiet: Komplexere Anwendungen mit umfangreicher Geschäftslogik.
- Vorteile: Bessere Wartbarkeit, Flexibilität, gute Abbildung von Geschäftsregeln.
- Nachteile: Höhere Komplexität und Lernkurve.
-
Table Module:
- Einsatzgebiet: Anwendungen, bei denen die Geschäftslogik eng mit der Datenbankstruktur verbunden ist.
- Vorteile: Strukturierte Abbildung der Geschäftslogik auf Tabellenebene.
- Nachteile: Kann zu einer engen Kopplung zwischen Geschäftslogik und Datenbank führen.
C4-Modell
Das C4-Modell bietet eine einfache Möglichkeit, Softwarearchitekturen auf vier Ebenen zu visualisieren:
- Context:
- Überblick über das System und seine Umgebung.
- Container:
- Darstellung der verschiedenen Container (Applikationen, Datenbanken, etc.) im System.
- Component:
- Details der Hauptkomponenten innerhalb eines Containers.
- Code:
- Details auf der Code-Ebene (Klassen, Methoden).
Architecture Canvas
Vorteile:
- Ermöglicht eine strukturierte Visualisierung und Planung von Architekturentscheidungen.
- Fördert die Zusammenarbeit und Kommunikation zwischen Teams.
- Bietet eine klare Übersicht über die wichtigsten Architekturkomponenten und -abhängigkeiten.
Nachteile:
- Kann bei großen Systemen unübersichtlich werden.
- Erfordert regelmäßige Aktualisierung und Pflege.
Einsatz:
- Wird verwendet, um die Architektur eines Systems zu planen, zu dokumentieren und zu kommunizieren.
Zusammenfassung
Software-Architektur umfasst die grundlegenden Strukturen und Prinzipien, die zur Gestaltung und Verwaltung von Softwareanwendungen verwendet werden. Verschiedene Architekturstile und Patterns bieten spezifische Vorteile und Herausforderungen, die je nach Anwendungskontext berücksichtigt werden müssen. Das C4-Modell und das Architecture Canvas sind wertvolle Werkzeuge zur Visualisierung und Kommunikation von Architekturentscheidungen.
Cynefin Framework
Nutzen von Cynefin im Kontext der Softwareentwicklung
Das Cynefin Framework bietet einen konzeptionellen Rahmen, um Probleme und Entscheidungssituationen zu kategorisieren und zu verstehen. Es hilft Teams, die Natur ihrer Herausforderungen besser einzuschätzen und entsprechende Lösungsansätze zu wählen. Im Kontext der Softwareentwicklung ermöglicht Cynefin:
- Bessere Problemerkennung: Unterscheidung zwischen verschiedenen Arten von Problemen, um geeignete Ansätze und Methoden auszuwählen.
- Anpassungsfähigkeit: Förderung von Flexibilität und Anpassungsfähigkeit in der Herangehensweise an Projekte und Probleme.
- Effiziente Ressourcennutzung: Vermeidung von Verschwendung durch den Einsatz passender Techniken und Ressourcen für verschiedene Problemarten.
Begriffe und Erklärungen
Cynefin:
- Ein konzeptionelles Framework, das von Dave Snowden entwickelt wurde, um Organisationen zu helfen, Kontext zu schaffen, Entscheidungen zu treffen und Probleme zu lösen, indem es fünf verschiedene Domänen zur Klassifizierung von Problemen und Entscheidungssituationen bietet.
Framework:
- Ein strukturiertes Modell oder System, das als Grundlage dient, um Ideen zu organisieren, Entscheidungen zu treffen und Probleme zu analysieren.
Exaptation:
- Der Prozess, bei dem eine Funktion oder Fähigkeit, die ursprünglich für einen bestimmten Zweck entwickelt wurde, für einen neuen, oft unvorhergesehenen Zweck verwendet wird.
4 + 1 Domains des Cynefin Frameworks
-
Simple (Eindeutig):
- Beschreibung: Klar definierte Probleme mit bekannten Lösungen.
- Vorgehen: Best Practices anwenden. Probleme erkennen, kategorisieren und reagieren.
- Beispiel: Standardisierte Software-Installationen.
-
Complicated (Kompliziert):
- Beschreibung: Probleme, die analysiert und von Experten gelöst werden können.
- Vorgehen: Good Practices anwenden. Analysieren, Experten hinzuziehen und reagieren.
- Beispiel: Architekturdesign für eine komplexe, aber bekannte Anwendung.
-
Complex (Komplex):
- Beschreibung: Probleme mit unbekannten Lösungen, die durch Probe und Irrtum entdeckt werden müssen.
- Vorgehen: Emergent Practices anwenden. Probieren, Muster erkennen und reagieren.
- Beispiel: Entwicklung eines innovativen Produkts in einem neuen Markt.
-
Chaotic (Chaotisch):
- Beschreibung: Probleme, die sofortiges Handeln erfordern, ohne Zeit für Analyse.
- Vorgehen: Novel Practices anwenden. Handeln, Stabilität herstellen und dann analysieren.
- Beispiel: Krisenmanagement nach einem unerwarteten Systemausfall.
-
Disorder (Unordnung):
- Beschreibung: Zustand, in dem nicht klar ist, welche der anderen Domänen zutrifft.
- Vorgehen: Ziel ist es, die Situation in eine der vier anderen Domänen einzuordnen, um geeignete Maßnahmen zu ergreifen.
Weitere Begriffe
Causality (Kausalität):
- Das Prinzip, dass jede Wirkung eine Ursache hat. In einfachen und komplizierten Domänen sind Ursache und Wirkung klar erkennbar, in komplexen und chaotischen Domänen nicht.
Correlation (Korrelation):
- Eine Beziehung zwischen zwei Variablen, die miteinander in Verbindung stehen, aber nicht notwendigerweise eine Kausalität implizieren.
Constraint (Einschränkung):
- Regeln oder Grenzen, die das Verhalten innerhalb einer Domäne beeinflussen. In verschiedenen Domänen können die Art und der Grad der Einschränkungen variieren.
Codefin Framework
Praktische Anwendung in der Softwareentwicklung
Das Codefin Framework ist eine spezialisierte Anwendung des Cynefin Frameworks in der Softwareentwicklung. Es hilft Entwicklern, verschiedene Arten von Softwareprojekten und -problemen zu identifizieren und die besten Ansätze zur Bewältigung dieser Probleme zu wählen.
-
Simple Projekte:
- Beispiel: Regelmäßige Wartungsaufgaben oder kleine Updates.
- Ansatz: Anwendung bewährter Methoden und Automatisierung.
-
Complicated Projekte:
- Beispiel: Entwicklung eines Moduls für eine bekannte Anwendung.
- Ansatz: Einbindung von Experten, Durchführung detaillierter Analysen und Planung.
-
Complex Projekte:
- Beispiel: Entwicklung eines neuen, innovativen Produkts oder Features.
- Ansatz: Agile Methoden anwenden, iterativ vorgehen, Experimente durchführen und regelmäßig Feedback einholen.
-
Chaotic Situationen:
- Beispiel: Krisenmanagement bei schwerwiegenden Systemausfällen.
- Ansatz: Sofortige Maßnahmen ergreifen, um das System zu stabilisieren, und anschließend die Ursachen analysieren.
-
Disorder:
- Ansatz: Die Situation analysieren und versuchen, sie einer der anderen Domänen zuzuordnen, um geeignete Maßnahmen zu ergreifen.
Zusammenfassung
Das Cynefin Framework bietet eine strukturierte Methode zur Klassifizierung von Problemen und Entscheidungssituationen, die es Teams in der Softwareentwicklung ermöglicht, geeignete Ansätze zur Problemlösung zu wählen. Durch die Anwendung des Codefin Frameworks können Entwickler spezifische Softwareprojekte und -probleme identifizieren und passende Strategien anwenden, um die Effizienz und Effektivität ihrer Arbeit zu steigern.
Agile, Lean Software Development, Scrum, XP und Kanban
Herkunft und Zusammenhang
-
Agile: Entstand 2001 aus dem Agile Manifesto und beschreibt eine Reihe von Prinzipien und Werten, die Flexibilität, Zusammenarbeit und Kundenorientierung in der Softwareentwicklung fördern. Agile umfasst verschiedene Methoden und Frameworks wie Scrum, XP und Kanban.
-
Lean Software Development: Basierend auf den Prinzipien der Lean Production aus der Automobilindustrie (insbesondere Toyota). Ziel ist es, Wert zu maximieren und Verschwendung zu minimieren. Wurde von Mary und Tom Poppendieck auf die Softwareentwicklung angewendet.
-
Scrum: Ein agiles Framework, das auf Iterationen, genannt Sprints, basiert. Es fördert Selbstorganisation, regelmäßige Inspektion und Anpassung.
-
Extreme Programming (XP): Eine agile Methode, die sich auf technische Exzellenz und kontinuierliche Verbesserung konzentriert. Enthält Praktiken wie Pair Programming, TDD und kontinuierliche Integration.
-
Kanban: Ursprünglich aus der Lean Production stammend, wurde Kanban von David J. Anderson auf die Softwareentwicklung übertragen. Es konzentriert sich auf die Visualisierung des Workflows und die Begrenzung von Work-in-Progress (WIP).
Kanban Principles, Practices und Values
Principles:
- Start with what you do now: Kanban baut auf bestehenden Prozessen auf.
- Agree to pursue incremental, evolutionary change: Veränderungen werden schrittweise eingeführt.
- Respect the current process, roles, responsibilities, and titles: Anerkennung der bestehenden Strukturen und Verantwortlichkeiten.
- Encourage acts of leadership at all levels: Jeder im Team wird ermutigt, Verantwortung zu übernehmen.
Practices:
- Visualize the Workflow: Darstellung der Arbeitsprozesse auf einem Kanban Board.
- Limit Work in Progress (WIP): Begrenzung der Anzahl gleichzeitiger Aufgaben, um Überlastung zu vermeiden.
- Manage Flow: Überwachung und Optimierung des Arbeitsflusses.
- Make Process Policies Explicit: Klare Definition und Kommunikation der Arbeitsregeln.
- Implement Feedback Loops: Regelmäßige Überprüfung und Anpassung der Prozesse.
- Improve Collaboratively, Evolve Experimentally (Kaizen): Kontinuierliche Verbesserung durch kleine, experimentelle Änderungen.
Values:
- Transparency: Klare Sicht auf alle Arbeitsprozesse und Aufgaben.
- Collaboration: Förderung der Zusammenarbeit im Team.
- Customer Focus: Ausrichtung auf die Bedürfnisse des Kunden.
- Flow: Kontinuierlicher und effizienter Arbeitsfluss.
Kanban Board und Cumulative Flow Diagram (CFD)
Kanban Board:
- Funktion: Visualisierung des Workflows durch Spalten, die verschiedene Phasen des Arbeitsprozesses darstellen (z.B. To Do, In Progress, Done).
- Elemente: Karten repräsentieren Aufgaben, die durch die Spalten bewegt werden, um den Fortschritt zu zeigen.
Cumulative Flow Diagram (CFD):
- Funktion: Visualisierung der Arbeitselemente über die Zeit, um Engpässe und Arbeitsfluss zu analysieren.
- Elemente: Zeigt die Menge der Arbeit in verschiedenen Phasen (Spalten des Kanban Boards) über die Zeit an.
Begriffe
Pull System:
- Aufgaben werden basierend auf der Kapazität und Verfügbarkeit des Teams gezogen, nicht geschoben.
Limited WIP (Work in Progress):
- Begrenzung der Anzahl gleichzeitiger Aufgaben, um Überlastung zu vermeiden und den Fokus zu verbessern.
Visualize the Workflow:
- Darstellung des Arbeitsprozesses auf einem Kanban Board, um Transparenz und Effizienz zu erhöhen.
Cumulative Flow Diagram:
- Visualisierung des Arbeitsflusses über die Zeit, zeigt die Menge der Arbeit in verschiedenen Phasen an.
Lead Time vs. Cycle Time:
- Lead Time: Zeit von der Auftragserteilung bis zur Lieferung.
- Cycle Time: Zeit, die benötigt wird, um eine Aufgabe vom Beginn der Bearbeitung bis zur Fertigstellung abzuschließen.
Kaizen:
- Kontinuierliche Verbesserung durch kleine, inkrementelle Veränderungen.
Waste in Software Development:
- Überflüssige Aktivitäten, die keinen Mehrwert liefern, wie unnötige Meetings, Wartezeiten, Defekte und redundante Arbeiten.
Principles of Lean
- Value: Kundenwert definieren und maximieren.
- Value Stream: Wertstrom analysieren und optimieren.
- Flow: Kontinuierlichen Arbeitsfluss sicherstellen.
- Pull: Aufgaben basierend auf Nachfrage ziehen.
- Perfection: Kontinuierliche Verbesserung anstreben.
Unterschiede und Gemeinsamkeiten von Scrum und Kanban
Gemeinsamkeiten:
- Beide fördern Transparenz, Zusammenarbeit und kontinuierliche Verbesserung.
- Beide nutzen visuelle Werkzeuge zur Verfolgung des Arbeitsfortschritts (Task Board in Scrum, Kanban Board in Kanban).
- Beide zielen darauf ab, den Arbeitsfluss zu optimieren und Engpässe zu identifizieren.
Unterschiede:
- Scrum: Strukturierte Iterationen (Sprints) mit festgelegten Rollen (Scrum Master, Product Owner, Entwicklungsteam) und Zeremonien (Sprint Planning, Daily Scrum, Sprint Review, Sprint Retrospective).
- Kanban: Keine vorgeschriebenen Iterationen, Rollen oder Zeremonien. Fokus liegt auf kontinuierlichem Fluss und WIP-Limits.
Zusammenfassung
Agile, Lean Software Development, Scrum, XP und Kanban sind eng miteinander verbunden und haben gemeinsame Ziele: Flexibilität, Effizienz, Kundenorientierung und kontinuierliche Verbesserung. Kanban bietet spezifische Prinzipien und Praktiken, die den Arbeitsfluss visualisieren und optimieren, während Lean Prinzipien sich auf die Eliminierung von Verschwendung und die Maximierung des Kundenwerts konzentrieren. Scrum und Kanban bieten unterschiedliche Ansätze zur Organisation und Optimierung der Arbeitsprozesse, können jedoch auch kombiniert werden, um die jeweiligen Vorteile zu nutzen.
Tidy First
"Tidy First?" ist ein Buch von Kent Beck, das sich mit der Bedeutung von Code- und Datenstrukturen für die Softwareentwicklung beschäftigt. Hier sind die Hauptpunkte zusammengefasst:
-
Ordnung vor Funktionalität: Beck argumentiert, dass es wichtig ist, den Code und die Datenstrukturen zuerst aufzuräumen, bevor man neue Funktionalitäten hinzufügt. Ein sauberer und gut strukturierter Code ist einfacher zu erweitern und zu warten.
-
Kontinuierliches Refactoring: Regelmäßiges Refactoring ist entscheidend, um den Code lesbar und verständlich zu halten. Dies hilft dabei, technische Schulden zu vermeiden und die langfristige Gesundheit des Projekts zu sichern.
-
Testgetriebene Entwicklung (TDD): Beck betont die Bedeutung von TDD, bei der Tests geschrieben werden, bevor der eigentliche Code implementiert wird. Dies stellt sicher, dass der Code korrekt ist und hilft dabei, Fehler frühzeitig zu erkennen.
-
Lesbarkeit und Verständlichkeit: Der Code sollte so geschrieben sein, dass er leicht zu verstehen ist. Dies bedeutet, klare und aussagekräftige Namen zu verwenden, Kommentare sinnvoll einzusetzen und die Struktur des Codes logisch und konsistent zu halten.
-
Kleine Schritte: Veränderungen sollten in kleinen, überschaubaren Schritten durchgeführt werden. Dies minimiert das Risiko von Fehlern und macht es einfacher, Probleme zu identifizieren und zu beheben.
-
Verantwortung und Teamarbeit: Jeder im Team sollte Verantwortung für den Code übernehmen. Gemeinsame Standards und Praktiken helfen dabei, eine konsistente Codebasis zu gewährleisten.
-
Werkzeuge und Techniken: Beck empfiehlt den Einsatz von Werkzeugen und Techniken, die das Aufräumen und Refactoring unterstützen, wie z.B. statische Codeanalyse-Tools und automatisierte Tests.
Zusammengefasst betont "Tidy First?" die Wichtigkeit einer sauberen und geordneten Codebasis als Grundlage für eine erfolgreiche Softwareentwicklung. Das Buch bietet praktische Ratschläge und Techniken, um dies zu erreichen und langfristig zu erhalten.
MPC
Acronyms
Acronym | Description |
---|---|
UE | Unit of Execution |
Patterns
Multiple patterns may be used concurrently.
How to find concurrency?
- Finding a general solution and applying that pattern
- Perform a dependency analysis
- Either progress to algorithm-structure or return to finding a general solution
Finding Concurrency
- Task Decomposition pattern
- Dependency analysis pattern
Algorithm structure
- Task parallelism pattern
Supporting Structure
- Fork / Join pattern
Implemenation mechanism
- Processes & Threads
State Pattern
- Allows an object to alter its behavior when its internal state changes
Task Decomposition Pattern
- Decomposes a problem into concurrently executable tasks
- Define the solution space of a task, as independent as possible
- Define the features of said task
Possible solutions
- Functional Decomposition: When splitting the program based on the function
- Loop Splitting: Distinct iterations of a loop
Control Flow Graph
The control flow graph displays all possible execution paths of a program
-> Each buble represents a identifiable task
Dependency analysis pattern
Group Task Pattern
- How to group tasks that make up a problem to simplify analysing dependencies
Order Task Pattern
- How must the groups of tasks be ordered to satisfy constrains
Data Sharing Pattern
- How is data shared amongst tasks
Solution
Shared Data can be:
- read only
- effectively-local
- read write
- accumulate
- multiple-read single-write
Task Parallelism pattern
Dependencies
- Order Dependencies must be encored
- Data dependencies
- Removable dependencies - can be removed by code / loop transformation
- seperable dependencies - data strouctrues could be replicated (replicated data)
Schedule
- How are tasks assigned to UEs and how are they scheduled for execution?
Static Scheulde
- Defined a priori
- low management effort but low numebers of tasks
Dynamic schedule
Higher management but for unknown or unpredictable task load
- Define a task queue and when UE completes is current task, it gets a new one from the queue
-> UEs with tasks completed faster will take more from the queue
Work-Stealing: if UE has nothign to do, it looks to the queue of otehr UEs and takes a task to do (adds significant complexity)
Fork / Join Pattern
- Implies the cloning of section of code into another (structurally - but not necessarily in terms of data) independent task
- teh fork-join pattern is NOT an abstraction of the POSIX fork, if anything vice-versa
Use-cases
- recursive structure, such as trees - lend themselves to fork-join
- irregular sets of connected tasks
- where diffeent functions are mapped onto different tasks
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM_PROCESSES 4
#define ARRAY_SIZE 1000
int array[ARRAY_SIZE];
void sum_array(int start, int end, int* result) {
for (int i = start; i < end; i++) {
*result += array[i];
}
}
int main() {
// Initialize array with values 1 to ARRAY_SIZE
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i + 1;
}
pid_t pids[NUM_PROCESSES];
int partial_sum[NUM_PROCESSES] = {0};
for (int i = 0; i < NUM_PROCESSES; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pids[i] == 0) {
int start = i * (ARRAY_SIZE / NUM_PROCESSES);
int end = (i + 1) * (ARRAY_SIZE / NUM_PROCESSES);
sum_array(start, end, &partial_sum[i]);
exit(partial_sum[i]);
}
}
int total_sum = 0;
for (int i = 0; i < NUM_PROCESSES; i++) {
int status;
waitpid(pids[i], &status, 0);
total_sum += WEXITSTATUS(status);
}
printf("Total sum using fork-join: %d\n", total_sum);
return 0;
}
Geometric Decomposition
- Sequence of operations on a core data structure
Solution
- Assign chunks
- Ensure data available for chunks
- Define the updating process for chunks
- Group and map chunks to UEs
Data sharing between chunsk
Exchange Options
- (A) A-Priori - perform exchange
- copying or messaging
- updating is straight forward
- (B) Computation and communication overlap
- compute local data whils boundary data is being transmitted
- updating requires multi-threading, communication and computation may work
Loop Parallelism Pattern
Loop parallelism is a technique to speed up programs by running loop iterations at the same time on multiple processors.
Problem
You have a loop in your code that takes a long time to run. How can you make it faster?
Solution
- Check Independence: Ensure each loop iteration can run without waiting for others.
- Parallelize: Use tools (like OpenMP) to split the loop so each part runs on a different processor.
Result: The loop runs faster because multiple processors handle different parts simultaneously.
Benefits
- Speed: Reduces the time needed to run the loop.
- Efficiency: Uses multiple processors effectively.
Considerations
- Complexity: Managing parallel tasks and ensuring no conflicts (race conditions) can be tricky.
- Debugging: Finding and fixing errors is harder in parallel programs.
Use it when: Your program has loops that take a long time and can run independently.
Solution
- Check Independence: Ensure each loop iteration can run without waiting for others.
- Parallelize: Use tools (like OpenMP) to split the loop so each part runs on a different processor.
Distributed Array Pattern
Distributed array is a method for dividing arrays between multiple processors to improve efficiency and performance in parallel computing.
Problem
Large arrays need to be processed efficiently across multiple processors. How can you distribute the array elements effectively?
Solution
- Partition Arrays: Divide the array into blocks that can be distributed among processors.
- Map Blocks to Processors: Assign blocks to processors in a way that balances the load and minimizes communication overhead.
Result: The array elements are processed more efficiently as each processor works on a different part of the array.
Benefits
- Load Balancing: Distributes work evenly among processors.
- Memory Efficiency: Ensures efficient use of memory by aligning array distribution with the flow of computation.
Considerations
- Communication Overhead: Managing data exchange between processors can introduce delays.
- Index Mapping: Aligning local and global indices requires careful management to ensure correct data processing.
Use it when: Your program involves large arrays that need to be processed in parallel, and you want to optimize performance and memory usage.
Single Programm Multiple Data Pattern (SPMD Pattern)
Single Program Multiple Data (SPMD) is a parallel computing model where multiple processors run the same program but on different data.
Problem
Managing interactions between multiple processors running the same program but on different data sets. How can this be organized efficiently?
Solution
- Initialize: Load the same program on all processors and establish communication.
- Unique Identifiers: Each processor gets a unique identifier to differentiate its tasks.
- Distribute Data: Each processor works on its own portion of the data.
Result: The program runs on multiple processors simultaneously, each handling different data, improving overall performance.
Benefits
- Scalability: High scalability as each processor can handle different data independently.
- Maintainability: Easier to maintain a single code base.
Considerations
- Data Distribution: Efficient data distribution and management are crucial.
- Synchronization: Proper synchronization to manage data dependencies and interactions between processors.
Use it when: You need to run the same operations on different data sets across multiple processors for better performance and scalability.
Data Decomposition
-> Organise by data
Flexibility
Granularity Know
-
Defines Chunks whose size and number are parameterizable -> Used for fitting the solution onto different HW • Impact of granularity
-
Overhead of dependency management of chunks versus computational effort - The time managing dependencies must be small wrt computational effort - Boundary cells
- Cells at boundary of chunk -> set size increases with surface area
- Cost = constant overhead + per cell effort
- Computational effort -> scales with volume
- Rough-tune dependency management with computational effort
Efficiency
- Work versus overhead (see above)
- How do chunks map onto UEs
Geometric Decomposition
General Solutions
- Array based computations
- Recursive data structures
Tasks
Distinct
- independently of each other
- do not rely on each other's results & resources during their execution
Dependent
- relies on the results of another task
Processes
- Unit of resource ownership
- Unit of scheduling
- POSIX defined
Definition
- has its own main
- owns resources (memory, file handles, ...)
- is assumed to have exclusive access to all computing and hardware resources 100% of the time
- may be managed by an operating system
- in a multi-process OS, the OS will schedule the processes accoring to some policy
- the memory protection unit (HW) will protecte the integrity of the process' memory from other processes
POSIX Processes
Portable Operating System Interface (POSIX)
- Behavioural specification
- defines how processes and threads are created and terminated
- operating systems may (or may not) suppport POSIX
- Process is created through a fork
- fork clones (total copy) the calling process to child
- child inherits copies of resources
- child/parent variables are handled seperateely (copy on write)
- parent and child run independently of each other
- os scheduled seperately by the OS
POSIX Threads
- Under POSIX, created within a process
- Operates in the context of a process
- Can access all process reosurce including memory
- Kernel management threads -> kernel knows threads exist and schedules them
- default-linux-behaviour
- user-threads -> threading in user space
- kernel does not know of their existence
- user-threads library schedules, not OS scheduler, schedules threads
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 4
#define ARRAY_SIZE 1000
int array[ARRAY_SIZE];
int partial_sum[NUM_THREADS] = {0};
pthread_t threads[NUM_THREADS];
void* sum_array(void* arg) {
int thread_part = *((int*)arg);
int start = thread_part * (ARRAY_SIZE / NUM_THREADS);
int end = (thread_part + 1) * (ARRAY_SIZE / NUM_THREADS);
for (int i = start; i < end; i++) {
partial_sum[thread_part] += array[i];
}
pthread_exit(0);
}
int main() {
// Initialize array with values 1 to ARRAY_SIZE
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i + 1;
}
int thread_parts[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
thread_parts[i] = i;
pthread_create(&threads[i], NULL, sum_array, &thread_parts[i]);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
int total_sum = 0;
for (int i = 0; i < NUM_THREADS; i++) {
total_sum += partial_sum[i];
}
printf("Total sum using pthreads: %d\n", total_sum);
return 0;
}
UE - Units of Execution
- Creation and destruction of threads are costly
- reuse them as much as possible
OpenMP - Open Multi-Processing
- Operates on top of an OS
- Supports a shared memory model
- Opens – per directive - a team of threads to parallelise code sections - Operates according to the Fork-Join pattern master thread
omp_get_thread_num()
- retreive the unique integer of the currently executing thread within the parallel region
- starting from 0
Run with as many threads as possible
#pragma omp parallel
{
// Code to be executed in parallel
printf("Hello, world! I'm thread %d\n", omp_get_thread_num());
}
Run multiple sections in parallel
#pragma omp parallel sections
{
#pragma omp section
{
for (i = 0; i <= maxval; i++) {
do_some_work_here
}
}
#pragma omp section
{
for (i = 0; i <= maxval; i++) {
do_some_other_work_here
}
}
}
Statememts
Run for loop
#include <stdio.h>
#include <omp.h>
int main() {
#pragma omp parallel
{
#pragma omp for
for (int i = 0; i < 10; i++) {
printf("Thread %d is processing iteration %d\n", omp_get_thread_num(), i);
}
}
return 0;
}
Lock
A lock is used, whenever a critical section must not be run with multiple threads in parallel
#include <stdio.h>
#include <omp.h>
int main() {
// Parallel region
#pragma omp parallel
{
// Each thread will execute this block
// Only one thread at a time will be allowed to execute the critical section
#pragma omp critical
{
// Critical section
printf("Thread %d is inside the critical section\n", omp_get_thread_num());
}
}
return 0;
}
Syncing threads
#include <stdio.h>
#include <omp.h>
int main() {
// Parallel region
#pragma omp parallel
{
// Some work done by each thread
printf("Thread %d is doing some work before the barrier\n", omp_get_thread_num());
// Synchronize all threads at this point
#pragma omp barrier
// After the barrier, all threads will execute this
printf("Thread %d passed the barrier\n", omp_get_thread_num());
}
return 0;
}
Run on only master thread
#include <stdio.h>
#include <omp.h>
int main() {
// Parallel region
#pragma omp parallel
{
// This block of code is executed by all threads
printf("This is executed by all threads\n");
// The following block of code is executed only by the master thread
#pragma omp master
{
printf("This is executed only by the master thread\n");
}
// This block of code is executed by all threads
printf("This is executed by all threads again\n");
}
return 0;
}
Execute by one thread only
{
#pragma omp parallel
{
#pragma omp single
{
// This block of code will be executed by only one thread
printf("This is executed by only one thread\n");
}
// Code executed by all threads
}
}
Task paralellism
#pragma omp parallel
{
// Code executed by all threads
#pragma omp task
{
// creates a task that can be executed asynchronously by any available thread in the team.
// Code inside the task
}
}
Do not Wait
- nowait
a();
#pragma omp parallel
{
b();
#pragma omp for nowait
for (int i = 0; i < 10; ++i) {
c(i);
}
d();
}
z();
SIMD - Single Instruction, Multiple Data
Architectures
- Single Instruction Single Data (SISD)
- Single Instruction Multiple Data Streams (SIMD)
- Multiple Instructions Single Data Stream (MISD)
- Multiple Instructions Multiple Data Streams (MIMD)
Single Instruction Single Data (SISD)
- Standard general-purpose single-core CPU operation
- Generally associated with a single Functional Unit (ALU)
Single Instruction Multiple Data (SIMD)
- A single instruction works on multiple data streams
- Associated with multiple Functional Units (FU)
- Abstracted to Single Instruction Multiple Threads (Nvidia)
- Further abstracted to Single Program Multiple Data (pattern)
Single Program Multiple Data (SPMD)
Running the same programm on multiple UEs
(Multiply-accumulate) MAC Functional Units
- Efficiency in Arithmetic Operations
- Digital Signal Processing (DSP)
- Machine Learning and AI
- Scientific Computing
-> Many algorithms multiply and then accumulate
SIMD Vectorisation Operation
NEON Intrinsics
- allows for efficient parallel processing of data, which can significantly enhance performance for certain types of computations.
https://developer.arm.com/architectures/instruction-sets/intrinsics/#q=
Single Instruction, Multiple Data (SIMD) instructions on ARM NEON (Advanced SIMD) are designed to perform the same operation on multiple data points simultaneously, enhancing performance in multimedia, signal processing, and other applications. Here is a summary of key aspects of SIMD instructions on ARM NEON:
Overview
- SIMD Architecture: NEON operates on vectors, which are groups of elements (8, 16, 32, or 64 bits in size) processed in parallel.
- Registers: NEON has 32 64-bit or 16 128-bit registers, referred to as Q (quad-word) and D (double-word) registers.
Data Types
- Integer: 8-bit, 16-bit, 32-bit, and 64-bit integers.
- Floating Point: 32-bit and 64-bit floating point.
- Fixed Point: Supports fixed-point arithmetic, useful for certain DSP applications.
- Polynomials: Supports polynomial arithmetic operations.
Key Instructions
-
Load/Store Instructions:
- VLDn: Load multiple elements from memory into NEON registers.
- VSTn: Store multiple elements from NEON registers to memory.
-
Arithmetic Instructions:
- VADD: Vector add.
- VSUB: Vector subtract.
- VMUL: Vector multiply.
- VMLS: Vector multiply and subtract.
- VMLA: Vector multiply and accumulate.
-
Comparison Instructions:
- VCGE: Vector compare greater than or equal.
- VCGT: Vector compare greater than.
- VCLE: Vector compare less than or equal.
- VCLT: Vector compare less than.
-
Logical Instructions:
- VAND: Vector bitwise AND.
- VORR: Vector bitwise OR.
- VEOR: Vector bitwise exclusive OR (XOR).
- VBIC: Vector bitwise clear.
-
Shift Instructions:
- VSHR: Vector shift right.
- VSHL: Vector shift left.
- VRSHR: Vector round and shift right.
- VRSHL: Vector round and shift left.
-
Miscellaneous Instructions:
- VABS: Vector absolute.
- VNEG: Vector negate.
- VSQRT: Vector square root.
- VMOV: Vector move (transfer data between registers).
- VDUP: Vector duplicate (replicate a scalar across a vector).
Execution
- Parallelism: Executes operations on multiple data elements in parallel, improving throughput.
- Pipeline: NEON instructions can be pipelined to enhance performance further.
- Optimized Libraries: ARM provides optimized libraries (e.g., ARM Compute Library) leveraging NEON for common tasks.
Use Cases
- Multimedia Processing: Efficient handling of audio, video, and image processing tasks.
- Signal Processing: Enhancements in digital signal processing (DSP) applications.
- Machine Learning: Accelerates inference operations for neural networks and other ML models.
- Games and Graphics: Improves performance in graphics rendering and game physics calculations.
Performance Considerations
- Alignment: Data alignment is crucial for maximizing performance.
- Memory Access Patterns: Optimized memory access patterns can prevent stalls and improve efficiency.
- Instruction Scheduling: Proper instruction scheduling can maximize NEON pipeline utilization.
ARM NEON provides a powerful set of SIMD instructions that significantly enhance the performance of applications requiring parallel data processing. Its comprehensive set of instructions covers a wide range of operations, making it a versatile tool in the ARM architecture.
Summary: Inter-Processor Communication (IPC) in the Document
Introduction to Inter-Processor Communication (IPC):
- IPC is crucial for enabling communication and data exchange between multiple processors in a system. It is especially important in multicore and parallel processing environments where tasks are distributed across different processing units.
Key Concepts in IPC:
-
Synchronization:
- Synchronization ensures that multiple processes or threads coordinate their execution to maintain data consistency and avoid race conditions.
- Techniques such as barriers, locks, and semaphores are commonly used to synchronize tasks.
-
Data Sharing:
- Efficient data sharing mechanisms are essential for performance in parallel systems.
- Data can be shared using shared memory, message passing, or other IPC mechanisms depending on the system architecture and requirements.
IPC Mechanisms:
-
Shared Memory:
- Shared memory allows multiple processes to access the same memory space. It is fast and efficient for data exchange but requires careful synchronization to prevent conflicts.
- Shared memory is often used in systems with a common physical memory accessible by all processors.
-
Message Passing:
- Message passing involves sending data from one process to another through messages. It is used in distributed systems where processes may not share a common memory.
- Libraries such as MPI (Message Passing Interface) provide standardized functions for message passing.
-
Semaphores:
- Semaphores are synchronization primitives used to control access to shared resources by multiple processes.
- They can be binary (indicating availability) or counting (indicating the number of available resources).
-
Mutexes (Mutual Exclusion):
- Mutexes are used to prevent concurrent access to a resource by more than one process or thread.
- A mutex ensures that only one process can access a critical section of code at a time.
-
Barriers:
- Barriers synchronize multiple threads or processes, ensuring that they all reach a certain point in the execution before any of them can proceed.
- Barriers are useful in parallel algorithms where stages of computation must be synchronized.
Implementation in Different Environments:
-
POSIX (Portable Operating System Interface):
- POSIX defines standard APIs for IPC mechanisms such as shared memory, semaphores, and message queues.
- Examples include
sem_open
,sem_close
,sem_post
,sem_wait
for semaphores, andshm_open
,shm_unlink
for shared memory.
-
OpenAMP (Open Asymmetric Multi-Processing):
- OpenAMP facilitates IPC in heterogeneous systems with different types of processors.
- It uses mechanisms such as
rpmsg
(remote processor messaging) andvirtio
for efficient message passing between processors. - Example usage in OpenAMP involves creating endpoints and channels for message passing.
-
Virtio:
- Virtio provides a standardized interface for efficient data transfer between virtual machines and their host or between processors in a system.
- It supports features like data queues and notifications to facilitate high-speed communication.
Examples of IPC Usage:
-
Signal Handling in openAMP:
- Signals are used to notify processes of events such as interrupts or task completions.
- Example:
void SignalHandler(int sig) { printf("signal handler called by signal %d\n", sig); flag--; } int main(void) { struct sigaction sig; sig.sa_handler = SignalHandler; sigemptyset(&sig.sa_mask); sigaction(SIGUSR1, &sig, NULL); while (flag > 0); printf("main terminates, flag = %d\n", flag); }
-
Message Passing in MPI:
- MPI provides functions such as
MPI_Send
andMPI_Recv
to facilitate message passing in parallel applications. - It supports point-to-point communication and collective communication for coordinating multiple processes.
- MPI provides functions such as
Performance Considerations:
-
Latency and Bandwidth:
- The performance of IPC mechanisms is influenced by latency (the time it takes to send a message) and bandwidth (the amount of data that can be transmitted in a given time).
- Choosing the appropriate IPC mechanism based on the application's latency and bandwidth requirements is crucial for optimal performance.
-
Overhead:
- IPC mechanisms introduce overhead due to context switching, synchronization, and data transfer.
- Minimizing overhead through efficient implementation and minimizing unnecessary communication can enhance performance.
-
Scalability:
- IPC mechanisms must scale efficiently with the number of processors to maintain performance in large systems.
- Techniques such as hierarchical communication structures and load balancing can help achieve scalability.
Conclusion:
- IPC is essential for enabling effective communication and coordination in multicore and parallel processing systems.
- Understanding and utilizing various IPC mechanisms, such as shared memory, message passing, semaphores, mutexes, and barriers, are crucial for developing efficient parallel applications.
- By optimizing IPC mechanisms for performance, developers can ensure that their applications run smoothly and efficiently on modern parallel and distributed systems.
Memory
Cache
- fast memory close to, or integrated with, the processor
- transparent to the programmer
- When the code accesses a variable the cache controller checks whether the contents of the address is in cache memory
- If yes -> cache hit
- If no -> cache miss -> processor accesses the address in memory and reads a line/block
- whilst being read by processor, the line read is also put into the cache memory
-> Good for speeding up transfers into the core
Paging
-
An address space is divided into pages (typically 4k, now -> 2M)
-
A physical memory is divided into frames of the same size
-
When code is required or a memory access happens the entire page is fetched into memory from the hard-disk
-
Due to relative addressing, the page can be placed in any frame in memory
-
Thus only the pages the code is actually using need to be in main memory
-
Efficient use of main memory
- Support for multiprocessing
Summary: GPU Memory and Memory Management
Introduction to GPU Memory Architecture
- GPU memory architecture is designed to maximize parallel processing efficiency. It includes multiple memory types with varying latencies and bandwidths to optimize performance for different tasks.
Key Memory Types in GPUs:
-
Global Memory:
- Global memory is the largest memory space on the GPU, accessible by all threads but with higher latency compared to other memory types.
- It is used for storing large datasets and is essential for applications requiring significant data storage.
-
Shared Memory:
- Shared memory is a smaller, faster memory space accessible by all threads within a block.
- It allows for efficient data sharing and communication between threads, significantly reducing the need for accessing slower global memory.
- Shared memory is particularly useful for implementing data parallel algorithms, where multiple threads need to access the same data.
-
Registers:
- Registers are the fastest memory on the GPU, used to store variables for individual threads.
- They offer low-latency access and are crucial for the execution of compute-intensive tasks.
- However, the number of registers per thread is limited, and excessive use can lead to register spilling into slower memory.
-
Constant Memory:
- Constant memory is read-only memory that is cached and optimized for reading by multiple threads.
- It is ideal for storing constant values that do not change during kernel execution, such as coefficients in mathematical computations.
-
Texture Memory:
- Texture memory is a specialized memory used for texture mapping in graphics rendering.
- It is optimized for spatial locality and offers efficient data retrieval for 2D and 3D textures.
Memory Access Patterns:
- Efficient memory access patterns are critical for achieving high performance on GPUs. Poor access patterns can lead to memory bottlenecks, reducing the overall throughput.
- Coalesced Memory Access:
- Threads in a warp (a group of 32 threads) should access consecutive memory addresses to achieve coalesced memory access.
- Coalesced access minimizes the number of memory transactions, reducing latency and increasing bandwidth utilization.
Memory Hierarchies and Data Locality:
- GPUs utilize a memory hierarchy to manage data locality and access speeds:
- Registers > Shared Memory > Constant/Texture Memory > Global Memory.
- Proper use of memory hierarchies can significantly enhance performance by ensuring that frequently accessed data resides in faster memory.
Memory Management Techniques:
-
Memory Allocation:
- Memory allocation on the GPU is managed through APIs such as CUDA and OpenCL.
- Developers allocate memory using functions like
cudaMalloc
for CUDA orclCreateBuffer
for OpenCL. - It is crucial to free allocated memory after use to prevent memory leaks using
cudaFree
orclReleaseMemObject
.
-
Data Transfer:
- Data transfer between the host (CPU) and the device (GPU) is a critical aspect of GPU programming.
- Transfers are managed using functions like
cudaMemcpy
orclEnqueueWriteBuffer
. - Minimizing data transfer and maximizing data reuse on the GPU can reduce transfer overhead and improve performance.
-
Shared Memory Utilization:
- Shared memory can be explicitly managed within kernels to store intermediate results or frequently accessed data.
- Synchronization primitives like
__syncthreads()
ensure that all threads in a block have a consistent view of shared memory.
-
Memory Alignment:
- Proper memory alignment is essential to avoid inefficient memory access patterns.
- Aligning data structures to match memory boundaries can lead to more efficient memory transactions.
Optimization Strategies:
-
Avoiding Bank Conflicts:
- Shared memory is divided into banks, and accessing multiple addresses within the same bank can cause bank conflicts, leading to serialization.
- To avoid bank conflicts, ensure that memory accesses are distributed evenly across different banks.
-
Using Constant Memory:
- Utilize constant memory for read-only data that is accessed frequently by multiple threads.
- This reduces global memory access and leverages the caching mechanism of constant memory.
-
Prefetching Data:
- Prefetching involves loading data into shared memory before it is needed by threads, reducing the wait time for memory access.
- This technique can be implemented using double-buffering strategies, where one buffer is being processed while the other is being loaded with new data.
-
Leveraging Texture Memory:
- For applications involving spatial data, such as image processing, use texture memory to take advantage of its spatial locality optimizations.
Conclusion:
- Understanding and effectively utilizing the various types of GPU memory is crucial for maximizing the performance of parallel applications.
- Proper memory management, efficient access patterns, and optimization strategies can significantly reduce memory bottlenecks and enhance computational throughput.
- By leveraging the full potential of the GPU's memory hierarchy, developers can achieve substantial performance gains in their applications.
CPU Architectures
GP - CPU Architectures
Parallelizing CPU architectures
- Multicore
- Multiple FUs -> ALUs, Multipliers
- Data parallelism
- Multiple pipelines
- Instruction parallelism
- Multiple Heterogeneous PEs -> DSP, GPU ...
DSP Architectures (Digital Signal Processing)
- Very application specific
- Offers mac (.M)
- Data access (.D)
- GP-ALU (.L)
- Shifter / ALU (.S)
Software Pipelining
- Technique to reduce pipeline stalls from instruction
- Together with VLIW get very tight loop kernels
Pipeline Architectures
CISC
- Each construciton seperate
- move mem, reg
- move reg, reg
- Microcode decodes instruction and performs operation
RISC
- Store and load
- lots of instructions required
- hardware architecture, inc. pipelining makes execution fast
- compiler friendly
Superpipelining
- increase the number of instructions in the pipeline
- instructions can be issued faster
Superscalar
- Increase the number of pipelines
- Several instructions issued in a cycle
- scheduling becomes an issue
Scheduling
Co Processors
- Processor recognises instructions as its own
- Processor does not recognise instruction it passes it on to connected Co-Pros
- Either recognised or not recognised
- If not then unknown instruction exception
- If then processed by Co-Pro
- Co-pros usually have separate clocks
- Often own channels to memory/cache (NEON)
-> Independent/parallel execution possible
ARM Cortex-A53 (v8 diagram)
NEON (v7 description)
- Has instruction queue (16 deep) and data queue (8 entries)
- A53 regards instruction as complete (performs checking and fetches)
- NEON must decode and process instruction
- If instruction/data queue full then A53 stalls
Co-processors face uncertain future
- (Co-Pro) Instruction decoders expensive
- (Co-Pro) Only really useful for generic operations
- (Co-Pro) Video co-processors exis(ted) but standards advance so quickly
- (CPU) Tight integration also costs G-CPU silicon
- (Toolset) Added compiler maintenance because of architecture specific instruction sets
(SMT) Simultaneous Multi Threading
- Intel calls this hyperthreading
- Supporting cores execute two threads -> 4 cores -> 8 threads
- Superscalar architecture necessary
- Assumption is that CPU resources (ALU ...) are not always being used
- With two threads in parallel better chance of available resources being used
- BIOS and OS must support this feature
- Core looks like two (thread capable) cores to OS
- Need to lock process on core to avoid hyperthreading when it is turned on
- Speedup for some applications, reported by Intel, ~30%
- Improvement more likely dependent on externals like cache stalls than on FU availability
- Security issues abound
(AMP) Asymmetric Multiprocessing
AMP - Scenarios
- AMP with different Instruction Set Architecture (ISA)
- Typically specialisation – PRU, GPU, DSP ... • AMP with same ISA
- Task driven, one processor for comms, one for I/O ...
- Master-slave (typical) or peer-to-peer driven
- AMP with same ISA clocked at different rates
- AMP with same ISA, different architectures (64/32-bit)
- F.i. big.LITTLE
- AMP with different or no OS
openAMP
- rpmsg message passing service
- Every device is a communication channel with a remote processor
- Devices are called channels
- Each has a source/destination address
// send
int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len);
// receive
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo);
GPU Architectures
SM (Streaming Mutiprocessor)
- Contains many Cores
NVIDIA Fermi Architecture
-
32-Compute Unified Device Architecture cores
-
4 SFUs
-
16 L/S units
-
A GPU has threads (of execution)
Rendering Pipeline
- 3D Models are calculated on the CPU in world coordinates
- Vertices Shader converts these into a 2D model (local coordinates), applices textures
- Geometry shader: optional additional manipulator
- Fragment shader shades the individual pixels
Organization
Summary: GPU (Graphics Processing Unit) Concepts from the Document
Introduction to GPU Architecture
- GPUs are specialized hardware designed for parallel processing. They excel at handling multiple operations simultaneously, making them ideal for tasks such as graphics rendering, scientific simulations, and machine learning.
Key Components and Functions
-
Processing Elements:
- GPUs consist of numerous smaller cores or processing elements that work together to perform computations.
- These cores are designed for high-throughput computations, allowing the GPU to handle a large number of parallel tasks efficiently.
-
Task Scheduling:
- GPUs use a unique scheduling system to distribute tasks among the available cores.
- Task scheduling in GPU architecture involves distributing tasks among multiple GPU cores based on workload and resource availability.
- The scheduling is typically managed by the GPU driver and runtime environment, ensuring optimal use of the available cores.
-
Command Queue:
- A command queue in OpenCL (Open Computing Language) is a crucial component for managing the execution order of commands.
- The command queue allows for asynchronous execution, enabling multiple tasks to be processed concurrently.
- It also manages resource allocation and ensures that tasks are executed in the correct order.
Programming Models:
-
OpenCL:
- OpenCL is a framework for writing programs that execute across heterogeneous platforms, including CPUs, GPUs, and other processors.
- It provides a standardized interface for developing parallel programs, allowing developers to harness the power of GPUs.
- OpenCL programs consist of kernels, which are functions executed on the GPU, and the host code, which runs on the CPU and manages kernel execution.
-
Single Instruction, Multiple Data (SIMD):
- SIMD is a parallel computing architecture where a single instruction operates on multiple data points simultaneously.
- GPUs leverage SIMD to process large datasets efficiently, as the same operation can be applied to many data points in parallel.
- This approach is highly effective for tasks like image processing, where the same operation needs to be performed on each pixel.
-
Single Program, Multiple Data (SPMD):
- SPMD is a parallel programming model where multiple processors execute the same program but operate on different data.
- In the context of GPUs, this means that each core runs the same kernel but processes different chunks of data.
- This model simplifies the programming process and ensures scalability across a large number of cores.
Synchronization and Data Sharing:
-
Synchronization:
- Synchronization in GPU programming is essential to ensure that tasks are completed in the correct order and data integrity is maintained.
- Techniques like barriers and atomic operations are used to synchronize threads within a kernel.
-
Data Sharing:
- Efficient data sharing mechanisms are crucial for performance in parallel processing.
- GPUs provide shared memory spaces accessible by all threads within a block, enabling fast data exchange.
- Developers must carefully manage data access patterns to minimize conflicts and maximize throughput.
Performance Considerations:
-
Granularity:
- The granularity of tasks—how finely they are divided—affects the performance of GPU programs.
- Fine-grained tasks can exploit more parallelism but may incur higher overhead due to increased synchronization and data management.
-
Load Balancing:
- Ensuring even distribution of work across all GPU cores is essential for optimal performance.
- Load balancing techniques help avoid scenarios where some cores are idle while others are overloaded.
-
Memory Hierarchies:
- Understanding the GPU memory hierarchy (registers, shared memory, global memory) is crucial for optimizing performance.
- Efficient use of fast, low-latency memory (like registers and shared memory) can significantly boost performance.
Use Cases and Applications:
-
Graphics Rendering:
- GPUs were initially designed for rendering graphics, providing the computational power needed for high-resolution, real-time rendering.
-
Scientific Computing:
- GPUs are widely used in scientific computing for simulations, modeling, and data analysis due to their ability to handle large-scale parallel computations.
-
Machine Learning:
- The parallel processing capabilities of GPUs make them ideal for training machine learning models, particularly deep neural networks.
Conclusion:
- GPUs play a critical role in modern computing by providing the parallel processing power necessary for a wide range of applications.
- Understanding the architecture, programming models, synchronization mechanisms, and performance considerations of GPUs is essential for leveraging their full potential in computational tasks.
Memory Categories
OpenCL
__kernel void add_numbers(__global float4* data,
__local float* local_result, __global float* group_result) {
float sum;
float4 input1, input2, sum_vector; // array of 4 floats which support vectorization
uint global_addr, local_addr;
global_addr = get_global_id(0) * 2;
input1 = data[global_addr];
input2 = data[global_addr+1];
sum_vector = input1 + input2; // perform four floating-point additions simultaneously
local_addr = get_local_id(0);
local_result[local_addr] = sum_vector.s0 + sum_vector.s1 +
sum_vector.s2 + sum_vector.s3;
barrier(CLK_LOCAL_MEM_FENCE);
if(get_local_id(0) == 0) {
sum = 0.0f;
for(int i=0; i<get_local_size(0); i++) {
sum += local_result[i];
}
group_result[get_group_id(0)] = sum;
}
}
MPI
Process spatially related data on the same UE (Units of execution)
- moving data around takes time and causes latency
MPI RUN
https://www.open-mpi.org/doc/current/man1/mpirun.1.php
run multiple programms
mpirun -np 2 a.out : -np 2 b.out
-
Runs in total 4 processes
-
Runs 2 processes for a.out with rank 0-1
-
Runs 2 processes for b.out with rank 2-3
Test Status non blocking
#include <mpi.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int data = rank;
MPI_Request request;
MPI_Status status;
if (rank == 0) {
// Initiate a non-blocking send
MPI_Isend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
int flag = 0;
while (!flag) {
// Do some work here...
printf("Rank 0 is doing some work while waiting for send to complete\n");
// Check if the send is complete
MPI_Test(&request, &flag, &status);
}
printf("Rank 0: Send completed\n");
} else if (rank == 1) {
// Initiate a blocking receive
MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
printf("Rank 1: Received data = %d\n", data);
}
MPI_Finalize();
return 0;
}
Element-wise reduction
On the root process
#include <mpi.h>
int MPI_Reduce(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);
Distributed on many systems
#include <mpi.h>
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
Here are comprehensive answers to all 44 questions extracted from the document:
-
Where have we seen reduction before?
- Reduction is used in array operations where multiple elements are combined using an operation (e.g., sum, min, max) to produce a single result.
-
What is the data scope of the variables in the following code?
#include <omp.h> int i = 0; int n = 10; int a = 7; #pragma omp parallel for for (i = 0; i < n; i++) { int b = a + i; ... }
i
andb
are private to each thread, whilen
anda
are shared among threads.
-
What is the data scope of the variables in the following code?
#include <omp.h> #pragma omp parallel for shared(n, a) for (int i = 0; i < n; i++) { int b = a + i; ... } #pragma omp parallel for shared(n, a) private(b) for (int i = 0; i < n; i++) { b = a + i; ... }
- In the first loop,
n
anda
are shared,i
andb
are private. In the second loop,n
anda
are shared,i
is private, andb
is explicitly private.
- In the first loop,
-
What is the data scope of the variables in the following code?
#include <omp.h> int a, b, c, n; #pragma omp parallel for default(shared) private(a, b) for (int i = 0; i < n; i++) { // a and b are ? variables // c and n are ? variables }
a
andb
are private, whilec
andn
are shared.
-
How do the machines boot in Asymmetric Multiprocessing (AMP) with different Instruction Set Architectures (ISA)?
- Machines in AMP boot either after reset or on-demand. They may boot from a bootloader or reserved memory, or boot code may be provided on demand.
-
How tightly coupled are the applications in AMP?
- The level of synchronization, data transfer, and organization of communications determine the coupling of applications in AMP.
-
How do you use signals in openAMP?
- Signals in openAMP are used through handlers. For example:
void SignalHandler(int sig) { (void) printf("signal handler called by signal %d\n", sig); flag--; } int main(void) { struct sigaction sig; sig.sa_handler = SignalHandler; (void) sigemptyset(&sig.sa_mask); (void) sigaction(SIGUSR1, &sig, NULL); while (flag > 0); (void) printf("main terminates, flag = %d\n", flag); }
-
Which is preferable: thread put in wait queue or a busy loop?
- Putting a thread in a wait queue is generally preferable as it avoids consuming CPU resources unnecessarily, unlike a busy loop.
-
What determines which thread goes first once released?
- The thread scheduling policy of the operating system determines which thread goes first once released.
-
How many actions are distinct?
- Actions are distinct based on their functionality and independence in the control flow graph.
-
How many tasks are independent?
- Tasks are independent if they do not rely on each other’s results and can be executed concurrently without dependencies.
-
Is there potential for parallelisation?
- Yes, if tasks are independent and can be executed concurrently, there is potential for parallelization.
-
Discuss which pattern seems most relevant for grouping tasks, ordering tasks, and data sharing.
- The patterns relevant for this are Task Decomposition, Dependency Analysis, and Data Sharing patterns.
-
Identify the data categories: read-only, effectively-local, read-write, accumulate, multiple-read single-write.
- Data categories help in understanding how data is accessed and modified by tasks:
- Read-only: Data read by tasks without modification.
- Effectively-local: Data modified by one task but not shared.
- Read-write: Data read and written by tasks.
- Accumulate: Data combined from multiple tasks.
- Multiple-read single-write: Data read by multiple tasks but written by one.
- Data categories help in understanding how data is accessed and modified by tasks:
-
Is data decomposition feasible for the edge detection application?
- Yes, as edge detection can be divided into smaller tasks that operate on different parts of the image independently.
-
What are the tasks in data decomposition?
- Tasks in data decomposition include breaking down the image into sections, processing each section for edge detection, and combining the results.
-
Explain the granularity knob and the impact of granularity.
- The granularity knob refers to adjusting the size of tasks. Fine granularity means smaller tasks with more parallelism, while coarse granularity means larger tasks with less parallelism but reduced overhead.
-
Explain the terms presented in task decomposition and dependency analysis.
- Task decomposition involves breaking down a problem into smaller tasks. Dependency analysis involves identifying dependencies among tasks to determine the execution order.
-
What is a fork-join?
- Fork-join is a parallel pattern where tasks are split (forked) into parallel subtasks and then combined (joined) after execution.
-
Explain the forces and solutions in the fork-join pattern.
- Forces include the need for parallelism and synchronization. Solutions involve using thread or process management to handle task execution and joining results.
-
How do you allocate components in design space exploration?
- Components are allocated based on performance, resource availability, and optimization goals.
-
What are the stages of design space exploration?
- Stages include requirement analysis, component selection, system modeling, simulation, and optimization.
-
How do you schedule operations in design space exploration?
- Operations are scheduled based on task dependencies, resource availability, and performance metrics.
-
What is the importance of measuring performance in design space exploration?
- Measuring performance helps in evaluating design choices, identifying bottlenecks, and optimizing the system.
-
How do you handle unseen parallelisms and serializations in hardware?
- By using techniques such as pipelining, out-of-order execution, and speculative execution.
-
What are functional units in hardware abstraction?
- Functional units are components in a processor that perform operations like arithmetic, logic, and memory access.
-
What is the role of processing elements in hardware abstraction?
- Processing elements execute tasks and operations in parallel computing architectures.
-
What are tasks in software abstraction?
- Tasks are units of work scheduled for execution by the operating system or runtime environment.
-
What is the difference between binding and pinning in design space exploration?
- Binding refers to associating tasks with resources, while pinning refers to fixing tasks to specific resources to avoid migration.
-
What is the significance of hardware abstraction layers?
- Hardware abstraction layers provide a consistent interface for software to interact with different hardware components.
-
How do you map software functions to hardware components?
- Mapping involves associating software functions with specific hardware units for execution, considering performance and resource constraints.
-
What is the difference between a function and a procedure in software implementation?
- A function returns a value and is used in expressions, while a procedure performs actions but does not return a value.
-
How do you ensure data sharing over defined interfaces in task scheduling?
- By using synchronization mechanisms, data structures, and communication protocols.
-
What are the levels of synchronization required in asymmetric multiprocessing?
- Synchronization levels include task-level, data-level, and system-level synchronization to ensure proper coordination and data integrity.
-
How are communications organized in asymmetric multiprocessing?
- Communications are organized using shared memory, message passing, and inter-process communication (IPC) mechanisms.
-
What is the role of the platform management unit in Zynq UltraScale+ MPSoC?
- The platform management unit manages system resources, power, and boot processes in Zynq UltraScale+ MPSoC.
-
How does the PMU bootloader work in Zynq UltraScale+ MPSoC?
- The PMU bootloader initializes the system, loads the boot image, and hands off control to the main application.
-
What is the significance of the ELF file in rproc_boot?
- The ELF (Executable and Linkable Format) file contains the executable code and data for remote processors in rproc_boot.
-
How does the rproc_boot function handle resource tables?
- The rproc_boot function parses resource tables to configure memory, peripherals, and communication channels for remote processors.
-
What is the role of virtio in inter-processor communication?
- Virtio provides a standardized interface for efficient inter-processor communication and data transfer.
-
How does rpmsg handle message passing in openAMP?
- Rpmsg (remote processor messaging) facilitates message passing between processors using a virtual communication channel.
-
**What are the components of an rpmsg endpoint structure
?** - Components include the destination address, source address, payload buffer, and communication channel configuration.
-
How does the scheduling of tasks occur in a GPU architecture?
- Task scheduling in GPU architecture involves distributing tasks among multiple GPU cores based on workload and resource availability.
-
What is the significance of a command queue in OpenCL?
- A command queue in OpenCL manages the execution order of commands, enabling asynchronous execution and resource management.
These answers provide a comprehensive understanding of the questions related to multicore and parallel processing, as discussed in the document.
BSY
OS
- control resource usage, granting resource requestst, accounting for usage, mediate conflicting requests.
CPU - Central Processing Unit
- Fetch first instruction from memory into registers, decode it to determine its type and operands, execute it, and then fetch, decode and execute subsequent instructions.
Hardware Review
Hyperthreading / Parallel pipelines
- Innerhalb eines Intel Cores, werden mehrere Threads gleichzeitig ausgeführt.
- Optimierung der Aufgaben
Caches
Der Cache ermöglicht einen deutlich schnelleren Zugriff auf Daten als im Memory, Speicher. Der Cache ist aber relativ teuer in der Produktion.
- a: quad-core chip with a shared L2 cache
- b: quad core chip with seperate L2 caches
Memory
Input Output
- Huge amount of I/O Devices, Hard Drives, Network Interfaces, Serial Board, Keyboard, Mouse, Graphics, Cameras, etc.
- Devices connected with CPU via a Bus System (SW) I/O Interfaces (SW) and I/O Controllers (HW)
- There are many different Bus Standars
- PCI
- PCIe
- DMI
- SATA
- US
Operating System Variants
- Mainframe Operating Systems
- Server Operating Systems
- Multiprocessor Operating Systems
- Personal Computer Operating Systems
- Handheld Computer Operating Systems
- Embedded Operating Systems
- Sensor Node Operating Systems
- Real-Time Operating Systems
- Smart Card Operating Systems
Context switch
Der Zustand eines Prozesses wird durch die Inhalte der Register auf dem CPU bestimmt.
- Der Dispatcher befüllt die Register in der CPU.
- Im Durchschnitt 6 Switches pro Sekunden
File Reading
- 11 Schritte
TRAP / Software interrupt
Der einzige Weg, um von User Space in den Kernel Space zu gelangen
Readme
BRICKED CT BOARD
- CONNECT 5V with PA9 (on opposite side of PA10)
- Connect BOOT0 with VDD (on opposite side of BOOT0)
- POWER ON BOARD
- PRESS RST BUTTON
- PRESS LOAD Button
- RELEASE RST BUTTON
Signale
- Es gibt unterschiedliche Signaltypen. Die meisten sind jedoch auf eine "CLK - Clock" angewiesen.
- Die meisten Signal sind auf eine Flanke angewiesen, dies ist besonders beim Bus Signal zu sehen.
NOT
In vielen Schemas wird ein negiertes Signal, dass heist wenn Signal=0, dann aktiv, mit einem "N"[Signalname] bezeichnet.
CLK - Clock
Die CLK ist ein (meistens) gleichbleibendes Signal.
Synchronous Mode
- Master und Slave haben ein geteiltes CLK Signal
Asynchroner Mode
- Master und Slave haben je ein eigenes, voneinander unabhängiges CLK Signal
Synchrones Bus Signal
Der bus des CT-Boardes wird durch eine steigende Flanke (rising edge) des CLK-Signals getriggert.
Ein synchrones bus signal, besteht aus folgenden Komponenten:
- Address lines A[31:0]
- Data lines D[31:0]
- Control
- CLK
- NE: NOT Enable, bestimmt wann das Signal aktiv ist ist
- NWE: NOT Write Enable, bestimmt wann geschrieben wird
- NOE: NOT Output Enable, bestimmt wann gelesen wird
Timing Diagram
NBL Signal - NOT Byte Line
Das NBL Signal zeigt, wann welche Bytes geschrieben werden sollen. Es ist invertiert, also 0=>aktiv
CPU Schreibzeit
GND - Ground
Das GND Signal wird als virtuellen Nullpunkt bezeichnet. Das GND Signal ist in einem System das meist verbreitete
VCC - Voltage Common Collector
Das VCC Signal ist die Eingangsspannung des Gerätes. Es wird immer relativ zum GND Signal bemessen.
GPIO (General Purpose Input/Output)
- GPIOS sind allgemeine Anbindungen. Mit ihnen lassen sich digitale und analoge Signale lesen bzwh. schreiben.
- Die Pins können mit Code / Software konfiguriert werden
- Die meisten GPIO Pins haben eine alternative digitale oder analoge Funktion. Man kann also auswählen, was man braucht.
Nachfolgend eine Tabelle mit den Abkürzungen, diese sind weit verbreitet, und in den meisten Schemas (die Skizeen mit den Strichen und Blöcken) auch so bezeichnet.
Pin bezeichung | Beschreibung |
---|---|
GP | General purpose |
PP | Push Pull |
PU | Pull UP |
PD | Pull Down |
OD | Open Drain |
AF | Alternate Function |
Funktionsmodis
Wie bereits oben beschrieben, haben GPIOs IMMER mehrere Funktionen. Diese können konfiguriert werden.
- Mit den grauen Blöcken wird der Port jediglich konfiguriert.
- Pinke Signale sind dann aktiv, wenn die alternativen Funktionen aktiv sind.
Konfiguration
Die Konfiguration von Pins wird in der echten Welt selten händisch gelöst. Dafür gibt es diverse IDE's die einem das Leben weitgehend vereinfachen. (z.B. STM32CubeIDE) Dies hat den Grund, da jede Konfiguration herstellerabhängig ist.
Die Registernamen haben ein R am Ende des Namens, was für Register steht. Bitte nenne es nicht MODER sonder MODE-REGISTER.
Direction (MODER)
Die Richtung muss immer konfiguriert werden. Gelesen kann vom GPIO auch dann, wenn dieser als Output definiert ist.
MODER[1:0] | Beschreibung |
---|---|
00 | Input |
01 | General purpose output mode |
10 | Alternate function mode |
11 | Analog mode |
Output Type (OTYPER)
OTYPER | Beschreibung |
---|---|
0 | Push - Pull (Reset State) |
1 | Open Drain |
Push Pull
Push Pull ist der Reset State. Bei diesem sind HIGH / LOW möglich. Der Pegel kann nach oben oder unten gezogen werden.
Ist Internal Signal
- LOW, so ist das Output Signal HIGH (links)
- HIGH, so ist das Output Signal LOW
Open Drain
Open Drain wird dann verwendet, wenn du mehrere Geräte an diesem Port anschliessen wilst. Der Pin kann NUR nach LOW gezogen werden.
Ist Internal Signal
- LOW, so ist das Output Signal floating
- HIGH, so ist das Output Signal LOW
Pull-up / Pull-down / floating
- In der Elektronik haben wir das Problem, dass, wenn ein Signal nicht gesetzt wird, dann ist es schwebend (floating). Schwebend bedeutet dann, dass der Wert irgendetwas sein kann. Dies wollen wir soweit wie möglich vermeiden. Deshalb verbinden wir das Eingangssignal über einen Widerstand zu HIGH / LOW. Wird die Leitung nun von aussen gesetzt, so muss das Signal, entgegen des Widerstandes gezogen werden.
- HIGH aktive Signale haben normalerweise eine Pulldown-Koniguration
- LOW Aktive (invertierte / NOT) Signale normalerweise eine Pullup Konfiguration ()
Speed
OSPEEDR[1:0] | Beschreibung | Geschwindigkeit |
---|---|---|
00 | Low Speed (Reset State) | 2 MHz |
01 | Medium Speed | 10 MHz |
10 | High Speed | 50 MHz |
11 | Very high speed | 100 MHz |
## Code
Wie kann denn nun ein GPIO Pin konfiguriert werden. Wir können selten einen einzelnen GPIO über ein eigenes Register steuern. Wir müssen diesen über einen Port steuern. Denn ein Port ist der Zugang zum einzelnen GPIO Pin.
Ports liegen neben anderer Hardware direkt im Memory. Jeder Port hat eine Basis Adresse. Diese Adresse zeigt auf die wirklichen Register des Portes.
Der Port ist dann in unterschiedliche Sektionen aufgeteilt. Diese liegen direkt nebeneinander im Memory. Somit können wir ein Struct verwenden, um auf die Konfiguration zuzugreifen.
typedef struct {
volatile uint32_t MODER; /**< Port mode register. */
volatile uint32_t OTYPER; /**< Output type register. */
volatile uint32_t OSPEEDR; /**< Output speed register. */
volatile uint32_t PUPDR; /**< Port pull-up/pull-down register. */
volatile uint32_t IDR; /**< Input data register. */
volatile uint32_t ODR; /**< output data register. */
volatile uint16_t BSRR[2]; /**< [0]: Bit set register,
[1]: Bit reset register. */
volatile uint32_t LCKR; /**< Port lock register. */
volatile uint32_t AFR[2]; /**< [0]: Alternate Function register pin 0..7,
[1]: Alternate Function register pin 8..15. */
} reg_gpio_t;
// Hier sagen wir wo der GPIOA im Memory liegt
// Indem wir einen Pointer (Zeiger) erstellen, der direkt auf die Adresse zeigt
#define GPIOA ((reg_gpio_t *) 0x40020000)
#define GPIOB ((reg_gpio_t *) 0x40020400)
#define GPIOC ((reg_gpio_t *) 0x40020800)
#define GPIOD ((reg_gpio_t *) 0x40020c00)
#define GPIOE ((reg_gpio_t *) 0x40021000)
#define GPIOF ((reg_gpio_t *) 0x40021400)
#define GPIOG ((reg_gpio_t *) 0x40021800)
#define GPIOH ((reg_gpio_t *) 0x40021c00)
#define GPIOI ((reg_gpio_t *) 0x40022000)
#define GPIOJ ((reg_gpio_t *) 0x40022400)
#define GPIOK ((reg_gpio_t *) 0x40022800)
Continously read from address
#define ADDRESS 0x43004000
volatile uint32_t* const registerAddress = (volatile uint32_t*) ADDRESS;
uint32_t value;
while(1) {
value = *registerAddress;
// Check if bit 6 is set
if (value & (1 << 6)) {
break; // Exit the loop if bit 6 is 1
}
}
Continously write to address
#define ADDRESS 0x43004000
volatile uint32_t* const registerAddress = (volatile uint32_t*) ADDRESS;
uint32_t value = 0x00;
while(1) {
*registerAddress = value;
}
SPI - Serial Peripheral Interface
SPI wird für die serielle Kommunikation zwischen einem Microcontroller und Peripheriegeräten verwendet. Das System ist vom Typ Master-Multiple Slave.
Signal | Beschreibung |
---|---|
SCLK | Serial Clock |
MOSI | Master Out Slave In, to all slaves |
MISO | Master In Slave Out, all slaves to one master |
SSX | Slave Select X, Auswahl des Slaves |
Der Master, auch main genannt, ist dabei für die abstimmung der Kommunikation der einzelnen Slaves zuständig.
Connection (wires)
- Serial data line(s)
- Optional control lines
Communication Mode | Beschreibung |
---|---|
Simplex | Unidirectional, one-way only |
Half-duplex | Bidirectional, only one direction at a time |
Full-Duplex | Bidirectional, both directions simultaneously |
Timing | Beschreibung |
---|---|
Synchron | Beide Anbindungen nutzen ein geteiltes CLK Signal |
Asynchron | Jede Anbindung nutzt ein eigenes CLK Signal |
Implementierung
Die Implementierung wird dabei mit Hilfe einse Schieberegisters implementiert. Das Schieberegister (Buffer) wird gefüllt und dann ausgelesen. Der Grund ist, dass die Kommunikation unabhängig der aktuellen Aufgaben der CPU ausgeführt werden können.
## Timing Diagramm
Das Timing Diagram sieht zwar Komplex aus, ist es aber eigentlich gar nicht.
- Der Master zieht das CS (SS) Signal des gewünschten Slaves nach unten. Der Strich oben auf dem Namen bezeichnet das Signal als Low Aktiv.
- Nun werden die 8 Bit aus dem Schieberegister bidirektional gesendet. (Das MSB wird dabei normalerwise als erstes gesendet)
- Daten werden dabei auf dem toggling edge gesendet
- Während Daten auf dem samping edge gelesen werden. Dies wird dann gemacht, um Lesefehler aufgrund von Signalwechsel zu vermeiden. Es wird in der Mitte des Bites gelesen.
- Nachdem 8 Bit gelesen wurden, wird das CS Signal wieder angehoben. Somit ist die Kommunikation beendet.
SPI Modes (toggling / raising edge)
- TX: toggling edge
- RX: raising edge
## Synchronisierung
TX
Sobald das Signal (TXE / TX Buffer Empty) HIGH ist, kann das nächste TX Byte auf das Register SPI_DR geschrieben werden
### RXNE
Ist dann aktiv, wenn Daten auf SPI_DR sind. Also können wir dann Daten von diesem Register lesen gehen.
Modes
MODE=0, CPOL = 0, CPHA = 0, SCK idle state = low
MODE=1, CPOL = 0, CPHA = 1, SCK idle state = low
MODE=2, CPOL = 1, CPHA = 0, SCK idle state = high
MODE=3, CPOL = 1, CPHA = 1, SCK idle state = high
U(S)ART (Universal Asynchronous Receiver Transmitter)
-
UART wird für allgemeine serielle Kommunikation über eine serielle Schnittstelle verwendet. Dies wird häufig bei Microprozessoren verwendet.
-
Die Kommunikation ist asynchron, dafür muss jedes Paket einzel durch den Identifikator (START Bit) synchronisiert werden.
-
Im Gegensatz zu SPI hat UART nur zwei Leitungen.
-
Die Sendefrequenz muss vor dem Verbindungsaufbau bekannt sein. Sender und Empfänger haben eine eigene CLK, die aber die gleiche Frequenz hat.
-
Paritymode (even / odd) muss ebenfalls vor dem Verbindungsaufbau bekannt sein.
Protokoll
- IDLE Zustand: HIGH
- Bits werden auf der steigenden Flanke von CLK gelesen.
- Leitung nach LOW ziehen
- 5-8 Datenbits senden (LSB first)
- Parity senden
- Even / Gerade Parity: (Wenn Anzahl 1=gerade, dann parity = 0)
- Odd / Ungerade Parity: (Wenn Anzahl 1=ungerade, dann parity = 1)
- None => keine
- mark => HIGH
- space => LOW
Eigenschaften
Transmission Rate (Baud Rate)
Gibt an, wie viele Symbole pro Sekunde gesendet / empfangen werden sollen
Typische Werte sind:
- 2400
- 4800
- 9600
- 19200
- 38400
- 57600
- 115200
Anzahl an Datenbits
- 5
- 6
- 7
- 8
Anzahl an stop bits
- 1
- 1.5
- 2
Parity
- none
- mark (logic HIGH)
- space (logic LOW)
- even
- odd
CPOL=0,CPHA=0
CPOL=0,CPHA=1
CPOL=1,CPHA=0
CPOL=1,CPHA=1
RS-485
- Braucht 4 Leitungen, jeh zwei zum Senden / Empfangen
- Signale auf den 2 Leitungen sind gegenseitig gespiegelt, was weniger anfällig zu noice (Fremdspannungen) auf Leitungen ist.
I2C
- Bidirektional 2 Wire
- Mit CLK (SCL)
- Daten werden auf SDA gesendet
- Synchron, half duplex
- Jedes Gerät auf dem Bus, hat eine einzigartige Adresse
- Multi Master möglich
Protokoll
1. START
- SCL wird auf HIGH gezogen
- SDA wird nach unten gezogen
- 1 Bit=LOW wird auf SCL gesendet, damit wird der BUS in den BUSY Mode gesetzt.
2. Adresse + Richtung senden
-
7 Bits für die Slave Adresse
-
Richtung
- 0: Write (Master -> Slave, Slave=Empfänger)
- 1: Read (Slave -> Master, Master=Empfänger)
-
Nicht verwirren lassen, die Kästen auf SDA sagen jediglich, dass da ein Bit ist.
3. ACK von Slave
- Der Slave zieht die SDA Leitung zu LOW.
- Macht dies der Slave nicht, so wird die Kommunikation beendet (STOP)
4. Daten von Transmitter
- Der Sender gibt 1 Byte auf den Bus aus
- Der Empfänger sendet ein ACK Signal auf SDA
- Der Sender kann erneut Daten senden
- Sendet der Empfänger kein ACK, so schreibt der Sender auch keine weitere Daten mehr auf den SDA
STOP
- Master zeiht SDA von LOW zu HIGH
# Timer / Counter
Ein Timer, auch Eieruhr genannt, hat den Zweck, zu sich wiederholenden Zeiten ein Signal oder einen Interrupt auszuführen.
Um den Einstieg ein wenig zu vereinfachen, kann ein Timer auch im Code nachgebaut werden.
while (1) {
for (uint32_t arrs=0; arrs < ARRS; psc++) {
for (uint32_t psc=0; psc < PSC; psc++) {
// sleep for 1 CPU Cycle
// increase psc by 1
}
// increase ARRS by 1
}
// ARRS OVERFLOW OCCURED
// INTERRUPD CALLED
UIF();
}
Zähler
Bits | Von | Bis |
---|---|---|
16 bit | 0 | 2^16 |
32 bit | 0 | 2^32 |
Es gibt zwei Arten von Zählern:
### Up-Counter
- Zählt nach oben, beginnt bei 0, endet bei ARR
- Generiert einen Overflow, also wenn der Wert über ARR kommt
Down-Counter
- Zählt nach unten, beginnt bei ARR bis 0
- Generiert einen Underflow, also wenn der Wert unter 0 kommt.
2 Bit Zähler
Prescaler
Der Prescaler besimmt wie viele Impulse benötigt werden, damit der Counter erhöht / verringert wird.
Timer berechnen
Name | Symbol | Einheit | Beschreibung |
---|---|---|---|
Quelle | FCLK | Hz | Frequenz des Eingangssignales |
Prescaler Wert | PSC | - | |
Auto Reload Register | ARR | - | Auf welchen Wert das Register gesetzt werden soll, wenn |
Zeitinterval | tout | s | in welchem Abstand, der Timer ausgeführt werden soll |
Aufgaben
- Der ARR soll jede 100us erhöht werden
- Der Counter soll im Interval von 1s ticken
- FCLK = 1 Mhz
dann folgt folgendes daraus für den Prescaler (PSC)
- FCLK = 1'000'000 Hz
- (PSC+1) = 100us * FCLK
- (PSC+1) = 0.000'100s * FCLK
- (PSC+1) = 0.000'100s * 1'000'000Hz
- (PSC+1) = 4200
- PSC = 4200 - 1 = 4199
Und für den ARR
- FCLK = 1'000'000 Hz
- CLK = 100us
- (ARR+1)= 1s / 100us
- (ARR+1)=100'000
- ARR = 100'000 - 1 = 99'999
# PWM - Pulse Width Modulation
Mit PWM wird mit gan schnellem ein-und ausschalten ein Sinussignal nachgemacht.
Duty cycle
Gibt in Prozent an, wie oft das Signal an ist.
- 50% => 50%
- 0.5 => 50%
Average Signal
ADC - Analog Digital Converter
Die Welt ist analog, dass heisst ein Wert kann von 0 - 1 alles mögliche sein, dass heisst auch 0.034566, oder 0.9999.
Um jetzt dieses analoge Signal auch in der digitalen Wert verwenden zu können, gibt es ADC's. Diese wandeln den analogen Wert in einen digitalen Wert um.
Wir machen uns dabei ein Phenomen der Elektrotechnik zugunsten, wo wir mit hilfe von Widerständen eine Spannung aufteilen können.
Wenn wir jetzt bei einem der Signale, 1-5V einen Komparator, auch Vergleicher genannt, hinzufügen, dann ist es relativ einfach zu verstehen wie der ADC funktioniert.
Das Verhältnis der Widerstände ist jeh nach ADC unterschiedlich.
Dem ADC wird ein sehr genaues Referenzsignal (VREF) hineingefügt.
Fehler
Full-scale-error = offset error + gain error
Quantization
- Analoges Signal ist eine gerade
- Digitals Signal kann nur gewisse Werte analoge Werte weiderspiegeln.
- Fehler zwischen -0.5 und + 0.5, der kleinsten Auflösung (LSB)
Offset
- Nullpunkt nicht korrekt eingestellt
Gain
Conversion Modes
Single Channel | Multi channel | |
---|---|---|
Single Conversion | Convert 1 channel, then stop. | Convert all channels in group, one after the other, then stop. Sequence can be programmed |
Continous conversion | Continously convert 1 channel until stop order is given | Continously convert a group of several channels until the stop order is given. Sequence can be programmed |
Timing
Register Bits
Common Control Register (ADC_CCR_ADC)
Register | Beschreibung | additional |
---|---|---|
TSVREFE | enable/disable temp sensor and vREFITNT | |
VBATE | enable/disable vbat | VBAT => Batteriespannung, nur wenn eine angeschlossen ist |
ADCPRE[1:0] | Prescaler for ADCCLK | APB2 clock divided by 00 -> 2 01 -> 4 10 -> 6 11 -> 8 , APB2 clock = 42 Mhz on this board |
Status Register (ADC_SR)
Register | Beschreibung |
---|---|
OVR | Overrun, Daten wurden überschhrieben |
STRT | Conversion started (regular channel) |
EOC | End of conversion. Cleared by reading result of conversion |
Control Register 1 (ADC_CR1)
Register | Beschreibung | Additional |
---|---|---|
OVRIE | Override Interrupt enable | |
EOCIE | EOC Interrupt enable | |
RES[1:0] | Conversion resolution | 00 -> 12 bit 01 -> 10 bit 10 -> 8 bit 11 -> 6 bit |
SCAN | Enable scan mode |
ADC Control Register 2 (ADC_CR2)
Register | Beschreibung | Additional |
---|---|---|
SWSSTART | Start conversion of regular channel by software | Is cleared by hardware |
EXTEN[1:0] | External trigger for regular channel | 00 -> disabled 01 -> positive edge 10 -> negative edge 11 -> positive & negative edge |
EXTSEL | External event select to trigger conversion of a regular group | |
ALIGN | Data Alignment | 0 -> right aligned 1->left aligned |
EOCS | EOC Selection | 0 -> EOC shows end of each sequence of conversions 1 -> end of each conversion |
DMA | Enable DMA | kopiere direkt vom adc in den memory. |
CONT | Continous mode | 0 -> single conversion mode 1 -> continous conversion mode |
ADON | ADC On | aktivert den ADC |
Sample Time
ADC Sample time register
Konfiguriert die Zeit, in der die Spannung gemessen ist.
Sampling for channel: SMPx[2:0]
- 0x00: '000b' -> 3 cycles
- 0x01: '001b' -> 15 cycles
- 0x02: '010b' -> 28 cycles
- 0x03: '011b' -> 56 cycles
- 0x04: '100b' -> 84 cycles
- 0x05: '101b' -> 112 cycles
- 0x06: '110b' -> 144 cycles
- 0x07: '111b' -> 480 cycles
ADC_SMPR1
- Address offset 0x0C
- Reset Value: 0x0000 0000
ADC_SMPR2
- Address offset 0x10
- Reset Value: 0x0000 0000
Sequence of Channels (ADC_SQR1/2/3)
- Address offset: 0x2C
- Reset value: 0x0000 0000
Register | Beschreibung | Additional |
---|---|---|
L[3:0] | Sequence length | L+1 = number of conversions in regular sequence |
SQx[4:0] Channel number of xth conversion ins sequence | 1 <= x <= 16 |
e.g. SQ3[4:0] = 0x7 means the 3rd conversion takes place on channel 7
Rechnungen
Sampling Time
Max sampling rate
- PBA2 -> ADC Frequency, by default 42Mhz => 42'000'000 Hz
Cache
- Blocks sind in diesem Beispiel 4 Byte lang, können aber auch grösser, bzwh. kleiner sein.
Aufbau im Memory
- Gruppen von Blocks werden im Memory hintereinander gebilted.
- Die CPU kopiert gewisse Blocks mit deren ID in den Cache.
Bezeichnung | Symbol |
---|---|
Anzahl Bits für die Adressierung | t + i + o |
Anzahl Bits für Offset | o |
Anzahl Bits für Index | i |
Anzahl Bits für Tag | t |
- Grösse des RAM in Bytes (Memory Size): 2^(t+i+o)
- Anzahl Zeilen im Cache: 2^i
- Grösse einer Cache-Zeile in Bytes (nur Nutzdaten): 2^o
- Grösse des Cache in Bytes (Cache Size, nur Nutzdaten): 2^i * 2^o
Cache Organization
Fully Associative
Direct Mapped
N-Way Set Associative
- max index corresponds to number of sets (s=m/n)
Memory Hierarchy
]
Principle of locality
for (i=0; i < 10000; i++) {
a[i] = b[i]; // spatial locality
}
if (a[1234] == a[4320]) { // temporal locality
a[1234] = 0;
}
Interrupt
Periodic Query of Status Information (Polling)
- Synchronous with main programm
- Advantages
- Simple and straightforward
- Implicit synchronization
- Deterministic
- No additional interrupt logic required
- Disadvantages
- Busy wait (wastes CPU time)
- Reduced trhoughput
- Long reaction times
if (spi_is_txe_set()) {
spi_write_data(...);
}
if (uar_is_rxne_set()) {
uard_data = uart_read_data();
}
if (adc_is_eoc()) {
adc+data = adc_read_data();
}
Interrupt Driven I/O
Interrupt Performance
- tISR > "Time between two interrupt events"
- Some interrupt events will not be serviced (lost)
- Data will be lost
- fINT as well as tISR may vary over time
- Average may be ok, but individual interrupt events may still be lost
- Some interrupt events will not be serviced (lost)
Impact on system performance
Keyboard
tISR = 6 us = 0,000006s fInt=20Hz = 20 1/s
Impact = 20 Hz * 6us * 100% = 0.012%
Interrupt Priorities
Code
void TIM2_IRQHandler(void)
{
}
/**
* \brief Timer 3 ISR: Generating load
*/
void TIM3_IRQHandler(void)
{
hal_timer_irq_clear(TIM3, HAL_TIMER_IRQ_UE);
tim3_interrupt_counter++;
}
CT1 - HS23
Cortex Architektur
Die Cortex Architektur ist auch als ARM Architektur bekannt. Diese ist in den meisten Embedded Prozessoren, in letzter Zeit aber auch in Mobile, sowie Desktop (Apple Silicon) vertreten.
CPU Modell
Die CPU besteht aus einzelnen Komponenten. Diese sind in folgende:
- Core Registers
- 32 Bit ALU (Arithmetic Logic Unit)
- Flags (APSR)
- Control Unit with IR (Instruction Registers)
- Bus Interface
16 Core Registers
- Jedes Register ist 32 Bit breit
- 13 General Purpose Register
- Low Registers R0 - R7
- High Registers R8 - R12
- Used for temporary storage of data and addresses
- Program Counter (R15)
- Addresss der nächsten Instruktion
- Stack Pointer (R13)
- Last-In First-Out für temporäre Datenspeicherung
- Link Register (R14)
- Enthält die Adresse zu der zurückgesprungen wird, wenn eine Routine fertig ist.
In den meisten Fällen werden nur die unteren Low Registers (Byte verwendet). Würde die Standardeinheit jetzt 16 oder gar 32 Bit sein, dann müssen die High Registers zusätzlich noch gelesen werden.
ALU (Arithmetic Logic Unit)
- 32 bit wide data processing unit
- Inputs A and B
- Results in C
- Integer Arithmetic
- Addition / Subtraction
- Multiplication / Division
- Sign extension
- logic operations
- AND
- NOT
- OR
- XOR
- shift /rotate
- left
- right
Flags (APSR)
Bits werden von der CPU anhand des Resultats der ALU gesetzt
Wert | Bedeutung |
---|---|
N | Negative |
Z | Zero |
C | Carry |
V | Overflow |
Control Unit
- Instruction Register (IR)
- Machine code (opcode) of instruction that is currently being executed
- Controls execution flow based on instruction on IR
- Generates control signals for all other CPU components
Bus Interface
- Interface zwischen interner CPU und externen Bus Systemen
- Beinhlatet register um addressen zu speichern
Instruction Set Architecture (ISA)
Instruction Set
- Verfügbare Anweisungen
Processing Width
- Bitbreite (8/16/32 Bit)
Register Set
- Wie viele Register gibt es?
- Wie gross sind die Register?
Addressodell
- Wie kann auf Speicher zugegriffen werden?
- Wie kann auf IO zugegriffen werden?
### Arbeitsweise
Prozessoren interpretieren binären Code als Instruktionen.
Styleguide
7 Segment Anzeige
Eine 7-Segment-Anzeige wird für Hexadezimalzeichen verwendet. Dabei wird jedes Segment der Anzeige einzeln angesteuert.
Theorie
Bei der Zahl 1 würden die Segment B und C aufleuchten. Im Register heisst dass dann die Bits 1 und 2 aktiv sind. Somit würde der Wert 0000'0110 bzwh. 0x06 sein.
Ist die Anzeige active-low (invertiert), dann muss 1111'1001 bzwh. 0xF9 geschrieben werden.
Bei 16 Zeichen ist die manuelle Umwandlung relative mühsam. Wir können das Umwandeln aber auch einfach dem Compiler überlassen. Dies hat zudem den Vorteil, dass es deutlich einfacher zu verstehen ist.
Code
Ich empfehle den Code immer so "blöd" wie möglich zu machen. Jedoch ist es auch wichtig, dass nicht einfach zufällige Zahlen irgendwo stehen (sog. Magic Numbers).
Define für jedes Segmentstück definieren
Wie bereits oben beschreiben, ist es relativ mühsam die einzelnen Zahlen zu definieren. Um dies zu vereinfachen können wir die einzelnen Bits einem define zuordnen.
Dass könnte dann z.B. so aussehen. Es ist auch möglich die decimalzahlen, bzwh. die HEX Zahlen direkt zu verwenden.
#define LED_A 1<<0
#define LED_B 1<<1
#define LED_C 1<<2
#define LED_D 1<<3
#define LED_E 1<<4
#define LED_F 1<<5
#define LED_G 1<<6
#define LED_DP 1<<7
Definieren der Zahlen
Nun ist es wessentlich einfacher diversen Zeichen darzustellen. Dazu müssen wir nur noch die verwendeten Segmente mit einem ODER verknüpfen.
#define LED_SEGMENTE_ZAHL_1 (LED_B | LED_C)
#define LED_SEGMENTE_ZAHL_2 ...
Umwandeln
Das umwandeln ist durch einen array mit den 16 Zeichen oder ein switch case möglich. Ich empfehle den Ansatz des arrays, da dieser weniger Aufwand ist.
const static uint8_t segment_zeichen[16] = {
LED_SEGMENT_ZAHL_1,
LED_SEGMENT_ZAHL_2,
...
};
Entrpellung
Im Gegensatz zu digitalen Schaltern, sind analoge Schalter nicht sofort 1/0. Sie prellen.
Um dieses Problem zu lösen, werden Flanken erkannt. Also die Zuständsänderungen.
Flankenerkennung
uint8_t is_positive_flank(uint8_t previous, uint8_t current, uint8_t mask)
{
if ((previous & mask) == 0x00 && (current & mask) == mask)
{
return 1;
}
return 0;
}
uint8_t is_negative_flank(uint8_t previous, uint8_t current, uint8_t mask)
{
if ((previous & mask) == mask && (current & mask) == 0x00)
{
return 1;
}
return 0;
}
Verwendung
#define ADDR_SWITCHES ...
#define MASK_OF_INTEREST 1<<2
int main ()
{
uint8_t previous_value = read_byte(ADDR_SWITCHES);
uint8_t current_value = read_byte(ADDR_SWITCHES);
uint8_t counter = 0;
while (1)
{
current_value = read_byte(ADDR_SWITCHES);
if (is_positive_flank(previous_value, current_value, MASK_OF_INTEREST))
{
// do something
counter += 1;
}
write_byte(LEDS, counter);
previous_value = curent_value;
}
}
Assembler
Special Characters
Charakter(s) | Meaning / Usage |
---|---|
# | Precedes a number (decimal 1234 or hex 0x1234) |
[ ] | Content in squared brackets is interpreted as address |
= | Pseudo- Instruction: Assembler reserves space in literal pool to store the value following the "=" sign. The value located at that address is then loaded to the target address. |
{ } | Enumeration, e.g. comma seperated list of registers to be pushed on stack / written to memory |
! | "increment after" in load/store multiple |
, | Seperates operands |
; | Preceds a comment |
Other | Characters at beginning of line (no leadnig blanks) are interpreted as label |
Flags
Flag | Meaning | Action | Operands |
---|---|---|---|
Negative | MSB = 1 | N = 1 | signed |
Zero | Result = 0 | Z = 1 | signed, unsigned |
Carry | Carry | C = 1 | unsigned |
Overflow | Overflow | V = 1 | signed |
N: Wenn MSB des Resultats =1 ist
Z: Wenn Resultat =0 ist
C: Wee
Overflow
The overflow flag is thus set when the most significant bit (here considered the sign bit) is changed by adding two numbers with the same sign (or subtracting two numbers with opposite signs). Overflow cannot occur when the sign of two addition operands are different (or the sign of two subtraction operands are the same).
Usage
MRS <Rd>, APSR ; Rd = APSR
MSR APSR, <Rn> ; APSR = Rn
Calculation
Binäre Subtraktion
HEX: 0x82 - 0x12
BIN: 1000'0010 - 0001'0010
subtrahend invertieren
1000'0010 - 1110'1101
subtrahend +1
1000'0010 - 1110'1110
Zahlen addieren
1000'0010 +
1110'1110
---------
10111'0000
=========
Data Transfer
Transfer Types
Moving Data
Copy value of Rm to Rd
Low and High registers
MOV <Rd>, <Rm>
Only low registers
MOVS <Rd>, <Rm>
Exampes
address opcode instruction comment
00000002 4621 MOV R1,R4 ; low reg to low reg
Loading Literals
Immediate Data
- Copy immediate 8-bit value (literal) to register (only low registers)
- 8 Bit literal is part of the opcode (imm8)
- Registerbits 31 to 8 set to 0
MOVS <Rd>, #<imm8>
Assembler Directive
- Symbolic definitions of literals and constants
- Comparable to #define in C
MY_CONST8 EQU 0x12
MOVS R1, #MY_CONSTS
Load - LDR (literal)
- Indirect access relative to PC
- PC offset
- If PC not word-aligned, align on next upper word-address
LDR <Rt>. [PC, #<imm>]
Pseudo Instruction
LDR R3,myLit
myList DCD 0x12345678
Storing Data
Arithmetic Operations
Mnemonic | Insruction | Function |
---|---|---|
ADD /ADDS | Addition | A + B |
ADCS | Addiction with carry | A + B + c |
ADR | Address to register | PC + A |
SUB / SUBS | Subtraction | A - B |
SBCS | Subtraction with carry (borrow) | A - B - NOT(c) |
RSBS | Reverse Subtract (negative) | -1 * A |
MULS | Multiplication | A * B |
Add Instruction
Update flags
- Update of Flags
- Result and two operands
- Only low registers
ADDS <Rd>, <Rn>, <Rm> ; Rd = Rn + Rm
Dont update flags
- No update of flags
- High and low registers
- Rdn contains result and operand
ADD <Rdn>, <Rm> ; Rdn = Rdn + Rm
Immediate (3 bytes)
- Update of flags
- Two different low registers and immediate value 0-7
ADDS <Rd>,<Rn>, #<imm3> ; Rd = Rn + <imm3>
Intermediate (8 bytes)
- Update of flags
- Low register with intermediate value 0-255d
- Rdn stores result and operation
ADDS <Rdn>, <#imm8> ; Rdn = Rdn + <imm8>
Negative Zahlen
2er Komplement
RSBS <Rd>, <Rn>, #0
Rd = 0 - Rn
Subtraction
SUBS <Rd>, <Rn>, <Rm> ; Rd = Rn - Rm
; = Rn + NOT(Rm) + 1
Mutli-Word Arithmetic
ADCS <Rdn>, <Rm> ; Rdn = Rdn + Rm + C
SBCS <Rdb>, <Rm> ; Rdn = Rdn - Rm - NOT(C)
; = Rdn + NOT(Rm) + C
Multiplication
MULS <Rdm>, <Rn>, <Rdm> ; Rdm = Rn + Rdm
Assembler
Load value from registers
### Load Byte value
ADDR_DIP_SWITCH_7_0 EQU 0x60000200
LDR R0, =ADDR_DIP_SWITCH_7_0 ; read dip switch address into r0
LDRB R0, [R0] ; read dip switches 15 to 8 into r0
### Load half word value
ADDR_DIP_SWITCH_7_0 EQU 0x60000200
LDR R0, =ADDR_DIP_SWITCH_7_0 ; read dip switch address into r0
LDRH R0, [R0] ; read dip switches 15 to 8 into r0
Storing Value
Store byte
ADDR_LED_31_24 EQU 0x60000103
LDR R6, =ADDR_LED_31_24 ; store led address in R6
STRB R3, [R6] ; display array_index
Store halfword
ADDR_SEG7_BIN EQU 0x60000114
LDR R6, =ADDR_SEG7_BIN ; load address
STRH R0, [r6, #0] ; Store Index and Value to SSD
Bitwise AND
Bitwise and between value and address
BITMASK_LOWER_NIBBLE EQU 0x0F
LDR R2, =BITMASK_LOWER_NIBBLE ; read address of lower bitmask nibble
ANDS R0, R0, R2 ; store into r0 <- (r0 and mask: r2)
Incrementing address
Increment R4 by one
ADDS R4, R4, #1 ; increment register address
Shift
Shift Left
LSLS R1, R1, #1 ; increment index by multiplying it x2
Moving Register
MOV R5, R1 ; copy R1 into R5
Type Casting
Sign Extension (signed)
Extend word without changing value
SXTB R3, R7 ; extend R7 into R3 (8 bit to 32 bit)
SXTH R3, R7 ; extend R7 into R3 (16 bit to 32 bit)
Zero Extension (signed)
Extend word without changing value
UXTB R3, R7 ; extend R7 into R3 (8 bit to 32 bit)
UXTH R3, R7 ; extend R7 into R3 (16 bit to 32 bit)
Bitmanipulation
OR
ORRS R3, R7 ; OR MASK
CLR - Clear Bits
BICS R3, R7 ;
XOR - Bitwise invert
EORS R3, R7 ;
AND
ANDS R3, R7 ;
Bits löschen
ADDRESS ECU ...
LDR R6, =ADDRESS ; ADDRESSE in R6 laden
LDR R1, [R6] ; Wert von R6 in R1 laden
LDR R2, =0x2220 ; Maske 0x2220 in R2 laden
BICS R1, R2 ; Bits R2 in R1 löschen
STR R1, [R6] ; R1 in R6 schreiben
Kontrollstrukturen
Switch Case
NR_CASES EQU 0x2
jump_table ; ordered table containing the labels of all cases
; STUDENTS: To be programmed
DCD case_dark
DCD case_add
DCD ...
case_dark
LDR R0, =0
B display_result
case_add
ADDS R0, R0, R1
B display_result
CMP R2, #NR_CASES
BHS case_bright
LSLS R2, #2 ; * 4
LDR R7,=jump_table
LDR R7, [R7, R2]
BX R7
Alu
def has_carry(result, bit_limits): m = (1<
Memory
Directives
REQUIRE8
specifies that the current file requires 8-byte alignment of the stack
### PRESERVE8 specifies that the current file preserves 8-byte alignment of the stack
THUMB
- Provides 16 Bit instruction set
- fetches instructions by 2 bytes
### ARM
- Provides 32 bit instruction set
- Fetches instructions by 4 bytes
DCB (1 byte)
Allocates one or more bytes of memory, and defines the initial runtime contents of the memory
DCD (4 bytes)
Allocates one or more words of memory, aligned on 4-byte boundaries, and defines the initial runtime contents of the memory.
DCW (2 bytes)
Allocates one or more halfwords of memory, aligned on 2-byte boundaries, and defines the initial runtime contents of the memory
### SPACE (1 byte)
store_table SPACE 16 ; reserves 16 byte
AREA Instruction
The AREA directive instructs the assembler to assemble a new code or data area. Areas are independent, named, indivisible chunks of code or data that are manipulated by the linker.
AREA name{,attr}{,attr}...
Code
AREA myCode, CODE, READONLY
Variables
AREA myAsmVar, DATA, READWRITE
; VARIABLES
ALIGN
Impressum
Verlass dich und du wirst verlasssen.
Obligationen
"obligo" bedeutet verpflichten im latein.
Kommen bei folgenden Aktionen zustande
- Verträge
- Schadenersatz (OR 41)
- Ungerechtfertigte Bereicherung(OR 62)
- Steuergesetz
Sitte
Für bestimmte Lebensbereiche einer Gemeinschaft geltende, dort übliche, als verbindlich betrachtete Gewohnheit, Gepflogenheit, die im Laufe der Zeit entwickelt, überliefert wurde.
Behauptung
Wird druch Grundlage (Gesetzesartikel) und notwendigen Beweis gestützt.
Schlussfolgerung
Gestützt auf Grundlage (Gesetzesartikel) und notwendigem Beweis ergibt sich die Schlussfolgerung.
Gewaltentrennung
Drei unabhängige Institutionen auf eidgenössischer, kantonaler und kommunaler Ebene.
- Legislative: Parlamente & Bürger
- Exekutive: Verwaltung
- Judikative: Gerichte
Rechtsordnung
- Rang (Verfassung, Gesetz, Verordnung)
- erlassendem Gemeinwesen (Bundesrecht, kantonales- und Gemeinderecht)
- Rechtsquelle (geschriebenes Recht, Gewohnheitsrecht, Gerichtspraxis, ZGB 1)
- Beteiligten Personen (Privatrecht, öffentliches Recht)
Rechtsart
Privatrecht
Private - Private
Öffentliches Recht
öffentlich - Private
- Notengebung an der ZHAW
Betreibung
Bricht eine Partei einen Vertrag, dann kann der andere Partner die Vertragbrechende Person betreiben.
Parteikosten wegschlagen
- Die Kosten der eigenen Verteidigung müssen selber übernommen haben.
Rechtsstaat
Legislative: Parlamente & Bürger Exekutive: Verwaltung Judikative: Gerichte
Weshalb gibt es überhaupt Recht?
Gibt es keine Regeln, so muss jeder mit dem anderen aushandeln. Es ist gesellschachtflich einfacher sich an Regeln zu halten.
Strafverfahren
Essentials
- Verfahren sind in StGB & StPO geregelt. Polizei ungerliegt überweigend kantolaner Obheit & ist dort geregelt.
- Ortliche Zuständigkeit ergibt sich aus dem Tag- oder ERfolgsort.
- Für Antragsdelikte gilt eine Frist von 3 Monaten
- Staatsanwalt (StA) leitet Untersuchung, muss belastende & entlastende Aspekte sammeln
- Als "Opfer" hat man nur begrenzte Einsicht in Untersuchungsmassnahmen. Ausser man bringt sich als Privatstrafkläger ein!
- StA stellt wentweder Verfahren ein, straft (max. 6 Monate Freiheitsstrafen und/oder 180 Tagessätze) oder überweist den Fall zur Beurteilung an das Strafgericht.
Vorgehen
- Anschreiben
- Straffanzeige einreichen
## Aufgaben
Staatsanwaltschaft
- Belastende Aspekte sammeln
- Entlastende Aspekte sammeln
Zivilprozess
Essentials
- Vor dem örtlich / sachlich zuständigen Gericht
- In der REgel vorher eine Schlichtungsverhandlung / Friedensrichter
- In Zivilverfahren muss regelmässig ein Gerichtskostenvorschuss, der vom Streitwert abhängt, bezahlt werden! Ohne Zahlung kein Prozess!
- In Zivilrechtsverfahren muss der Kläger dem behaupteten Anspruch beweisen - Das Gericht sucht keine Beweise
- Wer den Zivilprozess verliert, muss die Gerichtskosten sowie Parteikosten der anderen seite übernehmen.
- Wer einene Förderungsprozess gewinnt, hat das Geld noch nicht...
Gutes Geld nicht schlechtem Geld hinterherwerfen
Andere Möglichkeiten
- Richter einigen sich gemeinsam
- Aussergerichtliche Einigung
IT Verträge
- Vertrauen ist extrem wichtig
- Klare, einfache Verträge sind wichtig
- An das Ende der Zusammenarbeit denken
Anforderungen
- Vertrauen
- Austausch zwischen den Parteien / Kommunikation
- Ideen
- Umfang des Projektes
Beweisbarkeit
Es gilt:
- Was die Parteien tatsächlich gelebt haben.
- Was wie bereits vor der Vertragsunterzeichnung besprochen / zugesichert haben.
Softwareentwicklung
Klassisch
Pflichtenheft, Milestones & Abnahme = Werkvertrag + viiiel Zeit + Lizenz / Kaufvertrag
## Agile Softwareentwicklung
= Werkvertrag? (Resultat zählt)
= Auftrag? (Dienstleistung / Zusammenarbeit zählt)
= Einfache Gesellschaft? (Zusammenarbeit für das Erreichen eines bestimmten Zwecks)
Nichts passt wirklich - daher müssen die Verträge individuell zusammengestellt werden
### Vorgehen
Phase 1: 1,2,3,4
Phase 2: 5,6,7,8,9,10
- Genügend Zeit in Phase 1 investieren
Vertragsgestalltung
Konfliktmanagement
- Passiert in allen Softwareprozessen
- Etwas steuern
State Of the Art
- Flexibilität & Abänderbarkeit des Vertragsverhältnisses
Offerte & Vertragsschluss
Art. 1 OR: Vertrag kommt (nur) durch gegenseitige, übereinstimmende Willensäusserung zustande. Diese kann ausdrücklich oder stillschweigend erfolgen.
Offerte = verbindlicher Antrag, den Vertrag unter bestimmten Bedingungen (Preis, Menge etc.) abschliessen zu wollen.
Akzept = Annahme der Offerte.
Willensäusserung = schriftlich, mündlich oder konkludentes Verhalten
Unlauterer Wettbewerb
- Hat ein Prospekt einen Einfluss auf die Willensbildung, dann ist der Preis rechtsgültig.
- Falsche Angaben von Liefermengen
Wird keine Auftragsbestätigung gesendet, dann ist das nur eine Einladung zur Offerstellung und ist somit nicht Rechtsgültig.
Vertragsarten
Nominatverträge
Gesetzlich geregelte Vertragsformen - zwingende oder dispositive Bestimmungen: "klare Regelun""
- Kaufvertrag (Standardsoftware, Infrastruktur)
- Auftrag (Consulting, Installation, Entwicklung, Projektmanagement, SLA)
- Werkvertrag (kundespezifische Software, Softwareerweiterung, Infrastruktur)
- Miete (Hardware)
- Zusammengesetzte Verträge (Hosting, Entwicklung, Projektvertrag, Lizensierung)
Gemischte Verträge
Zusammengesetzt aus Nominatverträgen - aus dem Gesetz wird das passende genommen - oder später angewendet...
Innominatverträge
Gesetzlich NICHT geregelte Vertragsformen. Das Prinzip der Vertragsfreiheit ermöglicht neue, innovative Vertragsinhalte & Zusammenarbeitsformen.
- Leasingvertrag
- Lizenzvertrag
- Factoring-Vertrag
- Escrow-Agreement
- Software-Entwicklungsvertrag
- Service Level Agreement (SLA)
- Non Disclosure Agreement (NDA) / Non Solicitation Agreement (NSA)
Schwierige Fälle
- Verzögerung in der Leistungserstellung
- Mehrere an der Leistungserfüllung Beteiligte
- (angeblich) keine oder zu späte Lieferung
- Fehlerhaftes Produkt / Garantieleistung
- Unklares Abnahmeverfahren
- Fehlende Zahlung
- Übermässige Bindung
- andere Mängel (Nichtigkeit, Unmöglichkeit, Übervorteilung, etc.)
Wenn es schwierig wird - "Papier" (Beweismittel) produzieren
ITS
TLS (Transport Layer Security)
Secure Communication between applications
Properties
- Authenticated
- Integrity Protected
- Confidential
- Secure against replay and deletion
Building Blocks
- Strong block ciphers (AES, Twofish, ...)
- Authenticated encryption mpdes (GCM, CCM)
- Diffie-Hellman Key agreement
- Groups that yield small keys and fast DH operations (ECC)
- Public key authentication with certificates
- Authentication trough signatures
- Cryptographic hash functions for key generation (HKDF) (e.g. AES session keys)
A cipher suite determinces the set of cryptographic algrogithms to be used.
Layer
- TLS works on top of TCP
- Can be implemented in user space (directly in the application software)
- Does not have to worry about lost/retransmitted data
Protocol stack
TLS 1.3 Record Protocol
TLS Record Protocol
Defines the TLS packet format; all data that are using TLS are transported within TLS Records
Handshake Protocol
Used to establish TLS sessions
Change Cipher Spec Protocol (deprecated)
Dummy messages only. In TLS 1.2, it indicated switching to the newly negotiated secure communication relationship and keys
Alert Protocol
Indicates warnings/errors (e.g., certificate expired)
Application Data
Data meaningful to applications (not for TLS)
TLS message formats
Phases
Handshake
Authentication and establishment of cryptographic algorithms and key material
Data Exchange
Exchange protected data
Connection teardown
Disconnect safely
VPN - Virtual Private Network
IPSEC
- Works on layer 3
ESP-Protected packets with sequence numbers
- As TLS is based on TCP records cannot arrive in the wrong order unless theres an attack or a serious error has happened -> clsoe with error
Open VPN
- Runs on top of UDP or TCP
- Usually uses UDP, as TCP over TCP results in poor performance
- Software on the VPN endpoints run in the user space
- Protect IP Packets between the VPN endpoints
- Uses TLS handshake protocol for mutual authentication and key exchange
Protocol Stack
Packet format
Wireguard
- Works on layer 3
- Very little configurability, deliberately no cipher and protocol agility
- much smaller attack surface
- uses modern crypto primitives (ChaCha20, Ruve25519)
- Has a 1-RTT handshake
- Has perfect forward secrcy (PFS)
- Has DoS mitigation techniques
Message Flow
MLDM
Data Processing
In Machine Learning werden Milliarden an einzelnen Daten benötigt.
Daten als Grundlage müssen verstanden sein.
Quellen
- Sensoren
- Umfragen
- Simulationen
- Social Media
- Texte
- Finanzen
- Multimedia
- ERP System Data
Datentypen
Nominal Categorical Data
Benutzt für Bezeichnungen, die ungeordnet sind
- Haarfarbe
- Geschlecht
Ordinal Categorical Data
Geordnete Bezeichnungen
- Rang
- Bewertung in Sternen
Continous Numerical Data
Werte die gezählt werden können
- Anzahl Personen in einem Raum
Discrete Numerical Data
Interval Daten, werden oft gemessen.
- Exakte Menge ([0, 20])
Datenklassen
- Eindimensionale Daten
- Mehrdimensionale Daten
- Netzwerkdaten
- Hierarchische Daten
- Zeitserien
- Geographische Daten
Struktur
### Strukturiert
-
Datenmodelle
-
csv
-
ods
-
xlsx
-
HDF (Hierachical Data Format)
Nicht strukturiert
- Hat kein fixes Format
- Hat keine Struktur
Metadata
Beschreibende Daten zu Bildern
Data Pre Processing
Shit in - Shit out
Probleme
- Schreibfehler in Quellen
- Falsches Format von Daten
- Falsche Berechnung von Eingabedaten
- Verschiedene Klassifizierung von gleichen Inhalten
- Doppelte Inhalte in falschen Werten
Methodiken
Duplicate Search
Vektorvergleich
Near Duplicate Search
- Titeln: Die Levensteindistanz gibt an, wie viele Operationen notwendig sind, um von einem Text auf den anderen zu kommen.
#### Vektoren
CoSine Similiarity
### Fehlende Daten
- Interpolation / Annahme durch andere Werte
- Löschen / nicht zu oft, um Mengen nicht zu verfälschen
Cost Function
Gradient Descent
Generic Gradient Descent Algorithm
- Repeat until covergence
Learning Rate / Schrittgrösse:
for every j=1..n
Learning Rate
- Zu Beginn grosse Lernrate, um möglichst schnell ans Minimum zu gelangen
### Regeln
- Learning Rate too small -> slow convergence
- Learning Rate too large -> might "jump too far", might not converge or even diverge
### Learning Rate Optimization / Decay
Momentum as Learning Rate Optimizer
Idea: add a fraction of previous update direction to the update:
Effect: pushes the "jumps" of SGD in general direction towards the minimum
Regularization
- Ist klein, kann gross, somit kann das Polygon möglichst genau den Werten enstsprechen.
- wird nicht beeinflusst.
Hypothesis:
Cost Function:
Grosses
- Es wäre eine Gerade
- Die Gerade wäre möglichst flach bei 0
Hyperparameter Tuning
Hyperparameter sind Einstellungen oder Konfigurationen, die vor dem Training eines maschinellen Lernalgorithmus festgelegt werden müssen. Sie steuern Aspekte des Lernprozesses und beeinflussen, wie ein Modell trainiert wird. Im Gegensatz zu den Modellparametern, die während des Trainings aus den Daten gelernt werden, werden Hyperparameter manuell ausgewählt und können die Leistung und das Verhalten eines Modells erheblich beeinflussen. Beispiele für Hyperparameter sind Lernrate in neuronalen Netzen, Tiefe eines Entscheidungsbaums in Entscheidungsbäumen und die Anzahl der Cluster in k-means Clustering. Die Auswahl geeigneter Hyperparameter ist oft ein wichtiger Schritt beim Entwickeln von Machine-Learning-Modellen.
A hyperparameter in machine learning is a configuration setting that is external to the model and whose value cannot be learned from the data. Examples include learning rate, batch size, and the number of hidden layers in a neural network. Tuning hyperparameters is crucial for optimizing a model's performance.
Reinforcement Learning
- Der Comnputer simuliert die Umgebung. In dieser passiert etwas.
- Der Agent probiert durch viele Episoden, ob etwas funktioniert.
- Der Interpreter gibt dem Agenten ein Reward, was den Agenten fördert, positive Schritte zu wählen.
Ziel: maximieren der Rewards
Supervised Machine Learning
Labels werden Beispieldaten manuell hinzugefügt.
Klassifizierung / Classification
Es wird etwas in eine Kategorie klassifiziert.
Regression
Es wird ein Wert anhand vieler Werte errechnet.
Unsupervised Machine Learning
- Muster in einer Menge an Daten finden
- Gruppierungen in einer Menge an Daten finden
Dimensional Reduction
Daten werden aus hochdimensionalen Daten zu 2D umgewandelt. Diese sind für den Menschen einfacher zu verstehen.
Association Rule Mining
Zusammenhänge zwischen Produkten werden gesucht.
- Wo sollten Produkte optimal platziert werden
- Welche Produkte werden oft gemeinsam gekauft
Outlier Detection
Findet Dinge die komplett von anderen abweichen.
Regression
Univariate
Statistische Auswertung nur eines Merkmals. Beispiel: für zehn Personen wird das Körpergewicht gemessen und dann ein Mittelwert bzw. Durchschnitt gebildet. Statt univariat könnte man auch eindimensional sagen.
K Nearest Neighbour Regression
In den meisten Fällen haben nah beieinander liegende Datenpunkte auch ähnliche Kategorien / Werte.
- Es gibt keine theoretische spezifikation wie viele k Nachbaren verwendet werden sollen.
- Je grösser k ist, desto geringer ist es für das Model "lokale" patterns zu erkennen, jedoch wird die Varianz kleiner und die Prediction wird stabiler.
- Es werden K Datenpunkte verwendet, die dem Suchwert am nähesten liegen.
- Mittelwert der K Punkte ergibt annäherung.
Linear Regression
Die Daten sind linear, es gibt also eine Linie. Diese kann dafür verwendet werden, anhand einer Gerade einen Wert Y zu dem Wert X zu bestimmen.
- : Schnittpunkt mit der Y-Achse
- : Steigung der Gerade
Loss
Abstand von einem Sample Wert zu seinem richtigen Wert
Wird pro einzelnes Samples berechnet
Cost
Abstand aller Samples zu ihrem korrekten Wert.
Beispiel
X | Y |
---|---|
1.00 | 1.00 |
2.00 | 2.00 |
3.00 | 1.30 |
4.00 | 3.75 |
5.00 | 2.25 |
Eigenschaften
Linearity: Die Abhängigkeit zwischen X und Y muss linear sein.
Homoscedasticity: Die Varianz ist für alle Werte gleich
Independence: Der Ausgangswert ist unabhängig der Eingabewerte
Normality: Glockenkurve der Varianz
Linear Regression
Closed Form
Funktioniert nur mit univarianten Daten. Beruht auf der Ableitung von Varianzen.
Normal Equation
Lässt sich für alle Probleme anwenden.
Runtime
- Mehr als 15'000 Features sind nicht möglich.
Matrix X ist:
Umgewandlet, bedeutet dass: , wobei N die Features sind. Die Matrix wird dadurch riesig.
Cost function
A cost function s a a measure, of how well a machine learning model performs by quantifiying the difference between predicted and actual outputs.
Die Werte werden quadriert, um:
- Die Werte zu normieren
- Grosse Fehler werden verstärkt
### Optimaler Wert der Cost-Function
-
Die Cost-Function ist genau dann 0, wenn alle Punkte genau auf einer Gerade sind.
-
Der Fehler der Kostenfunktion soll so klein sein wie möglich, der Wert wird dabei einfach minimiert.
Mit der Normal Equation können wir die optimalen Theta Werte bestimmen.
## Hypothesis
Polynomial Regression
Künstliche Hypothese
polynomial_features = PolynomialFeatures(degree=2) x_poly = polynomial_features.fit_transform(X_train)
Grad 2
Variable und haben folgende Optionen:
Grad 3
Variable und haben folgende Optionen:
Clasification
The training data comes in pairs. Yet the target values belong to a finite set. The goal is to pick the correct one as often as possible.
- Analyzing a text to determine if the sentiment is happy or sad.
- Using hockey players' statistics to predict which of two teams will probably win a match.
- Analyzing a song to determine if it is classical, techno, pop, or rock.
Binary Classification
There are only two classes to distinguish, typically "positive" or "negative".
Logistic Regression
If x is a Vector
Training
Loss Function
Cost Function
Evaluation
- Comparing training and testing result. If training accuracy is nearly 100%, it is overfitting.
- Since testing data is new data, it is used to test the accuracy on completely new data
Confusion Matrix
T = True F = False
Precision: TPos / (TPos + TNeg + TNeu)
Recall: TPos / (TPos / FNeg / FNeu)
F-Measure: 2*precision * recall (precision + recall)
Accuracy: (all true) / (all data)
SVM (Support Vector Machines)
Tries to find the "w" and "b" value in a way that maximizes the seperation between different classes of data points.
Weight Vector (w)
- Tells how much each feature matters
Bias Term (b)
- Is like an anchor point for the decision boundary. It shifts the boundary away from the origin
Classification
The values in the context of Support Vector Machines (SVM) represent the decision function values for individual data points. These values can provide insights into how confidently and correctly the SVM classifies these data points. Here's how to interpret them:
-
Positive Values of :
- If , it means the SVM confidently classifies the data point as belonging to the class with a positive label. The larger the positive value, the higher the confidence in the classification.
-
Negative Values of :
- If (y^{(m)} < 0), it indicates the SVM confidently classifies the data point as belonging to the class with a negative label. The more negative the value, the higher the confidence in this classification.
-
Near 0:
- If is close to 0, it suggests that the data point is located near the decision boundary. In this case, the SVM may be less certain about the classification.
-
Magnitude of :
- The magnitude of reflects the confidence level. Larger magnitude values indicate higher confidence in the classification, whether positive or negative.
So, you can use values to understand how well the SVM has classified individual data points. Large positive or negative values indicate strong confidence, values near 0 suggest proximity to the decision boundary, and the sign of the value indicates the predicted class.
Calculate distance
import numpy as np # Assuming you have the weight vector 'w' from your trained SVM model w = model.coef_[0] # Calculate the margin width margin_width = 2 / np.linalg.norm(w) print("Margin Width:", margin_width)
## Scatterplot generieren
import numpy as np import matplotlib.pyplot as plt # Assuming you have the feature data 'X' and class labels 'y' # Also, the trained SVM model with optimized parameters: model, model.intercept_, model.coef_ # Scatterplot of data points plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, s=30) # Decision boundary ax = plt.gca() xlim = ax.get_xlim() ylim = ax.get_ylim() # Create a grid of points xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], 50), np.linspace(ylim[0], ylim[1], 50)) # Calculate decision function values for the grid points Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) # Plot decision boundary plt.contour(xx, yy, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--']) # Highlight the support vectors plt.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=100, facecolors='none', edgecolors='k') plt.title('SVM Decision Boundary with Gutters') plt.show()
## Calculate accuraccy
from sklearn.model_selection import cross_val_score from sklearn.svm import SVC # Create an SVM model with your preferred parameters model = SVC(kernel='linear', C=1.0) # Assuming you have feature data X and labels y # Use cross_val_score to calculate accuracy accuracy_scores = cross_val_score(model, X, y, cv=5, scoring='accuracy') # cv=5 for 5-fold cross-validation # Calculate the mean accuracy mean_accuracy = accuracy_scores.mean() print("Mean Accuracy:", mean_accuracy)
Hyperplane
Standardization and Normalization
Ist wichtig, wenn die Features numerisch sehr unterschiedlich sind.
# Neural Networks
Neuron
Applies an activation function to the weighted sum of its inputs plus a bias term.
Deep Neural Networks
Are Neural Networks with more than one hidden layer.
Activation Functions
An activation function in neural networks introduces non-linearity to the model by determining the output of each neuron. Common activation functions include ReLU (Rectified Linear Unit) and Sigmoid, helping the network learn complex patterns and relationships in data during training.
## Softmax
Goal: For Classification tasks with mutually exclusive classese, in the output layer we want to have 1.0 for the correct class and 0 for all other classes.
Training in Feedforward Neural Networks
SCAD
The idea of SCAD is to simplify the development, hence the engineers
Serverless Computing Paradigm
Optimal Use Cases
- Single action repeats every time the user clicks on something
- Usage of services vary
Developer Mindset
- Serverless architectures are application designs stat ... remove much of the need for a traditional always-on server component
- Serverless architectures may benefit from significantly reduced operational cost, complexity, and engineering lead time
- Serverless architecture comes at a cost of increased reliance on vendor dependencies and comparativately immature supporting services
Formal Definition
Serverless computing is a cloud computing paradigm offering a high-level application programming model that allows one to develop and deploy cloud applications WITHOUT allocatign and managing virtualized serves and resources or being concerned about other operational aspects. The responsibility for operational aspects, such as fault tolerance or the elastic
Architekturen
XaaS Everything as a Service
Application-specific
SaaS - Software
DaaS - Data
DBaaS - Database
Cloud Services
IaaS - Infrastrucutre
PaaS - platform, CaaS - Containers
Serverless - FaaS, (+CaaS), BaaS
Function as a Service (FaaS)
Stateless & Shortlived
high elastic scalability
triggered by external events
single functionality & responsiblity
pay per use
Function can for example run in a CaaS (Container)
Backend as a Service (BaaS)
FaaS hat nur Computing Ressourcen (fast) kein Storage.
Daher BaaS welche stateful ist mit low latency
Dafür zusätliche kosten.
Databases, Message brokers …
Platform as a Service (PaaS)
- Long Lived
- More Flexible resource models
- Language constrained
Container as a Service (CaaS)
- Long Lived
- More flexible resource models
- unconstrained runtime
- "serverless CaaS": short-lived, resource constraints
- often underlying PaaS/FaaS, especially "FaaS with contaniers"
CaaS
LXC
Background
- vendor neutral,
- linux specified
Booting
- from a directory, e.g. unpackaged image
/config -> text file for e.g. mounts, capabilites, networks, ...
/rootfs -> /bin /dev/ etc /home
Namespacing of system resources
- file systems, users, processes, network interfaces, ...
- in the linux kernel
Application integration
- native python library
- frontends such as Flockport
import lxc c = lxc.Container("myubuntucontainer") c.create("download", 0, { "dist": "ubuntu", "release": "xenial", "arch": "amd64 }) # Downloadint the rootfs... (~400 MB; alpine would be ~7 MB) # -> True c.running # -> False c.start() # - True import os def f(): os.system("ps waux; ls -l /home") c.attach_wait(f) # - pid
Rocket
Docker
Background
- Docker inc
- Silicon Valley
Booting
- from a Docker container image with a layered file system
- images created by integration of Dockerfiles
Namespacing of system resources
- as processes through central daemon (or daemon-less, e.g. Podman)
Application integration
- composition and distributed computing tools (Docker Compose & Swarm)
- diverse networkign models, e.g. host mode, NAT bridge
- pass-through access to hardware via device files, e.g. GPU, USB
- access to local file system through volumes
FROM python:3
RUN pip install requests && \
rm /usr/lib/python3.6/site-packages/requests/__init__.py && \
wget https://blalbla.deb && \
dpkg -i blabla.deb
ADD my-application /srv/my-application
WORKDIR /srv
EXPOSE 8000
ENTRYPORT ["/usr/bin/dumb-init", "--"]
CMD ["/srv/my/application/start.sh]
Build Dockerimage
docker build -t mycontainer
View images
docker images
Run container
docker run -p 8080:8080 -it --rm \
-v /var/www/html/:/usr/local/tomcat/webapps/news \
mycontainer
Debug
- docker ps
- docker logs
- docker exec
Docker Compose
Compose files (docker-compose.yaml) describe
- a set of microservices, implemented as Docker continers
- build and run configuration
- dependencies
- isntance details: replicas, restart policies, placement, networking, volumes
Compose tool
- canonical command: docker-compose up
- in contrast docerk-compose down --volumes
- log merging
- command execution per instance
Helper Tools
- service dependencies (-> V11): shell script wait-for-it
Web Architektur
Internet
- Weltweites Netzwerk, dass aus vielen Rechnernetzwerk besteht.
- Urprünglich: ARPANET (1969: vier Knoten)
- Als Internet ab 1987 bezeichnet (ca. 27'000 Knoten)
Technologien
Clientseitig (Frontend)
- Beschränkt auf das, was der Browser kann
- HTML, CSS, JavaScript, einige weitere Sachen
Serverseitig (Backend)
- Praktisch unbeschränkt: Plattform, Programmiersprache, ...
- Erzeugt und gesendet wird das, was der Browser kann
Statische Webseiten
Clientseitige Programmlogik
Serverseitige Programmlogik
Javascript
Beschreibung
- Dynamisches Typenkonzept
- Objektorientierter und funktionaler Stil möglich
positiv
- Mächtige unde moderne Sprachkonzepte
- Leistungsfähige Laufzeitumgebungen
negativ
- Design Mängel aus den Anfangstagen
- Grundlegende Änderungen nicht möglich, da nur Erweiterungen möglich sind
Standards
- ECMAScript
- versionen
- ES3: 2000 - 2010 (verbreitete Version)
- ES4: Übung 2008 abgebrochen
- ES5: 2009, kleineres Update
- ES6: 2015, umfangreiche Neuerungen
- ES7, Javascript 2016
- Nun jährliche Updates
- Transpiler: Babel
Console
### Formatiertes ausgeben
let x = 30;
console.log("the value of x is ", x);
// -> the value of x is 30
console.log("my %s has %d ears", 'cat' 2);
// -> my cat has 2 ears
Konsole löschen
console.clear()
Stack trace ausgeben
Wird bei Fehlern oft automatisch ausgegeben.
console.trace()
Zeit messen
console.time()
console.log("Welcome to Programiz!");
console.timeEnd()
// default: 3.572ms
### Fehler ausgeben
console.error()
Zahlen
- Zahlentyp in JavaScript: Number
- 64 Bit Floating Point entsprechend IEEE 754 (double in Java)
- Enthält alle 32 Bit Ganzzahlen
- Konsequenz alle Java int auch in JavaScript exakt dargestellt
- Weitere Konsequenz: oft Rechnunggenauigkeit mit Zahlen mit Nachkommastellen
## Zahlenliterale
| Typ | Beispiel | | Ganzzahlliteral | 17 | | Dezimalstellen | 3.14 | | Dezimalpunktverschiebung (*10^x) | 2.998e8 |
Nicht exakte Zahlen
0.1 = 0x3FB999999999999A, entspricht nicht exakt 0.1 0.25 // hexadezimal 3FD0000000000000, entspricht exakt 0.25
Ausdrücke
- Rechenoperationen wie in Java
- Spezielle Zahlen: Infinity, -Infinity, NaN
100 + 4 * 11 // 114
(100 + 4) * 11 // 1144
314 % 100 // 14
1/0 // Infinity
Infinity + 1 // Infinity
0/0 // NaN (Not a Number)
BIGINT
- Mit ES2020 eingeführt
- Lieterale mit angehängem n
- Keine automatische Typumwandlung von/zu Number
1n + 2n // 3n
2n ** 128n // 340282366920938463463374607431768211456n
BitInt(1) // 1n
Number(1n) // 1
1n + 1 // Cannot mix BigInt and other types
Typeof
- Operator, der TypString seines Operanden liefert
typeof(12) // 'number'
typeof(2n) // 'bigint'
typeof(Infinity) // 'number'
typeof(NaN) // 'number'
typeof('number') // 'string'
## Strings
- Sequenz von 16-Bit-Unicode-Zeichen
- Kein spezieller char-Typ
- Strings mit "..." und '...' verhalten sich gleich
String-Verkettung
`con` + "cat" + 'enate' // 'concatenate'
Escape Sequencen
Sequenz | Bedeutung |
---|---|
\\ | Backslash |
\n | newline |
\r | carriage return |
\v | vertical tab |
\b | backspace |
\f | form feed |
\uXXXX (4 Hexziffern) | UTF-16 code unit |
\xXX (2 Hexziffern) | ISO-8859-1 character |
\XXX (1-3 Oktalziffern) | ISO-8859-1 character |
Template Strings
- \ wird als \ interpretiert (Ausnahme vor `, $ und Leerzeichen)
- Kann Zeilenwechsel enthalten
- String-Interpolation: Werte in String einfügen
`half of 100 is ${100 / 2}` // half of 100 is 50
`erste Zeile
zweite Zeie` // 'erste Zeile\nzweite Zeil'
Logische Ausdrücke
- Typ boolean mit Werten: true / false
- Vergleiche liefern Ergebnis vom Typ boolean
- Logische Operatoren entsprechen denen in C und Java
- Strings sind Werte: Vergleich mit == ist kein Problem
typeof(true) // 'boolean'
3 > 4 // false
1 < 2 && 2 < 3 // true
4 >= 5 || !(1 == 2) // true
"ab" == "a" + "b" // true
NaN == NaN // false
Spezielle Werte
null // null
undefined // undefined
let wert // wert = undefined
wert // undefined
Nodejs
Ist nicht die einziege Javascript-Laufzeitumgebung ausserhalb von Browsern. Weitere Alternativen sind:
-
Deno (https://deno.land)
-
Bun (https://bun.sh)
-
Asynchrone, ereignisbasierte Javascript-Laufzeitumgebung
-
Grundlage für skalierbare Netzwerk-Anwendungen
-
Basiert auf Googles V8 Engine
-
Open Source und plattformübergreifend
Beispiel
// ==== hello-world.js ====
const http = require('http');
const hostname = '127.0.0.1';
const port = '3000';
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plan');
res.end('Hello, World!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Verwendung
Startet das Script, zu dem der Pfad führt
$ node hello-world.js
Server running at http://127.0.0.1:3000/
# Abbruch mit CTRL-C
Startet eine interaktive REPL (Read Eval Print Loop) Shell gestartet werden
$ node
Welcome to Node.js vs.12.16.2
Type ".help" for more information.
Frameworks
- Node-js ist eine low-level Plattform
- Zahlreiche Frameworks und Tools bauen darauf auf
Beispiele
- Express: Webserver, Nachfolger: Koa
- Socket.io: Echtzeitkommunikation
- Next.js: serverseitiges React Rendering
- Webpack: JavaScript Bundler
- Babel, TypeScript
- uvm.
NPM
- Paketverwaltung für Node.js
- Repository mit > 1 Mio Paketen
- Werkzeuge zum Zugriff auf das Repository (npm, yarn)
- Seit 2020: Github / Microsoft
Objekte
## Objektliterale
let person = {
name: "John Baker",
age: 23,
"exam rsults": [5,5, 5.0, 5.0, 6.0, 4.5]
}
Zugriff
console.log(person.name)
console.log(person["exam results"])
Zugriff auf nicht vorhandenes Attribut liefert "undefined"
Optional
console.log(person.age.month) // TypeError
console.log(person.age?.month) // undefined
Erweitern
person.lastname = "Rüdiger"
Entfernen
let obj = {
message: "ready",
ready: true,
tasks: 3
}
delete obj.message
Attribute in Objekt
let obj = {
message: "ready",
ready: true,
tasks: 3
}
"message" in obj // true
Methoden
let cat = {
type = "cat",
sayHello: () => "Meow"
}
cat.sayHello() // Meow
## Interner Zugriff
let cat = {
type = "cat",
sayHello() { "Meow from " + this.type }
}
- Pfeilnotation funktioniert nicht
## Analyse
Keys
let obj = {a:1, b: 2}
Object.keys(obj)
Values
let obj = {a:1, b: 2}
Object.values(obj)
Zusammenführen
let obj1 = {a:1, b: 2}
let obj2 = {c:1, d: 2}
let obj3 = {e:1, f: 2}
Object.assign(obj1, obj2)
Object.assign(obj1, obj2, obj3)
let objCopy = Object.assign({}, obj1)
objCopy == obj1 // false
Spread Syntax
let obj1 = {a:1, b: 2}
let obj2 = {c:1, d: 2}
{...obj1, ...obj2, f:4}
Objekte Destrukturieren
let obj = {a:1, b: 2}
let {a, b} = obj
console.log(a) //1
Arrays
- Können von beliebigen Typen sein
- Typen können gemischt werden
Is Array
let data = [1, 2, 3]
typeof(data) // 'object'
Array.isArray(data) // true
Methoden
Hinzufügen
let data = [1, 2, 3]
data.push(10) // 4
data.push(11, 12) // 6
Letztes Element löschen
let data = [1, 2, 3]
data.pop(10) // 36
### Shift
Entfernen am Array Anfang
let data = [1, 2, 3]
data.shift() // 1
console.log(data) // [2, 3]
Unshift
Entfernen am Array Anfang
let data = [1, 2, 3]
data.unshift(5) // 4
console.log(data) // [5, 1, 2, 3]
Erstes Element in Array finden
let data = [1, 2, 3, 1]
console.log(data.indexOf(1)) // 0
Letzes Element in Array finden
let data = [1, 2, 3, 1]
console.log(data.lastIndexOf(1)) // 3
### Bereich eines Arrays ausschneiden
let data = [1, 2, 3, 1]
data.slice(1, 3) // [2, 3]
Arrays zusammenhängen
let data = [1, 2, 3, 1]
data.concat([100, 101]) //[1, 2, 3, 1, 1000, 101]
Iterieren
Standard for schleife
for (let i=0; i < myArray.length; i++) {
console.log(myArray[i]);
}
Einfachere Variante
for (let item in myArray) {
console.log(item);
}
Spread-Syntax
let parts = ["shoulders", "knees"]
["head", ...parts, "and", "toes"] // ["head", "shoulders", "knees", "and", "toes"]
Destrukturieren
let numbers = [1,2,3]
let [a,b,c] = numbers
console.log(c) // 3
Abbilden
let num = [5, 2, 9, -3, 15, 7, -5]
num.map(n => n*n) // [25, 4, 81, 9, 225, 49, 25]
Reduzieren
let num = [5, 2, 9, -3, 15, 7, -5]
num.reduce((curr, next) => curr + next) // 30
Funktionen
Funktion definieren
const squareV1 = function(x) {
return x * x;
}
function squareV2(x) {
return x*x;
}
const squareV3 = x => x * x;
Gültigkeitsbereiche
let m = 10 // block scope
const n = 10 // constant with block scope
var p = 10 // variable with function scope
Globale Variablen
- Ausserhalb von Funktionen definiert
- In Funktionen kein const, let, var
- Gültigkeitsbereich möglichst einschränken
result = 1 + 2
## Parameter
Parameter sind standardmässig undefined, somit müssen nicht alle Parameter gesetzt werden.
Rest-Parameter
function max ([...numbers]) {
let result = -Infinity;
for (let number of numbers) {
if (number > result) {
result = number;
}
}
return result
}
console.log(max(4, 1, 9, -2))
Alternative
function max () {
let result = -Infinity;
for (let number of arguments) {
if (number > result) {
result = number;
}
}
return result
}
console.log(max(4, 1, 9, -2))
Dekorieren
function trace (func) {
return (...args) => {
console.log(args)
return func(...args)
}
}
let factorial = (n) => (n <= 1) ? 1 : n * factorial(n-1)
factorial = trace(factorial)
console.log(factorial(3))
Prototypen
function speak (line) {
console.log(`The ${this.type} rabbit says '${line}'`)
}
let whiteRabbit = {type: "white" , speak}
let hungryRabbit = {type: "hungry", speak}
hungryRabbit.speak("I could use a carrot right now.")
// → The hungry rabbit says 'I could use a carrot right now.
Strict Mode
"use strict"
function speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`)
}
speak("I could use a carrot right now.")
// -> TypeError 'Cannot read property type of undefined'
Call / Apply
function speak (line) {
console.log(`The ${this.type} rabbit says '${line}'`)
}
let hungryRabbit = {type: "hungry", speak}
speak.call(hungryRabbit, "Burp!") // The hungry rabbit says 'Burp!'
speak.apply(hungryRabbit, ["Burp!"]) // The hungry rabbit says 'Burp!'
Bind
function speak (line) {
console.log(`The ${this.type} rabbit says '${line}'`)
}
let hungryRabbit = {type: "hungry", speak}
let boundSpeak = speak.bind(hungryRabbit)
boundSpeak("Burp!") // The hungry rabbit says 'Burp!'
Lambdas
let test = []
test.map(n => n**2)
Prototype Chain
var Dragon = function(location){
/*
* <Function>.call is a method that executes the defined function,
* but with the "this" variable pointing to the first argument,
* and the rest of the arguments being arguments of the function
* that is being "called". This essentially performs all of
* LivingEntity's constructor logic on Dragon's "this".
*/
LivingEntity.call(this, location);
//canFly is an attribute of the constructed object and not Dragon's prototype
this.canFly = true;
};
/*
* Object.create(object) creates an object with a prototype of the
* passed in object. This example will return an object
* with a prototype that has the "moveWest" and "makeSound" functions,
* but not x, y, or z attributes.
*/
Dragon.prototype = Object.create(LivingEntity.prototype);
/*
* If we didn't reset the prototype's constructor
* attribute, it would look like any Dragon objects
* were constructed with a LivingEntity constructor
*/
Dragon.prototype.constructor = Dragon;
/*
* Now we can assign prototype attributes to Dragon without affecting
* the prototype of LivingEntity.
*/
Dragon.prototype.fly = function(y){
this.y += y;
}
var sparky = new Dragon({
x: 0,
y: 0,
z: 0
});
Klassen
class Person {
constructor (name) {
this.name = name
}
toString() {
return `Person with name '${this.name}'`
}
}
let john = new Person("John")
console.log(john.toString()) // Person with name 'John'
Vererbung
class DateFormatter extends Date {
get FormattedDate() {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return `${this.getDate()}-${months[this.getMonth()]}-${this.getFullYear()}`;
}
}
console.log(new DateFormatter('August 19, 1975 23:15:30').FormattedDate);
// Expected output: "19-Aug-1975"
Properties
class Person {
constructor(name) {
this.name = name;
}
get name() {
return this._name;
}
set name(newName) {
newName = newName.trim();
if (newName === '') {
throw 'The name cannot be empty';
}
this._name = newName;
}
}
let p1 = new Person()
p1.Name = "hey"
console.log(p1.Name) // hey
# Webserver
Port | Service |
---|---|
20 | FTP - Data |
21 | FTP - Control |
22 | SSH Remote Login Protocol |
23 | Telnet |
25 | Simple Mail Transfer Protocol (SMTP) |
53 | Domain Name System (DNS) |
80 | HTTP |
443 | HTTPs |
HTTP Requests
Methode | Beschreibung |
---|---|
GET | Resource Laden |
POST | Information senden |
PUT | Resource anlegen, überschreiben |
PATCH | Rresource anpassen |
DELETE | REsource löschen |
HTTP Response Codes
Code | Beschreibung |
---|---|
1xx | Information (101 switching protocols) |
2xx | Erolg (200 OK) |
3xx | Weiterleitung (301 moved permanently) |
4xx | Fehler in Anfrage (403 Forbidden, 404 Not Found) |
5xx | Server-Fehler (501 Not implemented) |
Einfacher Web Server
const {createServer} = require("http")
let server = createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/html"})
response.write(`
<h1>Hello!</h1>
<p>You asked for <code>${request.url}</code></p>`)
response.end()
})
server.listen(8000)
console.log("Listening! (port 8000)")
Einfacher Web Client
const {request} = require("http")
let requestStream = request({
hostname: "eloquentjavascript.net,
path: "/20_node.html",
method: "GET",
headers: {Accept: "text/html"}
}, response => {
console.log("Server responded with status code:", response.statusCode);
})
requestStream.end()
Streams
Server
const {createServer} = require("http")
createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"})
request.on("data", chunk =>
response.write(chunk.toString().toUpperCase()))
request.on("end", () => response.end())
}).listen(8000)
Client
const {request} = require("http")
let rq = request({ hostname: "localhost", port: 8000,
method: "POST"
}, response => {
response.on("data", chunk =>
process.stdout.write(chunk.toString()));
})
rq.write("Hello server\n")
rq.write("And good bye\n")
rq.end()
File Server
const {createServer} = require("http") const methods = Object.create(null)
createServer((request, response) => {
let handler = methods[request.method] || notAllowed; handler(request)
.catch(error => {
if (error.status != null) return error return { body: String(error), status: 500 }
})
.then(({body, status=200, type="text/plain"}) => {
response.writeHead(status, {"Content-Type": type}) if (body && body.pipe) body.pipe(response)
else response.end(body)
})
}).listen(8000)
[File Server](https://eloquentjavascript.net/20_node. html#h_yAdw1Y7bgN)
Express Web Server
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
### Routing
app.get('/', function (req, res) { res.send('Hello World!')
})
app.post('/', function (req, res) {
res.send('Got a POST request')
})
app.put('/user', function (req, res) { res.send('Got a PUT request at /user')
})
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user')
})
Statische Dateien
app.use(express.static('public'))
// http://localhost:3000/css/style.css
// Pfad zur Datei: public/css/style.css
app.use('/static', express.static('public'))
// http://localhost:3000/static/css/style.css
// Pfad zur Datei: public/css/style.css
Cookies
- HTTP als zustandsloses Protokoll konzipiert
- Cookies: Speichern von Informationen auf dem Client
- RFC 2965: HTTP State Management Mechanism
- REsponse: Set-Cookie-Header, Request: Cookie-Header
- Zugriff mit JavaScript möglich (ausser HttpOnly ist gesetzt)
Sessions
- Cookies auf dem Client leicht manipulierbar
- Session: Client-spezifische Daten auf dem Server speichern
- Identifikation des Clients über Session-ID (Cookie o.ä.)
- GEFAHR: Session-ID gerät in falsche Hände (Session-Hijacking)
const express = require('express')
const cookieParser = require ('cookie-parser')
const session = require('express-session')
const app = express();
app.use(cookieParser())
app.use(session({secret: "Strong Secret Here!"}))
app.get('/', function(req, res) {
if (req.session.page_views) {
req.session.page_views++
res.send(`You visited this page ${req.session.page_views} times`)
}
else {
req.session.page_views = 0
res.send("Welcome to this page for the first time")
}
})
app.listen(3000)
Fetch API
const url = "https://.."
fetch(url).then(response => {
console.log(response.status) // 200
console.log(response.headers.get("Content-Type")) // text/plain
response.text().then(text => {
// GET TEXT
})
response.json().then(json => {
// GET JSON
})
})
DOM - Document Object Model
- Repräsentiert die angezeigte Website
Window
- Globales Objekt der Browser Plattofrm
- Alle globalen Variablen und Methoden sind hier angehängt
- Neue globale Variablen landen ebenfalls hier
window.alert === alert // true
window.setTimeout === setTimeout // true
window.parseInt === parseInt // true
Navigator
Represents the state and the identity of the user agent. It allows scripts to query it and to register themselves to carry on some activities.
navigator.userAgent // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:104.0) Gecko/20100101 Firefox/104.0"
navigator.language // "de"
navigator.platform // "MacIntel"
navigator.onLine // true
Location
- Aktuelle Webadresse im Browser
location.href // "https://gburkert.github.io/selectors/"
location.protocol // "https:"
document.location.protocol // "https:"
DOM Scripting
let aboutus = document.getElementById("aboutus")
let aboutLinks = document.getElementsByTagName("a")
let aboutImportant = aboutus.getElementyByClassName("important")
let navLinks = document.querySelectorAll("nav a")
Asynchron
File API
Dateinformationen
const fs = require('fs')
fs.stat('test.txt', (err , stats) => {
if (err) {
throw err
}
stats.isFile() // true
stats.isDirectory() // false
stats.isSymbolicLink() // false
stats.size // 1024000 (bytes)
})
Lesen einer Datei
const fs = require('fs')
fs.readFile('test.txt', "utf8", (err , data) => {
if (err) {
throw err
}
console.log(data)
})
Beschreiben einer Datei
const fs = require('fs')
const content = "Node was here!"
fs.readFile('test.txt', content, (err , data) => {
if (err) {
throw err
}
console.log(data)
})
Funktion | Bezeichnung |
---|---|
fs.access | Zugriff auf Datei oder Ordner prüfen |
fs.mkdir | Verzeichnis anlegen |
fs.readdir | Verzeichnis lesen, liefert Array von Einträgen |
fs.rename | Verzeichnis umbenennen |
fs.rmdir | Verzeichnis löschen |
fs.chmod | Berechtigungen ändern |
fs.chown | Besitzer und gruppe ändern |
fs.copyFile | Datei kopieren |
fs.link | Erstellt einen neuen festen Link |
fs.symlink | Erstellt einen neuen symbolic link |
fs.watchFile | Datei auf Änderungen überwachen |
## Calllbacks
Callbacks erlauben es Funktionen an einen EventHandler, oder anderen Funktionen mitzugeben.
document.getElementById("button").addEventListener("click", () => {
// element of id "button" clicked
});
Set Timeout
- Der Callback wird zu einem späteren Zeitpunkt (in ms) ausgeführt
- Eintrag in die Timer-Liste, auch wenn Zeit auf 0 gesetzt wird
- Kann mit clearTimeout entfernt werden
const id = setTimeout(() => {
// runs after 50 milliseconds
}, 50);
clearTimeout(id);
Intervall
const id = setTimeout(() => {
// runs every 2000 milliseconds = 2 seconds
}, 2000);
clearInterval(id);
### Sofort als möglich
setImmediate(() => {
// runs as best as possible
});
Event Emitter
Auf etwas hören
const EventEmitter = require('events')
const door = new EventEmitter()
door.on('open', () => {
console.log("Door was opened")
});
door.on('open', (speed) => {
console.log(`Door was opened, speed: ${speed || 'unknown'}`)
});
Event auslösen
door.emit('open')
door.emit('open', 'slow')
Promises
// Funktion, die einen Promise erstellt und zurückgibt
function doSomethingAsync() {
return new Promise((resolve, reject) => {
// Simuliert eine asynchrone Aufgabe (z. B. eine Netzwerkanfrage)
setTimeout(() => {
const success = true;
if (success) {
resolve("Erfolg!");
} else {
reject("Fehler!");
}
}, 2000); // Simuliert eine Verzögerung von 2 Sekunden
});
}
// Verwendung des Promises
doSomethingAsync()
.then((result) => {
console.log("Erfolgreich: " + result);
})
.catch((error) => {
console.error("Fehler: " + error);
});
Zustände
Zustand | Beschreibung |
---|---|
Pending | Ausgangszustand |
Fullfilled | Erfolgreich abgeschlossen |
rejected | Ohne Erolg abgeschlossen |
- Der Zustandswechsel kann nur einmalig passieren
- Der Endzustand kann entweder fullfilled oder rejected sein
Promise.All
- Erhält einen Array von Promises
- fullfilled sobald ALLE fullfilled sind
- Ist ein Promise rejected, dann ist das gesamte Objekt rejected
Promise.Race
- Erhält einen Array von Promises
- Erfüllt sobald eine davon fullfileed ist
- Ist ein Promise rejected, dann ist das gesamte Objekt rejected
Async / Await
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout( () => {
resolve(x)
});
});
}
async function add1(x) {
var a = resolveAfter2Seconds(20)
var b = resolveAfter2Seconds(30)
return x + await a + await b
}
add(10).then(console.log)
KT
Allgemeine Formeln
Beschreibung | Symbol | Einheit | Formel |
---|---|---|---|
Signaldämpfung | db | ||
Signaldämpfung über Spannung | db | ||
Amplitude | V | ||
Maximale Anzahl erkennbare Zustände | bit/s | ||
Maximal erreichbare Bitrate (Hartley's Gesetz) | bit/s | ||
Signalleistung | |||
Rauschleistung | |||
Kanalkapazität | |||
Nettobitrate | |||
Maximale Bitrate | |||
Unterscheidbare Signalzustände | Anzahl | ||
Amplitude | |||
Bandbreite | |||
max. Symbolrate | Hz | oder | |
Symboldauer | s |
Log Umwandeln: =>
dB umwandeln:
OSI Schichten
Serielle Schnittstelle

OSI Referenzmodell
Verbindungsorientiert
- Verbindungsaufbau nötig
- Ziel muss bereit sein
Verbindungslos
- Jederzeit Nachrichten schicken
- Ziel muss nicht „bereit“ sein
Zuverlässig
- Kein Datenverlust
- Sicherung durch Fehelr-Erkennung -/korrektur
- Text-Nachrichten
Unzuverlässig
- Möglicher Datenverlust
- Keine Sicherung
- Streaming

7. Application Layer (Verarbeitungsschicht)
Anwendungen greifen auf das Internet zu
6. Presentation Layer (Darstellungsschicht)
Daten werden für die Anwendung formatiert (HTTP, Email, ...)
5. Session Layer (Session Layer)
Koltrolliert Sessions und Ports
4. Transport Layer (Transportschicht)
Transport von Daten in Protokollen (TCP, UDP, ...)
3. Network Layer (Vermittlungsschicht)
- Paketierung von Daten / Unterteilen von Daten
- Fragmente erstellen / zusammensetzen
- Flusskontrolle
- Zwischenspeichern von Daten
2. Datalink Layer (Sicherungsschicht)
- Adressierung
- Zugriff auf Ressourcen
- Netzüberlastung vermeiden / Fehlererkennung
1. Physical Layer (Bitübertragungsschicht)
- Rohe Übertragung von Bits (ungesichert)
Application Layer
DNS (Domain Name Space)
- Leserliche Darstellung von IP-Adressen
- Hauptdömäne ist Root (.)
Beschreibung | Domain |
---|---|
Fully Qualified Fomain Name | bob.sw.eng. |
Root | . |
TLD (Top Level Domain) | eng |
SLD (Second Level Domain) | sw |
TFTP (Trivial File Transfer Protocol)
Basiert auf UDP


DHCP
- Client sucht DHCP Server mittels Broadcast
- DCHP Server antwortet (DHCP offer)
- Der Client wählt einen Server und fordert eine Auswahl der angebotenen Paramter (DHCP Request)
- Der Server bestätigt mit einer Message, welche die engültigen Parameter enthält
- Vor Ablauf der Lease-Time erneuert der Client die Adresse.
Transport Layer
User Datagramm Protocoll (UDP)
Daten werden an "Adresse" gesendet. Es wird auf kein ACK gewartet.
- Verbindungslos
- Unzuverlässig

Transmission Controll Protocoll

Feld | Beschreibung |
---|---|
Sequence Number | Nummer zur Ordnung der Segmente |
Acknowledgment Number | Bisherige ACK No + Anzahl Daten |
Window Size | Anzahl Bytes die empfangen werden können |
Verbindungsaufbau & Abbau



Fluss Steuerung
Stop and Wait
- Verhindert den Datenüberlauf
- Ineffiziente Nutzung der Netzbandbreite
- Paket senden
- ACK antworten
Sliding-Window
- Höhere Durchsatzrate
- Feste "Fenster-Grösse"
- X Pakete senden
- X ACK antworten
Network Layer / Internet Layer
Internet Protokoll Format (IP Header)
IP-Packet besteht aus einem Header (min. 20 Bytes) und Nutzdaten

Feld | Beschreibung |
---|---|
Version | IPv4 / IPv6 |
IHL | Header Length in 4-Byte (20 Byte -> 5) |
TOS (Type of Service) | Erlaubt Priorisierung |
Total Length | Länge des IP-Packets (Header + Nutzdaten) |
Identification | Identifkiation des IP-Packets / Fragments |
Flags | Kontroll-Flags für Fragmenteriung |
Fragment Offset | Gibt an, wo ein Fragment hingehört (Position in buffer) |
TTL (Time to Live) | Hop Counter, 0 -> Paket wird verworfen |
Protocol | Übergeordnetes Protokoll |
Internet Control Message Protocol (ICMP)

Maximum Transfer Unit (MTU)
- Der Sender kennt die MTU der Netze nicht.
- Die MTU gibt die maximale Paketgrösse (Layer 3) an.
Maximale Daten bei gegebener MTU
TCP




ARP


Data Link Layer
Framing
Asynchron
Keine Daten -> Nichts wird gesendet
Zu Beginn eines Frames wird ein Start-Bit gesendet
Synchron
- Frames werden ohne Unterbruch gesendet

Fehlererkennung / Fehlerkorrektur
- FER (Frame Error Ratio)
- RER (Residual Error Ratio)
- BER (Bit Error Ratio)
Parity
- Even Parity: Wenn die Anzahl Bits, die 1 sind, gerade ist, dann ist der Wert: 1
- Odd Parity: Wenn die Anzahl Bits, die 1 sind, ungerade ist, dann ist der Wert 1
Physical Layer (Bitübertragungsschicht)
Serielle asynchrone Übertragung (ohne Synchronisations-Takt)
Zwischen Sender und Empfänger werden folgende Abmachungen gemacht
- Bitrate
- Anzahl Datenbits (Typischerweise 1 Byte)
- Anzahl Stopbits (Typischerweise 1 Bit)

Die Taktrückgewinnung ist möglich, solange regelmässig Zustandsänderungen auftreten.
Serielle synchrone Übertragung
Bei der synchronen Übertragung arbeitet der Empfänger mit dem gleichen Takt wie der Sender
- Es werden keine Start- und Stopbits benötigt
- Der Takt muss zusätzlich übertragen werden
Die Übertragung des Takts erfolgt über ein Codierungsverfahren oder eine zusätzliche Leitung.
Kommunikationsarten (Verkehrsbeziehung)
- Simplex: Ein Kanal, in eine Richtung
- Halbduplex: Ein Kanal, abwechslungsweise in zwei Richtungen
- Vollduplex: Ein Kanal pro Richtung
Arten der Verbindungen (Kopplung)
- Punkt-Punkt: Direkte Verbindung zweier Kommunikationspartner
- Shared Medium: Mehrere Partner verwenden das gleiche Medium
Datenübertragungsrate
- Baudrate: Symbole pro Sekunde
- Zeichenrate: Zeichen pro Sekunde
Ethernet
Klasse bestimmen
Klasse | Addressbereich | Netzmaske |
---|---|---|
A | 0.0.0.0 - 127.255.255.255 | 255.X.0.0 |
B | 128.0.0.0 - 191.255.255.255 | 255.255.X.0 |
C | 192.0.0.0 - 223.255.255.255 | 255.255.255.X |
Subnetzmaske berechnen
Bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
Dezimal | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 |
Es werden jeweils zwei Adressen für benötigt:
- Broadcast (höchste Adresse)
- Netzadresse (tiefste Adresse)
CIDR | Subnet Maske | Anzahl Adressen | Nutzbare Adressen |
---|---|---|---|
/32 | 255.255.255.255 | 1 | 1 |
/31 | 255.255.255.254 | 2 | 2 |
/30 | 255.255.255.252 | 4 | 2 |
/29 | 255.255.255.248 | 8 | 6 |
/28 | 255.255.255.240 | 16 | 14 |
/27 | 255.255.255.224 | 32 | 30 |
/26 | 255.255.255.192 | 64 | 62 |
/25 | 255.255.255.128 | 128 | 126 |
/24 | 255.255.255.0 | 256 | 254 |
/23 | 255.255.254.0 | 512 | 510 |
/22 | 255.255.252.0 | 1024 | 1022 |
/21 | 255.255.248.0 | 2048 | 2046 |
/20 | 255.255.240.0 | 4096 | 4096 |
/19 | 255.255.224.0 | 8192 | 8190 |
/18 | 255.255.192.0 | 16,384 | 16,382 |
/17 | 255.255.128.0 | 32768 | 32766 |
Anzahl Adressen:
Anzahl nutzbare Adressen:
### Subnetze aufteilen Die Subnetzmaske darf sich von der kleinsten zur grössten nicht ändern. Also von klein nach gross.
VLAN
Erlaubt es ein grosses Netz in unabhängige logische Netze aufzuteilen. Jeder Port am Switch kann einem beliebigen VLAN zugeordnet werden.

Spanning Tree Protokoll (STP)
- Alle Knoten werden genau einmal verbunden
- Verbindungen, die zu Schleifen führen werden deaktiviert
Bridge Identifier: Priorität + Mac Adresse
- Root bestimmen mittels Bridge-Identifier
- Direkt angeschlossene Bridges bestätigen
- Weitere Verbindungen abhängig von Kosten und Bridge-Identifier eintragen
Routing
Vom grössten Netz zum kleinsten.
Ethernet Systeme
Autonegotiation: Ermittlung der besten Betriebsart durch Austausch derLeistungsmerkmale zweier Netzwerkkomponenten
Link Pulses
- NLP: Link Presence Detection
- FLP: Autonegotiation, Autopolarity

Codierungen
Local Area Networks (LAN)
Beschreibung | Symbol | Einheit | Formel |
---|---|---|---|
Framerate | |||
Bitrate | |||
Framelänge | Bits | Data + Prä + SFD + FCS = Data + 7Bit + 1Bit + 4Byte | |
Nutzbitrate | |||
Payload | |||
InterFrameGap | IFG | Bytes | 12 |
Übertragungsarten
- Unicast: an einzelne Stationen
- Broadcast: an alle Stationen
- Multicast: an eine Gruppe von Stationen
Leitungscode
Als Leitungscode wird ein Manchester eingesetzt
- 1 positive Flanke, 1 negative Flanken
- Erlaubt die Taktrückgewinnung auf einfache Weise
- Benötigt die doppelte Bandbreite des theoretischen Minimums

Kollosionen
Können durch die Überlagerungen von Signalen entstehen. Kollosionen müssen erkannt werden.

Kollosionserkennung

Topologie

Beschreibung | Symbol | Einheit | Formel |
---|---|---|---|
Nutzdaten | Bits | ||
Länge zwischen Switch und Knoten | |||
Bandbreite | |||
Framedauer | s | ||
Transferdauer | s |
Ethernet

IEEE Mac Adressen

Base64
- ASCII in binär umwandeln
- Binär in je 6 grosse Bitgruppen abstechen
- Wenn letzer Block kleiner als 6, mit 0 auffüllen
- Tabelle nachsehen und Char nehmen

WING
St.Galler Management Modell (SGMM)
Umweltsphären
Bezeichnen relevante Bezugsräume im Umfeld der Unternehmung.
Ökonomische (wirtschaftliche) Umwelt Rahmenbedingungen für ein Unternehmen (Entwicklung der Wirtschaft, Arbeitsmarkt, Teuerung, Wirtschaftsbeziehungen zum Ausland, etc.)
Technologische Umwelt Umfasst alles, was in den Berich Technik- und Naturwissensschaften fällt (Produktionsverfahren, Materialien, Transport- und Kommunikationsmittel, etc)
Soziale Umwelt Betrifft den Menschen mit seinen Wünschen und Vorstellungen (politische und gesellschaftliche Trendds, Wohlbefinden der einzelnen Menschen, etc)
Ökologische Umwelt Betrifft den Gesamthaushalt der Natur (Rohstoffe, Energie, Klima, Abfälle, etc)
Anspruchsgruppen / Stakeholder
Begriffskategorien
Ordnungsmomente
- Stategie
- Struktur
- Kultur
Entwicklungsmodi
- Optimierung
- Erneuerung (schaffung von völlig neuem)
Prozesse
- Managementprozess (Grundlegende Aufgaben, der Lenkung eines Unternehmens)
- Geschäftsprozess (Leistungserstellung, Vertrieb)
- Unterstützungsprozess (Personal, EDV, Finanzen)
Interaktionsthemen
Gegendstände der Austauschbeziehungen zwischen Anspruchsgruppen und der Unternehmung
- Personen- und kulturgebundene Elemente
- Objektgebunde Elemente (Resourcen)
Interaktionsthemenanalye
- Was beschreibt das Interaktionsthema, also den streitbaren Sachverhalt?
- Welche Resource (Produktionsfaktor) des Unternehmens ist betroffen?
- In welcher Umweltsphäre (Kontext) spielt sich der Sachverhalt ab?
- Welche Anspruchsgruppe ist betroffen?
- Welche Anliegen (veralgemeinerungsfähige Ziele) brint die Ansrpuchsgruppe vor?
- Welchce Interessen (unmittelbarer Eigennutzen) verfolgt die Anspruchsgruppe dabei?
- Welche Normen (Gesetze und Regeln) stützen das Anliegen der Anspruchsgruppen?
- Welche Werte (grundlegende Ansichten) stützen die Anliegen der Anspruchsgruppe?
- Was sind aus Unternehemssicht die...
- Gefahren, die sich aus der Situation für die betreffende Unternehmung ergeben?
- Reaktionsmöglichkeiten, die vom betreffenden Unternehmen ergriffen werden können?
Technische Ausgabe | Wirtschaftliche Ausgabe |
---|---|
Die erforderliche Menge und Qualität des Materials ist zur richtigen Zeit am richtigen Ort bereitzustellen. | Die Kosten der Materialbeschaffung sind zu optimieren |
Bestandteile
Beschaffungslogistik
- Bedarfsmittlung: Wie viel Material wird benötigt
- Beschaffungsmarktforschung: Welche Preise gelten auf dem Markt? Welche Lieferanten drängen sich auf?
Lagerlostik
- Lagerung: Wo sollen die Materialien gelagert werden?
- Innenbetrieblicher Transport: Wie gelangen die Materialien zu den Verarbeitungsstellen?
- Bestandsermittlung: Wie wird der aktuelle Lagerbestand ermittelt?
Produktionslogistik
- Verbrauchsermittlung: Wie viel Material verbraucht die Fertigung?
- Produktionsplanung: Wie kann der Produktionsprozess optimiert werden?
Absatzlogistik (extern)
- Distribution: Wie wird der Vertrieb der Endprodukte organisiert?
Entsorgungslogistik
Entsorgung: Welche Materialien können wieder verwendet werden?
Beschaffungskonzepte
Vorratsbeschaffung (order to stock)
Die Beschaffungsmenge ist grööser als der aktuelle Beschaffungsbedarf. Dieses Konzept bietet sich bei erwarteten Preissteigerungen, bei vermutenden Lieferengpässen und bei unverderblichen Materialien an.
Fallweise Beschaffung (order to make)
Der Beschaffungsvorgang wird ausgelöst, wenn ein Materialbedarf festgestellt wird. Die Lagerhaltung wird damit an den Lieferanten übertragen. Dieses Konzept bietet sich nur dann an, wenn das Material jederzeit beschaffbar ist, der Materialbedarf nicht für längere Zeit im Voraus geplant werden kann und eine Vorratsbeschaffung aufgrund der hohen Kapitalbindung kostspielig wäre.
Just in Time (JIT)
Dieses Konzept passt die Beschaffung in zeitlicher und mengenmässiger Hinsicht genau dem Bedarf an. Dabei beginnt im Unterschied zur "Fallweisen Beschaffung" auch der Lieferant erst dann mit der Fertigung, wenn ein Kundenauftrag vorliegt. Eine Lagerhaltung erübrigt sich entsprechend sowohl beim Besteller als auch beim Lieferanten. Dieses Konzept bietet sich dann an, wenn auf eine pünktliche Lieferung des Lieferanten vertraut ewden kann. Kleinste Verspätungen würden zu einem Produktionsstopp führen.
Insourcing
Verlagerung von zuvor im Markt bezogenen Leistungen in die eigene Wertschöpfung.
Vorteile:
- Reduktion von Lieferzeiten
- Unabhängigkeit von Lieferanten, Preisen und Absatzmengen
- Aufrechterhaltung von Qualitätsstandards
- Auslastung der Fertigungskapazitäten
Outsourcing
Verlagerung von Teilen der Wertschöpfung auf externe Lieferanten (langfristig)
Vorteile
- Minimierung der Fixkosten
- Beschaffungsmenge der Zeitspanne flexibel planbar
- Minimierung der Lagerkosten
- Ausweichmöglichkeit bei Kapazitätsengpässen
Magisches Dreieck
Besteht aus:
- Kapitalbindung und Lagerunterhalt
- Beschaffungskosten
- Lieferbereitschaft
Ziele:
- Lieferbereitschaft
- Beschaffunskosten
- Kapitalbindung
Zielkonflikte:
- Lieferbereitschaft vs. Kapitalbindung
- Beschaffungskosten vs. Kapitalbindung
Analysen
ABC-Analyse
Kategorie | Wertanteil am Gesamtwert | Mengenanteil an der Gesamtmenge |
---|---|---|
A Güter | 70% - 80% | < 30% |
B Güter | 15% - 20% | 30% - 50% |
C Güter | 5% - 10% | 40% - 60% |
XYZ-Analyse
Kategorie | Bedarf / Vorhersagegenauigkeit | Beispiel |
---|---|---|
X Güter | Regelmässiger Bedarf/Vorhersagegenauigkeit hoch | Grundnahrungsmittel |
Y Güter | Schwankender Bedarf/ Vorhersagegenauigkeit begrenzt | Glace |
Z Güter | Unregelmässiger Bedarf/Vorhersagegenauigkeit gering | Medikamente |
Lagerfunktionen
Zeitüberbrückung
Das Lager soll den Bedarf der Produktion an Materialien so schnell wie möglich bereitstellen und damit die Zeit bist zur Auslieferung überbrücken. Damit wird eine reibungslose Produktion sichergestellt.
Sicherung
Unternehmen unterliegen unvorhergesehenen Liefer- oder Bedarfsschwankungen. Arbeitet ein Lieferanten nicht fristgerecht, muss ein Unternehmen auf seine Sicherheitsbestände zurückgreifen können, um Produktionsausfälle zu vermeiden.
Spekulation
Auch Materialpreise unterliegen Preisschwankungen. Erwartet ein Unternehmen einen Preisanstieg (z.B. durch Verteuerung von Rohstoffen, Wechselkursschwankungen), kann ein Einkauf auf Vorrat trotz zusätzlicher Lagerkosten sinnvoll sein.
Veredelung bzw. Umformung
Durch die Lagerung entsteht eine gewollte Qualitätsverbesserung der Bestände. Gewisse halbfertige Produkte müssen beispielsweise zuerst auskühlen, bevor sie weiterverarbeitet werden können; andere durchlaufen vor der Auslieferung zuerst einen Reifeprozess, wie z.B. Käse. In diesem Fall ist die Lagerung Teil des Produktionsprozesses. Sie steigert den Wert des Produktes.
Assortierung
Bei Handelswaren dient die Lagerhaltung der Sortierung von Sammellieferungen oder teilweise auch der Warenpräsentation.
Kennzahlen
Durchschnittlicher Lagerbestand
Zählt zu den zentralen Kennzahlen der Lagerhaltung. Sie geben Auskunft darüber, wie hoch der Lagerbestand im Durchschnitt ist.
Lagerumschlagshäufigkeit
Diese Zahl gibt an, wie oft der durchschnittliche Lagerbestand in einem Jahr umgesetzt wird. Je niedriger diese Zahl, desto tiefer ist die Umschlagshäufigkeit. Je höher der Lagerumschlag, desto niedriger ist das im Lager gebundene Kapital.
Durschnittliche Lagerdauer
Diese Kennzahl gibt an, wie lange das Gut im Durchschnitt im Lager verbleibt. Je kürzer die Lagerung, desto geringer ist die Kapitalbindungsdauer. Die durchschnittliche Lagerdauer sinkt mit steigender Umschlagshäufigkeit ist.
Finanzbuchhaltung (FIBU)
Der laufende Geschäftsverkehr chronisch (im Journal) und systematisch (in den Konten) erfasst.
Die Finanzbuchhaltung dient
- der Ermittlung der Vermögenslage mittels Inventar und Bilanz
- der Feststellung des Erfolges mittels Erfolgsrechnung
- Die Liquiditätssteuerung und Finanzplanung mittels Geldflussrechnung
Die Zahlen der Finanzbuchhaltung sind für die Führung der Unternehmung zum Teil ungeeignet, weil sie einerseits stile Reserven enthalten und anderseits nur den Gesamterfolg und nicht den Erfolg je Prdukt oder Dienstleistung ausweisen.
Betriebsbuchhaltung
In der Betriebsbuchhaltung werden die durch die Leistungserstellung entstandenen Kosten erfasst und den durch die Leistungsveräusserungen erzielten Erlösen gegenübergestellt.
Die Kostenerfassung erfolgt nach
- Kostenarten (z.B. Materialkosten, Personalkosten, Abschreibungen)
- Kostenstellen (z.B. nach verschiedenen Abteilungen wie Einkauf, Lager, Fabrkation, Verkauf)
- Kostenträgern (z.B. nach den hergestellten Produkten oder Leistungen)
Die Betriebsbuchhaltung ist eine rein interne Rechnung. Sie stellt Unterlagen für die Kalkulation sowie die Kosten- und Erfolgskontrolle bereit.
Vollkostenrechnung
Verteilt die gesamten Kosten auf die verschiedenen Kostenträger
Kostenarten | Kostenstellen | Kostenträger |
---|---|---|
Welche Kosten fallen an? | Wo fallen Kosten an? | Wofür fallen Kosten an? |
z.B. Vollkornmehl, Hefe, Salz und Wasser, sowie Mietkosten für die Backstube, den Lagerraum und die Verkaufsläden | z.B. in der Backstube, im Lagerraum und in den Verkaufsläden | z.B. für Vollkornbrote oder Apfelkrapfen |
-> Graphische Darstellungsform der Vollkostenrechnung ist der Betriebsabrechnungsbogen BAB
Kostenartenrechnung
Einzelkosten (direkte Kosten)
Erklärung: Alle während dem Leistungsprozess für ein bestimmtes Produkt (Kostenträger) entstandenen Kosten, welche sich diesem direkt zuordnen lassen. Beispiel: Die Materialkosten können jedem einzelnen Produkt genau zugeordnet werden. Ein Kleid braucht eine genau bestimmte Menge Stoff. Verwendung: Übertragung in die Kostenträgerrechnung
Gemeinkosten (indirekte Kosten)
Erklärung: Alle während dem Leistungsprozess entstandenen Kosten, welche sich nicht einem bestimmten Produkt zuordnen lassen, sondern zur Erstellung mehrerer Produkte angefallen sind. Es ist nur bekannt, in welchem Bereich sie entstanden sind. Beispiele: Die Mietkosten für das Fabrikationsgebäude lassen sich nicht direkt den einzelnen Produkten zuordnen, denn sie werden von allen erstellten Produkten gemeinsam verursacht. Verwendung: Übertragung in die Kostenstellenrechnung
Sachliche Abgrenzung
- Kalkulatorische Zinsen (berücksichtigt, dass eigentlich auf dem Eigenkapital auch ein Zins bezahlt werden müsste -> Opportunitätskosten)
- Kalkulatorischer Unternehmerlohn: fkitives Gehalt des Unternehmers
- Kalukatorische Mieter: fiktive Miete für die Benutzung eigener Räumlichkeiten des Unternehmens -> Opportunitätskosten
- Stille Reserven: Über-/Unterbewertung Anlagevermögen, Lager
- Kalkulatorische Abschreibungen: Abweichender Abschreibungsaufwand
Kostenstellenrechnung
- Verteilugn der Kostenarten (Gemeinkosten) auf Kostenstellen
- Umlage von Kostenstellenkosten auf andere Kostenstellen (innerbetriebliche Leistungsverrechnung)
- Ermittlung von Zuschlagssätzen für die Kostenträgerrechnung
- Ermittlung von Soll-Ist-Abweichungen zur Wirtschaftlichkeitskontrolle
Kostenträgerrechnung
Die hergestellten Güter und die erbrachten Dienstleistungen werden als Kostenträger bezeichnet. Diese Kostenträger haben die von ihnen verursachten Kosten zu tragen.
Zuschlagssätze
Zuschlagsatz
GemeinKostenZuschlag (GKZ)
Mateiralgemeinkostenzuschlagsatz
Fertigungsgemeinkostenzuschlagsatz
Verwaltungskostenzuschlagsatz
Vertriebsgemeinkostenzuschlagsatz
Teilkostenrechnung
Umfasst alle Kostenrechnungssysteme, welche sich auf die Betrachtugn der direkt einem Kostenobjekt zurechenbaren Kosten (=Einzelkosten) beschränken.
Fixkosten: Diese Kosten fallen in konstanter Höhe an, unabhängig von produzierten Mengen eines Produkts, z.B. Mietkosten. Variable Kosten: Diese Kosten hängen von den produzierten Menge eines Produktes ab, z.B. Materialeinzelkosten Deckungsbeitrag: Dieser entsteht, wenn der Verkaufspreis grösser als die variablen Kosten pro Stück ist. Er steht zur Deckung der fixen Kosten zur Verfügung. Break-Even-Absatzmenge: Bei dieser Verkaufsmenge wir dei Gewinnschwelle erreicht.
Gesamtkosten Fixkosten + variable Kosten
Break-Even-Analyse (Gewinnschwelle / Nutzschwelle)
Mengenmässige Nutzschwelle berechnen
Deckungsbeitrag / Stück:
Break-Even-Point berechnen
Kostenfunktion = Erlösfunktion Fixkosten (FK) + [var. Kosten (VK) /Stk * Menge(x)] = Preis(P)/Stk. * Menge(x)
Break-Even-Absatzmenge (x)
Bilanz
Zeigt Aktiv- und Passivbestände am Schluss bzwh. am Anfang einer Rechnungsperiode. Sie ist eine Momentanaufnahme, denn sie bezieht sich auf einen Zeitpunkt. Die Bilanz zeigt das Vermögen eines Unternehmens.
Funktionen
Dokumentation: Stellt eine BEstandsaufnahme der im Unternehmen vorhandenen Vermögen und Schulden an einem Stichtag dar Gewinnermittlung: In der Bilanz wird der Gewinn bzwh. der Verlust einer bestimmten Periode ersichtlich. Information: Informiert intern (als Steuerungsinstrument für das Unternehmen) sowie extern (z.B. Kapitalgeber, Staat, usw.) über die finanziellle Lage des Unternehmens.
Erfolgsrechnung
Sie zeigt die einer Rechnungs-periode, also in einem Zeitraum, entstandenen Aufwendungen und Erträge. Sie gibt dinene Einblick in das betriebliche Geschehen (Leistung) der Unternehmung und zeigt die Finanzierung des Vermögens.
Aktiven
Passiven
Erfolgsrechnung
Nettoverkaufspreis
+ Verkaufssonderkosten = Nettobarverkaufspreis + Skonto = Nettokreditverkafspreis + Rabatt = Bruttokreditverkaufspreis + MwST. = Bruttokreditverkaufspreis
Bruttokreditankaufspreis
Katalogpreis des Lieferanten
- Rabatt/-e = Nettokreditankaufspreis (= Rechnungsbetrag) - Skonto = Nettobareinkaufspreis + Bezugskosten = Einstandswert / Einstandspreis
Gesamt-Kalkulation
auf 5 Rappen gerundent (am Ende)
Strategie
Strategische Analyse
SWOT-Analyse
PESTEL-Analyse
5-Forces Modell von Porter
Unternehmensstrategie
Elemente
Identität
- Wer sind wir?
- Welchen generellen Sinn und/oder Zweck erfüllt unsere Existenz?
Ziele
- Welchen wirtschaftlichen Zweck verfolgen wir?
- Welche Produkte/Dienstleistungen stellen wir her?
Verhaltensgrundsätze
- Wie verhalten wir uns gegenüber Anspruchsgruppen?
- Welche Grundsätze gelten für unser tägliches Handeln?
Elemente | Frage | Erläuterung |
---|---|---|
Differenzierung | Wie können wir gewinnen | Kunden für uns gewinnen / Von Konkurrenz abheben |
Wirtschaftlichkeit | Wie gelangen wir dzu unseren Einnahmen? | Tiefe Produktionskosten / Hohe Verkaufspreise |
Strategische Entscheidungen
Müssen zur Kultur, der Organisation, den Werten und zum Leitbild passen!
Strategisches Planen
Absatzmärkte
B - to - B
Busniess to Business-Marketing (Fachkundengeschäft)
- Vermarktung von einem Unternehmen zu einem andern Unternehmen (z.B. Produktionsmaschinen)
B - to - C
Business to Consumer Marketing (Privatkundengeschäft)
- Vermarktugn von einem Unternehmen zu einem Privathaushalt (z.B. Konsumgüter)
C - to - C
Customer-to-Customer Marketing
- Vermarktung von einem Privathaushalt zu einem anderen Privathaushalt (z.B. Privatverkauf über eBay)
Marktforschung
Quantitativ (Marktdaten)
Ziel: Zahlenwerte über den Markt ermitteln
- Wie gross ist das Marktvolumen?
- Wie gross ist der Marktanteil eines Unternehmens?
Qualitativ (Kundenbenbedürfnisse)
Motive für Verhaltenswiesen der Konsumenten herausfinden
- Welche Erwartungen haben Kunden von bestimmten Produkten?
- Warum kauft ein Kunde ein bestimmtes Produkt?
Erhebungsmethoden
Primärmarktforschung
Ermittlung neuer, bisher noch nicht erfasster Daten
- Befragung (mündlich oder schriftlich mittels Fragebogen)
- Beobachtungen (können verdeckt oder offen durchgefüht werden)
- Experimente, z.B. Wirkungsweisen von Produkten
- Kundenkaretn
Sekundärmarktforschung
Die Auswertungen basieren auf bereits vorhandenen Daten, die ursprünglich für andere Zwecke erfasst wurden.
- innerbetriebliche Quellen wie Absatzstatistiken, Reparaturlisten oder Kundenreklamationen
- externe Quellen, z.B. Forschungsergebnisse, veröffentlichte Statistiken, Geschäftsberichte anderer Unternehmen
Marktvolumen: Tatsächlicher Absatz bzwh. Umsatz aller Anbieter auf dem Markt Marktpotenzial: Anzahl möglicher Kundne, bzwh. Menge wenn alle Kunden kaufbereit sind (beim gegebenen Preis) Marktkapazität: Gesamtanzahl möglicher Kunden, bzwhl. max. Menge an Produkten, die gebraucht /verbraucht werden können (wenn Preis kene Rolle spielt) Marktanteil: Abgesetze Menge, bzwh. Umsatz eines Anbieters / ihres Unternehmens im Verhältnis zum gesamten Marktvolume
Marketing 4-P-Mix
Gibt vor ob man sich als Unternehmen gesamthaft auf möglichst höhe Qualität, oder möglichst tiefe Preise fokussiert
Prduct (Produkt)
- Absatzsprogramm / Sortimen
- Produkteigenschaften
- Verpackung
- Serviceleistungen
- Garantieleistungen
Price (Preis)
- Preisbestimmung
- Preisstrategie
- Konditionen
Place (Distribution)
- Absatzwege
- Transportmittel
Promotion (Kommunikation)
- Werbung
- Öffentlichkeitsarbeit
- Sponsoring
HM2
Ableitung in Python
from sympy import * import numpy as np x = Symbol('x') y = x**2 + 1 yprime = y.diff(x) print(yprime)
Tangentengleichung
Verallgemeinerter Tangentengleichung
Es gilt:
Spezialfall
Python
import sympy as sp x0_values = [1, 0 ,1] # Variablen definieren x1, x2, x3 = sp.symbols('x1, x2, x3') # Aus einzelnen Variablen eine Matrix machen x = sp.Matrix([x1, x2, x3]) # Gegebene Werte befüllen x0 = sp.Matrix(x0_values) # Skalare Terme erzeugen f1 = x1 * sp.cos(x2) + x3 f2 = x1**2 + sp.sin(x2)*x3**3 # Aus Termen eine Matrix machen f = sp.Matrix([f1, f2]) # Terme zahlenmässig auswerten fx0 = f.subs([ (x1, x0_values[0]), (x2, x0_values[1]), (x3, x0_values[2]) ]) # Jacobi Matrix bilden Df = f.jacobian(x) # Jacobi Matrix zahlenmässig auswerten Dfx0 = Df.subs([ (x1, x0_values[0]), (x2, x0_values[1]), (x3, x0_values[2]) ]) # Linearisierung von f an Stelle x0 bilden g = fx0 + Dfx0@(x- x0) # Skalare Komponenten der Linearisierung herausnehmen g1 = g[0, 0] g1_value = g1.subs([ (x1, x0_values[0]), (x2, x0_values[1]), (x3, x0_values[2]) ]) g2 = g[1, 0] g2_value = g2.subs([ (x1, x0_values[0]), (x2, x0_values[1]), (x3, x0_values[2]) ]) print ("Das Resultat lautet (Matrixförmig)") print (g1) print (g2)
Mehrdimensionales Newtonverfahren
$$ f: \mathbb{R} \to \mathbb{R}^2 $ $$
Python
import sympy as sp # Variablen in Funktion definieren x1, x2 = sp.symbols('x1, x2') # Funktionen definieren f1 = x1**2 + x2 - 11 f2 = x1 + x2**2 - 7 f = sp.Matrix([ [f1], [f2] ]) def f_at(x): return f.subs([ (x1, x[0]), (x2, x[1]) ]) # x0 definieren x0 = sp.Matrix([ [1.], [1.] ]) # Partielle Ableitungsmatrix erstellen Df = sp.Matrix([ [sp.diff(f1, x1), sp.diff(f1, x2)], [sp.diff(f2, x1), sp.diff(f2, x2)] ]) print ("Df = \n", Df) #x0 in Df einsetzen def Df_at(x): Df_at = Df.subs([ (x1, x[0, 0]), (x2, x[1, 0]) ]) return Df_at # Newtonverfahren x = [ x0 ] # Nullstelle von f xstar = sp.Matrix([ [3], [2] ]) # Anzahl Iterationsschritte print ("Newtonverfahren") for i in range(1, 5): x_prev = x[i-1] Df_prev = Df_at(x_prev) Df_inv = Df_prev.inv() f_at_xi = f_at(x_prev) xi = x_prev - Df_inv @ f_at_xi x.append(xi) print ('----------------------------------------') print (f"i = {i}") print ("xi = \n", xi) print (f"Fehler ||xi - xstar || = {(xi - xstar).norm(2)}")
Newtonverfahren mit Dämpfung
import numpy as np import sympy as sp x1, x2 = sp.symbols('x1, x2') x = sp.Matrix([x1, x2]) f1 = x1**2 + x2 - 11 f2 = x1 + x2**2 - 7 f = sp.Matrix([f1, f2]) Df = f.jacobian(x) f = sp.lambdify([ [[x1],[x2]] ], f, 'numpy') Df = sp.lambdify([ [[x1],[x2]] ], Df, 'numpy') x0 = np.array([[1.],[1.]]) print ('x0 =', x0) print ('|| f(x0) || = ', np.linalg.norm(f(x0), 2)) imax = 5 for i in range(1, imax+1): delta0 = np.linalg.solve(Df(x0), -f(x0)) kmax = 4 k = 0 while (k <= kmax) and (np.linalg.norm(f(x0 + delta0/2**k),2) >= np.linalg.norm(f(x0), 2)): k = k +1 if k == kmax + 1: k = 0 x1 = x0 + delta0 / 2**k print ('------------------------------------') print ('Iteration i =', i) print ('Dämpfung k =', k) print ('xi =', x1) print ('|| f(xi) || =', np.linalg.norm(f(x1), 2)) x0 = x1
Jacobi Matrix
# Manueller Weg Df = sp.Matrix([ [sp.diff(f1, x1), sp.diff(f1, x2)], [sp.diff(f2, x1), sp.diff(f2, x2)] ])
f = sp.Matrix([f1, f2]) x = sp.Matrix([x1, x2]) Df = f.jacobian(x )
Skalarwertige Funktionen mit mehreren Variablen
- Eine Funktion mit $$ unabhängigen Variablen $1, …, x) und einer abhängigen Variablen $$ versteht man eine Vorschrift, die jedem geordneten Zahlentupel ( $1, x_2, …, x)) aus einer Definitionsmenge $ \in \mathbb{R}^) genau ein Element $$ aus einer Wertemenge $mathbb{W} \in \mathbb{R}$ zuordnet. Symbolische Schreibweise:
$$ f: \mathbb{D} \in \mathbb{R}^n \rightarrow \mathbb{W} \mathbb{R} (x_1,x_2,…,x_n) \mapsto y = f(x_1, x_2, …, x_n) $$
- Da das Ergebnis $ \in \mathbb{R}$ ein Skalar (eine Zahl) ist, redet man von einer skalarwertigen Funktion.
Partielle Ableitung
$$ z = f(x,y) = x^5 + x^7 $$
Beispiel
Gegen ist die folgende Funktion: $(x,y) = x^5 + y^). Soll diese nach x abgeleitet werden, dann entsteht nachstehendes. Da y als Zahl angesehen wird, fällt dieses weg. $$ \frac{ \partial f(x,y) }{ \partial x }
5x^4 $$
Definition
Für eine Funktion $: \mathbb{R} \rightarrow \mathbb{R}$ mit einer Variablen ist die Ableitung an der Stelle $) bekanntlich definiert als: $$ f’(x_0) = \lim\limits{\Delta x \rightarrow 0} \frac{ f(x_0 + \Delta x) - f(x_0) }{ \Delta x } $$ Aus geometrischer Sicht entspricht dies der Steigung $ = f‘(x_0)$ der im Punkt $(x_0, f(x_0))$ angelegten Kurventangente $$ t(x) = f(x_0) + f‘(x_0)(x- x_0) $$
Python
from sympy import symbols, diff # Variablen in Funktion definieren x, y = symbols('x y', real=True) # Funktion definieren f = x**5 + x**7 print(diff(f, x))
An einer Stelle
$$ \frac{\partial f}{\partial x} (x, y)
x^2 - x\cdot y^2 + \frac{1}{2} \cdot y^3 + 10 $$
from sympy import symbols, diff # Variablen in Funktion definieren x, y = symbols('x y', real=True) # Funktion definieren f = x**2 -x * y**2 + 1/2. * y**3 + 10 # Partielle ableitungen berechnen df_x = diff(f, x) df_y = diff(f, y) # An einer Stelle (-1, 1) print ("Partielle Ableitung nach x:", df_x) print ("Partielle Ableitung nach y:", df_y) x0 = -1 y0 = 1 df_x_eval = df_x.subs([ (x, x0), (y, y0) ]) df_y_eval = df_y.subs([ (x, x0), (y, y0) ]) print(f"An Stelle x={x0}, y={y0} = ({df_x_eval}, {df_y_eval})")
Interpolation
Die Interpolation ist ein Speziialfall der linearen Ausgleichsrechnung, bei dem wir zu einer Menge von vorgegebenen Punkten eine Funktion suchen, die exakt (und nicht nur näherungsweise) durch diese Punkte verläuft.
Polynominterpolation
#Sample data points x = [1, 2, 3, 4, 5] y = [2, 1, 3, 2, 4] #Polynomial interpolation n = len(x) coefficients = [] for k in range(n): numerator = 1 denominator = 1 for j in range(n): if j != k: numerator *= (x[k] - x[j]) denominator *= (x[k] - x[j]) coefficient = y[k] * (numerator / denominator) coefficients.append(coefficient) #Evaluate the polynomial at a specific point x_new = 2.5 y_new = sum(coefficients[i] * x_new**(n-1-i) for i in range(n)) print("Interpolated value at x =", x_new, "is y =", y_new)
Lagrange Interpolation
x = [8.00, 10.00, 12.00, 14.00] y = [11.2, 13.4, 15.3, 19.5] xp = 11 m = len(x) n = m -1 yp = 0 for i in range(n+1): p = 1 for j in range(n+1): if j != i: p *= (xp - x[j])/(x[i] - x[j]) yp += y[i] * p print (f"Y Wert für x= {xp}: {yp}")
Splineinterpolation
# Define the original data points x = [1, 2, 3, 4, 5] y = [2, 4, 1, 5, 3] # Define the points where you want to estimate values x_new = [1.5, 3.5] # Perform spline interpolation y_new = [] for xi in x_new: # Find the interval where xi lies for i in range(len(x) - 1): if x[i] <= xi <= x[i + 1]: h = x[i + 1] - x[i] t = (xi - x[i]) / h # Calculate the estimated y-value using cubic spline interpolation formula a = (-t**3 + 2 * t**2 - t) * y[i] b = (3 * t**3 - 5 * t**2 + 2) * y[i + 1] c = (-3 * t**3 + 4 * t**2 + t) * (y[i + 1] - y[i]) / h d = (t**3 - t**2) * (y[i + 1] - y[i]) / h y_new.append(a + b + c + d) break print(y_new)
Ausgleichsrechnung
Im Gegensatz zur Interpolation versuchen wir bei der Ausgleichsrechnung nicht, eine Funktion zu finden, die exakt durch sämtliche Datenpunkte geht, sondern diese nur möglichst gut approximiert.
- Gegeben sind Wertepaare (, ) i=1, ..., n mit für .
- Gesucht ist eine stetige Funktion , die die Wertepaare in einem gewissen Sinn bestmöglich annähert, d.h. dass möglichst genau gilt:
für alle
Ansatzfunktionen
-
Gegeben sei eine Menge von stetzigen Ansatzfunktionen auf dem Intervall sowie Wertepaare () für .
-
Ein Element heisst Ausgleichsfunktion von zu den gegebenen Wertepaaren, falls das Fehlerfunktional
für minimal wird, d.h. .
- Man nennt das gefundenen dann optimal im Sinne der kleinsten Fehlerquadrate (least squares fit).
Lineare Ausgleichsprobleme
# Sample data points x = [1, 2, 3, 4, 5] y = [2, 1, 3, 2, 4] # Calculate the coefficients (slope and intercept) of the best-fit line n = len(x) sum_x = sum(x) sum_y = sum(y) sum_xx = sum(x_i**2 for x_i in x) sum_xy = sum(x[i] * y[i] for i in range(n)) slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x**2) intercept = (sum_y - slope * sum_x) / n # Evaluate the best-fit line at a specific point x_new = 2.5 y_new = slope * x_new + intercept print("Interpolated value at x =", x_new, "is y =", y_new)
Gauss Newton mit QR ohne Dämfpung
import numpy as np def gauss_newton_no_damping(J, r, x0, max_iter=100, tol=1e-6): x = x0 for i in range(max_iter): Jx = J(x) rx = r(x) Q, R = np.linalg.qr(Jx) delta = np.linalg.lstsq(R, -rx, rcond=None)[0] x += delta if np.linalg.norm(delta) < tol: break return x # Beispielanwendung def J(x): # Jacobimatrix n = len(observed_x_values) Jx = np.zeros((n, 3)) # 3 ist die Anzahl der Parameter (a, b, c) Jx[:, 0] = observed_x_values ** 2 # Ableitung nach dem Parameter a Jx[:, 1] = observed_x_values # Ableitung nach dem Parameter b Jx[:, 2] = 1 # Ableitung nach dem Parameter c return Jx def r(x): # Residuenvektor a, b, c = x return a * observed_x_values ** 2 + b * observed_x_values + c - observed_y_values observed_x_values = np.array([0., 1., 2.]) observed_y_values = np.array([1., 2., 8.]) x0 = np.array([1.0, 1.0, 1.0]) # Anfangswert für die Parameter a, b, c result = gauss_newton_no_damping(J, r, x0) print("Ergebnis:", result)
Numerische Integration
Im Gegensatzu zur Ableitung, die für alle differenzierbaren Funktionen analytisch berechnet werden kann, können Integrale für eine Vielzahl von Funktionen nicht analytisch gelöst werden. Verfahren zur numerischen Integration (man spricht auch von Quadratur) spielen daher eine wichtige Rolle.
Rechteckregel
def rectangle_rule(f, a, b, n): """ Approximates the integral of a function using the rectangle rule. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of subdivisions (rectangles) for the approximation. Returns: The approximate value of the integral. """ width = (b - a) / n integral = 0.0 for i in range(n): x = a + i * width integral += f(x) integral *= width return integral # Example usage def f(x): return x**2 # Function to integrate a = 0 # Lower bound b = 1 # Upper bound n = 100 # Number of subdivisions approximation = rectangle_rule(f, a, b, n) print("Approximated integral:", approximation)
Summierte Rechteckregel
import numpy as np # Implementation def sum_reckteck(f, a, b , h): n = int((b-a)/h) R = 0 for i in range(0, n): x = a + i *h R = R + f(x+h/2) R = h*R return R # Beispiel def f(x): return 1./x a = 2. b = 4. h = 0.5 R = sum_reckteck(f, a, b, h) print ('R =', R)
Trapezregel
def trapezoidal_rule(f, a, b, n): """ Approximates the integral of a function using the trapezoidal rule. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of subdivisions (trapezoids) for the approximation. Returns: The approximate value of the integral. """ width = (b - a) / n integral = 0.0 for i in range(n): x0 = a + i * width x1 = a + (i + 1) * width integral += (f(x0) + f(x1)) / 2 integral *= width return integral # Example usage def f(x): return x**2 # Function to integrate a = 0 # Lower bound b = 1 # Upper bound n = 100 # Number of subdivisions approximation = trapezoidal_rule(f, a, b, n) print("Approximated integral:", approximation)
Summierte Trapezregel
import numpy as np # Implementation def sum_trapez(f, a, b , h): n = int((b-a)/h) T = (f(a) + f(b))/2 for i in range(1, n): x = a + i *h T = T + f(x) T = h*T return T # Beispiel def f(x): return 1./x a = 2. b = 4. h = 0.5 T = sum_trapez(f, a, b, h) print ('T =', T)
Simpson-Regel
def simpson_rule(f, a, b, n): """ Approximates the integral of a function using Simpson's rule. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of subdivisions for the approximation (must be even). Returns: The approximate value of the integral. """ h = (b - a) / n integral = 0.0 for i in range(n): x0 = a + i * h x1 = x0 + h / 2 x2 = x0 + h integral += (f(x0) + 4 * f(x1) + f(x2)) integral *= h / 6 return integral # Beispielverwendung def f(x): return x**2 # Funktion, die integriert werden soll a = 0 # Untere Grenze b = 1 # Obere Grenze n = 100 # Anzahl der Unterteilungen (muss gerade sein) approximation = simpson_rule(f, a, b, n) print("Approximiertes Integral:", approximation)
Summierte Sympsonregel
def composite_simpson_rule(f, a, b, n): """ Approximates the integral of a function using the composite Simpson's rule. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of subdivisions for the approximation (must be even). Returns: The approximate value of the integral. """ h = (b - a) / n x = [a + i * h for i in range(n + 1)] integral = 0.0 for i in range(0, n, 2): integral += (f(x[i]) + 4 * f(x[i + 1]) + f(x[i + 2])) integral *= h / 3 return integral # Beispielverwendung def f(x): return x**2 # Funktion, die integriert werden soll a = 0 # Untere Grenze b = 1 # Obere Grenze n = 100 # Anzahl der Unterteilungen (muss gerade sein) approximation = composite_simpson_rule(f, a, b, n) print("Approximiertes Integral:", approximation)
Fehler der Sympsonregel
def composite_simpson_error(f, a, b, n): """ Estimates the error of the composite Simpson's rule. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of subdivisions for the approximation (must be even). Returns: The estimated error of the integral approximation. """ h = (b - a) / n fourth_derivative = max(abs(fourth_derivative_func(f, x)) for x in numpy.linspace(a, b, 1000)) error = -((b - a) * h**4 / 180) * fourth_derivative return error def fourth_derivative_func(f, x): """ Computes the fourth derivative of a function at a given point using finite differences. Args: f: The function. x: The point to evaluate the fourth derivative. Returns: The fourth derivative of the function at the given point. """ h = 1e-5 return (f(x - 2*h) - 4*f(x - h) + 6*f(x) - 4*f(x + h) + f(x + 2*h)) / h**4
## Romberg-Extrapolation
def romberg_integration(f, a, b, n): """ Approximates the integral of a function using Romberg integration. Args: f: The function to integrate. a: The lower bound of the interval. b: The upper bound of the interval. n: The number of iterations for the extrapolation. Returns: The approximate value of the integral. """ r = [[0] * (n + 1) for _ in range(n + 1)] h = b - a r[0][0] = (f(a) + f(b)) * h / 2 for i in range(1, n + 1): h /= 2 sum_term = 0 for j in range(1, 2**i, 2): sum_term += f(a + j * h) r[i][0] = r[i-1][0] / 2 + sum_term * h for k in range(1, i + 1): r[i][k] = r[i][k-1] + (r[i][k-1] - r[i-1][k-1]) / (4**k - 1) return r[n][n] # Beispielverwendung def f(x): return x**2 # Funktion, die integriert werden soll a = 0 # Untere Grenze b = 1 # Obere Grenze n = 5 # Anzahl der Iterationen approximation = romberg_integration(f, a, b, n) print("Approximiertes Integral:", approximation)
Differentialgleichung
Transformation in DGL System 1. Ordnung
- Die Differentialgleichung nach der höchsten vorkommenden Ableitung der unbekannten Funktion auflösen.
- Neue Hilfsfunktionen für die unbekannte Funktion und deren Ableitungen bis Ordnung der höchsten Ableitung minus 1 einführen.
- Das System erster Ordnung durch Ersetzen der höheren Ableitung durch die Funktionen aufstellen.
- Das entsprechende Anfangswertproblem in vektorieller Form aufschreiben.
Aus Ableitungen die Funktion nehmen
Ersatzwerte für Funktionen erstellen
Ersatzwerte einsetzen
Gleichungssystem aufstellen
Wenn Anfangswert gegeben:
mit
Gewöhnliche Differentialgleichung
In vielen Prozessen in der Natur oder Technik spielen zeitliche Änderungen von beobachtbaren Grössen ('Observablen'), nennen wir z.B. eine wichtige Rolle. Häufig ist die zeitliche Änderung einer solchen Grösse, also , abhängig von der Grösse selbst, d.h. wir können durch eine Funktion ausdrücken, die von der Zeit aber eben auch von der eigentlichen Grösse abhängt:
Richtungsfeld
import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x + y x = np.linspace(-5, 5, 20) # x-Werte im Bereich von -5 bis 5 y = np.linspace(-5, 5, 20) # y-Werte im Bereich von -5 bis 5 X, Y = np.meshgrid(x, y) U = 1 # x-Komponente der Vektoren (konstant 1) V = dy_dx(X, Y) # y-Komponente der Vektoren basierend auf der Differentialgleichung plt.quiver(X, Y, U, V) # Richtungsfeld zeichnen plt.xlabel('x') plt.ylabel('y') plt.title('Richtungsfeld der Differentialgleichung dy/dx = x + y') plt.grid(True) plt.show()
## Euler Verfahren
import numpy as np import matplotlib.pyplot as plt def dy_dx(x, y): return x + y def euler_method(f, x0, y0, h, num_steps): x = np.zeros(num_steps + 1) y = np.zeros(num_steps + 1) x[0] = x0 y[0] = y0 for i in range(num_steps): y[i+1] = y[i] + h * f(x[i], y[i]) x[i+1] = x[i] + h return x, y x = np.linspace(-5, 5, 20) # x-Werte im Bereich von -5 bis 5 y = np.linspace(-5, 5, 20) # y-Werte im Bereich von -5 bis 5 X, Y = np.meshgrid(x, y) U = 1 # x-Komponente der Vektoren (konstant 1) V = dy_dx(X, Y) # y-Komponente der Vektoren basierend auf der Differentialgleichung plt.quiver(X, Y, U, V) # Richtungsfeld zeichnen # Lösungskurve berechnen x0 = 0 # Anfangswert für x y0 = 3 # Anfangswert für y h = 0.1 # Schrittweite num_steps = 100 # Anzahl der Schritte x_sol, y_sol = euler_method(dy_dx, x0, y0, h, num_steps) plt.plot(x_sol, y_sol, 'r', label='Lösungskurve für y(0) = 3') plt.xlabel('x') plt.ylabel('y') plt.title('Richtungsfeld und Lösungskurve der Differentialgleichung dy/dx = x + y') plt.grid(True) plt.legend() plt.show()
Runge Kutta Verfahren
def runge_kutta(f, x0, y0, h, num_steps): x = [x0] y = [y0] for i in range(num_steps): k1 = h * f(x[i], y[i]) k2 = h * f(x[i] + h/2, y[i] + k1/2) k3 = h * f(x[i] + h/2, y[i] + k2/2) k4 = h * f(x[i] + h, y[i] + k3) x.append(x[i] + h) y.append(y[i] + (k1 + 2*k2 + 2*k3 + k4) / 6) return x, y def dy_dx(x, y): return x + y x0 = 0 # Anfangswert für x y0 = 3 # Anfangswert für y h = 0.1 # Schrittweite num_steps = 100 # Anzahl der Schritte x_sol, y_sol = runge_kutta(dy_dx, x0, y0, h, num_steps) plt.plot(x_sol, y_sol, 'r', label='Lösungskurve für y(0) = 3') plt.xlabel('x') plt.ylabel('y') plt.title('Lösungskurve der Differentialgleichung dy/dx = x + y mit dem Runge-Kutta-Verfahren') plt.grid(True) plt.legend() plt.show()
Runge Kutta in Vektorieller Form
import numpy as np import matplotlib.pyplot as plt def rk4_vektor(f, a, b, y0, h): x = np.arange(a, b + h, h) y = np.zeros((y0.size, x.size)) n = x.size # AB y[:, 0] = y0 for i in range(0, n-1): k1 = f(x[i], y[:, i]) k2 = f(x[i] + h / 2, y[:, i] + h / 2 * k1) k3 = f(x[i] + h / 2, y[:, i] + h / 2 * k2) k4 = f(x[i] + h, y[:, i] + h * k3) y[:, i + 1] = y[:, i] + h/6 * (k1 + 2*k2 + 2*k3 + k4) return x, y def fh(t, z): u = (300000.0 - 80000.0) / 190.0 vrel = 2600.0 ma = 300000.0 g = 9.81 z1 = z[0] z2 = z[1] return np.array([z2, vrel*(u/(ma - u * t)) - g - (np.exp(-z1/8000.0)/(ma - u * t)) * z2**2]) # Tstart a = 0. # Tende b = 190.0 h = 0.1 # h(0) = h'(0) = 0 y0 = np.array([0.0, 0.0]) # z[0] = z1 = h(t); z[1] = z2 = v(t) x, z = rk4_vektor(fh, a, b, y0, h) def z3(t, z): u = (300000.0 - 80000.0) / 190.0 vrel = 2600.0 ma = 300000.0 g = 9.81 z1 = z[0] z2 = z[1] return vrel*(u/(ma - u * t)) - g - (np.exp(-z1/8000.0)/(ma - u * t)) * z2**2 plt.plot(x, z[0, :], label="h(t)") plt.title("h(t)") plt.grid() plt.figure() plt.plot(x, z[1, :], label="v(t)") plt.title("v(t)") plt.grid() plt.figure() plt.plot(x, z3(x, z), label="a(t)") plt.title("a(t)") plt.grid() plt.show()
Stabilitätsfunktion
import numpy as np import matplotlib.pyplot as plt def stability_function(z): return 1 + z + 0.5*z**2 + (1/6)*z**3 + (1/24)*z**4 z = np.linspace(-5, 5, 100) # Werte für z im Bereich von -5 bis 5 S = stability_function(z) plt.plot(z, np.abs(S), label='|Stabilitätsfunktion|') plt.xlabel('Re(z)') plt.ylabel('|Stabilitätsfunktion|') plt.title('Stabilitätsfunktion des Runge-Kutta-Verfahrens (RK4)') plt.grid(True) plt.legend() plt.show()
Mittelpunktverfahren
Mit Anzahl Iterationen
def mittelpunktverfahren(f, a, b, n): h = (b - a) / n # Berechne die Schrittweite result = 0.0 for i in range(n): x = a + (i + 0.5) * h # Berechne den Mittelpunkt des aktuellen Intervalls result += f(x) # Addiere den Funktionswert am Mittelpunkt zum Ergebnis result *= h # Multipliziere mit der Schrittweite, um das Integral zu approximieren return result def f(x): return x**2 integral_approximation = mittelpunktverfahren(f, 0, 1, 100) print(integral_approximation)
Mit Schrittweite
def mittelpunktverfahren(f, a, b, h): n = int((b - a) / h) # Berechne die Anzahl der Teilintervalle result = 0.0 for i in range(n): x = a + (i + 0.5) * h # Berechne den Mittelpunkt des aktuellen Intervalls result += f(x) # Addiere den Funktionswert am Mittelpunkt zum Ergebnis result *= h # Multipliziere mit der Schrittweite, um das Integral zu approximieren return result def f(x): return x**2 integral_approximation = mittelpunktverfahren(f, 0, 1, 0.1) print(integral_approximation)
Person:
- polynominterpolation in python
ChatGPT:
- Polynomial interpolation in Python can be done using the NumPy library. Here's a simple example:
In this example, we have x
and y
arrays representing the sample data points. The np.polyfit
function fits a polynomial of degree len(x)-1
to the data, and the resulting coefficients are stored in the coefficients
array. The np.poly1d
function creates a polynomial object using these coefficients.
You can then evaluate the interpolated polynomial at a specific point by calling the polynomial object with that point. In this case, we calculate y_new
at x_new = 2.5
.
Note that the degree of the polynomial is set to len(x)-1
to ensure that the polynomial passes through all the given points.
Person:
- ohne numpy
ChatGPT:
- If you don't want to use NumPy, you can still perform polynomial interpolation in Python using the
polyfit
function from thenumpy.polynomial
module. Here's an example:
In this code, we import the Polynomial
class from numpy.polynomial
module. The Polynomial.fit
function performs polynomial interpolation on the given data points. The deg
parameter specifies the degree of the polynomial to fit, which is set to len(x)-1
to ensure interpolation through all the data points.
Once the polynomial is obtained, you can evaluate it at a specific point by calling the polynomial object with that value. In this example, we calculate y_new
at x_new = 2.5
.
Please note that although the NumPy library is not directly used, the numpy.polynomial
module is still imported for the Polynomial.fit
function.
Person:
- ganz ohne python
ChatGPT:
- Ohne die Verwendung von Python ist es schwierig, polynomiale Interpolation durchzuführen, da dies eine Programmiersprache ist, die speziell für solche Aufgaben entwickelt wurde. Wenn Sie jedoch eine andere Methode zur Durchführung polynomieller Interpolation suchen, gibt es auch mathematische Software oder spezialisierte Tools, die diese Funktionalität bieten können.
Ein beliebtes mathematisches Softwarepaket ist MATLAB. MATLAB bietet eine integrierte Funktion namens polyfit
, die für die polynomiale Interpolation verwendet werden kann. Es gibt auch andere mathematische Software wie Mathematica oder Octave, die ähnliche Funktionen bieten.
Wenn Sie keine Programmiersprache verwenden möchten, könnten Sie auch ein spezialisiertes Software-Tool wie Microsoft Excel verwenden. Excel bietet Funktionen zur Kurvenanpassung, mit denen Sie eine polynomiale Interpolation durchführen können.
Allerdings ist es wichtig zu beachten, dass die Verwendung einer Programmiersprache wie Python oder mathematischer Software wie MATLAB in der Regel die flexibelsten und leistungsfähigsten Möglichkeiten bietet, polynomiale Interpolation durchzuführen und Ergebnisse zu analysieren.
Person:
- ganz ohne numpy also manuell in python
ChatGPT:
- Wenn Sie die polynomiale Interpolation manuell in Python ohne die Verwendung von NumPy durchführen möchten, können Sie dies mit den grundlegenden Funktionen und Listen in Python erreichen. Hier ist ein Beispiel:
In diesem Beispiel werden die Datenpunkte in den Listen x
und y
angegeben. Die Interpolation erfolgt manuell, indem die Koeffizienten des Polynoms berechnet werden. Der Code verwendet eine verschachtelte Schleifenstruktur, um die Koeffizienten einzeln zu berechnen.
Anschließend wird das interpolierte Polynom an einem bestimmten Punkt x_new
ausgewertet, indem die Koeffizienten entsprechend der Potenz des Punkts multipliziert werden.
Beachten Sie, dass dieses manuelle Verfahren für polynomiale Interpolation nicht so effizient ist wie die Verwendung von NumPy oder spezialisierten Bibliotheken. Es ist jedoch eine Möglichkeit, dies in Python ohne externe Bibliotheken zu erreichen.
Person:
- lineares ausgleichsproblem in python ohne bibliotheken
ChatGPT:
- Wenn Sie das lineare Ausgleichsproblem in Python ohne die Verwendung externer Bibliotheken lösen möchten, können Sie die Methode der kleinsten Quadrate implementieren. Hier ist ein Beispiel:
In diesem Beispiel werden die Datenpunkte in den Listen x
und y
angegeben. Das lineare Ausgleichsproblem wird gelöst, indem die Koeffizienten (Steigung und Y-Achsenabschnitt) der besten Anpassungslinie berechnet werden.
Der Code verwendet die Formeln für die Methode der kleinsten Quadrate, um die Koeffizienten zu berechnen. Anschließend kann die beste Anpassungslinie an einem bestimmten Punkt x_new
ausgewertet werden, indem der Punkt in die lineare Gleichung y = mx + c
eingesetzt wird.
Bitte beachten Sie, dass diese Methode für das lineare Ausgleichsproblem funktioniert, aber für komplexere Anpassungsprobleme oder nichtlineare Funktionen möglicherweise nicht ausreicht. In solchen Fällen sind externe Bibliotheken wie NumPy, SciPy oder scikit-learn mit speziellen Funktionen und Algorithmen für die lineare Regression möglicherweise besser geeignet.
Person:
- rechteckregel in python
ChatGPT:
- Die Rechteckregel ist eine einfache numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Rechteckregel in Python:
Dieses Beispiel definiert eine Funktion rectangle_rule
, die die Rechteckregel verwendet, um das Integral einer gegebenen Funktion f
über das Intervall von a
bis b
zu approximieren. Die Anzahl der Unterteilungen (Rechtecke) wird durch den Parameter n
angegeben.
Die Funktion berechnet die Breite jedes Rechtecks als (b - a) / n
. Dann wird die Summe der Funktionswerte an den linken Randpunkten der Rechtecke berechnet. Der Ausdruck f(x)
gibt den Funktionswert an der Position x
zurück. Schließlich wird das Ergebnis mit der Breite multipliziert, um die Approximation des Integrals zu erhalten.
Hier ist ein Beispiel, wie Sie die rectangle_rule
-Funktion verwenden können:
In diesem Beispiel wird das Integral der Funktion f(x) = x^2
über das Intervall von 0 bis 1 approximiert, indem die rectangle_rule
-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.
Person:
- Trapezregel in python
ChatGPT:
- Die Trapezregel ist eine weitere numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Trapezregel in Python:
Dieses Beispiel definiert eine Funktion trapezoidal_rule
, die die Trapezregel verwendet, um das Integral einer gegebenen Funktion f
über das Intervall von a
bis b
zu approximieren. Die Anzahl der Unterteilungen (Trapezoide) wird durch den Parameter n
angegeben.
Die Funktion teilt das Intervall in n
Abschnitte mit einer Breite von width = (b - a) / n
. Für jedes Trapez werden die Funktionswerte an den Randpunkten x0
und x1
berechnet und dann durch 2 addiert. Die Summe dieser Werte wird anschließend mit der Breite multipliziert, um die Approximation des Integrals zu erhalten.
Hier ist ein Beispiel, wie Sie die trapezoidal_rule
-Funktion verwenden können:
In diesem Beispiel wird das Integral der Funktion f(x) = x^2
über das Intervall von 0 bis 1 approximiert, indem die trapezoidal_rule
-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.
Person:
- Sympson regel in python ohne bibliothek
ChatGPT:
- Die Simpson-Regel ist eine weitere numerische Methode zur Approximation von Integralen. Hier ist ein Beispiel für die Implementierung der Simpson-Regel in Python ohne Verwendung externer Bibliotheken:
Dieses Beispiel definiert eine Funktion simpson_rule
, die Simpson's Regel verwendet, um das Integral einer gegebenen Funktion f
über das Intervall von a
bis b
zu approximieren. Die Anzahl der Unterteilungen für die Approximation wird durch den Parameter n
angegeben, der eine gerade Zahl sein muss.
Die Funktion verwendet eine Schleife, um das Intervall in n
Abschnitte zu unterteilen. Für jedes Teilintervall werden die Funktionswerte an den Randpunkten x0
, x1
und x2
berechnet und mit den entsprechenden Gewichtungen addiert. Die Summe dieser Werte wird anschließend mit h/6
multipliziert, um die Approximation des Integrals zu erhalten.
Hier ist ein Beispiel, wie Sie die simpson_rule
-Funktion verwenden können:
In diesem Beispiel wird das Integral der Funktion f(x) = x^2
über das Intervall von 0 bis 1 approximiert, indem die simpson_rule
-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.
Person:
- summierte sympsonregel
ChatGPT:
- Die summierte Simpson-Regel (auch bekannt als zusammengesetzte Simpson-Regel) erweitert die Grundidee der Simpson-Regel auf mehrere Teilintervalle, um eine genauere Approximation des Integrals zu erhalten. Hier ist ein Beispiel für die Implementierung der summierten Simpson-Regel in Python ohne Verwendung externer Bibliotheken:
In dieser Implementierung wird die Summe der summierten Simpson-Regel verwendet, um das Integral einer gegebenen Funktion f
über das Intervall von a
bis b
zu approximieren. Die Anzahl der Unterteilungen für die Approximation wird durch den Parameter n
angegeben, der eine gerade Zahl sein muss.
Die Funktion teilt das Intervall in n
Teilintervalle mit einer Schrittweite h = (b - a) / n
. Eine Liste x
wird erstellt, die die Randpunkte aller Teilintervalle enthält. Dann wird eine Schleife verwendet, um über die Teilintervalle zu iterieren und die entsprechenden Funktionswerte in der summierten Simpson-Regel-Formel zu berechnen und aufsummiert.
Die Summe der Funktionswerte wird anschließend mit h / 3
multipliziert, um die Approximation des Integrals zu erhalten.
Hier ist ein Beispiel, wie Sie die composite_simpson_rule
-Funktion verwenden können:
In diesem Beispiel wird das Integral der Funktion f(x) = x^2
über das Intervall von 0 bis 1 mit der summierten Simpson-Regel approximiert, indem die composite_simpson_rule
-Funktion mit den entsprechenden Parametern aufgerufen wird. Das Ergebnis wird dann ausgegeben.
Person:
- der fehler der summierten quadraturformel
ChatGPT:
- Der Fehler der summierten Quadraturformel (wie die summierte Simpson-Regel) kann mithilfe des Richardson-Extrapolationsverfahrens geschätzt werden. Der Fehler der summierten Quadraturformel ist abhängig von der Anzahl der Teilintervalle und von der Ableitungsordnung der Funktion.
Die Fehlerabschätzung für die summierte Quadraturformel lautet:
E=−(b−a)⋅h4180⋅f(4)(ξ)E = - \frac{{(b - a) \cdot h^4}}{{180}} \cdot f^{(4)}(\xi)E=−180(b−a)⋅h4⋅f(4)(ξ)
wobei hhh die Schrittweite ist und f(4)(ξ)f^{(4)}(\xi)f(4)(ξ) die vierte Ableitung der Funktion fff an einer Stelle ξ\xiξ im Intervall [a, b].
Es ist jedoch wichtig zu beachten, dass die genaue Berechnung des Fehlers oft schwierig ist, da die vierte Ableitung der Funktion nicht immer bekannt oder leicht zu berechnen ist. Die Fehlerabschätzung bietet jedoch eine gute Richtlinie für die Genauigkeit der Approximation.
Hier ist ein Beispiel, wie Sie den Fehler der summierten Quadraturformel für die summierte Simpson-Regel in Python berechnen können:
In diesem Beispiel wird die Funktion composite_simpson_error
verwendet, um den Fehler der summierten Quadraturformel für die summierte Simpson-Regel abzuschätzen. Die Funktion verwendet die vierte Ableitung der Funktion f
anhand der Funktion fourth_derivative_func
, die die vierte Ableitung numerisch mithilfe von finiten Differenzen berechnet. Der maximale Betrag der vierten Ableitung im Intervall [a, b] wird verwendet, um den Fehler abzuschätzen.
Bitte beachten Sie, dass die numerische Schätzung des Fehlers mit finiten Differenzen möglicherweise nicht immer genau ist und von der Funktion und der Wahl der Schrittweite abhängen kann.
Person:
- romberg extrapolation
ChatGPT:
- Die Romberg-Extrapolation ist eine Methode zur Verbesserung der Genauigkeit von numerischen Quadraturverfahren, wie z.B. der Trapezregel oder der Simpson-Regel. Sie basiert auf der Idee, sukzessive approximierte Werte zu berechnen und diese dann zu extrapolieren, um eine genauere Schätzung des Integrals zu erhalten.
Hier ist ein Beispiel für die Implementierung der Romberg-Extrapolation in Python:
Diese Implementierung verwendet eine Matrix r
, um die approximierten Werte während des Extrapolationsprozesses zu speichern. Die Romberg-Extrapolation erfolgt in n
Iterationen. Zuerst wird der Anfangswert r[0][0]
mit Hilfe der Trapezregel berechnet. Dann erfolgen die Extrapolationsschritte, bei denen sukzessive genauere Approximationen berechnet werden. Schließlich wird der Wert r[n][n]
als die beste Schätzung des Integrals zurückgegeben.
Hier ist ein Beispiel, wie Sie die romberg_integration
-Funktion verwenden können:
In diesem Beispiel wird das Integral der Funktion f(x) = x^2
über das Intervall von 0 bis 1 mit Hilfe der Romberg-Extrapolation approximiert. Die romberg_integration
-Funktion wird mit den entsprechenden Parametern aufgerufen und das Ergebnis wird ausgegeben. Je höher die Anzahl der Iterationen n
, desto genauer wird die Approximation.
Person:
- richtungsfeld einer differentialgleichung
ChatGPT:
- Ein Richtungsfeld ist eine grafische Darstellung von Vektorfeldern, die die Richtungen von Tangenten an Lösungskurven einer Differentialgleichung anzeigen. Es gibt verschiedene Möglichkeiten, ein Richtungsfeld für eine Differentialgleichung zu zeichnen, aber im Folgenden werde ich eine grundlegende Methode beschreiben:
- Identifiziere die Differentialgleichung und ihre Unbekannte. Zum Beispiel nehmen wir an, wir haben die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy=x+y mit der Unbekannten y(x)y(x)y(x).
- Wähle ein Raster von Punkten im Koordinatensystem, um das Richtungsfeld zu erstellen. Du kannst zum Beispiel eine Gitterstruktur auf dem Bereich der interessierenden xxx- und yyy-Werte definieren.
- An jedem Punkt im Raster berechne den Wert der Steigung dydx\frac{dy}{dx}dxdy basierend auf der gegebenen Differentialgleichung. Verwende diesen Wert, um einen Vektor von dem Punkt aus zu zeichnen, der in die Richtung der Steigung zeigt.
- Wiederhole den Schritt 3 für alle Punkte im Raster. Die resultierenden Vektoren bilden das Richtungsfeld.
- Optional: Zeichne Lösungskurven in das Richtungsfeld ein, um eine Vorstellung von den Lösungen der Differentialgleichung zu bekommen. Du kannst dies tun, indem du die Differentialgleichung numerisch löst oder analytisch bekannte Lösungen verwendest.
Hier ist ein Beispielcode in Python, der ein Richtungsfeld für die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy=x+y erstellt:
Dieser Code verwendet die Bibliotheken NumPy und Matplotlib, um das Richtungsfeld zu erstellen. Wir definieren eine Funktion dy_dx(x, y)
, die die Ableitung dydx\frac{dy}{dx}dxdy basierend auf der Differentialgleichung berechnet. Dann erstellen wir ein Gitter von Punkten im Bereich von -5 bis 5 für x- und y-Werte. Die x-Komponente der Vektoren ist konstant 1, und die y-Komponente basiert auf der Funktion dy_dx(x, y)
. Schließlich verwenden wir die Funktion plt.quiver
von Matplotlib, um das Richtungsfeld zu zeichnen.
Das erstellte Richtungsfeld zeigt die Richtungen der Tangenten an Lösungskur
Person:
- zeichne zudem die lösungskurve für y(0) =3 ein
ChatGPT:
- Um eine Lösungskurve in das Richtungsfeld einzufügen, können wir die Differentialgleichung numerisch mit einem geeigneten Verfahren lösen. Ein einfaches numerisches Verfahren ist das Euler-Verfahren. Hier ist der aktualisierte Code, der das Richtungsfeld mit der Lösungskurve für die Anfangsbedingung y(0)=3y(0) = 3y(0)=3 zeigt:
In diesem aktualisierten Code haben wir eine Funktion euler_method
, die das Euler-Verfahren verwendet, um die Differentialgleichung numerisch zu lösen. Wir rufen diese Funktion auf und erhalten die x- und y-Werte für die Lösungskurve. Dann verwenden wir plt.plot
von Matplotlib, um die Lösungskurve in Rot einzufügen.
Jetzt wird das Richtungsfeld zusammen mit der Lösungskurve für y(0)=3y(0) = 3y(0)=3 angezeigt.
Person:
- das eulerverfahren
ChatGPT:
- Entschuldigung für das Missverständnis. Hier ist der Code, der das Euler-Verfahren zur Lösung der Differentialgleichung verwendet und die Lösungskurve in das Richtungsfeld einfügt:
Hier wird das Euler-Verfahren verwendet, um die Differentialgleichung dydx=x+y\frac{dy}{dx} = x + ydxdy=x+y numerisch zu lösen. Die Funktion euler_method
führt das Euler-Verfahren aus und gibt die x- und y-Werte für die Lösungskurve zurück. Anschließend wird die Lösungskurve mit plt.plot
in Rot in das Richtungsfeld eingefügt.
Das aktualisierte Diagramm zeigt das Richtungsfeld zusammen mit der Lösungskurve für die Anfangsbedingung y(0)=3y(0) = 3y(0)=3.
Person:
- Runge Kutta Verfahren
ChatGPT:
- Das Runge-Kutta-Verfahren ist eine beliebte Methode zur numerischen Lösung von gewöhnlichen Differentialgleichungen. Es verwendet eine Kombination von Funktionsauswertungen an verschiedenen Punkten, um die Genauigkeit der Approximation zu verbessern. Hier ist eine Implementierung des klassischen Runge-Kutta-Verfahrens (auch bekannt als RK4) in Python:
Diese Funktion runge_kutta
nimmt die Differentialgleichung f
, den Anfangswert x0
und y0
, die Schrittweite h
und die Anzahl der Schritte num_steps
. Sie führt das Runge-Kutta-Verfahren aus und gibt die Arrays x
und y
zurück, die die berechneten x- und y-Werte der Lösungskurve enthalten.
Hier ist ein Beispiel, wie du das Runge-Kutta-Verfahren verwenden kannst:
In diesem Beispiel verwenden wir die Differentialgleichung dy_dx
, den Anfangswert y0
, die Schrittweite h
und die Anzahl der Schritte num_steps
, um das Runge-Kutta-Verfahren auf die Differentialgleichung anzuwenden. Die berechneten x- und y-Werte werden dann mit plt.plot
in das Diagramm eingefügt.
Das erstellte Diagramm zeigt die Lösungskurve der Differentialgleichung dy/dx = x + y
für die Anfangsbedingung y(0) = 3
, berechnet mit dem Runge-Kutta-Verfahren.
Person:
- stabilitätsfunktion
ChatGPT:
- Die Stabilitätsfunktion ist eine Methode zur Analyse der Stabilität von numerischen Integrationsverfahren für Differentialgleichungen. Es untersucht, wie sich kleine Störungen im Anfangswert oder im Lösungsverlauf des Verfahrens im Laufe der Zeit ausbreiten.
Für das Runge-Kutta-Verfahren (RK4) kann die Stabilitätsfunktion als Polynom dargestellt werden. Hier ist eine Implementierung der Stabilitätsfunktion für das RK4-Verfahren in Python:
Dieser Code berechnet die Stabilitätsfunktion für das RK4-Verfahren und zeichnet den Betrag der Stabilitätsfunktion als Funktion des Realteils des Komplexwerts z. Die Stabilitätsfunktion wird durch das Polynom 1+z+12z2+16z3+124z41 + z + \frac{1}{2}z^2 + \frac{1}{6}z^3 + \frac{1}{24}z^41+z+21z2+61z3+241z4 dargestellt.
Das erzeugte Diagramm zeigt den Betrag der Stabilitätsfunktion für verschiedene Werte von z. Wenn der Betrag der Stabilitätsfunktion kleiner als 1 ist, bedeutet das, dass das Verfahren stabil ist und kleine Störungen im Laufe der Zeit gedämpft werden. Wenn der Betrag größer als 1 ist, bedeutet das, dass das Verfahren instabil ist und kleine Störungen stark anwachsen können.
Es ist wichtig zu beachten, dass die Stabilitätsfunktion nur die absolute Stabilität des Verfahrens bewertet und nicht seine Genauigkeit oder Konvergenzordnung. Es ist üblich, die Stabilitätsfunktion zusammen mit anderen Analysen wie der Konsistenz und Konvergenz zu betrachten, um ein umfassendes Bild der Eigenschaften eines numerischen Integrationsverfahrens zu erhalten.
SNP
Motivation
Effizient
- C erlaubt es, sehr effizienten Code zu schreiben.
- Der verbraucht Arbeitsspeicher (Memory Footprint) ist sehr leicht.
- Die Ausführungsgeschwindigkeit ist hoch, weil Optimierungen zur Compilezeit ausgeführt werden.
Verbreitet
- C/C++ ist mit grossem Abstand die wichtigste System- und systemnahe Sprache
Systemverständnis
- C fördert und bedingt das Verständnis für das unterliegende System
Hauptprgrogramm
#include <stdio.h> //printf
#include <stdlib.h>
int main (void)
{
printf("Hello world in C\n"); // Newline ist wichtig
return EXIT_SUCCESS;
}
Datentypen
Datentyp | Bytes | Wertbereich |
---|---|---|
signed char | 1 | -128 bis 127 |
unsigned char | 1 | 0 bis 255 |
[signed] short [int] | 2 | -32768 bis 32767 |
unsigned short [int] | 2 | 0 bis 65636 |
[signed] int | 4 | -2^31 bis 2^31-1 |
unsigned [int] | 4 | 0 bis 2^32-1 |
[signed] long [int] | 8 | -2^63 bis 2^63-1 |
unsigned long [int] | 8 | 0 bis 2^64-1 |
[signed] long long [int] | 8 | -2^63 bis 2^63-1 |
unsigned long [int] | 8 | -2^63 bis 2^64-1 |
long double | 19 | 1.2 * 10^4932 bis 1.2 * 10^4932 |
Standard Typen
Integer
#include <stdint.h>
int8_t signedValue;
uint8_t unsignedValue;
int16_t signedValue;
uint16_t unsignedValue;
int32_t signedValue;
uint32_t unsignedValue;
int64_t signedValue;
uint64_t unsignedValue;
Bool
#include <stdbool.h>
bool trueValue = true;
bool falseValue = false;
Literalle / Konstanten
- Im Code eingefügte, unveränderliche Werte
- Ist die Zahlt zu gross für int, wird diese als long interpretiert.
int decimal = 3987; // beginnt mit 1 - 9
unsigned int octal = 037; // starts with 0
unsigned int hexadecimal = 0x23; // starts with 0X or 0x
char asciiCode = 'A'; // single ascii code
double decimalPoint = 1.23;
double exponent = 1.6e3;
Explizite Angabe
int interpretedAsLong = 134L;
int interpretedAsLongLong = 134LL;
// U kann alleine, oder zusätzlich zu L / LL hinzufgefügt werden.
int interpretedAsUnsigned = 134U;
float interpretedAsFloat = 1.23F; // f geht auch
double interpretedAsDouble = 1.23L; // Long float
Zeichenliterale
Zeichen | Beschreibung |
---|---|
\n | new line |
\t | horizontal tab |
\ | backslash |
\' | Single quote |
\" | double quote |
\0 | Ascii Code 0 |
Ascii Tabelle
Strings
Durch Anführungszeichen eingeschlossene Zeichenfolgen werden als strings interpretiert.
char[] iAmString = "Hello World!";
Symbolische Konstanten
Symbolische Konstanten werden Textmässig ersetzt.
#define MAX_LENGTH 1000
Anwendung
#define MAX_LENGTH 1000
int length = MAX_LENGTH; // MAX_LENGTH wird durch den Wert im define ersetzt
Definitionen
- Sind Variablen ausserhalb von Funktionen definiert, sind diese lokal in der Source (.c) Datei.
Variablen
Eine Variable sollte immer auf einen Wert initialisiert werden, da es keine Standardwerte gibt.
double hoehe;
int laenge, breite; // mach das nicht
double r,
radius = 15.0; // Besser so, dann ist es klar, dass nur radius auf 15.0 ist
Konstanten
Konstanten sind nicht veränderlich.
const double pi = 3.14159;
Typ Alias
Erlaubt es eigene Typen zu definieren, oder auch Synonyme.
typedef int Index; // sagt, dass Index von typ int ist.
Index index = 0; // Index kann jetzt als Typ verwendet werden
typedef struct position
{
int x;
int y;
} Position;
Position position;
position.x = 1;
position.y = 2;
Statisch
Statische Variablen sind prozessübergreifend.
static int value;
- Wenn im Header definiert, dann sind diese sobald diese inkludiert wird, verfügbar.
- Wenn in der Source Datei definiert, dann sind diese in der gleichen Datei in allen Prozessen verfügbar.
Bit-Operatoren
Vorrang und Reihenfolge
Type Casts
Ein type cast wird benötigt, um ein typ explizit in einen anderen Typen zu wandeln
int value = 5;
int divider = 3;
double result = (double) value / divider;
Kontrollstrukturen
If-Else / Else-if
const int SOME_CONSTANT = 10;
int value = 5;
if (value > SOME_CONSTANT) {
// Do something if greater than SOME_CONSTANT
} else if (value < SOME_CONSTANT) {
// Do something if less than SOME_CONSTANT
} else {
// Do something if equal to SOME_CONSTANT
}
For-Schleife
for (int i=1; i <= 5; i++) {
// Mache etwas in der for schleif
}
While
Zuerst wird die Bedingung geprüft, dann der Inhalt der Schleife ausgeführt
int i=0; // Initialisierung its wichtig
while (i < 5)
{
// Mache etwas in der for schleife
i++; // i muss manuell verändert werden
}
Do while
Im Gegensatz zur While schleife, wird in der Do-While der Inhalt der Schleife zuerst ausgeführt.
int i=0; // Initialisierung its wichtig
do
{
// Mache etwas in der for schleife
i++; // i muss manuell verändert werden
// WIRD MINDESTENS 1 MAL AUSGEFÜHRT
}
while (i < 5);
Switch
Es können keine Strings evaluiert werden!!
int n;
switch (n)
{
case 1:
{
// Wenn 1
break;
}
case 2:
case 3:
{
// Wenn 2 oder 3
break;
}
default:
{
// Alles andere
break;
}
}
Input
stdout / stdin / stderr sind auf der Konsole
Konstanten String auf StdOut ausgeben
#include <stdio.h>
int result = puts("Hello world");
if (result == EOF)
{
// Konnte nicht geschrieben werden
}
Einzelnes Zeichen auf StdOut ausgeben
#include <stdio.h>
int result = putchar('A');
if (result == EOF)
{
// Konnte nicht geschrieben werden
}
Einzelnes Zeichen von StdIn lesen
#include <stdio.h>
char c = getchar();
// Das Zeichen wird in c gespeichert
Formatierte Ausgabe / Eingabe
#include <stdio.h>
printf(format, arg1, arg2, . . .); // für Ausgabe
scanf(format, &x, &y, &z); // Parameter (ausser string (char*)) mit & angeben
Der Parameter format ist ein String. Jedes der zusätzlichen Argumente (arg1, arg2, …) ist in diesem Forhanden und kann folgedes bedeuten.
Zeichenfolge | Bedeutung |
---|---|
%d, %i | int |
%u | unsigned int |
%c | char |
%s | String (char *) |
%f | float |
Formatierung von Float
%m.df
Symbol | Bedeutung |
---|---|
m | Minimale Länge des Outputs |
d | Anzahl dezimalstellen |
#include <stdio.h>
double value = 5.12345;
printf("%f", value); //5.123450 (dezimal standard=6)
printf("%.3f", value); //5.123
printf("%10.3f", value); // 5.123 (6 Leerzeichen)
Enums
enum Wochentage
{
Monday, // int wert ist 0
Tuesday, // int wert ist 1, nachfolgende sind vortlaufend
Wednesday,
Thursday = 13, // int wert ist 13
Friday, // int wert ist 14
Saturday,
Sunday
};
// Direkt auf Monday zugreifen, keine Angabe des Namespaces notwendig
Wochentag tag = Monday;
int dayOfWeek = tag; //
Strukturen / Datenklassen
struct Point3D
{
double x;
double y;
double z;
}
struct Point3D ptA = {1.0, 3.0, 4.0}; // Standard initialisierung
ptA.x = 5.0; // Zugriff
Mit Typedef
typedef struct
{
double x;
double y;
double z;
} Point3D;
Point3D ptA = {1.0, 3.0, 4.0}; // Standard initialisierung
ptA.x = 5.0; // Zugriff
Arrays werden als ANEINANDERLIEGENDE Blocks im Memory definiert.
Länge: Die Länge des Arrays MUSS zur Kompilierzeit bekannt sein. Index: Beginnt bei 0
const int ARRAY_LEN = 6;
int[ARRAY_LEN] array = { 0 }; // Alle Werte auf 0 setzem
Modifzieren des Arrays
const int ARRAY_LEN = 6;
int[ARRAY_LEN] array = { 0 }; // Alle Werte auf 0 setzem
array[2] = 6; // setzte 3. Element auf 6
// AUFPASSEN BEI DER INDEXIERUNG
// Es wird keine IndexOutOfRange Exception geworfen, wenn ausserhalbt des Blockes geschrieben wird
arrray[12] = 7;
array[-3] = 9;
Initialisierung eines Arrays
int array[] = { 1, 2, 3}; // alloziert einen Array der Länge 3
Länge eines Arrays
Bevorzug
const size_t ARRAY_LEN = 100;
int array[ARRAY_LEN] = { 0 };
for (size_t i =0; i < ARRAY_LEN; i++) {
array[i] = i;
}
„Dynamische“ Länge
int array[100] = { 0 };
// Nicht in einer Funktion möglich!!
size_t len = sizeof(array) / sizeof(array[0]);
for (size_t i =0; i < len; i++) {
array[i] = i;
}
Mehrdimensionale Arrays
Wie ein Schachbrett
int array[2][3] = {
{ 1, 2, 3 },
{ 4, 5, 7}
};
array[0][0] = 5; // Zugriff
Char Arrays und Strings
In Format: %s
String terminator: Ein char array wird immer mit \0 beendet, ansonsten ist es unmöglich die Länge zu bestimmen. -> Also Grösse immer um 1 erhöhen als gewünschte Länge
char hello[] = "hello, world"; // kann modifziert werden
/*
h
e
l
l
o
,
w
o
r
l
d
\0
*/
Es ist aber möglich, den String-Terminator zu entfernen, indem man folgendes macht.
char[12] hello = "Hello, World";
String Funktionen
#include <string.h>
char[] hello = "Hello world";
// String läng bestimmen
int laenge = strlen(hello);
// String vergleich
int result = stcmp(hello, "Hi");
// 0 = beide sind gleich
// > 0 wenn der erste string einen grösseren Ascii Wert hat als der zweite
// < 0 wenn der zweite string einen grösseren Ascii wert hat als der erste
String kopieren
Kopiert source in dest, egal ob die Länge passt. Rückgabewert ist ein Pointer zur destination
char* strcpy(char dest[], const char source[]);
Kopiert source in dest, mit der maximalen Länge num Rückgabewert ist ein Pointer zur destination
char* strncpy(char dest[], const char source[], size_t num);
Strings aneinanderhängen
Hängt eine Kopie von source an destination.
char * strcat ( char * destination, const char * source );
Hängt die ersten num Zeichen von source an destination.
char * strncat ( char * destination, const char * source, size_t num );
Wird benötigt um Aufgaben aufzutrennen, und zu vereinfachen
Header
Datei endet mit .h
// list.h
#ifndef __LIST_H__
#define __LIST_H__
// Diese Defines sind wichtig, da sonst der Header doppelt kompiliert werden kann. Macht auch Probleme mit Defines, Konstanten, statics und Methodendeklarationen
// Methode wird im Header deklariert
// so weiss jedes andere Modul, heyy in dieser Datei finde ich die deklaration
// ausprogrammiert wird diese aber in der source (.c) datei
void list_clear(void);
// Diese Variable ist GLOBAL in ALLEN Dateien, die list.h inkludieren vorhanden
static int iAmGlobal = 0;
#endif __LIST_H__
Können aber auch durch folgendes ersetzt werden
// list.h
#pragma once
Source
// list.s
#include "list.h"
// ist für jedes include separat definiert
int iAmSpecific = 3;
// ist für jedes include gleich, kann für Prozesskommunikation verwendet werden.
static int iAmProcessWide = 5;
void list_clear()
{
// globale variable modifizieren
iAmGlobal = 2;
// verändert die Dateispezifische Variable
iAmSpecific = 5;
}
Makefile
Du gucken da:
Pointers zeigen auf eine Adresse im Speicher.
int *p; // Pointer auf ein Objekt vom typ int
char *c[20]; // Array von 20 Pointern auf Objekte vom typ char
double (*d)[20]; // Ist ein Pointer, der af ein Array mit 20 Elementen zeigt
char **ppc; // Zeigt auf ein Objekt vom Typ Pointer
// das auf ein Objekt vom Typ char zeigt
int * p, q; // p ist pointer
// q ist KEIN pointer
Sizeof: Systemarchitektur abhängig (32 bit = 32/8 -> 4 byte)
Operatoren
int i;
int *p;
int *op;
p = &i; // p zeigt auf die Adresse von i
*p = 4; // modifiziert den Wert von i
// Zeigt auf die gleiche Adresse wie p zum ZEITPUNKT des setzens.
op = p;
*op += 5; // modifiziert Objket i
Pointer müssen immer auf etwas zeigen, sonst wird IRGENDEIN Speicher modifizert.
Der alles mögliche Pointer (Void pointer)
double d = 1.0;
// Void pointer, zeigt auf alles mögliche
void *vp = &d;
// Zuerst auf double pointer casten
// dann wert von double auslesen
printf("%f", * (double*)vp);
Konstante Pointers
Konstant = nicht mehr modifizierbar
int i = 15;
int * const p = &i;
// i kann modifizert werden
i = 16;
printf("%d", *p);
// p aber nicht mehr
p++; // DAS GEHT NICHT!!!
NULL Pointer
Hat die Adresse 0
#include <stdio.h>
int *p1 = 0; //blöde idee, vorallem nicht lesbar
int *p2 = NULL; // perfekt
Für strukturen
typedef struct {
int studNr;
char name[30];
char vorname[30];
} Student;
Student notPointer;
Student *pointer;
notPointer.studNr = 4;
strcpy(notPointer.name, "Mueller");
// Hey, ich zeige auf den notPointer
pointer = ¬Pointer;
printf("%s", (*pointer).name); // Mueller
Pointer Arithmetik
int a[5] = {2, 4, 6, 8, 10};
int *p = a;
// alle folgenden Zeilen sind identisch
a[3] = 1; // vom Compiler intern als *(a + 3) = 1 generiert
*(a + 3) = 1; // a steht für die Start Adresse von a
*(p + 3) = 1; // p ist auf die Start Adresse von a gesetzt
p[3] = 1; // vom Compiler intern als *(p + 3) = 1 generiert
Iterator
int a[5];
// a+5 ist die Adresse *hinter* das
// letzte Element des Arrays: C++ Stil
for(int *it = a; it != a+5; ++it) {
*it = 0;
}
oder
int array[5];
int *it = &array[0]; // zeigt auf das erste Element von a
int *end = &array[5]; // zeigt auf das letze Element von a
for(it; it != end; ++it) {
*it = 0;
}
String Literale
char * str = "I am a literal";
printf("%s", str);
printf("%c", str[0]);
Jagged Arrays
Eindimensionale Arrays von pointern.
char *jagged[] = {
"January",
"February",
"March",
"April",
"Mai",
"June",
"July",
"August",
"September",
"October",
"November",
"December" };
if (jagged[3][2] == 'r') {
...
}
// jagged[3] is “April”
// jagged[3][2] is the third character of April
POINTER WIRD ÜBERGEBEN
void swap (int *a, int *b)
{
int saved_a = *a; // Wert von a zwischenspeichern
*a = *b;
*b = saved_a; // WIHCTIG: Wert anpassen, und nicht Adresse
}
Read only
void print_array(const int *a, int n)
{
// const verhindert die Modifikation von a
}
Arrays übergeben
void print_array(int *a, int n)
{ }
ist gleich wie
void print_array(int a[], int n)
{ }
Structs übergeben
void do_something(struct *s)
{ }
void do_something(const struct *s)
{
// struct kann nicht überschrieben werden
}
Heap
Zur Laufzeit dynamisch Speicher vom Heap anfordern
// aus stdlib.h
// alloziert "size" Bytes vom Heap und gibt die Start Adresse zurück
void *malloc(size_t size);
// alloziert "nitems" mal "size" Bytes, setzt sie auf 0, und gibt die Adresse zurück void *calloc(size_t nitems, size_t size);
// vergrössert (oder verkleinert) einen vorgängig angeforderten Speicherbereich
void *realloc(void *ptr, size_t size);
// gibt einen oben angeforderten Speicherbereich frei
void free(void *ptr);
Beispiel
#include <stdlib.h>
typedef struct {
char firstName[30];
char lastName[30];
} Person;
Person* pointerToPerson = (Person*)malloc(sizeof(Person));
if (pointerToPerson == NULL) {
// Etwas ist schief gelaufen. Pointer kann NICHT verwendet werden!!
return;
}
// Wandlung in Person, dann firstname setzen
strcpy((*pointerToPerson).firstName, "firstname");
// Direkter Speicherzugriff
strcpy(pointerToPerson->lastName, "lastName");
printf("%s", pointerToPerson->firstName);
printf("%s", (*pointerToPerson).lastName);
//GANNZ WICHTIG
free(pointerToPerson); // Speicherplatz wieder frei geben
Probleme
Buffer Overflow
- z.B. Schreiben von 21 Zeichen langem String in 20 Zeichen langen Array,
Datei öffnen
Eine Datei kann in unterschiedliche Modis geöffnet werden.
Modus | Beschreibung | Datei nicht vorhanden |
---|---|---|
r | ASCII lesen | NULL |
rb | Binär lesen | NULL |
w | ASCII schreiben | Erstellt Datei |
wb | Binäres schreiben | Erstellt Datei |
a | Anfügen | Erstellt Datei |
ab | Binäres Anfügen | Erstellt Datei |
r+ | Lesen und schreiben | NULL |
rb+ | Lesen und schreiben in binär | NULL |
w+ | lesen und schreiben | Erstellt Datei |
wb+ | Lesen und schreiben in binär | Erstellt Datei |
ab+ | Anfügen und lesen in binär | Erstellt Datei |
#include <stdio.h>
#include <stdlib.h>
FILE *f = fopen("Pfad", "mode");
Datei lesen (line by line)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char * line = NULL;
size_t len = 0;
ssize_t read;
FILE *f = fopen("Pfad", "r");
if (f == NULL) { // das gleiche wie if (!f)
perror("Datei konnte nicht geöffnet werden!");
exit(-1);
}
while ((read = getline(&line, &len, f)) != -1) {
printf("Retrieved line of length %zu:\n", read);
printf("%s", line);
}
// Datei wieder schliessen
fclose(f);
if (line)
{
free (line);
}
return EXIT_SUCCESS;
}
Datei schreiben (by Charater)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char * line = NULL;
size_t len = 0;
ssize_t read;
FILE *f = fopen("Pfad", "w");
if (f == NULL) { // das gleiche wie if (!f)
perror("Datei konnte nicht geöffnet werden!");
exit(-1);
}
// Datei wieder schliessen
fclose(f);
if (line)
{
free (line);
}
return EXIT_SUCCESS;
}
Datei anfügen
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char * line = NULL;
size_t len = 0;
ssize_t read;
FILE *f = fopen("Pfad", "a");
if (f == NULL) { // das gleiche wie if (!f)
perror("Datei konnte nicht geöffnet werden!");
exit(-1);
}
// Datei wieder schliessen
fclose(f);
if (line)
{
free (line);
}
return EXIT_SUCCESS;
}
Multi-Tasking
Task: Eine Aufgabe die von der CPU abgearbeitet wird Batch-Ausführen: Führ die anstehenden Tasks nacheinander durch. Multi-Tasking: Führt die Prozesse gleichzeitig / nebeneinander aus
Scheduling
Kooperativ: Jeder Task entschieden, wann sie die Kontrolle an einen anderen Task abgibt. Präemptiv: Kontrollübergabe an den nächsten Task wird erzwungen Scheduler
- Unterbricht präemtiv
- entscheidet welche Tasks dran sind
- Round robin (im Kreis herum)
- Priority driven
Fork
Nach dem fork existiert der Kindprozess als Kopie des original Prozesses.
- Bisherige Variablen werden also exakt kopiert
- Somit sind auch offene Files dann „doppelt“ vorhanden. Also immer aufpassen
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)
int main()
{
// Erstelle neuen Child prozess
// setzt DANACH die childId
pid_t childId = fork();
if (childId == -1)
{
PERROR_AND_EXIT("fork");
}
if (childId > 0)
{
// ChildId ist gesetzt, also sind wir im Parent
printf("Parent: %d forked child %d\n", getpid(), childId);
int wstatus;
// Jetzt warten wir, bis der Prozess des Kindes
// fertig ist, 0 -> kein timeout also unendlich lange
pid_t wpid = waitpid(childId, &wstatus, 0);
printf("Child exited with status: %d \n", WEXITSTATUS(wstatus));
// jetzt beenden wir den Parent mit Success
exit(EXIT_SUCCESS);
}
else
{
// Da childId noch nicht gesetzt ist, zum Zeitpunkt des forks
// Wissen wir, dass wir im child sind
printf("Child: %d forked by parent %d\n", getpid(), getppid());
sleep(3);
exit(123);
}
}
// Parent erstellt neues Kind
// Kind wird ausgeführt, blockiert parent bis abgeschlossen (exit)
// Parent wird abgeschlossen (exit)
Exec
Ersetzt den Childprozess durch ein anders ausführbares Programm.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)
int main()
{
// Erstelle neuen Child prozess
// setzt DANACH die childId
pid_t childId = fork(); // Erstelle neues Kind
if (childId == -1)
{
PERROR_AND_EXIT("fork");
}
if (childId > 0)
{
// ChildId ist gesetzt, also sind wir im Parent
printf("Parent: %d forked child %d\n", getpid(), childId);
int wstatus;
// Jetzt warten wir, bis der Prozess des Kindes
// fertig ist, 0 -> kein timeout also unendlich lange
pid_t wpid = waitpid(childId, &wstatus, 0);
printf("Child exited with status: %d \n", WEXITSTATUS(wstatus));
// jetzt beenden wir den Parent mit Success
exit(EXIT_SUCCESS);
}
else
{
// Da childId noch nicht gesetzt ist, zum Zeitpunkt des forks
// Wissen wir, dass wir im child sind
// Wir definieren die Befehle einzel
// in diesem fall „ls -l“
static char *eargv[] = { "ls", "-l", NULL };
if (execv("/bin/ls", eargv) == -1)
{
PERROR_AND_EXIT("execv: /bin/ls");
}
// Das exec denn Prozess ersetzt, wird diese Zeile nie getroffen
}
}
System
- Spaltet einen Prozess ab fork())
- startet die System-Shell, führt in dieser Shell das angegebene Kommando aus
- Wartet blockierend auf das Terminieren des Prozesses (waitpid())
WEXITSTATUS
Extrahiert den Exitcode aus dem zurückgegebenen Status des system() Befehles
int ret = system("/bin/ls -l");
POPEN
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)
const static int BUFSIZE = 100;
int main()
{
// 2 = error pipe in null schreiben, also entsorgen
FILE *df = popen("df -k --output=pcent . 2>/dev/null", "r");
if (!df) {
PERROR_AND_EXIT("popen: df -k .");
}
char line[BUFSIZE];
char *end = NULL;
long int used = -1;
while (fgets(line, BUFSIZE, df)) {
used = strtol(line, &end, 10);
if (end && end != line && *end == '%') {
break;
}
used = -1;
}
if (pclose(df)) {
PERROR_AND_EXIT("failed with pclose()");
}
if (used < 0 || used > 100) {
errno = ERANGE;
PERROR_AND_EXIT("df -k .");
}
char *msg
= used < 60 ? "Plenty of disk space (%d%% available) \n"
: used < 80 ? "Maybe some future disk space problems (%d%% available) \n"
: used < 90 ? "Need to clear out files (%d%% available) \n"
: "You may face soon some severe disk space problems (%d%% available) \n";
printf(msg, 100-used);
return EXIT_SUCCESS;
}
SO BAUEN
gcc -pthread t08/popen.c
Threads
Da das OS die Threads selber verwaltet, kann es sein, dass die Ausgabe auf der Konsole sich verändert. Wenn jetzt z.B. der Child Prozess eine höhere Priorität hat, dann ist es möglich, dass dieser VOR dem Main prozess endet.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define PERROR_AND_EXIT(M) do { perror(M); exit(EXIT_FAILURE); } while(0)
// Definiere den worker thread, diese Funktion
// wird im thread später ausgeführt
void *worker(void *arg)
{
printf("worker\n");
sleep(3);
static int ret_value = 123;
return &ret_value;
}
int main()
{
pthread_t thread;
// erstelle neuen thread
// thread -> speichere diesen in die thread adresse
// NULL -> pthread_attr_t: erlaubt also einige Attribute zu setzen
// worker -> welche Funktion soll ausgefüht werden
// NULL -> argument zur Funktion, da kann auch alles mögliche drin stehen
int status = pthread_create(&thread, NULL, worker, NULL);
if (status != 0)
{
// Thread konnte nicht erstellt werden
exit (status);
}
printf("main\n");
static void *retVal;
status = pthread_join(thread, &retVal);
if (status != 0)
{
// Thread konnte nicht beigetreten werden,
// es ist etwas schief gelaufen
exit (status);
}
printf("worker retval = %d\n", *((int*)retVal));
exit (EXIT_SUCCESS);
}
DU SO BAUEN
gcc -pthread t08/threads.c
Loslassen eines Threads (Detach)
Funktion: pthread_detach() Gibt an, dass sobald der Thread beendet wird, die Ressourcen freigegeben werden. Normalerweise passiert dies erst beim pthread_join.
-> Es darf NICHT mit pthread_join auf den Prozess gewartet werden, da dieser bereits weg sein kann.
Terminierung eines Threads
- return: Standardmässig, der Rückgabewert wird bei pthread_join zurückgegeben.
- pthread_exit: so ist es möglich, den thread zu beenden. Der Rückgabewert wird bei pthread_join gelesen.
- exit beendet den übergeordneten Prozess
- pthread_cancel: Wird verwendet um den Prozess zu beenden.
Kommunikation zwischen zwei Prozessen
POSIX Signale
Diese Signale können jedem Prozess gesendet werden. Somit sind die nachfolgenden Signale universal Anwendbar.
Mit htop können alle Prozesse unter Linux angesehen werden. Dieser dient als zudem als Task-Manager und erlaubt es Prozessen ein bestimmtes POSIX Signal zu senden.
htop
Signal | Default Aktion | Beschreibung |
---|---|---|
SIGINT | Terminiert | Interrupt Signal von der Tastatur (Ctrl-C) |
SIGQUIT | Code Dump | Quit-Signal von der Tastatur (Ctrl-\) |
SIGABRT | Code Dump | Abort Signal via abort() oder assert() |
SIGKILL | Terminiert | Kill-Signal |
SIGSEGV | Code Dump | Unzulässiger Speicherzugriff |
SIGALRM | Terminiert | Timer-Signal durch alarm() ausgelöst |
SIGTERM | Terminiert | Terminierungs-Signal |
SIGSTOP | Stoppt den prozess | Stoppt den Prozess (oder ignoriert falls gestoppt) |
SIGCONT | Reaktiviert den Prozess | Reaktiviert den Prozess (oder ignoriert falls am laufen) |
Beenden des Prozesses
#include <sys/types.h>
#include <signal.h>
// sag dem Childprozess, dass es normal (gracefully) beenden soll
// speichert alles und beendet dann
int status = kill(child_pid, SIGTERM);
if (status != 0)
{
// Prozess konnte nicht beendet werden
}
Signale verarbeiten
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static pid_t start_child(int wait_for_signal)
{
pid_t cpid = fork();
if (cpid == -1)
{
perror ("Fehler beim fork");
exit(EXIT_FAILURE);
}
if (cpid > 0)
{
// Der Parent gibt die child id zurück
// child hat cpid von 0
return cpid;
}
// Nur der Child Prozess kommt soweit
if (wait_for_signal)
{
// Nur der child 2 kommt soweit
if (pause() == -1)
{
perror("Fehler bei pause()");
exit(EXIT_FAILURE);
}
}
exit (123);
}
static void wait_for_child()
{
int wsts;
// bllockiere bis ein Child Prozess beendet ist
pid_t wpid = wait(&wsts);
if (wpid == -1)
{
perror("Fehler in wait");
exit(EXIT_FAILURE);
}
if (WIFEXITED(wsts))
{
printf("Child %d: exit=%d (status=0x%04X)\n", wpid, WEXITSTATUS(wsts), wsts);
}
if (WIFSIGNALED(wsts))
{
printf("Child %d: signal=%d (status=0x%04X)\n", wpid, WTERMSIG(wsts), wsts);
}
}
int main()
{
pid_t cpid1 = start_child(0); // beendet mit exit code
pid_t cpid2 = start_child(1); // beendet mit signal
sleep(1);
// sag dem Prozess2 dass er sich beenden soll
if (kill(cpid2, SIGTERM) == -1)
{
perror("Prozess 2 konnte nicht beendet werden");
exit(EXIT_FAILURE);
}
wait_for_child();
wait_for_child();
}
POSIX PIPE
- Dient als FIFO (First In First Out) Buffer von fixer Maximalgrösse.
- Unidirektional, von einer zu der anderen Seite.
Mit dem Pipe befehl wird eine Unidirektionale Verbindung erstellt.
int fd[2];
pipe(fd);
// Lese Seite der Pipe
fd[0];
// Schreibseite der Pipe
fd[1];
// schliessen der Read Pipe
close(fd[0]);
// Daten auf Schreib Pipe senden
// 12: länge des zu sendenden strings
write(fd[1], "Hello world\n", 12);
// Lesen von Read Pipe
// Geschieht erst, nachdem alle Kopien
// Des Write Buffers geschlossen worden sind
// Somit ist dies: BLOCKING
read(fd[0], buffer, BUFFER_SIZE);
Anonymous Pipe
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
const int BUFFER_SIZE = 100;
void main (void)
{
int fd[2];
char buffer[BUFFER_SIZE];
ssize_t nBytes;
int status;
status = pipe(fd);
if (status == -1)
{
perror("Pipe konnte nicht erstellt werden");
exit(EXIT_FAILURE);
}
pid_t child_id = fork();
if (child_id == -1)
{
perror("Child Prozess konnte nicht erstellt werden");
exit(EXIT_FAILURE);
}
if (child_id == 0)
{
// Wir sind in Child Prozess
// Da wir nichts mit der Schreibpipe zu tun haben,
// können wir diese schliessen
close(fd[1]);
// Lese Daten von readpipe in buffer
// Maximale Länge BUFFER_SIZE
nBytes = read(fd[0], buffer, BUFFER_SIZE);
// Wir haben unser Zeugs mit der readpipe gemacht,
// jetzt können wir die pipe schliessen
close(fd[0]);
exit(EXIT_SUCCESS);
}
if (child_id != 0)
{
// Sind in Parent Prozess
// Dieses if ist eigentlich nicht mehr notwendig
// Wir wollen nichts lesen,
// daher können wir in diesem Prozess die Pipe zumachen
close(fd[0]);
// Jetzt schreiben wir unsere Daten auf
// die Lesepipe
write (fd[1], "Hello world\n", 12);
// Schliessen der Pipe
close(fd[1]);
exit(EXIT_SUCCESS);
}
}
Non Blocking Version
In machen Fällen ist es notwendig, dass wir die Lesevorgänge der Pipe nicht blockierend machen.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
const static int BUFFER_SIZE = 100;
void set_nonblocking(int fd)
{
// Lese alle bisher gesetzten Flags
// Wir wollen nur das nonblocking flag setzen
int flags = fcntl (fd, F_GETFL, 0);
if (flags == -1)
{
perror("Pipe Attribute konnten nicht gelesen werden");
exit(EXIT_FAILURE);
}
int result = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if (result == -1)
{
perror("Pipe Attribute konnten nicht gesetzt werden");
exit(EXIT_FAILURE);
}
}
int main (void)
{
char buffer[BUFFER_SIZE];
int fd[2];
if (pipe(fd))
{
perror("Pipe konnte nicht erstellt werden");
exit(EXIT_FAILURE);
}
// pipe auf non blocking setzen
set_nonblocking(fd[0]);
// HOWTO LESEN
int reading = 1;
while (reading)
{
int n = read(fd[0], buffer, BUFFER_SIZE);
if (n == 0)
{
// polling aufhören
// da pipe geschlossen worden ist
close(fd[0]);
reading = 0;
continue;
}
if (n > 0)
{
// gelesene Daten verwenden
}
else if (n = EAGAIN)
{
// poll interval abwarten
// Es kann passieren, dass wir den Intervall so kurz halten
// dass wir das Lock der pipe nicht wirklich frei lassen
// dies wird als spin-lock bezeichnet
}
else
{
// Da haben wir verkackt
// Ernsthafter fehler
}
}
}
Named Pipes
Named pipes erlauben es, dass zwei unterschiedliche (sich nicht kennende) Prozesse eine Pipeverbindung aufbauen.
mkfifo pfad -m Mode
#include <sys/types.h>
#include <sys/stat.h>
int fd = mkfifo("Pfad", mode_t modus);
// Jetzt kannst du diesen Buffer
// wie in FileIO mit
// - fread
// - fwrite
// - fclose
// verwenden
POSIX Message Queue
Maximale Kapazität von NxM Bytes - N: Maximale Anzahl Messages - M: Maximale Grösse einer Message (Bytes)
Mehrere Leser und Schreiber
- Jede Meldung wird beim lesen aus der Queue entfernt
Der name der Queue zeigt auf eine Datei im Filesystem. Daher sollte diese auch mit einem „/„ anfangen, um immer die gleiche Queue zu verwenden.
// Öffnen einer Queue
// soll diese für alle gleich sein, dann / vorne anfügen,
// ansonsten ist die pipe relativ dem aktuellen Verzeichniss
mqd_t mqdes = mq_open ("/sideshow-bob", O_RDWR | O_CREAT,
0664, NULL);
// Öffnen einer Queue mit Attributen
Nachfolgendes Beispiel ist von hier
Publisher
/*
* Example used in following article:
*
* Implementace front zpráv podle normy POSIX
* https://www.root.cz/clanky/implementace-front-zprav-podle-normy-posix/
*/
#include <stdio.h>
#include <string.h>
#include <mqueue.h>
#define QUEUE_NAME "/queue1"
int main(void)
{
mqd_t message_queue_id;
unsigned int priority = 0;
char message_text[100];
int status;
message_queue_id = mq_open(QUEUE_NAME, O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG, NULL);
if (message_queue_id == -1) {
perror("Unable to create queue");
return 2;
}
strcpy(message_text, "Hello world!");
status = mq_send(message_queue_id, message_text, strlen(message_text)+1, priority);
if (status == -1) {
perror("Unable to send message");
return 2;
}
status = mq_close(message_queue_id);
if (status == -1) {
perror("Unable to close message queue");
return 2;
}
return 0;
}
Subscriber
/*
* Example used in following article:
*
* Implementace front zpráv podle normy POSIX
* https://www.root.cz/clanky/implementace-front-zprav-podle-normy-posix/
*/
#include <stdio.h>
#include <string.h>
#include <mqueue.h>
#define QUEUE_NAME "/queue1"
int main(void)
{
mqd_t message_queue_id;
char message_text[10000];
unsigned int sender;
int status;
message_queue_id = mq_open(QUEUE_NAME, O_RDWR);
if (message_queue_id == -1) {
perror("Unable to open queue");
return 2;
}
status = mq_receive(message_queue_id, message_text, sizeof(message_text), &sender);
if (status == -1) {
perror("Unable to receive message");
return 2;
}
printf("Received message (%d bytes) from %d: %s\n", status, sender, message_text);
status = mq_close(message_queue_id);
if (status == -1) {
perror("Unable to close message queue");
return 2;
}
return 0;
}
POSIX Queue
#include <mqueue.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#define PERROR_AND_WAIT(M) do { perror(M); exit(EXIT_FAILURE);} while(0)
// Zeigt auf das root verzeichnis
#define QNAME "/demo"
#define MSIZE 10
int main(void)
{
int q = 0;
int cpid = 0;
int n = 0;
int wpid = 0;
struct mq_attr a = {
.mq_maxmsg = 10,
.mq_msgsize = MSIZE
};
if ((q = mq_open(QNAME, O_CREAT | O_RDWR | O_NONBLOCK | O_EXCL, 0666, &a)) == -1) {
PERROR_AND_WAIT("mq_open");
}
if (cpid > 0) {
//parent: shares queue descriptor with child
if (mq_unlink(QNAME) == -1) {
PERROR_AND_WAIT("mq_unlink");
}
char msg[MSIZE +1];
while (wpid == 0) {
sleep(1);
while ((n = mq_receive(q, msg, MSIZE, NULL)) > 0) {
msg[n] = '\0';
printf("Message: '%s'\n", msg);
}
if (n == -1 && errno != EAGAIN) {
PERROR_AND_WAIT("mq_receive");
}
if ((wpid = waitpid(cpid, NULL, WNOHANG)) == -1) {
PERROR_AND_WAIT("waitpid");
}
}
if (mq_close(q) == -1) {
PERROR_AND_WAIT("mq_close");
}
} else
{
if (mq_send(q, "Hello", sizeof("hello"), 1) == -1) {
PERROR_AND_WAIT("mq_send");
}
sleep(2);
if (mq_send(q, "Hello", sizeof("Queue"), 1) == -1) {
PERROR_AND_WAIT("Queue");
}
}
}
gcc -lrt t09/posix_queue.c
POSIX Socket
Erlaubt Kommunikation über Netzwerk, oder über virtuelles LAN in einem System.
Verbindungsorientiert
Client
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define RECEIVE_TIMEOUT 0
#define SEND_TIMEOUT 0
int sockfd;
void connect_server (void)
{
struct addrinfo hints, *server_info, *p;
// alles auf 0 setzen
memset(&hints, 0, sizeof(hints));
hints.ai_familiy = AF_INET;
hints.ai_socktype = SOCK_STREAM;
// Hole Infos zu Socket auf
// localhost und port 80
int status = getaddrinfo("localhost", "80", &hints, &server_info);
if (status != 0) {
perror("getaddrinfo konnte nicht ausgeführt werden");
exit(EXIT_FAILURE);
}
for (p = server_info; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror ("socket error");
exit(EXIT_FAILURE);
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror ("Es konnte keine Verbinung hergestellt werden");
exit(EXIT_FAILURE);
}
// wenn erfolgreich verbunden, aus der Schleife ausbrechen
break;
}
if (p == NULL) {
perror ("Es konnte keine Verbinung hergestellt werden");
exit(EXIT_FAILURE);
}
// SOCKET VERBUNDEN
}
void write(char * buffer, int len)
{
int totalSent = 0;
while (totalSent < len) {
int bytesSent = send(
communicationSocket,
buffer + totalSent,
len - totalSent,
SEND_TIMEOUT);
if (bytesSent == -1) {
perror ("Error in send");
exit(EXIT_FAILURE);
}
totalSent += bytesSent;
}
}
int read(char *buffer, int len)
{
int totalReceived = 0;
while (totalReceived < len) {
int bytesReceived = recv(
communicationSocket,
buffer + totalReceived,
len - totalReceived,
RECEIVE_TIMEOUT);
if (bytesReceived == -1) {
perror("Error in Receive");
exit(EXIT_FAILURE);
}
totalReceived += bytesReceived;
}
return totalReceived;
}
void main (void)
{
connect_server();
write ("Hello World", 11);
char bufffer[1000];
int read = read(buffer, 1000);
close(sockf);
}
Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#define RECEIVE_TIMEOUT 0
#define SEND_TIMEOUT 0
#define MAX_QUEUE 1
void server_init(){
// BEGIN-STUDENTS-TO-ADD-CODE
struct addrinfo hints, *server_info, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int status = getaddrinfo(NULL,"80" , &hints, &server_info);
if(status != 0){
ExitOnError(status, "getaddrinfo");
}
int sockfd;
for (p = server_info; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("socket error");
exit(EXIT_FAILURE);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("connect error");
exit(EXIT_FAILURE);
}
break; // wenn erfolgreich verbunden, aus der Schleife ausbrechen
}
ListeningSocket = sockfd;
listen(ListeningSocket, MAX_QUEUE);
// END-STUDENTS-TO-ADD-CODE
}
int getRequest(char* requestBuffer, int max_len)
{
// BEGIN-STUDENTS-TO-ADD-CODE
int bytesReceived = recv(
connectedSocket,
requestBuffer,
max_len -1,
RECEIVE_TIMEOUT);
requestBuffer[bytesReceived] = '\0';
return bytesReceived;
// END-STUDENTS-TO-ADD-CODE
}
void sendResponse(char* response, int resp_len)
{
// BEGIN-STUDENTS-TO-ADD-CODE
send(connectedSocket, response, resp_len, 0);
// END-STUDENTS-TO-ADD-CODE
}
void server_close_connection(void)
{
// BEGIN-STUDENTS-TO-ADD-CODE
close(connectedSocket);
// END-STUDENTS-TO-ADD-CODE
}
int wait_client(void) {
connectedSocket = accept(ListeningSocket, 0, 0);
return connectedSocket;
}
void main (void)
{
server_init();
wait_client();
char buffer[1000];
getRequest(buffer, 1000);
sendResponse("Hello World", 11);
server_close_connection();
}
Verbindungslos
Client/Server Architektur • Client
- Socketdefinition (getaddrinfo(), socket())
- keine Verbindungsanfrage
- bidirektionales Lesen/Schreiben (recfrom(), sendto())
- danach Socket schliessen (close()) • Server
- Socketdefinition (getaddrinfo(), socket(), bind())
- kein Abwarten und Akzeptieren von Verbindungsanfragen
- bidirektionales Lesen/Schreiben (recfrom(), sendto())
- danach eventuell Socket schliessen (close())
Sync / Semaphores und Signals werden verwendet um Zugriff auf geteilte Ressourcen zu koordinieren. Zudem können damit Threads aufeinander warten.
Syncs werden notwendig, wenn ein Thread auf einen anderen warten muss.
- z.B. wenn eine Resource im einten geöffnet ist, dann darf der andere diese erst öffnen, sobald der erste durch ist.
- Oder in einer kritischen Funktion, die IMMER ganz durchlaufen werden muss.
// Mit dem volatile keyword wird klargestellt,
// dass diese Resource von mehreren Threads verwendet wird.
volatile int iAmShared = 0;
Mutex - Mutual Exclusion
- Resourcen werden gegenseitig ausgeschlossen, ein Task blockiert den Eintritt für andere Tasks in die Crititcal Section
Probleme
- Runtime Kosten (locks dauern)
- Rekursion
- Kein Unlock (Bei early return)
- Deadlock
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
const int N = 100000000;
// shared value
volatile int value = 0;
pthread_mutex_t mutex;
void * count(void * arg)
{
int delta = *(int*) arg;
for (int i = 0; i < N; i++)
{
if (pthread_mutex_lock(&mutex) != 0)
{
perror("Mutex konnte nicht besetzt werden");
exit(EXIT_FAILURE);
}
// Gemeinsame Variable auslesen
int a = value;
// Modifiziern
a += delta;
// Variable wieder setzen
value = a;
// Mutex wieder freigeben
if (pthread_mutex_unlock(&mutex) != 0)
{
perror("Mutex konnte nicht freigegeben werden");
exit(EXIT_FAILURE);
}
}
return NULL;
}
int main (void)
{
if (pthread_mutex_init(&mutex, NULL) != 0)
{
perror("Mutex konnte nicht initialisiert werden");
exit(EXIT_FAILURE);
}
pthread_t th_inc;
pthread_t th_dec;
int inc = +1;
int dec = -1;
if (pthread_create(&th_inc, NULL, count, &inc) != 0)
{
perror("Thread konnte nicht erstellt werden");
exit(EXIT_FAILURE);
}
if (pthread_create(&th_dec, NULL, count, &dec) != 0)
{
perror("Thread konnte nicht erstellt werden");
exit(EXIT_FAILURE);
}
// noch auf threads waretn
if (pthread_join(th_inc, NULL) != 0)
{
perror("Thread konnte nicht beendet werden");
exit(EXIT_FAILURE);
}
if (pthread_join(th_dec, NULL) != 0)
{
perror("Thread konnte nicht beendet werden");
exit(EXIT_FAILURE);
}
}
gcc -pthread t10/critical_section.c
Semaphore
Tasks können über Semaphoren Synchronisationspunkte vereinbaren
Probleme
- Synchronisation kostet Zeit
- Wenn ein wait zu viel, bzwh. ein signal zu wenig
- Deadlocks, System kann komplett blockiert werden
// C program to demonstrate working of Semaphores
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t mutex;
void* thread(void* arg)
{
//wait
sem_wait(&mutex);
printf("\nEntered..\n");
//critical section
sleep(4);
//signal
printf("\nJust Exiting...\n");
sem_post(&mutex);
}
int main()
{
// 1 -> Initial value
// unbedingt auf das achten
sem_init(&mutex, 0, 1);
pthread_t t1,t2;
pthread_create(&t1,NULL,thread,NULL);
sleep(2);
pthread_create(&t2,NULL,thread,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
sem_destroy(&mutex);
return 0;
}
gcc -pthread t10/semaphore.c
KT
Anforderungsanalyse
## Usability und User Experience (UX)
Usability
Wie einfach kann eine SW-Applikation benutzt werden.
Aspekte:
- Benutzer
- Seine Ziele / Aufgaben
- Sine Kontext
- Softwaresystem (inkl. UI)
### Effektivität
- Der Benutzer kann all seine Aufgaben vollständig erfüllen
- Mit der gewünschten Genauigkeit
Effizienz
-
Der Benutzer kann seine Aufgaben mit minimalem / angemessemen Aufwand erledigen
- Mental
- Physisch
- Zeit
Zufriedenheit
-
Mit dem System / der Interaktion
- Minimum: Benutzer ist nicht verärgert
- Normal: Benutzer ist zufrieden
- Optimal: Benutzer ist erfreut
Kontextszenario
Das Kontextszenario beschreibt, wie das Produkt effektiv verwendet wird. Es werden keine technischen Anforderungen beschrieben.
Persona
Das Persona beschreibt die Person, die die Anwendung verwendet. Dies besteht aus:
- Name
- Wer diese Person ist, und was sie macht
- Was sie sich vom Produkt erhofft
Szenario
Das Szenario beschreibt, wie die Persona das Produkt verwendet. Dabei wird die Verwendung verallgemeinert und Absätze beschreiben die Prozesse. Der Aufbau ist wie folgt:
- Vorbedingungen, was also gegeben sein muss.
- Wie der Anwender das Produkt verwendet, aufgeteilt in einzelne Abschnitte. Auch Interaktionen mit externen Anwendersystemen werden aufgezeigt.
Use Cases
Ein Use Case beschreibt einen einzelnen Prozess des Kontextszenarios. Im Gegensatz zu Scrum, hat ein Use Case nichts mit der Technik zu tun, sonder beschreibt jediglich die funktionalen Anforderungen (also was der Anwender sieht).
Anwendungsfalldiagramm (Use-Case Diagramm)
Beschreibt, wer (Systeme & Personas) mit den Anwendungsfällen interagiert. Jeder Use Case muss mit einer Nummer eindeutig identifizierbar sein.
# Fully dressed Use Case
- Name des Use Cases und Use Case Nummer zwecks eindeutiger Identifizierung
- Akteure
- Auslöser
- Kurzbeschreibung
- Beschreibung der essentiellen Schritte als Standardablauf
- Beschreibung von alternativen Abfolgen
- Vorbedingungen und Nachbedingungen
- Systemgrenzen
Das Domänenmodell dient als Überblick der gesamten Anwendung. Dabei werden die Teile des Systems in unterschiedliche Blöcke getrennt. Diese Blöcke besitzen Attribute, die IMMER einen Bezug zur Welt des Anwenders besitzen, also keine ids, etc.
Die Verbindungen zwischen diesen Blöcken sind beschreibend, also Verben. Das Modell wird als UML Klassendiagramm in einer vereinfachten Form gezeichnet.
Beziehungen
Komposition
Ein Objekt kann ohne das andere (Parent) nicht existieren. Wird das Parent gelöscht, so stirbt auch das Kind. Ein Parent kann mehrere Kinder besitzen. (1 -> *)
Aggregation
Es besteht eine hat Beziehung. Ein Parent kann mehrere Kinder besitzen (1 -> *)
Generalisierung / Spezialisierung
Eine Zusammenhang wird genauer definiert. Somit hat ein Typ nur noch die Funktionen, die NUR ihm zugeordnet werden können. Die isA Regel, sowie die 100% Regel darf nicht verletzt werden.
isA: Ist ein 100%: Eine Spezialisierung darf nicht etwas anderes können, als die Kasse selber es könnte.
Vorgehen
Die Vorgehensweise eines Kartografens wird angewendet, also zuerst das Grobe bestimmen / identifizieren und danach mit weiteren Details ausschmücken. Zudem wird unwichtiges weggelassen. Es wird NUR vorhandenes hinzugefügt.
- Konzepte identifizieren
- Konzepte mit Attributen versehen
- Konzepte in Verbindung zueinander setzen
Attribute
Datentypen
Die meisten Attributtypen sind einfach (primitiv)
- Integer, float, boolean
- Werden im DB normalerweise nicht angegeben
Konzepte
Sind in einer Klasse Attribute vorhanden, die normalerweise zu einem eigenen Typen gehören, oder sind diese mehrfach verwendet, so werden diese in Konzepte ausgelagert.
Anwendung:
- Der Type besteht aus mehreren Abschnitten (Adresse)
- Operationen darauf möglich sind, z.B. Validierung einer Kreditkartennummer
- Der Typ selber noch Attribute hat, z.B. ein Verkaufspreis der ein Anfangs- und Enddatum hat
- Der Typ mit einer Einheit verknüpft ist, z.B. ein Preis mit einer Währung
Anti-Pattern
- Assoziationen anstelle von Attributen verwenden um Konzepte in Beziehung zueinander zu setzen.
- Keine Software Klassen im Domänenmodell, die es nicht in der Fachdomäne gibt. z.B. print(), toString(), …
Beschreibungsklasse
Attribute, die für alle Artikel eines Typs gleich sind, werden in eine eigene Klasse herausgezogen.
Logische Architektur
Auftrennung in Module
ISO 25010
Diese ISO Norm beschreibt, wie eine Software zu analysieren ist. Functional Suitability
- Appropriateness
- Accuracy
- Compliance
Reliability
- Availability
- Fault tolerance
- Recoverability
- Compliance
Performance efficiency
- Timebehaviour
- Resourceutilisation
- Compliance
Operation (Useability)
- Appropriatenessrecogniseability
- Leranability
- Ease-of-use
- Helpfulness
- Attractiveness
- Technicalaccessibility
- Compliance
Security
- Confidentiality
- Integrity
- Non-repudiation
- Accountability
- Authenticity
- Compliance
Maintainability
- Modularity
- Reusability
- Analyzability
- Changeability
- Modification stability
- Testability
- Compliance
Transferability
- Portability
- Adaptability
- Installability
- Compliance
Schnittstellen (Interfaces)
Exportierte Schnittstellen
- Definieren angebotene Funktionalität
- Sind im Sinne eines Vertrags garantiert
- Definieren wie ein Modul verwendet werden muss
- Modul kann intern beliebig verändert werden, solange Schnittstellen gleich bleiben
Importierte Schnittstellen
- Verwendet ein Modul andere Module, so importiert sie deren Schnittstellen
- Einzige Kopplung zwischen Modulen
Kapselung und Austaschbarkeit
- Der Bausteil kapselt die Implementierung dieser Schnittstellen ab. Somit ist die Implementation unsichtbar für die Aussenwelt
- Daher kann dieser durch andere Bausteine problemlos ersetzt werden
Güte einer Modularisierung
Kohäsion
- Ein Mass für die Stärke des inneren Zusamenhangs
- Je höher die Kohäsion innerhalb eines Moduls, desto besser die Modularisierung
- schlecht: zufällig, zeitlich
- gut: funktional, objektbezogen
Kopplung
- Ein Mass für die Abhängigket zwischen Modulen
- Je geringer die wechselseitige Kopplung zwischen den Modulen, desto besser die Modularisierung
- schlechte: Globale Kopplung (Globale Daten)
- akzeptabel: Datenbereichskopplung: (Referenz auf gemeinsame Daten)
- gut: Datenkopplung (alle Daten werden beim Aufruf der Schnittstelle übergeben)
Clean Architecture
Entities
Kapseln die Business Rules gültig für das gesamte Unternehmen
Use Cases
Beinhalten die Business Rules einer Anwendung, orchestriert die Verwendung der Entities
Interface Adapters
Adapter für die Konvertierung von Daten aus Datenbank oder web
Frameworks and Drivers
![[Pasted image 20230602171312.png]]
N+1 View Model
Logical View
- Welche Funktionalität bietet das System gegen aussen an?
- Wichtige Aspekte: Schichten, Subsysteme, Pakete, Frameworks, Klassen, Interfaces
- UML: Systemsequenzdiagramme, Interaktionsdiagramme, Klassendiagramm, Zustandsdiagramme
Process View
- Welche Prozesse laufen wo und wie ab im System?
- Wichtige Aspekte: Prozesse, Threads, Wie werden Anforderungen wie Performance und Stabilität erreicht?
- UML: Klassendiagramme, Interaktionsdiagramme, Aktivitätsdiagramme
Development View (Implementation View):
- Wie wurde die logische Struktur (Layer, Schichten, Komponenten) umgesetzt?
- Wichtige Aspekte: Source Code, Executables, Artefakte
- UML: Paketdiagramme, Komponentendiagramme
Physical View (Deployment View):
- Auf welcher Infrastruktur wird ein System ausgeliefert/betrieben?
- Wichtige Aspekte: Prozessknoten, Netzwerke, Protokolle
- UML: Deployment Diagram
Scenarios (Use Cases)
- Welches sind die wichtigsten Use-Cases und ihre nichtfunktionalen Anforderungen? Wie wurden sie umgesetzt?
- Wichtige Aspekte: Architektonisch wichtige Ucs, deren nichtfunktionale Anforderungen und deren Implementation
- UML: UC-Diagramm, Systemsequenzdiagramme, UC- Realisierungen
Paketdiagramme
Design Patterns
Creational Patterns
Abstract Factory
Creating multiple objects with variations.
public interface Shape {
void draw();
}
public class RoundedRectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside RoundedRectangle::draw() method.");
}
}
public class RoundedSquare implements Shape {
@Override
public void draw() {
System.out.println("Inside RoundedSquare::draw() method.");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
// Abstract factory
public abstract class AbstractFactory {
abstract Shape getShape(String shapeType) ;
}
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
public class RoundedShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new RoundedRectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new RoundedSquare();
}
return null;
}
}
Builder
Objekte werden Schritt für Schritt zusammengebaut. Der Konstruktor baut die "Grundstruktur" und für jeden weiteren Schritt ist eine Methode vorhanden.
public class Computer {
//required parameters
private String HDD;
private String RAM;
//optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
public String getHDD() {
return HDD;
}
public String getRAM() {
return RAM;
}
public boolean isGraphicsCardEnabled() {
return isGraphicsCardEnabled;
}
public boolean isBluetoothEnabled() {
return isBluetoothEnabled;
}
private Computer(ComputerBuilder builder) {
this.HDD=builder.HDD;
this.RAM=builder.RAM;
this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
this.isBluetoothEnabled=builder.isBluetoothEnabled;
}
//Builder Class
public static class ComputerBuilder{
// required parameters
private String HDD;
private String RAM;
// optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
public ComputerBuilder(String hdd, String ram){
this.HDD=hdd;
this.RAM=ram;
}
public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
this.isGraphicsCardEnabled = isGraphicsCardEnabled;
return this;
}
public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
this.isBluetoothEnabled = isBluetoothEnabled;
return this;
}
public Computer build(){
return new Computer(this);
}
}
}
Factory Method
Ein Interface wird verwendet, um etwas zu beschreiben. Dieses wird von unterschiedlichen Implementationen implementiert.
public interface Button {
void render();
void onClick();
}
public class HtmlButton implements Button {
public void render() {
System.out.println("<button>Test Button</button>");
onClick();
}
public void onClick() {
System.out.println("Click! Button says - 'Hello World!'");
}
}
public class OtherButton implements Button {
public void render() {
...
onClick();
}
public void onClick() {
...
}
}
Button x = new HtmlButton();
Prototype
Wird verwendet, wenn Objekte exakt geklonnt werden sollen. Dabei werden nicht die einzelnen Felder kopiert, sondern das Klonen der Klasse überlassen.
public abstract class Tree {
// ...
public abstract Tree copy();
}
public class PlasticTree extends Tree {
// ...
@Override
public Tree copy() {
PlasticTree plasticTreeClone = new PlasticTree(this.getMass(), this.getHeight());
plasticTreeClone.setPosition(this.getPosition());
return plasticTreeClone;
}
}
public class PineTree extends Tree {
// ...
@Override
public Tree copy() {
PineTree pineTreeClone = new PineTree(this.getMass(), this.getHeight());
pineTreeClone.setPosition(this.getPosition());
return pineTreeClone;
}
}
Singleton
Wird verwendet, um von einer Klasse genau eine Instanz zu haben. Wird verwendet um Resourcen, z.B. Zugang zu einer Datenbank, Datei in genau einer Instanz zu halten.
public final class ClassSingleton {
private static ClassSingleton INSTANCE;
private String info = "Initial info class";
private ClassSingleton() {
}
public static ClassSingleton getInstance() {
if(INSTANCE == null) {
INSTANCE = new ClassSingleton();
}
return INSTANCE;
}
// getters and setters
}
Structural Patterns
Adapter
Das Adapter Pattern erlaubt es, Anwendungen die mit unterschiedlichen Protokollen / Schnittstellen arbeiten, miteinander zu verknüpfen.
public class Volt {
private int volts;
public Volt(int v){
this.volts=v;
}
public int getVolts() {
return volts;
}
public void setVolts(int volts) {
this.volts = volts;
}
}
public class Socket {
public Volt getVolt(){
return new Volt(120);
}
}
public interface SocketAdapter {
public Volt get120Volt();
public Volt get12Volt();
public Volt get3Volt();
}
Bridge
Eine Klasse wird aus unterschiedlichen anderen Klassen zusammengesetzt
public interface Color {
public void applyColor();
}
public abstract class Shape {
//Composition - implementor
protected Color color;
//constructor with implementor as input argument
public Shape(Color c){
this.color=c;
}
abstract public void applyColor();
}
public class Triangle extends Shape{
public Triangle(Color c) {
super(c);
}
@Override
public void applyColor() {
System.out.print("Triangle filled with color ");
color.applyColor();
}
Composite
Macht nur Sinn, wenn die Anwendung als Baum aufgebaut ist.
public interface Department {
void printDepartmentName();
}
// LEAFS
public class FinancialDepartment implements Department {
private Integer id;
private String name;
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
// standard constructor, getters, setters
}
public class SalesDepartment implements Department {
private Integer id;
private String name;
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
// standard constructor, getters, setters
}
// COMPOSITE
public class HeadDepartment implements Department {
private Integer id;
private String name;
private List<Department> childDepartments;
public HeadDepartment(Integer id, String name) {
this.id = id;
this.name = name;
this.childDepartments = new ArrayList<>();
}
public void printDepartmentName() {
childDepartments.forEach(Department::printDepartmentName);
}
public void addDepartment(Department department) {
childDepartments.add(department);
}
public void removeDepartment(Department department) {
childDepartments.remove(department);
}
}
Decorator
Erlaubt es neues Verhalten zu Objeken hinzuzufügen.
public interface ChristmasTree {
String decorate();
}
public class ChristmasTreeImpl implements ChristmasTree {
@Override
public String decorate() {
return "Christmas tree";
}
}
public abstract class TreeDecorator implements ChristmasTree {
private ChristmasTree tree;
// standard constructors
@Override
public String decorate() {
return tree.decorate();
}
}
public class BubbleLights extends TreeDecorator {
public BubbleLights(ChristmasTree tree) {
super(tree);
}
public String decorate() {
return super.decorate() + decorateWithBubbleLights();
}
private String decorateWithBubbleLights() {
return " with Bubble Lights";
}
}
Facade
Bietet ein vereinfachtes Interface für eine Bibliothek, Framework oder Set an komplexen Klassen.
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
// FACADE
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
Flyweight
Erlaubt es eine grössere Anzahl an Objekten in die gleiche Grösse an Arbeitsspeicher zu packen. Es werden gemeinsame Daten in einer gemeinsamen Tabelle gespeichert.
public class Tree {
private int x;
private int y;
private TreeType type;
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw(Graphics g) {
type.draw(g, x, y);
}
}
public class TreeType {
private String name;
private Color color;
private String otherTreeData;
public TreeType(String name, Color color, String otherTreeData) {
this.name = name;
this.color = color;
this.otherTreeData = otherTreeData;
}
public void draw(Graphics g, int x, int y) {
g.setColor(Color.BLACK);
g.fillRect(x - 1, y, 3, 5);
g.setColor(color);
g.fillOval(x - 5, y - 10, 10, 10);
}
}
public class TreeFactory {
static Map<String, TreeType> treeTypes = new HashMap<>();
public static TreeType getTreeType(String name, Color color, String otherTreeData) {
TreeType result = treeTypes.get(name);
if (result == null) {
result = new TreeType(name, color, otherTreeData);
treeTypes.put(name, result);
}
return result;
}
}
public class Forest {
private List<Tree> trees = new ArrayList<>();
public void plantTree(int x, int y, String name, Color color, String otherTreeData) {
TreeType type = TreeFactory.getTreeType(name, color, otherTreeData);
Tree tree = new Tree(x, y, type);
trees.add(tree);
}
@Override
public void paint(Graphics graphics) {
for (Tree tree : trees) {
tree.draw(graphics);
}
}
}
Proxy
Übertragt die Steuerung eines Objektes in eine vorgelagertes Stellvertreterobjekt. Das Proxy dient als Schnittstelle zu seinem Subjekt.
public interface ExpensiveObject {
void process();
}
public class ExpensiveObjectImpl implements ExpensiveObject {
public ExpensiveObjectImpl() {
heavyInitialConfiguration();
}
@Override
public void process() {
LOG.info("processing complete.");
}
private void heavyInitialConfiguration() {
LOG.info("Loading initial configuration...");
}
}
public class ExpensiveObjectProxy implements ExpensiveObject {
private static ExpensiveObject object;
@Override
public void process() {
if (object == null) {
object = new ExpensiveObjectImpl();
}
object.process();
}
}
public static void main(String[] args) {
ExpensiveObject object = new ExpensiveObjectProxy();
object.process();
object.process();
}
Proxy Type | Beschreibung | Vorteile | Nachteile |
---|---|---|---|
Virtual Proxy | Objekt wird erst geladen, wenn benötigt (Lazy loading) | Spart Arbeitsspeicher | |
Remot Proxy | Originales Objekt ist nicht im aktuellen Speicher präsent | ||
Protection Proxy | Erlaubt es eine zusätzliche Sicherheitsschicht einem Objekt hinzuzufügen | Erlaubt Zugriffsrechte |
Behaviroal Patterns
Chain of Responsibility
Anfragen werden entlang einer Kette weitergeleitet.
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
//next element in chain or responsibility
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger !=null){
nextLogger.logMessage(level, message);
}
}
abstract protected void write(String message);
}
public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
public class FileLogger extends AbstractLogger {
public FileLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
// DEMO
public class ChainPatternDemo {
private static AbstractLogger getChainOfLoggers(){
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO,
"This is an information.");
loggerChain.logMessage(AbstractLogger.DEBUG,
"This is an debug level information.");
loggerChain.logMessage(AbstractLogger.ERROR,
"This is an error information.");
}
}
Command
Daten die zum Ausführen einer Aktion benötigt werden, werden in einer eigenen Klasse gespeichert / ausgeführt.
Diese Aktionen werden werden von einem Empfänger erhalten und ausgeführt.
@FunctionalInterface
public interface TextFileOperation {
String execute();
}
public class OpenTextFileOperation implements TextFileOperation {
private TextFile textFile;
// constructors
@Override
public String execute() {
return textFile.open();
}
}
public class SaveTextFileOperation implements TextFileOperation {
// same field and constructor as above
@Override
public String execute() {
return textFile.save();
}
}
// RECEIVER
public class TextFile {
private String name;
// constructor
public String open() {
return "Opening file " + name;
}
public String save() {
return "Saving file " + name;
}
// additional text file methods (editing, writing, copying, pasting)
}
//SENDER
public class TextFileOperationExecutor {
private final List<TextFileOperation> textFileOperations
= new ArrayList<>();
public String executeOperation(TextFileOperation textFileOperation) {
textFileOperations.add(textFileOperation);
return textFileOperation.execute();
}
}
// VERWENDUNG
public static void main(String[] args) {
TextFileOperationExecutor textFileOperationExecutor
= new TextFileOperationExecutor();
textFileOperationExecutor.executeOperation(
new OpenTextFileOperation(new TextFile("file1.txt"))));
textFileOperationExecutor.executeOperation(
new SaveTextFileOperation(new TextFile("file2.txt"))));
}
Interpreter
Definiert eine Repräsentation für die Grammatik einer Sprache und die Möglichkeit, Sätze dieser Sprache zu interpretieren.
class Select implements Expression {
private String column;
private From from;
// constructor
@Override
public List<String> interpret(Context ctx) {
ctx.setColumn(column);
return from.interpret(ctx);
}
}
class From implements Expression {
private String table;
private Where where;
// constructors
@Override
public List<String> interpret(Context ctx) {
ctx.setTable(table);
if (where == null) {
return ctx.search();
}
return where.interpret(ctx);
}
}
class Where implements Expression {
private Predicate<String> filter;
// constructor
@Override
public List<String> interpret(Context ctx) {
ctx.setFilter(filter);
return ctx.search();
}
}
class Context {
private static Map<String, List<Row>> tables = new HashMap<>();
static {
List<Row> list = new ArrayList<>();
list.add(new Row("John", "Doe"));
list.add(new Row("Jan", "Kowalski"));
list.add(new Row("Dominic", "Doom"));
tables.put("people", list);
}
private String table;
private String column;
private Predicate<String> whereFilter;
// ...
List<String> search() {
List<String> result = tables.entrySet()
.stream()
.filter(entry -> entry.getKey().equalsIgnoreCase(table))
.flatMap(entry -> Stream.of(entry.getValue()))
.flatMap(Collection::stream)
.map(Row::toString)
.flatMap(columnMapper)
.filter(whereFilter)
.collect(Collectors.toList());
clear();
return result;
}
}
Iterator
Erlaubt es Datenstrukturen jeglicher Art zu durchlaufen.
public interface Iterator {
public boolean hasNext();
public Object next();
}
public interface Container {
public Iterator getIterator();
}
public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
Mediator
Wird benötigt, um einen "Kabelsalat" an Dependencies durch einen zentralen Zugang zu steuern.
public class Button {
private Fan fan;
// constructor, getters and setters
public void press(){
if(fan.isOn()){
fan.turnOff();
} else {
fan.turnOn();
}
}
}
public class Fan {
private Button button;
private PowerSupplier powerSupplier;
private boolean isOn = false;
// constructor, getters and setters
public void turnOn() {
powerSupplier.turnOn();
isOn = true;
}
public void turnOff() {
isOn = false;
powerSupplier.turnOff();
}
}
public class PowerSupplier {
public void turnOn() {
// implementation
}
public void turnOff() {
// implementation
}
}
Umwandeln in
public class Button {
private Mediator mediator;
// constructor, getters and setters
public void press() {
mediator.press();
}
}
public class Fan {
private Mediator mediator;
private boolean isOn = false;
// constructor, getters and setters
public void turnOn() {
mediator.start();
isOn = true;
}
public void turnOff() {
isOn = false;
mediator.stop();
}
}
public class Mediator {
private Button button;
private Fan fan;
private PowerSupplier powerSupplier;
// constructor, getters and setters
public void press() {
if (fan.isOn()) {
fan.turnOff();
} else {
fan.turnOn();
}
}
public void start() {
powerSupplier.turnOn();
}
public void stop() {
powerSupplier.turnOff();
}
}
Memento
Erlaubt es Zustände zu speichern und wiederherzustellen.
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
public class Originator {
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento saveStateToMemento(){
return new Memento(state);
}
public void getStateFromMemento(Memento memento){
state = memento.getState();
}
}
public class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: " + originator.getState());
}
}
### Observer
Definiert ein Mechanismus um mehrere Objekte zu informieren, wenn sich ein Objekt ändert.
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
State
Ein Objekt ändert sein Verhalten bestimmend nach seinen internen Zuständen.
public class Package {
private PackageState state = new OrderedState();
// getter, setter
public void previousState() {
state.prev(this);
}
public void nextState() {
state.next(this);
}
public void printStatus() {
state.printStatus();
}
}
public interface PackageState {
void next(Package pkg);
void prev(Package pkg);
void printStatus();
}
public class OrderedState implements PackageState {
@Override
public void next(Package pkg) {
pkg.setState(new DeliveredState());
}
@Override
public void prev(Package pkg) {
System.out.println("The package is in its root state.");
}
@Override
public void printStatus() {
System.out.println("Package ordered, not delivered to the office yet.");
}
}
public class DeliveredState implements PackageState {
@Override
public void next(Package pkg) {
pkg.setState(new ReceivedState());
}
@Override
public void prev(Package pkg) {
pkg.setState(new OrderedState());
}
@Override
public void printStatus() {
System.out.println("Package delivered to post office, not received yet.");
}
}
public class ReceivedState implements PackageState {
@Override
public void next(Package pkg) {
System.out.println("This package is already received by a client.");
}
@Override
public void prev(Package pkg) {
pkg.setState(new DeliveredState());
}
}
Strategy
Mehrere Algorithmen werden in separaten Klassen implementiert. Je nach Anwendung, kann zwischen diesen gewechselt werden.
public interface Strategy {
public int doOperation(int num1, int num2);
}
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
Template Method
Das Skelet von Algorithmen wird in der Superklasse definiert. Subklassen implementieren die offen gelegten Methoden der Superklasse.
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//template method
public final void play(){
//initialize the game
initialize();
//start game
startPlay();
//end game
endPlay();
}
}
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
Visitor
Algorithmen werden vom Objekt getrennt, auf dem sie operieren.
public class Document extends Element {
List<Element> elements = new ArrayList<>();
// ...
@Override
public void accept(Visitor v) {
for (Element e : this.elements) {
e.accept(v);
}
}
}
public class JsonElement extends Element {
// ...
public void accept(Visitor v) {
v.visit(this);
}
}
public class ElementVisitor implements Visitor {
@Override
public void visit(XmlElement xe) {
System.out.println(
"processing an XML element with uuid: " + xe.uuid);
}
@Override
public void visit(JsonElement je) {
System.out.println(
"processing a JSON element with uuid: " + je.uuid);
}
}
Auslosetool
Einflussfaktoren
Usability
- Graphische Benutzeroberfläche
- Presentation Layer
Zuverlässigkeit
- Schnittstellen der Schichten verstänldihc und testbar gestalten
- Verfikation der Import Datei auf Korrektheit (Ausgabe einer verständlichen Fehlermeldung)
Design Vorgaben
- Trennung der UI-Logik
- Schichten Architektur
Supportability
- Wiederverwendbarkeit
- Modularisierung
- Support von verschiedenen Import-Dateiformaten (Abstraktionsschicht)
Logische Architektur
Switfs 4 Restaurants
- Besteht aus Mobilgeräten für das Servierpersonal, mit dem sie Bestellungn aufnahmen können sowie Bezahlungen abwickeln können.
- Zentralkassensystem für die Theke
- Touchscreen für die Küche, wo die bestellten Waren erscheinen
Stakeholder Map
Domänenmodell
Jede Servierperson besitzt ein Mobilgerät, auf dem sie Ihre Bestellungen erfasst. Die Menus werden direkt auf das Display in der Küche geschickt, die sie zubereitet. Die Getränkebestellungen werden auf dem Zentralcomputer an der Theke angezeigt. Eine Servierperson kann eine laufende Bestellung von einer anderen Servierperson übernehmen, indem sie einfach die entsprechende Tischnummer eingibt auf dem Mobilgerät.
Diskussionsforum
Design-Klassendiagram (DCD)
Use Cases
Use Cas | Titel |
---|---|
UC01 | Sign In |
UC02 | Sign Out |
UC03 | Read a Discussion |
UC04 | Post a Contribution |
UC05 | Add a New Discussion |
UC06 | Add a new Topic |
UC07 | Show Statistics |
Systemoperationen
getNbrOfContributions
UC: UC07
Signatur: getNbrOfContributions()
Gibt die Anzahl Diskussionsbeitrage "Klasse Contribution" im gesamten Forum zurück.
- Die Systemoperation zur Berechnung der Anzahl Beiträge "Contributions" wird so realisiert, dass über alle Stufen der Zugehörigkeithierarchie "containment hierarch" iteriert wird und die jeweiligen Zwischenresultate aufsummiert werden.
- Alle oben aufgeführten Methoden müssen zusätzlich im DCD eingetragen werden. Beachten Sie die herarchische Nummerierung. Bezüglich Low Coupling .wird auf die Diskussion von getTotal() der Fallstudie verwiesen.
- Das Addieren der Zwischenresultate findet in den jeweiligen "getNumberOfContributions()" Methoden statt und wird hier gar nicht modelliert.
- Das Design direkt über User alle Contributions zu ermitteln, liefet eine falsche Anzahl, wennn Benutzer gelöscht werden!
### addNewDiscussion
UC: UC05
Signatur: addNewDiscussion(accessToken: byte[], userName: String, topicName: String, discussionName: String)
Es muss überprüft werden, ob so eine Diskussion bereits existiert und in diesem Fall muss eine Exception geworfen werden.
- Vom Fassaden-Controller aus wird zuerst das Access-Token validiert. Dann wird das richti- ge Thema («Topic») gewählt. Auf dem Topic wird die Diskussion («Discussion») erzeugt. Die Diskussion muss dann noch mit dem Topic verknüpft werden.
- Die Authentifizierung ist gemäss Aufgabenstellung ein Variationspunkt. Darum wird für die Validierung des Access-Tokens ein Interface «AccessTokenValidatorService» mit der Me- thode «isValidToken()» erstellt (analog für die Authentifzierung gemäss I in SOLID Interface Segregation). Zur Laufzeit kann dann eine konkrete Instanz eines AccessTokenValidator- Service dem Forum injiziert werden (Konstruktor).
- Das Sequenzdiagramm zeigt im Wesentlichen nur den Erfolgsfall. Die Fehlerbehandlung ist in den Kommentaren angegeben. Ihre Modellierung mittels UML würde das Diagramm nicht nur aufblasen, sondern auch sehr unleserlich machen.
- Alle oben aufgeführten Methoden müssen zusätzlich im DCD eingetragen werden (s. DCD auf der nächsten Seite). Die Zugriffe auf die Container Klassen wurden nicht explizit model- liert, dafür aber entsprechende Methoden auf der jeweiligen Domänenklasse angelegt (getXyzForName(...)). Diese können auch dafür verwendet werden, eine Exception zu wer- fen, wenn das Domänenobjekt mit dem gesuchten Namen nicht vorhanden ist. Werden die- se Tests explizit eingezeichnet, kann dies leicht zu einem unübersichtlichen Diagramm führen.
- Die Implementation folgt exakt den Diagrammen. Für die Erzeugung der aktuellen «Instant» wurde auf die abstrakte Klasse «java.time.Clock» zurückgegriffen. Man kann eine Variante davon erzeugen, die wirklich die aktuelle Systemzeit mit der eingestellten Zeitzone reprä- sentiert. Es gibt aber auch eine Variante, die immer dieselbe Zeit liefert. Diese ist primär für Testfälle gedacht. Die Erzeugung der aktuellen «Instant» wird der Controller Klasse zuge- ordnet, da dadurch nur eine Instanz von Clock notwendig ist, die bei der Instanziierung dem Controller «Forum» übergeben wird.
Erkennen von unangemessener Sprache
Lampentool
Flexible einsetzbare Lampensteuerung. Die dafür vorgesehenden Lampen sind bezüglich den Farbkombonennten Rot, Grün und Blau jeweils zwischen 0 und 100% einstellbar. Wie sie vielleicht wissen, ist jeder Farbton eine Mischung dieser 3 Grundfarben während Weiss alle 3 Komnponenten mit gleicher Stärke hat.
Um das Licht zu steuern, kommen Schalter zum Einsatz, die sowohl gedreht wie auch gedrückt werden können. Allerdings kennen die Schalter ihre aktuelle absolute Position nicht. Sie können nur der Lampensteuerung melden, ob sie gedrückt und in welche Richtung sie gedreht wurden.
Domänenmodell
- Die Lampensteuerung reagiert auf die Schalter-Ändeungen und verändert die Lampen, die dem Schalter zugeordnet sind.
- Die genaue Art der veränderung hängt vom gewählten Betriebsmodus ab.
- Ein Betriebsmodus ist fix einem Schalter zugeordnet.
- Die verfügbaren Lampen und Schalter, die Zuordnung der Lampen zu den Schaltern und der Betriebsmodus für einen Schalter werden beim Start der Lampensteuerung definiert und sind dann konstant.
- Es ist übrigens möglich, dass eine Lampe von mehr als einem Schalter angesteuert wird.
General Responsibility Assignment Software Patterns (GRASP)
Entwurfsmuster, mit denene die Zuständigkeit bestimmter Klassen objektorientierter Systeme festgelegt werden.
Information Expert
Ein allgemeines Prinzip des Objektentwurfs und der Zuweisung von Verantwortlichkeiten.
- Eine Klasse sollte nur die Aufgaben ausführen, die ihr Name auch beschreibt.
Creator
Soll eine Instanz einer Klasse erstellt werden, wird ein Creator Pattern verwendet.
Anwenden wenn mindestens etwas zutrifft:
- A eine Aggregation oder ein Kompositum von B ist
- A registriert oder erfasst B-Objekte
- A arbeitet eng mit B-Objekten zusammen oder hat eine enge Kopplung
- A verfügt über Initialisierungsdaten für B
Controller
Ein Controller koordiniert / empfängt als erstes Objekte jenseits der UI-Schicht als Systemoperation.
- Variante 1: Fassaden Controller (repräsentiert das System bzw. übergeordnetes System)
- Variante 2: Use Case Controller (pro Use case eine künstliche Klasse)
Controller macht selber nur wenig und delegiert fast alles
Low Coupling
Kopplung = Mass für die Abhängigkeit von anderen Elementen.
- Hohe Kopplung: Element ist von vielen anderen Elementen abhängig
- Niedrige Kopplung: Element ist nur von wenigen anderen Elementen abhängig
Der Aufruf der Funktionen sollte in einer Linie geschehen.
High Cohesion
Mass für die Verwandtschat und Fokussierung eines Elements.
- Hohe Kohäsion: Element erledigt nur wenige Aufgaben, die eng miteinander verwandt sind.
- Geringe Kohäsion: Element das für viele unzusammenhängende Dinge verantwortlich ist.
Wie bei Low Coupling, sollten die Aufgaben gebündelt werden.
Polymorphism
Polymorphism ist das bündeln von typengleichen oder ähnlichen Verhalten.
Tier
Ein Hund ist auch ein Tier
Pure Fabrication
Künstliche Hilfsklassen erzeugen um dem Domänenmodell gleich zu sein. Wird nur verwendet, um eine bessere Wiederverwendbarkeit zu realisieren.
Indirection
Direkte Kopplung zwischen zwei oder mehreren Objekten vermeiden.
-> Vermittler
Protected Variants
Veränderungen im Objekt sollen keinen Einfluss auf andere Elemente haben.
-> Interfaces einführen
- Spekulationen sind zu vermeiden, da dies zu unnötiger Komplexität führt.