Рубрики
Без рубрики

Монте сеу Строитель сейфов типа

Меню: программистов + о еде Дней назад, после рабочего дня… Помеченный java, качество кода.

Меню: программисты + о еде

Дней назад, после рабочего дня, изучал, как обычно, и дал мне ад голода!

Я решил закончить учебу, прежде чем пойти на ужин, но закуски Subway не выходите из моей головы…

Поэтому, когда бы понял, что класс, который я создал его именно таким:

public class Lanche {

    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List vegetais;
    private final Molho molho;

    public Lanche(Tamanho tamanho, Pao pao, Recheio recheio,
                  Queijo queijo, List vegetais,
                  Molho molho) {
        this.tamanho = tamanho;
        this.pao = pao;
        this.recheio = recheio;
        this.queijo = queijo;
        this.vegetais = vegetais;
        this.molho = molho;
    }

}

По-видимому, этот класс очень прост: это чисто Java, не имеет ничего слишком… Но, как и любой код, всегда есть что-то совершенствуется.

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

Não precisamos добытчики e сеттеры . Уходим от мысли, что только мы создаем добытчиками, когда действительно это необходимо. Как мы ставим все, что нам нужно в конструкторе, мы не опасность, создавать недопустимые объекты.

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

new Lanche(Tamanho.GRANDE,Pao.PARMESAO_OREGANO,Recheio.CHURRASCO,
Queijo.PRATO, List.of(Vegetal.ALFACE, Vegetal.CEBOLA),
Molho.CHIPOTLE);

Хорошо, так что это должен быть улучшен? Нет livro Código Limpa ( Чистый код де Роберт Мартин), нет capitulo де funçoes ха ум пункт респейто де параметрос де funçoes ( Аргументы функции ):

Оптимальное количество параметров для функции zero (нуль). Затем наступает (mônade), затем два (диада). Когда это возможно, следует избегать трех параметров (триады). Для более чем три, при этом следует иметь особой причины (políade) – так же не могут быть использованы.

Думаю в этой фразе, как бы нам делать, чтобы избежать вызова конструктора комплекса или нескольких setters на созданный объект?

Вход: стандарт Builder

Foi exatamente por este motivo que sergey o padrao Строитель , описание не имеет аналогов в проектах ( Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения do GOF). В Builder-это стандарт, проекты, порождающие, что позволяет разделение построение сложного объекта от его представления.

Код будет выглядеть так:

public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List vegetais;
    private final Molho molho;

    private Lanche(LancheBuilder lancheBuilder) {
        this.tamanho = lancheBuilder.tamanho;
        this.pao = lancheBuilder.pao;
        this.recheio = lancheBuilder.recheio;
        this.queijo = lancheBuilder.queijo;
        this.vegetais = lancheBuilder.vegetais;
        this.molho = lancheBuilder.molho;
    }

    public static LancheBuilder umLanche() {
        return new LancheBuilder();
    }

    public static class LancheBuilder {
        private Tamanho tamanho;
        private Pao pao;
        private Recheio recheio;
        private Queijo queijo;
        private List vegetais;
        private Molho molho;

        //Nao deixando chamar o construtor de fora desta classe
        private LancheBuilder() {
        }

        public LancheBuilder grande() {
            this.tamanho = Tamanho.GRANDE;
            return this;
        }

        public LancheBuilder normal() {
            this.tamanho = Tamanho.NORMAL;
            return this;
        }

        public LancheBuilder comPao(Pao pao) {
            this.pao = pao;
            return this;
        }

        public LancheBuilder comRecheio(Recheio recheio) {
            this.recheio = recheio;
            return this;
        }

        public LancheBuilder comQueijo(Queijo queijo) {
            this.queijo = queijo;
            return this;
        }

        public LancheBuilder comVegetais(Vegetal... vegetais) {
            this.vegetais = List.of(vegetais);
            return this;
        }

        public LancheBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche constroi() {
            return new Lanche(this);
        }
    }
}

И вызов выглядит так:

Lanche.umLanche().grande()
                .comPao(PARMESAO_OREGANO)
                .comMolho(CHIPOTLE)
                .comQueijo(PRATO)
                .comRecheio(CHURRASCO)
                .comVegetais(ALFACE, CEBOLA)
                .constroi();

Посмотрите, что я оставил конструктор класса Закуски как частные disponibilizei метод umLanche , который возвращает Builder. Это описано в книге Эффективное Java ( Эффективная Java де Джошуа Блох) и чамадо де статическая фабрика . Отличается от строителей мы можем назвать их, в результате чего стали более информативными. Кроме того, мы можем вернуть объект, который в принципе не является даже из нашего класса.

Теперь наша проблема была решена: в нашем классе Закуски у нас есть только один параметр в конструкторе: LancheBuilder .

Ближе число параметров, идеально (ноль/нуль).

Не знаю, все уже были в буфет называется Subway, но есть правила, чтобы собрать наши закуски:

Как правило работают немного отличается от фотографии, поэтому я не буду подробно форме, что обслуживающего персонала делают:

  1. Мы должны выбрать начинку и хлеб
  2. Выбираем размер
  3. Выбираем сыр
  4. Выбираем овощи
  5. Наконец, мы можем не выбрать соус

Это кафе работает именно в этом порядке. Таким образом, нам удалось выявить проблемы в наше текущее решение: формы, которые мы сделали Builder, мы не имеем порядке вызова, обратите внимание, что позвонил, все по порядку и посмотрим, что мы можем сделать:

Lanche.umLanche().constroi();

Мы подскочили на каждом шагу, мы построили объект в недопустимое состояние. ОК, мы могли бы бросить исключение, если что-то не хватает.

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

Принципал Прато: разработчики, безопасные для типов ОС

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

Идея очень проста: использовать систему типов Java, чтобы гарантировать, что во время компиляции все свойства заданы, прежде чем экземпляр класса будет построен.

Так, с помощью нашего проекта, идея Type-safe Builder, мы построим наш объект в действия. Возможное решение заключается в следующем:

public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List vegetais;
    private final Molho molho;

    private Lanche(LanchePassoFinalBuilder lanchePassoFinalBuilder) {
        this.tamanho = lanchePassoFinalBuilder.tamanho;
        this.pao = lanchePassoFinalBuilder.pao;
        this.recheio = lanchePassoFinalBuilder.recheio;
        this.queijo = lanchePassoFinalBuilder.queijo;
        this.vegetais = lanchePassoFinalBuilder.vegetais;
        this.molho = lanchePassoFinalBuilder.molho;
    }

    public static LancheBuilder umLanche(Recheio recheio, Pao pao) {
        return new LancheBuilder(recheio, pao);
    }

    static class LancheBuilder {
        private final Recheio recheio;
        private final Pao pao;

        private LancheBuilder(Recheio recheio, Pao pao) {
            this.recheio = recheio;
            this.pao = pao;
        }

        public LancheTerceiroPassoBuilder grande() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.GRANDE);
        }

        public LancheTerceiroPassoBuilder normal() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.NORMAL);
        }
    }

    static class LancheTerceiroPassoBuilder {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;

        public LancheTerceiroPassoBuilder(Recheio recheio,
                                          Pao pao,
                                          Tamanho tamanho) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
        }

        public LancheQuartoPassoBuilder comQueijo(Queijo queijo) {
            return new LancheQuartoPassoBuilder(recheio,
                    pao, tamanho, queijo);
        }
    }

    static class LancheQuartoPassoBuilder {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;
        private final Queijo queijo;

        public LancheQuartoPassoBuilder(Recheio recheio,
                                        Pao pao, Tamanho tamanho,
                                        Queijo queijo) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
            this.queijo = queijo;
        }

        public LanchePassoFinalBuilder comVegetais(Vegetal... vegetais) {
            return new LanchePassoFinalBuilder(tamanho,
                    recheio, pao,
                    queijo, List.of(vegetais));
        }
    }

    static class LanchePassoFinalBuilder {
        private final Tamanho tamanho;
        private final Recheio recheio;
        private final Pao pao;
        private final Queijo queijo;
        private final List vegetais;
        private Molho molho;

        public LanchePassoFinalBuilder(Tamanho tamanho,
                                       Recheio recheio, Pao pao,
                                       Queijo queijo,
                                       List vegetais) {
            this.tamanho = tamanho;
            this.recheio = recheio;
            this.pao = pao;
            this.queijo = queijo;
            this.vegetais = vegetais;
        }

        public LanchePassoFinalBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche build() {
            return new Lanche(this);
        }
    }
}

Таким образом, мы не можем пропустить этап обязательным. Мы должны выполнить правильно все действия для строительства. И самое главное: тот, кто будет использовать Builder не нужно знать правила торговли в кафе ранее. Код в этом кратком и, конечно, достаточно для работы в качестве собственной документации.

Помните, мы писали коды, чтобы другие люди понимали.

Результат создания закуски с Type-safe Builder выглядел так:

Lanche.umLanche(CHURRASCO, TRES_QUEIJOS)
                .grande()
                .comQueijo(PRATO)
                .comVegetais(CEBOLA, TOMATE, ALFACE)
                .comMolho(CHIPOTLE)
                .build();

Те, Type-safe Builders, справляются очень хорошо с необязательными параметрами. Просто вернуть их, чтобы недалеко от класса, который позволяет создавать конце объекта.

В Java, у нас есть библиотека jilt , который может помочь нам в создании Type-safe Builders. Языках, таких как Kotlin помогает в создании данного типа Builder.

Десерт: удаление indireções и заканчивая класса(или интерфейсы?)

Мы можем сказать, что мы создаем fluent interface .

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

Но, я покажу вам, что у нас еще есть некоторые indireções, которые могут привести к конфликтам:

Увидел только? Когда мы вводим в точку, и наша IDE нам помогает, какой метод вызывать, появляются действия внутренние.

Isso acontece porque definimos носсас классы интернас де Ланче (ос-дос-пассос) комо пакоте-привадо ( пакет-частный ).

Как будет, что мы можем сделать, чтобы еще больше ограничить доступ этих внутренних классов?

Мы можем не только определить как частный ( private ), так как иначе были бы недоступны вне класса Полдник .

В Java, у нас есть интерфейсы. Так же, как классы (не внутренние) могут быть только пакет-частного или общественного пользования.

Интерфейсы, нам удалось обеспечить API, контракт, который подписан, независимо от того, кто его реализует.

Интерфейсы определение шагов нашего Type-safe Builder бы так:

public interface CriacaoLanchePassos {

    interface LancheBuilder {
        LancheTerceiroPasso grande();
        LancheTerceiroPasso normal();
    }

    interface LancheTerceiroPasso {
        LancheQuartoPasso comQueijo(Queijo queijo);
    }

    interface LancheQuartoPasso {
        LanchePassoFinal comVegetais(Vegetal... vegetais);
    }

    interface LanchePassoFinal {
        LanchePassoFinal comMolho(Molho molho);
        Lanche build();
    }
}

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

Хорошо, после этих изменений, наш код был:

public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List vegetais;
    private final Molho molho;

    private Lanche(LanchePassoFinalBuilder lanchePassoFinalBuilder) {
        this.tamanho = lanchePassoFinalBuilder.tamanho;
        this.pao = lanchePassoFinalBuilder.pao;
        this.recheio = lanchePassoFinalBuilder.recheio;
        this.queijo = lanchePassoFinalBuilder.queijo;
        this.vegetais = lanchePassoFinalBuilder.vegetais;
        this.molho = lanchePassoFinalBuilder.molho;
    }

    public static CriacaoLanchePassos.LancheBuilder umLanche(Recheio recheio, Pao pao) {
        return new LancheBuilder(recheio, pao);
    }

    private static class LancheBuilder implements CriacaoLanchePassos.LancheBuilder {
        private final Recheio recheio;
        private final Pao pao;

        public LancheBuilder(Recheio recheio, Pao pao) {
            this.recheio = recheio;
            this.pao = pao;
        }

        public CriacaoLanchePassos.LancheTerceiroPasso grande() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.GRANDE);
        }

        public CriacaoLanchePassos.LancheTerceiroPasso normal() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.NORMAL);
        }
    }

    private static class LancheTerceiroPassoBuilder implements CriacaoLanchePassos.LancheTerceiroPasso {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;

        private LancheTerceiroPassoBuilder(Recheio recheio,
                                          Pao pao,
                                          Tamanho tamanho) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
        }

        public CriacaoLanchePassos.LancheQuartoPasso comQueijo(Queijo queijo) {
            return new LancheQuartoPassoBuilder(recheio,
                    pao, tamanho, queijo);
        }
    }


    private static class LancheQuartoPassoBuilder implements CriacaoLanchePassos.LancheQuartoPasso {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;
        private final Queijo queijo;

        private LancheQuartoPassoBuilder(Recheio recheio,
                                        Pao pao, Tamanho tamanho,
                                        Queijo queijo) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
            this.queijo = queijo;
        }

        public CriacaoLanchePassos.LanchePassoFinal comVegetais(Vegetal... vegetais) {
            return new LanchePassoFinalBuilder(tamanho,
                    recheio, pao,
                    queijo, List.of(vegetais));
        }
    }

    private static class LanchePassoFinalBuilder implements CriacaoLanchePassos.LanchePassoFinal {
        private final Tamanho tamanho;
        private final Recheio recheio;
        private final Pao pao;
        private final Queijo queijo;
        private final List vegetais;
        private Molho molho;

        private LanchePassoFinalBuilder(Tamanho tamanho,
                                       Recheio recheio, Pao pao,
                                       Queijo queijo,
                                       List vegetais) {
            this.tamanho = tamanho;
            this.recheio = recheio;
            this.pao = pao;
            this.queijo = queijo;
            this.vegetais = vegetais;
        }

        public LanchePassoFinalBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche build() {
            return new Lanche(this);
        }
    }
}

С этого, мы не имеем доступа на внутренние детали реализации:

Мы строго следовать идее сокрытия информации ( information hiding ) или туннеля. Мы не имеем никакого косвенности, у нас есть только один fluent API. Коды поэтому они обычно встречаются в библиотеках с хорошим дизайн-кода.

Você pode encontrar o código dess сообщение не является репозитарием: github.com/gabrielronei/typesafebuilder-subway-exemplo .

Хранилище делится на три ветви, которые являются три основные точки, которые мы к этому пришли: primeira_refatoracao , segunda_refatoracao и terceira_refatoracao .

Есть несколько возможных решений для этого кода, но это было решение, которое я нашел прохладно и хотел вам показать.

Я надеюсь, что идея не ясно, а что вы можете использовать что-то типа этого изо дня в день.

Помните, как все-в-Архитектура/Дизайн программного Обеспечения, это не серебряная пуля. Используется в избытке, может вызвать проблемы и трудности взаимопонимания.

Но это что-то очень интересное, что мы можем иметь, как письмо в нашу рукав!:)

Характеристика:

Ливры

  • Кодиго Лимпа (Чистый код Роберта К. Мартина) – https://amzn.to/3jzA8vh

  • Java Effectivo (Эффективная Java де Джошуа Блоха) – https://amzn.to/3yeSd67

  • Шаблоны Проектов: Решения для повторного использования программно-ориентированных объектов( Шаблоны проектирования: Элементы многоразового объектно-ориентированного программного обеспечения de GOF) – https://amzn.to/3h99Sq9

Оригинал: “https://dev.to/gabrielronei/monte-seu-type-safe-builder-1i71”