Table of Contents

Principy objektového stylu. Typické příklady objektových řešení. Návrhové vzory.

Klíčové koncepty modelování systémů: programovací paradigmata, dekompozice, hierarchie, abstrakce používané v softwarovém vývoji (jmenné, datové, funkcionální, objektové, SOLID, DRY).

Programovací paradigmata

Deklarativní vs imperativní přístup

Dekompozice

Hierarchie

Abstrakce v softwarovém vývoji

Principy návrhu

* SOLID principy:

* DRY (Don't Repeat Yourself)

Shrnutí

Objektově orientovaný přístup: objekty, třídy a hierarchie tříd, zapouzdření, interface a abstraktní třídy, dědičnost, class diagramy a příklady systémů modelovaných pomocí OOP, encapsulation, rozdíl mezi asociací, agregací a kompozicí, dědičnost versus kompozice, polymorfismus, overloading versus overriding.

Objekty a třídy

Zapouzdření (encapsulation)

Interface a abstraktní třídy

Interface použijeme, jestliže:

Abstraktní třídu použijeme, jestliže:

Dědičnost

Dědičnost vs. Kompozice

Class diagramy a příklady

Asociace, agregace, kompozice

Polymorfismus

Overloading vs. Overriding

Shrnutí OOP přístupů

Klíčové koncepty modelování systémů: abstraktní datové typy, mutabilita, imutabilita, rekurze, datové typy v rekurzi.

Abstraktní datový typ (ADT)

Typy operací v ADT:

Vhodný návrh ADT:

Příklady generických ADT:

Příklady doménových ADT:

Mutabilita vs Imutabilita

Invarianty

Invarianta je vlastnost, která je splněna pro jakýkoliv runtime stav programu ve všech jeho stabilních stavech a nezávisí na chování klienta

Pozn. Invariance by měla být zaručena pro volání public metod. Tedy mohou existovat mezistavy, kdy je invariance porušena.

Rekurze a datové typy v rekurzi

Shrnutí

Funkcionánlní programování v Java

Funkce první třídy (first-class functions)

    Predicate<Person> isRetired = p -> p.getAge() > 65;
 

Funkce vyššího řádu (higher-order functions)

    public List<Person> filter(List<Person> people, Predicate<Person> p) {
        return people.stream().filter(p).collect(Collectors.toList());
    }

Lambda expressions

    Comparator<String> comp = (s1, s2) -> s1.length() - s2.length();

Closures

    int base = 10;
    Function<Integer, Integer> addBase = x -> x + base;

Currying

    Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;
    add.apply(2).apply(3); // vrátí 5

Referential transparency

Lazy evaluation

    Stream<Integer> s = Stream.of(1, 2, 3).filter(x -> {
        System.out.println("filtrování " + x);
        return x > 1;
    });
    // kód nad tím nic nevytiskne – evaluace je lazy

Shrnutí rozdílů oproti OOP

| Oblast | Funkcionální přístup | Objektový přístup |
|-|-|-|
| Data | Imutabilní | Mutabilní |
| Exekuce | Libovolné pořadí volání | Definované pořadí volání metod |
| Iterace | Rekurze | Cykly |
| Paralelismus | Snadná podpora díky bezstavovým funkcím | Komplikovanější, vyžaduje synchronizaci |
| Stav | Explicitní (parametry, návraty) | Implicitní (instance proměnné, settery) |

Creational design patterns: factory a abstract factory, builder, prototype, singleton, dependency injection

Singleton

  public class Singleton {
      private static volatile Singleton instance;
      private Singleton() {}
      public static Singleton getInstance() {
          if (instance == null) {
              synchronized (Singleton.class) {
                  if (instance == null) {
                      instance = new Singleton();
                  }
              }
          }
          return instance;
      }
  }
 

Factory Method

  abstract class Dialog {
      public void renderWindow() {
          Button okButton = createButton();
          okButton.render();
      }
      public abstract Button createButton();
  }
 
  class WindowsDialog extends Dialog {
      public Button createButton() {
          return new WindowsButton();
      }
  }
 
  class WebDialog extends Dialog {
      public Button createButton() {
          return new HtmlButton();
      }
  }

Abstract Factory

  interface GUIFactory {
      Button createButton();
      Checkbox createCheckbox();
  }
 
  class WinFactory implements GUIFactory {
      public Button createButton() { return new WinButton(); }
      public Checkbox createCheckbox() { return new WinCheckbox(); }
  }

Prototype

  public class Employee implements Cloneable {
      private String name;
      private Address address;
 
      public Employee(String name, Address address) {
          this.name = name;
          this.address = address;
      }
 
      @Override
      public Employee clone() {
          return new Employee(this.name, this.address.clone());
      }
  }

Builder

  enum EColor {
      RED, WHITE, BLACK
  }
 
  public class House {
      private EColor color;
      private List<Window> windows = new ArrayList<>();
      private List<Wall> walls = new ArrayList<>();
      private Door door;
 
      public void setColor(EColor color) {
          this.color = color;
      }
 
      public void addWindow(Window window) {
          this.windows.add(window);
      }
 
      public void addWall(Wall wall) {
          this.walls.add(wall);
      }
 
      public void setDoor(Door door) {
          this.door = door;
      }
  }
 
  public class HouseBuilder {
      private House house = new House();
 
      public HouseBuilder addWall(){
          house.addWall(new Wall());
          return this;
      }
 
      public HouseBuilder addWindow(){
          house.addWindow(new Window());
          return this;
      }
 
      public HouseBuilder buildDoor() {
          house.setDoor(new Door());
          return this;
      }
 
      public HouseBuilder paint(EColor color){
          house.setColor(color);
          return this;
      }
 
      public House getResult(){
          return house;
      }
  }
  HouseBuilder builder = new HouseBuilder();
  House yourHouse = builder
      .addWall()
      .addWindow()
      .buildDoor()
      .paint(EColor.RED)
      .getResult();

Dependency Injection (DI)

  @Component
  public class Customer {
    @Autowired
    private Person person;
    private int type;
  } 
 
  @Component
  public class Customer {
      private Person person;
      @Autowired
      public void setPerson(Person person) {
          this.person = person;
      }
  }
  @Component
  public class Customer {
    private Person person;
    @Autowired
    public Customer (Person person) {
      this.person=person;
    }
  }

Structural design patterns: adapter, proxy, bridge, composite, facade, decorator, flyweight

Adapter

   class EuropeanSocket {
       public void connect(String wires) { ... }
   }
 
   interface CzechPlug {
       void zapoj();
   }
 
   class Adapter implements CzechPlug {
       private EuropeanSocket socket;
       public Adapter(EuropeanSocket socket) {
           this.socket = socket;
       }
       public void zapoj() {
           socket.connect("fáze + nulák");
       }
   }

Proxy

   interface Tower {
       void enter(Wizard wizard);
   }
 
   class TowerProxy implements Tower {
       private Tower tower = new WizardTower();
       public void enter(Wizard wizard) {
           if (wizard.getColor() != Color.GREEN) {
               tower.enter(wizard);
           } else {
               System.out.println("Green wizard is not allowed.");
           }
       }
   }

Facade

   public class KitchenFacade {
       private Microwave microwave = new Microwave();
       private Fridge fridge = new Fridge();
 
       public void prepareSnack() {
           fridge.open();
           microwave.heat();
           fridge.close();
       }
   }

Composite Design Pattern

* Účel:

* Struktura:

* Výhody:

Příklad použití: souborový systém

  interface FileSystemItem {
    void display(String indent);
  }
 
  class File implements FileSystemItem {
    private String name;
 
    public File(String name) {
        this.name = name;
    }
 
    public void display(String indent) {
        System.out.println(indent + "- File: " + name);
    }
  }
 
  class Folder implements FileSystemItem {
    private String name;
    private List<FileSystemItem> children = new ArrayList<>();
 
    public Folder(String name) {
        this.name = name;
    }
 
    public void add(FileSystemItem item) {
        children.add(item);
    }
 
    public void display(String indent) {
        System.out.println(indent + "+ Folder: " + name);
        for (FileSystemItem item : children) {
            item.display(indent + "  ");
        }
    }
  }
 
  public class CompositeDemo {
    public static void main(String[] args) {
        Folder root = new Folder("root");
        root.add(new File("readme.txt"));
 
        Folder src = new Folder("src");
        src.add(new File("Main.java"));
        src.add(new File("Utils.java"));
 
        root.add(src);
        root.display("");
    }
  }

Decorator

   interface Coffee {
       String getDescription();
       double getCost();
   }
 
   class BasicCoffee implements Coffee {
       public String getDescription() { return "Coffee"; }
       public double getCost() { return 1.0; }
   }
 
   class MilkDecorator implements Coffee {
       private Coffee coffee;
       public MilkDecorator(Coffee coffee) {
           this.coffee = coffee;
       }
       public String getDescription() {
           return coffee.getDescription() + ", milk";
       }
       public double getCost() {
           return coffee.getCost() + 0.5;
       }
   }

Flyweight

   class BulletType {
       private String texture;
       public BulletType(String texture) {
           this.texture = texture;
       }
   }
 
   class BulletFactory {
       private static Map<String, BulletType> cache = new HashMap<>();
       public static BulletType get(String texture) {
           return cache.computeIfAbsent(texture, BulletType::new);
       }
   }

Bridge

   public interface Color {
       String fill();
   }
 
   public class Blue implements Color {
       @Override
       public String fill() {
           return "Color is Blue";
       }
   }
 
   public class Red implements Color {
       @Override
       public String fill() {
           return "Color is Red";
       }
   }
 
   public abstract class Shape {
       protected Color color;
 
       public Shape(Color color) {
           this.color = color;
       }
 
       public abstract String draw();
   }
 
   public class Square extends Shape {
 
       public Square(Color color) {
           super(color);
       }
 
       @Override
       public String draw() {
           return "Square drawn. " + color.fill();
       }
   }

Srovnání

Behavioral design patterns

Iterator

   // Iterátor – definuje rozhraní pro iteraci
   public interface Iterator<T> {
       boolean hasNext();
       T next();
   }
 
   // Kontejner – definuje rozhraní, které vrací iterátor
   public interface Container<T> {
       Iterator<T> getIterator();
   }
 
   // Konkrétní implementace kontejneru
   public class NameRepository implements Container<String> {
       private String[] names = { "Anna", "Bob", "Charlie" };
 
       @Override
       public Iterator<String> getIterator() {
           return new NameIterator();
       }
 
       // Interní implementace iterátoru
       private class NameIterator implements Iterator<String> {
           int index = 0;
 
           public boolean hasNext() {
               return index < names.length;
           }
 
           public String next() {
               return hasNext() ? names[index++] : null;
           }
       }
   }
 
   // Použití iterátoru
   public class Demo {
       public static void main(String[] args) {
           NameRepository repo = new NameRepository();
           Iterator<String> it = repo.getIterator();
 
           while (it.hasNext()) {
               System.out.println("Name: " + it.next());
           }
       }
   }

Chain of Responsibility

   // Abstraktní handler
   abstract class Logger {
       public static int INFO = 1;
       public static int DEBUG = 2;
       public static int ERROR = 3;
 
       protected int level;
       protected Logger nextLogger;
 
       public void setNext(Logger nextLogger) {
           this.nextLogger = nextLogger;
       }
 
       public void logMessage(int level, String message) {
           if (this.level <= level) {
               write(message);
           }
           if (nextLogger != null) {
               nextLogger.logMessage(level, message);
           }
       }
 
       protected abstract void write(String message);
   }
 
   // Konkrétní implementace – konzolový logger
   class ConsoleLogger extends Logger {
       public ConsoleLogger(int level) {
           this.level = level;
       }
 
       protected void write(String message) {
           System.out.println("Standard Console::Logger: " + message);
       }
   }
 
   // Konkrétní implementace – souborový logger
   class FileLogger extends Logger {
       public FileLogger(int level) {
           this.level = level;
       }
 
       protected void write(String message) {
           System.out.println("File::Logger: " + message);
       }
   }
 
   // Konkrétní implementace – chybový logger
   class ErrorLogger extends Logger {
       public ErrorLogger(int level) {
           this.level = level;
       }
 
       protected void write(String message) {
           System.out.println("Error Console::Logger: " + message);
       }
   }
 
   // Klient, který sestaví řetězec a provede logování
   public class ChainPatternDemo {
       private static Logger getChainOfLoggers() {
           Logger errorLogger = new ErrorLogger(Logger.ERROR);
           Logger fileLogger = new FileLogger(Logger.DEBUG);
           Logger consoleLogger = new ConsoleLogger(Logger.INFO);
 
           errorLogger.setNext(fileLogger);
           fileLogger.setNext(consoleLogger);
 
           return errorLogger;
       }
 
       public static void main(String[] args) {
           Logger loggerChain = getChainOfLoggers();
 
           loggerChain.logMessage(Logger.INFO, "This is an informational message.");
           loggerChain.logMessage(Logger.DEBUG, "This is a debug level message.");
           loggerChain.logMessage(Logger.ERROR, "This is an error message.");
       }
   }

Strategy

   // Společné rozhraní pro strategii
   public interface PoliceStrategy {
       void checkDriver(Driver driver);
   }
 
   // Jednodušší strategie
   public class NicePolice implements PoliceStrategy {
       @Override
       public void checkDriver(Driver driver) {
           System.out.println("You're okay, have a nice day!");
       }
   }
 
   // Přísnější strategie
   public class HardPolice implements PoliceStrategy {
       @Override
       public void checkDriver(Driver driver) {
           if (driver.getAlcoholLevel() > 0) {
               System.out.println("You are under arrest!");
           } else {
               System.out.println("You're free to go.");
           }
       }
   }
 
   // Kontext používající zvolenou strategii
   public class PoliceOfficer {
       private PoliceStrategy strategy;
 
       public PoliceOfficer(PoliceStrategy strategy) {
           this.strategy = strategy;
       }
 
       public void setStrategy(PoliceStrategy strategy) {
           this.strategy = strategy;
       }
 
       public void performCheck(Driver driver) {
           strategy.checkDriver(driver);
       }
   }

Visitor

   public interface ComputerPart {
       void accept(Visitor v);
   }
 
   public class Mouse implements ComputerPart {
       public void accept(Visitor v) {
           v.visit(this);
       }
   }
 
   public class DisplayVisitor implements Visitor {
       public void visit(Mouse mouse) {
           System.out.println("Displaying Mouse.");
       }
   }

Observer

   // Rozhraní pozorovatele
   public abstract class Observer {
       protected Subject subject;
       public abstract void update();
   }
 
   // Třída Subject s registrací a notifikací
   public class Subject {
       private List<Observer> observers = new ArrayList<>();
       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);
       }
 
       private void notifyAllObservers() {
           for (Observer o : observers) {
               o.update();
           }
       }
   }
 
   // Konkrétní pozorovatelé
   public class HexObserver extends Observer {
       public HexObserver(Subject subject) {
           this.subject = subject;
           subject.attach(this);
       }
 
       public void update() {
           System.out.println("Hex: " + Integer.toHexString(subject.getState()));
       }
   }
 
   public class BinaryObserver extends Observer {
       public BinaryObserver(Subject subject) {
           this.subject = subject;
           subject.attach(this);
       }
 
       public void update() {
           System.out.println("Binary: " + Integer.toBinaryString(subject.getState()));
       }
   }
 
   public class OctalObserver extends Observer {
       public OctalObserver(Subject subject) {
           this.subject = subject;
           subject.attach(this);
       }
 
       public void update() {
           System.out.println("Octal: " + Integer.toOctalString(subject.getState()));
       }
   }
 
   // Ukázka použití
   public class ObserverDemo {
       public static void main(String[] args) {
           Subject subject = new Subject();
 
           new HexObserver(subject);
           new OctalObserver(subject);
           new BinaryObserver(subject);
 
           System.out.println("First state change: 15");
           subject.setState(15);
 
           System.out.println("Second state change: 10");
           subject.setState(10);
       }
   }

Template Method

   abstract class Game {
       final void play() {
           initialize();
           startPlay();
           endPlay();
       }
       abstract void initialize();
       abstract void startPlay();
       abstract void endPlay();
   }

State

   // Rozhraní pro stav
   public interface State {
       void onPlay(Player player);
       void onLock(Player player);
   }
 
   // Kontext – přehrávač
   public class Player {
       private State state;
 
       public Player() {
           this.state = new ReadyState();  // výchozí stav
       }
 
       public void setState(State state) {
           this.state = state;
       }
 
       public void pressPlay() {
           state.onPlay(this);
       }
 
       public void pressLock() {
           state.onLock(this);
       }
   }
 
   // Konkrétní stav – Ready
   public class ReadyState implements State {
       public void onPlay(Player player) {
           System.out.println("Start playing music.");
           player.setState(new PlayingState());
       }
 
       public void onLock(Player player) {
           System.out.println("Locked from ready state.");
           player.setState(new LockedState());
       }
   }
 
   // Konkrétní stav – Playing
   public class PlayingState implements State {
       public void onPlay(Player player) {
           System.out.println("Paused.");
           player.setState(new ReadyState());
       }
 
       public void onLock(Player player) {
           System.out.println("Locked while playing.");
           player.setState(new LockedState());
       }
   }
 
   // Konkrétní stav – Locked
   public class LockedState implements State {
       public void onPlay(Player player) {
           System.out.println("Can't play, device is locked.");
       }
 
       public void onLock(Player player) {
           System.out.println("Unlocked.");
           player.setState(new ReadyState());
       }
   }
 
   // Ukázka použití
   public class StateDemo {
       public static void main(String[] args) {
           Player player = new Player();
 
           player.pressPlay();  // Start playing music.
           player.pressLock();  // Locked while playing.
           player.pressPlay();  // Can't play, device is locked.
           player.pressLock();  // Unlocked.
           player.pressPlay();  // Start playing music.
       }
   }

Memento

   // Memento – reprezentuje uložený stav
   public class Memento {
       private final String state;
 
       public Memento(String state) {
           this.state = state;
       }
 
       public String getState() {
           return state;
       }
   }
 
   // Originator – třída s měnitelným stavem
   public class Originator {
       private String state;
 
       public void setState(String state) {
           this.state = state;
           System.out.println("Current state: " + state);
       }
 
       public Memento saveStateToMemento() {
           return new Memento(state);
       }
 
       public void getStateFromMemento(Memento memento) {
           state = memento.getState();
           System.out.println("State restored to: " + state);
       }
   }
 
   // Caretaker – uchovává historii stavů
   public class Caretaker {
       private List<Memento> mementoList = new ArrayList<>();
 
       public void add(Memento state) {
           mementoList.add(state);
       }
 
       public Memento get(int index) {
           return mementoList.get(index);
       }
   }
 
   // Použití
   public class MementoDemo {
       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");
 
           originator.getStateFromMemento(caretaker.get(0)); // State #2
           originator.getStateFromMemento(caretaker.get(1)); // State #3
       }
   }

Interpreter

   // Rozhraní pro výraz
   public interface Expression {
       int interpret(Map<String, Integer> context);
   }
 
   // Terminální výraz – proměnná
   public class Variable implements Expression {
       private String name;
 
       public Variable(String name) {
           this.name = name;
       }
 
       public int interpret(Map<String, Integer> context) {
           return context.get(name);
       }
   }
 
   // Non-terminal – součet
   public class Plus implements Expression {
       private Expression left, right;
 
       public Plus(Expression left, Expression right) {
           this.left = left;
           this.right = right;
       }
 
       public int interpret(Map<String, Integer> context) {
           return left.interpret(context) + right.interpret(context);
       }
   }
 
   // Non-terminal – rozdíl
   public class Minus implements Expression {
       private Expression left, right;
 
       public Minus(Expression left, Expression right) {
           this.left = left;
           this.right = right;
       }
 
       public int interpret(Map<String, Integer> context) {
           return left.interpret(context) - right.interpret(context);
       }
   }
 
   // Ukázka použití
   public class InterpreterDemo {
       public static void main(String[] args) {
           Expression expr = new Plus(
               new Variable("x"),
               new Minus(
                   new Variable("y"),
                   new Variable("z")
               )
           );
 
           Map<String, Integer> context = Map.of("x", 5, "y", 10, "z", 3);
           int result = expr.interpret(context); // 5 + (10 - 3) = 12
 
           System.out.println("Výsledek: " + result);
       }
   }

Datové struktury a patterny: lazy loading, object pool, cache map, filter, reduce pattern

Lazy Loading

   public class Company {
       private ContactList contacts;
 
       public ContactList getContacts() {
           if (contacts == null) {
               contacts = new ContactListProxy(); // virtual proxy
           }
           return contacts;
       }
   }
 
   public class ContactListProxy implements ContactList {
       private ContactList realList;
 
       public List<Customer> getCustomers() {
           if (realList == null) {
               realList = new RealContactList(); // expensive operation
           }
           return realList.getCustomers();
       }
   }

Object Pool

   public class ObjectPool<T> {
       private BlockingQueue<T> pool;
 
       public ObjectPool(Supplier<T> creator, int maxSize) {
           pool = new ArrayBlockingQueue<>(maxSize);
           for (int i = 0; i < maxSize; i++) {
               pool.offer(creator.get());
           }
       }
 
       public T borrow() throws InterruptedException {
           return pool.take();
       }
 
       public void release(T obj) {
           pool.offer(obj);
       }
   }
 
   // Použití:
   ObjectPool<Connection> connectionPool = new ObjectPool<>(MyConnection::new, 5);
   Connection conn = connectionPool.borrow();
   // ...
   connectionPool.release(conn);
 

Cache

* Vzor, který ukládá data, která se často opakovaně používají.

   public class SimpleCache<K, V> {
       private final int capacity;
       private final Map<K, V> cache = new LinkedHashMap<>();
 
       public SimpleCache(int capacity) {
           this.capacity = capacity;
       }
 
       public synchronized V get(K key, Supplier<V> dataLoader) {
           if (!cache.containsKey(key)) {
               if (cache.size() >= capacity) {
                   Iterator<K> it = cache.keySet().iterator();
                   it.next(); it.remove(); // remove oldest
               }
               cache.put(key, dataLoader.get());
           }
           return cache.get(key);
       }
   }
 
   // Použití:
   SimpleCache<String, Person> personCache = new SimpleCache<>(100);
   Person p = personCache.get("123", () -> loadPersonFromDB("123"));

Map, Filter, Reduce (stream API)

     List<String> names = people.stream()
         .map(Person::getName)
         .collect(Collectors.toList());
 
     // Příklad z prezentace – nalezení největšího čísla
     List<Integer> numbers = Arrays.asList(5, 12, 3, 21, 7);
 
     int max = numbers.stream()
         .reduce(Integer.MIN_VALUE, (a, b) -> a > b ? a : b); \\tady passujeme lambda funkci
 
     System.out.println("Maximum: " + max);
 
     List<Person> fromPraha = people.stream()
         .filter(p -> p.getCity().equals("Praha"))
         .collect(Collectors.toList());
 
     List<String> sortedNames = people.stream()
         .filter(p -> p.getCity().equals("Praha"))
         .map(Person::getName)
         .sorted()
         .collect(Collectors.toList());
 

Specifikace, návrhy specifikací, web API, Apiary, Swagger, GraphQL

Specifikace

= Formální popis toho, jak se má komponenta v systému chovat

Behaviorální ekvivalence

"Dva kódy jsou behaviorálně ekvivalentní, pokud při stejných vstupech ve stejném kontextu dávají stejné výstupy a vedou ke stejnému stavu systému."

Změna specifikace - silná vs. slabá

S2 je silnější nebo stejná jako S1 pokud:

=> můžeme nahradit S1 za S2 bez ovlivnění stávajících klientů

Přesnost popisu specifikace

Příklad: "vrátí první index i, kde arr[i] = val" vs "vrátí index i, kde arr[i] = val"

Deklarativní vs Operativní specifikace

(deklarativní preferovanější)

Testování - testy musí splňovat podmínky specifikace, testy nesmí být podřízeny implementaci, ale výhradně specifikaci rozhraní

REST (Representational State Transfer)

= architektonický styl pro návrh webových API (nemusí být nutně vázaný na HTTP protokol)

Existují různé úrovně implementace:

Web API

= api na webovém serveru nebo klientu

SOAP (Simple Object Access Protocol)

REST API

GraphQL

Open API

Swagger

na datové struktury se v rámci zpráv odkazuje pomocí REF - uri

APIARY