Возможно, вы читали предыдущую статью о принципах SOLID, которую я написал. Следуя принципам, вы сталкиваетесь с проблемой проектирования классов, то есть как убедиться, что созданный вами дизайн классов соответствует принципам SOLID? размышления об этом могут занять много времени, так как вы должны убедиться, что дизайн, который вы сделали, соответствует критериям SOLID. К счастью, для распространенных дизайнов классов есть шаблон, и большинство людей использовали этот шаблон без их ведома. Это называется Шаблон проектирования.

Шаблон проектирования — это шаблон стиля кодирования, который можно использовать для решения общей проблемы проектирования классов. Понимание шаблонов проектирования облегчает вам решение некоторых стилей кодирования, быстрее понимает код и создает НАДЕЖНЫЙ код. Шаблоны проектирования можно разделить на 3 основные категории: поведенческие, структурные и творческие.

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

Модель поведения

Первая категория, которую я объясню, — это модели поведения. Короче говоря, поведенческий паттерн — это распределение обязанностей между объектами. В основном используются 3 распространенных шаблона: стратегия, наблюдатель и итератор.

Стратегия

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

interface Sort {
    int[] sort(int[] arr);
}

class BubbleSort implements Sort {

    @Override
    public int[] sort(int[] arr) {
        int[] copiedArr = arr.clone();
        int n = arr.length;
        for (int i = 0; i < n - 1; i++)
            for (int j = 0; j < n - i - 1; j++)
                if (arr[j] > arr[j + 1]) {
            
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
        }
        return copiedArr;
    }

}

class InsertionSort implements Sort {

    @Override
    public int[] sort(int[] arr) {
     
        int[] copiedArr = arr.clone();
        int n = arr.length;
        for (int i = 1; i < n; ++i) {
            int key = arr[i];
            int j = i - 1;
 
         
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j = j - 1;
            }
            arr[j + 1] = key;
        }
        return copiedArr;
    }
}

здесь у нас есть интерфейс, называемый Sort, который имеет метод сортировки. Затем у нас есть еще 2 класса, которые будут реализовывать интерфейс Sort. таким образом, мы можем использовать пузырьковую сортировку или сортировку вставками во время выполнения. почему бы нам не написать оператор if else для одного класса сортировки? это нарушает принцип открытия и закрытия, поскольку мы меняем существующий код каждый раз, когда пишем новый код.

Используя стратегию, мы соблюдаем 2 принципа SOLID: принцип единой ответственности и принцип «открыто-закрыто». эта стратегия, вероятно, является одним из распространенных шаблонов, которые вы будете использовать, и очень полезным. Сила стратегии заключается в том, что мы можем изменить реализацию во время выполнения, в некоторых случаях вы хотите отсортировать с помощью пузырька, а в других случаях с помощью вставки.

Наблюдатель

Этот шаблон применим не только к стилю кодирования, но также широко используется в других шаблонах, таких как распределенные системы (часто называемые шаблоном pub/sub). В любом случае, шаблон Observer заставит вас определить 2 класса, один как субъект, а другой — как наблюдатель. Субъект сообщает наблюдателю об изменении состояния класса субъекта, отправляя события классу наблюдателя.

Менее абстрактной версией объяснения будет канал на YouTube. Вы подписываетесь на канал, и каждый раз, когда канал загружает новое видео, вы будете уведомлены о новом видео.

Вот пример кода шаблона наблюдателя.

class YoutubeChannel {
    ArrayList<YoutubeUser> subscribers = new ArrayList<>();

    public void subscribe(YoutubeUser user) {
        subscribers.add(user);
    }

    public void unsubscribe(YoutubeUser user) {
        subscribers.remove(user);
    }

    public void notify(String event) {
        for (YoutubeUser subscriber : subscribers) {
            subscriber.update(event);
        }
    }

}

class YoutubeUser {
    public void update(String event) {
        System.out.println("New video just came out!");
    }
}

Как видите, у нас есть YoutubeChannel в качестве субъекта и YoutubeUser в качестве наблюдателя. Всякий раз, когда происходит событие (в данном случае канал загружает видео), подписчик получает событие и обновляется соответствующим образом.

Паттерн Observer выполняет один из принципов SOLID, то есть Open Close. потому что когда вы добавляете новый класс наблюдателя или субъекта, вам не нужно переписывать реализацию. Этот шаблон полезен, когда вам нужен объект для наблюдения за изменением одного из изменений состояния объекта.

Структурный шаблон

Структурный шаблон фокусируется на том, как гибко собирать классы. Наиболее распространенным и очень простым в использовании является Facade Pattern.

Фасад

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

class NotifySubscriberSorted {
    Sort sortAlgorithm;
    YoutubeChannel channel;

    public NotifySubscriberSorted(Sort sortAlgorithm, YoutubeChannel channel) {
        this.sortAlgorithm = sortAlgorithm;
        this.channel = channel;
    }

    void notifySorted() {
        sortAlgorithm.sort(channel.subscribers);
        channel.notify("A new Video");
    }
}

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

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

Шаблон создания

Шаблон создания фокусируется на том, как вы должны создавать экземпляр объекта. недостаточно использовать конструктор? к сожалению, нет, иногда бывают уникальные случаи, когда, например, вам нужно определить шаги создания объекта или у объекта есть семейство связанных классов и так далее. Одним из наиболее часто используемых шаблонов является Singleton.

Одиночка

Шаблон Singleton позволяет вам создать только один экземпляр этого конкретного объекта во время выполнения. Обратите внимание, что разница между синглтоном и статическим классом основана на их функциональности, синглтон нацелен на общие ресурсы, а статический класс нацелен на определение набора фиксированных реализаций в одном классе (что-то вроде фасада).

Вариант использования Singleton может быть довольно сложным, но один из них — обработка общих ресурсов. Допустим, у вас есть объект купона, который можно использовать только один раз, чтобы гарантировать, что доступ к объекту возможен только по одному, вы можете использовать этот шаблон.

Вот пример кода.

class Coupon {
    private int id;
    private boolean used;
    private static Coupon _coupon;

    private Coupon(int id) {
        this.id = id;

    }

    public static Coupon getInstance(int id) {
        if (_coupon == null) {
            _coupon = new Coupon(id);
        }
        return _coupon;
    }

}

Заметил, что конструктор является закрытым, а это означает, что вы не можете создать его экземпляр с помощью конструктора. Единственный способ создать объект купона — вызвать метод getInstance. Этот метод проверит, является ли _coupon нулевым или нет, и если да, то он создаст новый на основе идентификатора.

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

Эти 4 — некоторые хорошо известные шаблоны проектирования, они отлично подходят для начинающих, поскольку их довольно легко реализовать и они весьма полезны на практике. Обратите внимание, что при использовании шаблона проектирования вы всегда можете комбинировать шаблоны вместе, например, вы можете комбинировать наблюдатель и стратегию, чтобы вы могли уведомлять наблюдатель на основе некоторой стратегии (например, вы хотели уведомить наблюдателя с наименьшим количеством X ). Сочетание шаблонов проектирования покажет вам, насколько полезен каждый шаблон.

Я надеюсь, что вы найдете эту статью полезной.

Спасибо за чтение 🙏