Skip to the content.

Технологии программирования

Назад к списку лекций

Архитектура приложения. Логгирование.

Логирование

Самый простой пример логирования - System.err.println

Из известных решений по логированию в Java можно выделить:

Что нужно логировать?

О чем стоит подумать?

Уровни логов:

Log4j

Зависимость в gradle файле:

    implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
    implementation 'org.apache.logging.log4j:log4j-core:2.20.0'

В ресурсных файлах нам необходимо сконфигурировать работу логгера. Создадим файлик log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Appenders>
        <File name="all_logs_file" fileName="logs/all.log">
            <PatternLayout>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1.} %L %m%n</pattern>
            </PatternLayout>
        </File>

        <File name="important_logs_file" fileName="logs/important.log">
            <Filters>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1.} %L %m%n</pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="all_logs_file"/>
            <AppenderRef ref="important_logs_file"/>
        </Root>
    </Loggers>
</Configuration>

Давайте рассмотрим строку ConversionPattern:

Использование:

import java.util.random.RandomGenerator;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LogExample {
    protected static final Logger logger = LogManager.getLogger(LogExample.class.getName());


    public static void main(String[] args) {
        makeOrder();
    }

    private static void makeOrder() {
        int orderId = RandomGenerator.getDefault().nextInt();
        if (orderId == 0) {
            logger.error("Cant create order");
            return;
        }
        logger.info("User made an order {}", orderId);
    }
}

Архитектура приложения. Паттерны проектирования

Паттерны проектирования

Эти паттерны решают проблемы обеспечения гибкости создания объектов

Эти паттерны решают проблемы эффективного построения связей между объектами

Эти паттерны решают проблемы эффективного взаимодействия между объектами

Книга «Design Patterns: Elements of Reusable Object-Oriented Software» Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес

Порождающие паттерны

Builder

Данный паттерн позволяет создавать сложные объекты пошагово.

class Temp {
    public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder();
        
        stringBuilder.append("1");
        stringBuilder.append("asdf");
        
        stringBuilder.toString();
    }
}

Factory Method

Создание объекта вынесено в отдельный статический метод.

import java.util.Calendar;

class Temp {
    private static final Logger logger = LogManager.getLogger(LogExample.class);

    public static void main(String[] args) {
        var calendar = Calendar.getInstance();
    }
}

Singleton (*антипаттерн)

Цель — обеспечить единственный экземпляр объекта на всё приложение. Могут возникнуть проблемы с многопоточностью.

class SingletonSchoolJournal {
    
    private static SingletonSchoolJournal instance = null;

    public static SingletonSchoolJournal getInstance() {
        if (instance == null) {
            instance = new SingletonSchoolJournal();
        }
        
        return instance;
    }
    
    private SingletonSchoolJournal() {
        
    }
    
    private String label;
}

Структурные паттерны

Цель — построение удобных в поддержке иерархий классов и их взаимосвязей

Proxy (Заместитель)

Заместитель имеет тот же интерфейс, что и реальный объект, поэтому для клиента нет разницы — работать через заместителя или напрямую.

interface NineHomeworkable{
    void suffer();
}

class HeavyObject implements NineHomeworkable {
    // очень долго создавать
    // например нам нужно подрубиться к базе данных
    
    @Override
    void suffer() {
        //
    }
}

class HeavyObjectProxy implements NineHomeworkable {
    private HeavyObject heavyObject;

    @Override
    void suffer() {
        if (heavyObject == null) {
            heavyObject = new HeavyObject();
        }
        heavyObject.suffer();
    }
}

Когда использовать:

Decorator (Заместитель)

Шаблон декоратора можно использовать для придания дополнительных обязанностей объекту. Декоратор предоставляет улучшенный интерфейс к исходному объекту.

interface ITree {
    void add(String value);
}

class Tree implements ITree {
    class Node {
        Node next;
        Node prev;
        String value;
        public Node(String value) {
            this.value = value;
        }
    }

    Node first;

    void add(String newNode) {
        first = new Node(newNode);
    }
}

class ChristmasTree implements ITree {
    private Tree tree = new Tree();
    
    void add(String newNode) {
        // timer
        tree.add(newNode + " Marry Christmas");
        // sout results
    }
}

Adapter

Шаблон адаптера используется для подключения двух несовместимых интерфейсов, которые в противном случае не могут быть подключены напрямую

Основные различия между шаблонами адаптера и прокси заключаются в следующем:

Facade

Фасад скрывает сложную подсистему за простым интерфейсом. Он скрывает большую часть сложности и делает подсистему простой в использовании.

class Engine {
    void warm() {}
    void pushKupplung() {}
    void startEngine() {}
}

class HandBremse {
    void turnOff() {}
}

class CarFacade {
    Engine engine = new Engine();
    HandBremse handBremse = new HandBremse();
    
    void start() {
        engine.warm();
        engine.startEngine();
        handBremse.turnOff();
        engine.pushKupplung();
    }
}

Поведенческие шаблоны

Одна из целей - обеспечить гибкость в изменении поведения объектов

Strategy (Стратегия)

При помощи паттерна “Стратегия” мы можем внутри объекта хранить то, каким образом мы будем выполнять действие, т.е. объект внутри хранит стратегию, которая может быть изменена, в том числе во время выполнения кода.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.stream.Collectors;

class Temp {
    public static void main(String[] args) {
        var a = new ArrayList<Integer>().stream()
                .map(i -> i++)
                .collect(Collectors.toList());

        var b = a.sort(Comparator.comparing(/*как сравнивать элементы*/));
    }
}

class Student {
    ExamPassStrategy strategy;
    
    void passExam(ExamPassStrategy strategy) {
        strategy.init();
    }
}

Command (Команда)

Различные команды можно представлять в виде разных классов. Очень похоже на паттерн “Стратегия”. Но в “Стратегии” мы переопределяли то, как будет выполняться конкретное действие, а в “Команде” же мы переопределяем то, какое вообще действие будет выполнено.

import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}

Iterator (итератор)

import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}

Полезные ссылки

Логирование: что, как, где и чем?

Учимся вести логирование с помощью Log4j

Шаблоны проектирования “банды четырех”

Паттерны проектирования в Java

Шаблоны прокси, декоратора, адаптера и моста