LINUX.ORG.RU

Статический метод-производитель вместо конструктора - это ок?

 


0

2

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

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

public class RussianMujik {
    private String snils;
    private String passport;
    private String oms;

    public static RussianMujik from(String snils, String passport, String oms) {
        RussianMujik dto = new RussianMujik();
        dto.snils = snils;
        dto.passport = passport;
        dto.oms = oms;
        return dto;
    }
}

По сути, оно выполняет ту же роль, что конструктор.

Хочется узнать, какие проблемы (или наоборот, фичи) в замене конструктора на такие методы?

Есть такие идеи: в конструкторе серверной жабе позволительно делать реордеринг, значит или мы не зовём из конструктора методы вообще, или должны заниматься маниакальнейшей safe publication с учетом всех сайд-эффектов. Иначе говоря, не можем мы звать из конструктора методы.

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

(Ну допустим, что мы будем выполнять это не только на x86_64, где оптимизаций 3,5 штуки, но еще и на всяких альфах, или что там умеет в них)

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

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

(не про этот простой пример, конечно, а вообще, на вырост).

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

Короче, напишите сюда своё имхо, плз. (А я пока наверну еще с десяток методов from xD)

=====================================================

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

public static enum FIELDS {
   snils, oms, passport;
}

public class RussianMujik {
    private String snils;
    private String passport;
    private String oms;

    public static RussianMujik from(ConcurrentHashMap data) {
        RussianMujik dto = new RussianMujik();
        dto.snils = data.get(FIELDS.snils);
        dto.passport = data.get(FIELDS.passport);
        dto.oms = data.get(FIELDS.oms);
        return dto;
    }
}

ВРОДЕ БЫ это отлично работает с синхронизацией с веб-интерфейсом с помощью сериализации (получаем JSON, конвертим в хэшмапу джексоном, посылаем в «неконструктор» как показано выше). Тут ясен огромный импакт от создания и навигации по мапе. И вообще, если веб-интерфейс, то все полимеры просраны на него, всё висит на IO и жрёт 50 гигабайтов рамы на стринги, байтоёбствовать уже не имеет смысла.

Очевидный профит тут в том, что можно простым образом зажилить в in-memory кэш параметры конструктора, и потом вместо клонирования объекта (а большинство объектов проще сделать повдоль, чем склонировать) просто конструировать их заново «неконструктором» с параметрами из кэша.

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

★★★★☆

Последнее исправление: stevejobs (всего исправлений: 6)

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

GblGbl ★★★★★
()
Последнее исправление: GblGbl (всего исправлений: 1)
Ответ на: комментарий от GblGbl

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

Во всем ищи более тонкие градации качества, не теряя из виду целое. И в чем вчера различал два-три качества, завтра увидишь массу новых (для себя) тонкостей. Нет мелочей - есть подробности. Эти «чуть-чуть» в сумме отличают Мастера от дилетанта.

— Сунь Лутан

stevejobs ★★★★☆
() автор топика

Фабрика вместо конструктора имеет смысл:

1. Если хочется отдавать разных потомков в зависимости от параметров. 2. Если хочется иметь несколько «конструкторов» с одинаковыми параметрами. 3. Если хочется иметь более выразительные имена.

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

Legioner ★★★★★
()

Так ты скинь нам выхлоп профайлера-то! Непонятно же, что и как оценивать.

anonymous
()

если у тебя течёт ссылка на недоконструированный объект

то ты что-то делаешь не так совсем в другом месте

anonymous
()
Ответ на: комментарий от Legioner

Про параллельность я нифига тебя не понял

зато

«никакой разницы между конструктором и методом нет»

ваше мнение очень важно для нас

anonymous
()

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

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

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

- можно заменить зоопарк конструкторов сервисом или DI-контейнером

- для вдохновения: матчинг одноименных функций, но с разными параметрами как в эрланг - обыденная вещь

anonymous
()
Ответ на: комментарий от anonymous

кстати да, крутая идея - запилить автоматический паттерн-матчинг по «сигнатуре» (которая на самом деле мапа), и как-то припилить его еще и к Spring IoC/AOP

stevejobs ★★★★☆
() автор топика
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от stevejobs

В java ведь кроме всего есть нативный матчинг одноименных методов, включая конструкторы - этого должно хватать.

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

Мне кажется хватает передавать в конструктор контейнер сервис-локатора. Да, в этом случае проблемой становится контроль типов. Поэтому в идеале, вместо передачи контейнера сервис-локатора, создавать объекты через DI-контейнер, который в свою очередь «знает» как явно вызвать конструкторы целевого класса с контролем параметров.

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

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

anonymous
()

Ты слишком мало знаешь jmm, поэтому у тебя получаются выводы как в том анекдоте - «Рабинович напел».

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

maloi ★★★★★
()

Ты что-то попутал. Энам нахывают как обычный класс, а варианты перечисления как константы.

Про преждевременную оптимизацию слышал же. Сам понимаешь.
Стиви, тебя взломали?)))

ii8_ ★★★★
()
Ответ на: комментарий от Legioner

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

ii8_ ★★★★
()
Последнее исправление: ii8_ (всего исправлений: 2)
Ответ на: комментарий от maloi

Что конкретно не так-то? Объясни давай, коли вызвался в столь ультимативной форме вброса :)

Очень прошу давать ответ в двух формах: в виде объяснения по формальной спецификации, и в виде объяснения по конкретной релизации. Более интересна, конечно, формальная модель. Т.е. оптимизации, которые еще не реализованы, но могут быть реализованы (т.е. формализмы не запрещают) расцениваются как вполне существующие.

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

stevejobs ★★★★☆
() автор топика
Последнее исправление: stevejobs (всего исправлений: 2)
Ответ на: комментарий от Legioner

конструктор имеет нечто типа берьера, т.е. jvm гарантирует что все что сделано в контрукторе с самим объектом будет видно после без дополнительной возни

Deleted
()
Ответ на: комментарий от Deleted

Она как гарантирует? Видимо вставляет какой-то барьер при вызове конструктора (или в конце конструктора). Значит всё, что сделали твои вызываемые методы внутри конструктора, тоже будет видно.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Legioner

не копал, читал что:

A new guarantee of initialization safety should be provided. If an object is properly constructed (which means that references to it do not escape during construction), then all threads which see a reference to that object will also see the values for its final fields that were set in the constructor, without the need for synchronization.

логично предположить что, да таки юзается синхронизация

Deleted
()
Ответ на: комментарий от Legioner

да. но это не совсем фабрики. Извиняюсь за занудство :)

ii8_ ★★★★
()
Последнее исправление: ii8_ (всего исправлений: 1)
Ответ на: комментарий от Deleted

Тут только про final-ы говорится. Собственно, поэтому их и рекомендуют пихать везде, где можно.

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

anonymous
()
Ответ на: комментарий от Legioner

в jsr133 всё подробно расписано. барьер в конце. но когда начинают лепить такой код как в оп-посте — это о другом.

anonymous
()
Ответ на: комментарий от stevejobs

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

И да, работать нужно не тебе одному, поэтому не жди что тебе возьмут и все разжуют.

maloi ★★★★★
()
Ответ на: комментарий от maloi

Нет там никакого барьера (если нет final полей)

Ага, т.е. добавляем одно final поле и конструктор сразу начинает работать иначе.

Deleted
()
Ответ на: комментарий от Deleted

Это final поля работают иначе. Синхронизировать остальные поля никто не обещает, даже если у тебя оно одно не final, а final - десяток.

maloi ★★★★★
()

Статический метод-производитель

Эт ты хорошо фабрики обозвал, конечно. :) Надо запомнить название. По делу - фабрики нужны бывают, конечно, тока ради бога в отдельном классе.

cherry-pick
()

Алсо:

    public static RussianMujik from(ConcurrentHashMap data) {
        RussianMujik dto = new RussianMujik();
        dto.snils = data.get(FIELDS.snils);
        dto.passport = data.get(FIELDS.passport);
        dto.oms = data.get(FIELDS.oms);
        return dto;
    }

А зачем ``ConcurrentHashMap``? Почему не просто ``Map`` - зачем фабрике тут знать, какую конкретно map в него засунут? Алсо, ты что во что мапишь - рандум в рандум? Если нет, то почему не указал в ``Map<X,Y>``, что куда мапишь? Ну и напоследок - нафига тут вообще Map, не пойму.

cherry-pick
()
Ответ на: комментарий от cherry-pick

Имхо стиви упоролся, надо видимо отдыхать.

Deleted
()
Ответ на: комментарий от Deleted

Давай лучше я процитирую тебя

конструктор имеет нечто типа берьера, т.е. jvm гарантирует что все что сделано в контрукторе с самим объектом будет видно после без дополнительной возни

и именно на это я говорил, что никаких барьеров нет.

maloi ★★★★★
()

Не знаю как в жабе, но в питоне это вполне идиоматичный способ добавить в класс несколько конструкторов.

PolarFox ★★★★★
()
Ответ на: комментарий от maloi

Неплохо еще и читать что цитируешь

нечто типа берьера

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

Deleted
()
Ответ на: комментарий от maloi

Еще немного и ты сможешь общаться с людями на улице.

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.