Привіт, дякую за відео. В мене питання. Пару місяців тому я експериментував/освоював білдер, находив різні приклади і вони виглядять як і в тебе(або ломбок), але я реалізував(сам дійшов) до іншого вигляду, і я розумію що тут всі праві а я щось пропускаю бо я бачу переваги(не бачу недоліків) свого білдера над іншим. Реалізація : private User(){}; private String name="Default Name"; public static class UserBuilder{ private User that; public UserBuilder(){ that=new User(); } UserBuilder name(String name){ that.name=name; return this; } User build(){ return that; } Переваги : 1) завжди є значення за замовченням. 2) не дублюються поля, в білдері їх зазвичай багато, а коли і юзерів багато то бере це памяті не мало. Що саме я пропускаю? Дякую.
Привіт TL;DR; З точки зору теорії - такий білдер програє за рахунок того, що юзер має бути мутабельним. З точки зору практики - в більшості випадків юзер і так буде мутабельним, тому це не проблема Ну і плюс цей білдер одноразовий, його інстанс не можна безпечно перевикористати Розгорнуто: Ось такий білдер простіший, так. Але є нюанс. Об'єкт, який створюється - потенційно мутабельний. В гіршому випадку в нас буде публічний сеттер (окей, хібернейт і інші фреймворки, які вимагають сеттер - окрема тема). А білдер дозволяє тримати мутабельний стан окремо від об'єкту, який він створює. Відповідно, у нас є гарантія, що після створення об'єкт вже не зміниться. А з іммутабельними об'єктами працювати простіше, вони безпечніші і призводять до меншої кількості проблем, особливо в багатопоточному середовищі. Це можна виправити приватним сеттером (ну або сетити в поле, що практично те саме). Або package-private сеттер. Суть та ж сама, просто потенційно більше класів можуть мати доступ до стану, це якось не по-ООПшному Проблема номер два. Стан об'єкту по суті зберігається в білдері. Якщо використати інстанс цього білдера для створення двох об'єктів... Результат навряд буде очікуваним - старий об'єкт теж зміниться. А це може бути не дуже приємно дебажити. Хороший білдер має або повертати новий об'єкт кожен раз, або бути одноразовим і при другому зверненні кидати ексепшн Але є простий варіант, як позбавити останньої проблеми твій білдер public User build() { User result = that; that = new User(); return result; } Але все ще лишається проблема потенційної мутабельності юзера Сказати, що це проблема - важко, бо, знов таки, хібернейт вміє працювати лише з мутабельними сутностями. Тому хочеш-не хочеш, а публічний сеттер робити доведеться (ну, не беремо до увагі ті унікальні проекти, де джава юзається без спрінга і хібера) Тут (з хібером) такий білдер скоріше має право на життя Білдер, який дублює стан (знов таки, в теорії) безпечніший. А ломбок якраз і "хороший" тим, що приховує це дублювання від нас. А оскільки він код генерує сам, нам не треба синхронізувати наші зміни в двох класах. Тому по суті дублювання є лише в результуючому коді, але не в сорцях. Відповідно, ніяких проблем, які властиві дублюванню коду, це не несе
@@FoxOwlet-ITДякую за таку розгорнуту відповідь, осягнув поки не все але зачерпнув багато. Ще мабуть "вузьке місце" щоб від створення that аж до методу build() цей стан що зберігається в білдері не змінувався де інде(в іншому потоці). Короче все воно добре "звідси аж до.. " потрібно знати де і коли використовувати і коли є своя реалізація можуть бути свої(не очевидні для гугла наприклад) проблеми/виключення. 👍
Ну проблема багатопоточності скоріше для будь-якого білдера буде актуальною, незалежно від реалізації. Але зазвичай білдер між потоками і шарити немає сенсу. Це ж по суті те саме, що намагатися виконання конструктора рознести на декілька потоків, що особливого сенсу не має
@@FoxOwlet-IT доречі, зараз подивився як я там тоді писав, я писав без сеттерів (внутрішній статичний клас бачить приватні поля, коли писав не задумовувався тому і перевірив зараз).
О шаблони проєктування, а потім і про SOLID можливо щось буде Дякую.
Так, до SOLIDу теж колись руки дійдуть_)
Привіт, дякую за відео. В мене питання.
Пару місяців тому я експериментував/освоював білдер, находив різні приклади і вони виглядять як і в тебе(або ломбок), але я реалізував(сам дійшов) до іншого вигляду, і я розумію що тут всі праві а я щось пропускаю бо я бачу переваги(не бачу недоліків) свого білдера над іншим.
Реалізація : private User(){}; private String name="Default Name"; public static class UserBuilder{ private User that; public UserBuilder(){ that=new User(); } UserBuilder name(String name){ that.name=name; return this; } User build(){ return that; }
Переваги : 1) завжди є значення за замовченням. 2) не дублюються поля, в білдері їх зазвичай багато, а коли і юзерів багато то бере це памяті не мало.
Що саме я пропускаю? Дякую.
Привіт
TL;DR; З точки зору теорії - такий білдер програє за рахунок того, що юзер має бути мутабельним. З точки зору практики - в більшості випадків юзер і так буде мутабельним, тому це не проблема
Ну і плюс цей білдер одноразовий, його інстанс не можна безпечно перевикористати
Розгорнуто: Ось такий білдер простіший, так. Але є нюанс. Об'єкт, який створюється - потенційно мутабельний. В гіршому випадку в нас буде публічний сеттер (окей, хібернейт і інші фреймворки, які вимагають сеттер - окрема тема). А білдер дозволяє тримати мутабельний стан окремо від об'єкту, який він створює. Відповідно, у нас є гарантія, що після створення об'єкт вже не зміниться. А з іммутабельними об'єктами працювати простіше, вони безпечніші і призводять до меншої кількості проблем, особливо в багатопоточному середовищі.
Це можна виправити приватним сеттером (ну або сетити в поле, що практично те саме). Або package-private сеттер. Суть та ж сама, просто потенційно більше класів можуть мати доступ до стану, це якось не по-ООПшному
Проблема номер два. Стан об'єкту по суті зберігається в білдері. Якщо використати інстанс цього білдера для створення двох об'єктів... Результат навряд буде очікуваним - старий об'єкт теж зміниться. А це може бути не дуже приємно дебажити. Хороший білдер має або повертати новий об'єкт кожен раз, або бути одноразовим і при другому зверненні кидати ексепшн
Але є простий варіант, як позбавити останньої проблеми твій білдер
public User build() {
User result = that;
that = new User();
return result;
}
Але все ще лишається проблема потенційної мутабельності юзера
Сказати, що це проблема - важко, бо, знов таки, хібернейт вміє працювати лише з мутабельними сутностями. Тому хочеш-не хочеш, а публічний сеттер робити доведеться (ну, не беремо до увагі ті унікальні проекти, де джава юзається без спрінга і хібера)
Тут (з хібером) такий білдер скоріше має право на життя
Білдер, який дублює стан (знов таки, в теорії) безпечніший. А ломбок якраз і "хороший" тим, що приховує це дублювання від нас. А оскільки він код генерує сам, нам не треба синхронізувати наші зміни в двох класах. Тому по суті дублювання є лише в результуючому коді, але не в сорцях. Відповідно, ніяких проблем, які властиві дублюванню коду, це не несе
@@FoxOwlet-ITДякую за таку розгорнуту відповідь, осягнув поки не все але зачерпнув багато. Ще мабуть "вузьке місце" щоб від створення that аж до методу build() цей стан що зберігається в білдері не змінувався де інде(в іншому потоці). Короче все воно добре "звідси аж до.. " потрібно знати де і коли використовувати і коли є своя реалізація можуть бути свої(не очевидні для гугла наприклад) проблеми/виключення. 👍
Ну проблема багатопоточності скоріше для будь-якого білдера буде актуальною, незалежно від реалізації. Але зазвичай білдер між потоками і шарити немає сенсу. Це ж по суті те саме, що намагатися виконання конструктора рознести на декілька потоків, що особливого сенсу не має
@@FoxOwlet-IT доречі, зараз подивився як я там тоді писав, я писав без сеттерів (внутрішній статичний клас бачить приватні поля, коли писав не задумовувався тому і перевірив зараз).