Расширение возможностей разработки программного обеспечения с помощью базовых концепций программирования: подробное руководство

Разработка программного обеспечения — это постоянно развивающаяся область, в которой постоянно появляются новые парадигмы и концепции для решения сложных задач современных приложений. Среди них несколько фундаментальных концепций программирования выдержали испытание временем, коренным образом изменив то, как мы пишем код и разрабатываем программное обеспечение.

Сегодня мы обсудим три концепции программирования, которые должен знать каждый программист. Эти концепции окажутся бесценными на протяжении всей вашей карьеры.

Регулярные выражения

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

Вы можете использовать регулярное выражение, чтобы делать такие вещи, как:

  • Проверка адресов электронной почты и номеров телефонов
  • Извлечение информации с веб-страниц и документов
  • Замените или удалите ненужные символы
  • И многое другое!

Давайте разберем основные компоненты регулярного выражения, а затем предоставим подробный пример Java:

  1. Литералы: символы, которые соответствуют сами себе. Например, регулярное выражение a будет соответствовать символу 'a'.
  2. Метасимволы: символы со специальным значением в регулярном выражении. Примеры включают . (соответствует любому одиночному символу), * (соответствует нулю или более вхождениям предшествующего символа), + (соответствует одному или нескольким вхождениям предшествующего символа), ? (соответствует нулю или одному вхождению предшествующего символа) и многие другие.
  3. Классы символов: набор символов, заключенный в квадратные скобки [], соответствует любому отдельному символу из этого набора. Например, [aeiou] будет соответствовать любой строчной гласной.
  4. Квантификаторы. Укажите, сколько раз должен встречаться шаблон. Примеры: * (ноль или более), + (один или несколько), ? (ноль или один), {n} (ровно n раз), {n,} (не менее n раз), {n,m} (от n до m раз).
  5. Якоря. Укажите позицию во входных данных, где должно произойти совпадение. Примеры включают ^ (совпадения в начале), $ (совпадения в конце), \b (граница слова), \B (не граница слова).
  6. Группировка и захват. При заключении шаблона в круглые скобки () создается группа. Группы захвата позволяют извлекать части совпадающей строки.

Теперь давайте рассмотрим подробный пример Java, демонстрирующий использование регулярных выражений:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        String inputText = "Hello, my email is [email protected], and I love regex!";

        // Define the regular expression pattern to match an email address
        String emailPattern = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b";

        // Create a Pattern object from the regex pattern
        Pattern pattern = Pattern.compile(emailPattern);

        // Create a Matcher object to find matches in the input text
        Matcher matcher = pattern.matcher(inputText);

        // Find and print all occurrences of the email addresses in the input text
        while (matcher.find()) {
            String matchedEmail = matcher.group();
            System.out.println("Found email: " + matchedEmail);
        }
    }
}

Шаблон регулярного выражения \\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b используется для сопоставления адресов электронной почты. Давайте разберем это:

  • \\b: Граница слов, чтобы убедиться, что мы получаем полные адреса электронной почты.
  • [A-Za-z0-9._%+-]+: Соответствует одному или нескольким вхождениям букв, цифр и некоторых специальных символов перед символом '@'.
  • @: Соответствует буквальному символу '@'.
  • [A-Za-z0-9.-]+: соответствует одному или нескольким вхождениям букв, цифр, а также '-' и '.' символы после символа '@' (доменное имя).
  • \\.: соответствует буквальной точке '.' характер.
  • [A-Z|a-z]{2,}: Соответствует двум или более вхождениям прописных или строчных букв после точки (расширение домена).
  • \\b: Граница слов, чтобы убедиться, что мы получаем полные адреса электронной почты.

Когда вы запустите приведенную выше программу Java, она выведет:

Found email: [email protected]

Регулярные выражения поддерживаются практически любым языком программирования и помогают сделать ваш код чище, делая больше с меньшими затратами.

Функциональное программирование

Функциональное программирование (ФП) — это стиль программирования, в котором вычисления рассматриваются как оценка функций, как и в математике. Он избегает использования изменяемых данных (данных, которые могут измениться) и фокусируется на использовании чистых функций, то есть функций, которые всегда производят один и тот же вывод для одного и того же ввода и не имеют побочных эффектов.

Чтобы понять FP более подробно, давайте углубимся в его основные концепции на примере Java:

Рассмотрим простой пример, где мы хотим обработать список чисел и выполнить следующие операции:

  1. Отфильтруйте четные числа из списка.
  2. Удвойте каждое оставшееся число.
  3. Сложите все удвоенные числа.

Императивный подход (нефункциональный):

В императивном подходе для этого можно использовать циклы и изменяемые переменные:

import java.util.ArrayList;
import java.util.List;

public class ImperativeApproach {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Filtering out even numbers
        List<Integer> evenNumbers = new ArrayList<>();
        for (Integer number : numbers) {
            if (number % 2 == 0) {
                evenNumbers.add(number);
            }
        }

        // Doubling each remaining number
        List<Integer> doubledNumbers = new ArrayList<>();
        for (Integer number : evenNumbers) {
            doubledNumbers.add(number * 2);
        }

        // Summing up the doubled numbers
        int sum = 0;
        for (Integer number : doubledNumbers) {
            sum += number;
        }

        System.out.println("Sum of doubled even numbers: " + sum); // Output: Sum of doubled even numbers: 60
    }
}

Функциональный подход:

Теперь давайте добьемся того же результата, используя концепции функционального программирования:

import java.util.List;

public class FunctionalApproach {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        int sum = numbers.stream()
                .filter(number -> number % 2 == 0) // Filtering out even numbers
                .map(number -> number * 2) // Doubling each remaining number
                .reduce(0, Integer::sum); // Summing up the doubled numbers

        System.out.println("Sum of doubled even numbers: " + sum); // Output: Sum of doubled even numbers: 60
    }
}

Объяснение функционального подхода:

  1. numbers.stream(): Мы начинаем с преобразования списка чисел в поток, что позволяет нам применять функциональные операции к элементам списка.
  2. .filter(number -> number % 2 == 0): Мы используем метод filter, чтобы оставить в потоке только четные числа.
  3. .map(number -> number * 2): С помощью метода map мы удваиваем каждое из оставшихся четных чисел в потоке.
  4. .reduce(0, Integer::sum): Наконец, мы используем метод reduce для суммирования всех удвоенных четных чисел в потоке. Первый аргумент reduce — это начальное значение аккумулятора (в данном случае 0), а второй аргумент — это лямбда-выражение (Integer::sum), указывающее, как накапливать значения.

Благодаря использованию концепций функционального программирования код становится более кратким, читабельным и понятным. Это также способствует лучшему разделению задач, разбивая проблему на более мелкие функции.

Разработка через тестирование

Разработка через тестирование (TDD) — это подход к разработке программного обеспечения, при котором разработчики пишут тесты перед написанием фактического кода.

Он основан на идее написания тестов перед написанием фактического кода.

Шаги, связанные с TDD, следующие:

  1. Написать тест (красный). Сначала напишите тест, определяющий желаемое поведение кода. Сначала этот тест не проходит, потому что код, который его удовлетворяет, еще не реализован. Следовательно, тест называется «красным».
  2. Напишите минимальный код (зеленый): напишите минимальный объем кода, необходимый для прохождения теста. Цель состоит в том, чтобы пройти тест как можно быстрее. Этот шаг называется «зеленым», потому что тест становится зеленым, чтобы указать на успех.
  3. Рефакторинг (улучшение). После того, как тест пройден, выполните рефакторинг кода, чтобы улучшить его дизайн, удобство сопровождения и производительность, гарантируя, что тест продолжит проходить. Этот шаг важен для поддержания кодовой базы в чистоте и сопровождении.

Цикл повторяется для каждой новой функции или изменения в коде, гарантируя, что кодовая база останется надежной, а новые изменения не приведут к неожиданным ошибкам.

Давайте посмотрим на пример TDD с использованием Java и среды тестирования JUnit:

Предположим, мы хотим реализовать простой класс, выполняющий основные арифметические операции. Давайте начнем с написания тестов для класса, прежде чем писать реальную реализацию:

Шаг 1. Напишите тест (красный)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class ArithmeticOperationsTest {

    @Test
    public void testAddition() {
        // Arrange
        int num1 = 5;
        int num2 = 10;

        // Act
        int result = ArithmeticOperations.add(num1, num2);

        // Assert
        assertEquals(15, result);
    }
}

Шаг 2. Напишите минимальный код (зеленый)

public class ArithmeticOperations {

    public static int add(int num1, int num2) {
        return num1 + num2;
    }
}

Шаг 3. Рефакторинг (необязательно)

В этом примере рефакторинг не требуется, так как код и так прост и понятен. Однако в реальных сценариях вы можете реорганизовать код, чтобы улучшить его структуру и удобство сопровождения.

После завершения цикла «красный-зеленый-рефакторинг» вы можете повторить процесс для других арифметических операций, таких как вычитание, умножение и деление, написав новые тесты, а затем реализовав минимальный код.

TDD побуждает разработчиков думать о желаемом поведении кода перед его написанием и помогает выявлять ошибки на ранних этапах процесса разработки.

Это также гарантирует, что каждый фрагмент кода тщательно протестирован и что изменения в кодовой базе не нарушают существующую функциональность. TDD может привести к созданию более надежного и удобного в сопровождении кода и обеспечить разработчикам уверенность при рефакторинге или расширении кодовой базы.

Независимо от того, создаем ли мы приложения для Интернета, мобильных устройств или настольных компьютеров, использование этих концепций закладывает основу для создания надежного и ориентированного на будущее программного обеспечения в постоянно развивающемся технологическом ландшафте.

Продолжайте исследовать, продолжайте учиться и продолжайте программировать!

В своих аккаунтах Twitter и Instagram я часто делюсь своим опытом программирования и разработки.

Спасибо за прочтение :)