Pull to refresh
36
0
Евгений @reforms

Back-End Разработчик

Send message
Ваша правда. С нашей стороны, более жизненный кейс — когда заказчик присылает скриншот от клиента, Внутренняя ошибка сервера и все. Требуется разобраться срочно, а логов нет, или не дают или присылают через 3 дня. Тогда, наличие дополнительного кода — это просто подарок.
С другой стороны, тот же заказчик выставляет определнные требования к ошибкам, отдел безопасности выставляет требования к сокрытию данных, да и самих клиентов порой выбешивает — Внутренняя ошибка сервера. EA125. Вот и получается, что не все так однозначно… :)
Если проект ориентирован на GUI, то ошибки подразделяются в прикладном смысле еще на 2 вида: Пользовательские и Системные.
Пользовательские — те, которые должны отображаться клиенту с нормальным текстом.
Все системные ошибки пользователь видит всегда как одну, например, Внутренняя ошибка сервера.
У большинства разработчиков, начинаются трудности в понимании, какую ошибку нужно выбрасывать в том или ином случае.

Вот здесь бы пару рецептов и лучших практик услышать…
Всегда пожалуйста :)

Какой прием, если не секрет?
Я думаю, самое время послушать Вас: Как Вы оцениваете предложенную реализацию в данном посте? Плюсы, минусы разумеется…
Добро пожаловать в наш клуб.
1) В java SDK нет контракта на неизменяемую (немодифицируемую) коллекцию, пожалуй основная проблема.
2) Если такой контракт добавлять, то неизбежно придется ответить на вопросы:
2.1) Какой интерфейс должен быть у неизменяемой коллекции?
2.2) Должен ли этот интерфейс/контракт наследоваться от Collection/List?
2.3) Должен ли Collection/List наследоваться от неизменяемой коллекции?
2.4) Можно ли ввести понятие неизменяемой коллекции, не сломав обратную совместимость с ранними версиями?
2.5) Должна ли неизменяемая коллекция порождать новую в методах add/remove и т.д. или бросать исключение?
Я осветил ключевые вопросы и проблемы. У каждого ответа есть свои последствия, которые могут влиять на всю эко систему SDK.
Вы чертовски проницательны: Если кому-то нужны гарантии — пусть явно попросит неизменяемый вариант коллекции.

Так эта одна из ключевых проблем, в которую упирается автор первоначальной статьи, автор этой и я с Вами. И за кажущейся простотой, скрывается далеко непростая задача.
Во-первых, с методом я не спорю (только с людьми)

А во-вторых:
>> 3) В любой части программы, работая с абстракцией (CharSequence), мы не можем сказать, какими свойствами данное представление обладает и какие гарантии может обеспечить (в общем случае)

Метод isImmutable — это следствие моих рассуждений по части пункта 3 и в контексте статьи, которую в качестве исходного материала я указал в начале.
Меня восхищает Ваша напористость и желание сделать SDK лучше.
Сразу скажу, что я согласен с автором поста Неизменяемых коллекций в Java не будет – ни сейчас, ни когда-либо
И это согласие есть результат статьи, которую я практически написал, но в последний момент отказался публиковать — так как изменил свое мнение на мнение автора :)
Вот совсем кратко мои рассуждения на тему списков:

Чтобы ответить на этот вопрос, сначала нужно поискать уже существующие решения в самом SDK, а во-вторых, посмотреть на потенциальное решение с практической точки зрения. Начнем с поиска.
Классическим представителем неизменяемых объектов в SDK являются экземпляры класса java.lang.String. Строки спроектированы таким образом, что поменять их через публичное API — невозможно. Строки имеют отношение к поиску истины и потому, что их можно рассматривать через призму коллекций. Фактически, строка — это набор символов. Но сейчас вернемся к самому типу String. Важно понять, что в SDK нет MutableString. Или перефразировав, прийти к тому, что нет такой полной иерархии как ImmutableString, UnmodifiableString и MutableString. Плохо это или хорошо — каждый решает сам.
Что касается иерархии, то я немного слукавил — иерархия есть, только не для типа String, как чего-то целого и завершенного, а для набора символов. Это всем известные StringBuilder и StringBuffer, которые как и String растут от CharSequence. Фактически, у нас есть 2 ветки: изменяемые и неизменяемые последовательности символов. Причем во главу угла поставлен контракт на чтение данных, так как CharSequence содержит только методы получения данных.
После ознакомления с готовым решением по строкам в SDK можно сделать несколько важных выводов про mutable и immutable:

1) Каждая реализация сохраняет свое свойство навсегда — нет перехода от immutable к mutable или обратно внутри одной реализации.
2) В любой части программы, работая с реализацией, мы можем точно сказать о ее свойствах — изменяемая (StringBuilder/StringBuffer) или неизменяемая (String).
3) В любой части программы, работая с абстракцией (CharSequence), мы не можем сказать, какими свойствами данное представление обладает и какие гарантии может обеспечить (в общем случае).
4) Логичным и подтвержденным реализацией подходом является переход от mutable к immutable, т.е. изменяемый набор порождает неизменяемый, но не наоборот.

Выводы как постулаты мы будем использовать для реализации неизменяемых списков. Но сначала, я хотел бы проанализировать сами выводы и понять их сильные и слабые стороны. Мой любимый — это пункт 3. На мой взгляд, для проектирования безопасного api — это огромный недостаток. Когда мы спускаем в engine некоторую абстракцию, вполне логичным, для меня, является то, что этот engine имеет право потребовать определенные гарантии надежности от источника, взамен, обещает сделать свою работу должным образом. Такие отношения можно формализовать с помощью простого метода, спросив с помощью него, какие гарантии несет реализация. В плоскости рассматриваемой проблемы им может стать контрактный метод isImmutable;
    boolean isImmutable();

Если мы используем фундаментальные принципы проектирования, то делать такой метод — неправильно. Но если мы ищем больше практичное решение, нежели фундаментальное — то допустимо.
Так как пункт 1 гласит, что свойство immutable/mutable сохраняется навсегда, то вмести с ним сохраняется и гарантия неизменяемости/изменяемости тоже — навсегда.
Рассматривая пункт 3, мы столкнулись с первой дилеммой — фундаментальность или практичность?
Пункт 2, можно интерпретировать и по-другому, что не должна одна реализация рости от второй. Т.е. ссылочное присвоение в обе стороны запрещены (ошибка компиляции).
Пункт 4, опять таки заставляет нас разобраться с дилеммой: фундаментальность или практичность? Потому что требует, наличие метода toImmutable в общем контракте. Можно читать toImmutable здесь как метод toString в классе CharSequence, с одной оговоркой.
    List<T> toImmutable();

Я утверждаю, что пункт 4 больше рожден из практической плоскости, нежели фундаментальной. И это важно. В противном случае, неизменяемая реализация должна порождать изменяемую, но на примере String, мы видим, что такого нет.
Есть еще одна особенность в типах String, StringBuilder и StringBuffer, если рассматривать их как набор символов, про которую я нарочно умолчал. Мы всегда работаем с реализацией, что сильно упрощает контекст восприятия.
Это недопустимо для списков в общем случае.
Если подытожить выше изложенную философию мысли, для решения проблемы с неизменяемыми коллекциями нам требуются в арсенале 2 метода isImmutable и toImmutable. Разумеется, исходим из того, что по факту сейчас есть в SDK.
interface Collection<E> extends Iterable<E> {
    // ...
    boolean isImmutable();
    Collection<E> toImmutable();
    // ...
}

Правильно? Не совсем. Потому что toImmutable возвращает контракт совместимый по апи с коллекциями в оба направления, что делает не возможным ограничение по типу.

И так далее, пока я не пришел к выводу, что новых коллекций не появится.

Если попытаться выдать желаемое за действительное — то в SDK все же неизменяемые коллекции могут быть и мне точно известно время — когда в java появится система типов, подобная TypeScript. Тогда различия между 2мя коллекциями будет в наличие значение у поля mutable: или true или false;
Но это совсем другая история, не находите?

Может я избалован vue, но мне приведенные примеры кажутся непосильно сложными, возможно из-за субъективного восприятия react. Приведу пример, как мы работает с модальными диалогами в vue и ts:
// Класс, отвечающий за логику сохранения документа
class SaveDocumentEngine {

    async saveDocument(doc: Document): Promise<void> {
        const docCount = 1; // константа введена для наглядности
        const userAnswer = await new SaveDocsConfirmDialog().show(docCount);
        // Если пользователь передумал
        if (userAnswer !== UserChoice.SAVE) {
            return;
        }
        // логика сохранения и проверки документа
    }
}

Явное создание диалога, вызов с параметрами и обработка результата — ничего лишнего. А вся специфика спрятана в самом диалоге.
/** Диалог подтверждения сохранения документа/документов */
@Component({
    // language=Vue
    template: `
<dialog-form title="Предупреждение" :width="500">
    <template slot="content">
        Вы действительно хотите сохранить {{data > 1 ? "документы" : "документ"}}?
    </template>
    <template slot="footer">
        <button class="btn btn-primary" @click="onSave">Сохранить</button>
        <button class="btn" @click="onCancel">Отмена</button>
    </template>
</dialog-form>
`
})
export class SaveDocsConfirmDialog extends CustomDialog<DocCount, UserChoice> {
    /** Желание сохранить */
    private onSave() {
        this.close(UserChoice.SAVE);
    }
    /** Желание отменить */
    private onCancel() {
        this.close(UserChoice.CANCEL);
    }
}

// Тип - Количество документов
type DocCount = number;

// Тип - Выбор пользователя
export enum UserChoice {
    SAVE, CANCEL
}
Хотелось бы увидеть реальные прикладные задачи, где нужен такой подход. И как в конечном счете это помогает/ухудшает код.
>> А у себя как решаете, нашли такого человека?

У нас стратегически другой подход: была сделана ставка на жесткий код-ревью. Это не значит 'взамен' рефакторингу. Но время на такой код-ревью существенно сокращает свободное.
На мой взгляд, важный момент — это наличие в команде человека, который любит свой продукт и которому не безразлична судьба проекта, кодовой базы и инфраструктуры. Как правило, именно он топит за баланс сил — рефакторниг, багфикс, разработка и ему хватает сил и мужества воплощать такое в жизнь. Без него, рефакторинг может быть мимолетной идеей, растворившейся еще на этапе обсуждения. Простите, накипело…
Задачка интересная. Сходу мне решить ее не удалось. Подумаю на досуге.
За ссылку спасибо, в какой-то мере там кладезь знаний по типа ts
Выглядит как магия.

У нас по этому поводу в компании ходит шутка: 'В коде не должно быть никакой магии, а волшебство — пожалуйста'
Огонь! Это комментарий к статье спустя 2.5 года. Приятно что читают
Обратите внимание на создание Enum(вишенка на торте), вообщем нельзя просто так взять и создать Enum.


Не совсем понятен код:
    if (parameter.getType().isEnum()) {
        methodParam[index] = Enum.valueOf(
            (Class<Enum>) (parameter.getType()),
            parameter.getType().getEnumConstants()[0].toString()
        );
      }

В чем вишенка и почему нельзя упростить:
    if (parameter.getType().isEnum()) {
        methodParam[index] = parameter.getType().getEnumConstants()[0];
    }


С enum действительно все не просто, такой код заработает в TestUtil:

    public enum ExtEnum {
    
    A("1") {}, // фигурные скобки заданы умышлено
    B("2") {}; // фигурные скобки заданы умышлено
    
    private final String text;
    
    
    ExtEnum(String text) {
        this.text = text;
    }
    
    String getTextWith(String title) {
        return title + ":" + text;
    }    
}

Подскажите, а как насчет IDE??? Такое решение все известные поддерживают???

Почему класс, а не интерфейс и типы?

Это след экспериментов, хотел подружить нормальное описание данных в классе с декораторами на типы Joi. Что-то слету у меня не получилось. А так конечно уместен интерфейс (тип), а не класс :)
Представляю вам другой способ, так сказать 'наизнанку мысли'. Тем не менее он более лаконичен и прост в понимании, со своими минусами, но как же без них
type TS<K extends boolean, SimpleType, JoiType> = K extends true ? SimpleType : JoiType;

class EtalonUser<K extends boolean = true> {
    readonly name: TS<K, string, Joi.StringSchema> = null;
    readonly age: TS<K, number, Joi.NumberSchema> = null;
    readonly phone: TS<K, string | number, Joi.AlternativesSchema> = null;
}

// Обратите внимание, как красиво получаем нужный контракт пользователя
interface IUser extends EtalonUser {
}

// Обратите внимание, как красиво получаем нужный контракт валидации пользователя
interface IJoiUserSchema extends EtalonUser<false> {
}

const User: IUser = {
    name: "Evg Pal",
    age: 33,
    phone: "+79151231231"
}
const UserSchema: IJoiUserSchema = {
    name: Joi.string(),
    age: Joi.number(),
    phone: Joi.alternatives([Joi.string(), Joi.number()])
}

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity