JavaScript Паттерны #1 - Singleton (Одиночка)
Вставка
- Опубліковано 1 жов 2024
- #YauhenK #webDev #JS #JSPatterns
Всех приветствую в курсе «JavaScript Паттерны».
В данном видео-курсе мы с вами рассмотрим самые распространённые паттерны проектирования, которые используются при разработке.
Паттерны, или шаблоны - это определённые, зарекомендовавшие себя конструкции, которые служат для решения типовых задач программирования.
Рассматривать мы их с вами будем на примере языка JavaScript в синтаксисе ES6.
✒ Репозиторий курса:
✔ GitHub: github.com/Yau...
✒ Используемые ресурсы и инструменты:
✔ Carbon (Screenshots): carbon.now.sh/
✒ Полезные ссылки:
✔ ES6: • ES6
✒ Полный список готовых и планируемых курсов:
✔ Trello: trello.com/b/R...
✒ Автор курса:
✔ UA-cam: / yauhenkavalchuk
✔ Instagram: / yauhenkavalchuk
✔ Twitter: / yauhenkavalchuk
✔ VK: YauhenK...
✔ LinkedIn: / yauhenkavalchuk
✔ GitHub: github.com/Yau...
✔ VK (Группа): webdevcom
✒ Поддержать развитие канала: github.com/Yau...
Некоторые спрашивают, зачем это нужно на практике? Приведу свой пример: есть класс, инкапсулирующий работу с хранилищем firebase. Снаружи он реализует интерфейс типа "дай то", "дай это", "запиши то", "запиши это". Внутре же он инициализирует соединение с базой данных, содержит данные конфигурации и путей, преобразует хотелки в конкретные запросы согласно API. Так вот, не желательно при наличии созданного подключения пытаться создать его заново. Не критично, но и не желательно.
Вся история происходит в React, сервер могут пинать разные компоненты из разных мест, каждому из них нужна ссылка на экземпляр класса, они его получают через import. Мне хотелось бы гарантировать, что соединение будет инициализированно один раз. Запилил singletone, и не парюсь, один раз прочитает webpack импорты, или не один.
это только пишущим на react надо объяснять что такое синглтон, у нас в ангуляре все сервисы синглтоны
@@eugenenovikov671да умница, возьми с полки пирожок.
@@Sergey_Klimov долго думал что бы написать такое?
@@eugenenovikov671 нет. Аналогичный вопрос и тебе, чсвшный ангулярщик
@@Sergey_Klimov ммм подгорело, понимаю.
instance.count = 0 наверное должен быть в блоке if, иначе он каждый раз будет обнулять счетчик?(3:22)
Полез в комменты написать это. А тут ты )).
@@ИмяФамилия-э4ф7в я такаяже хуйня
Спасибо за видео и старания. Но даже оставив вопрос о том, где этот паттерн может быть полезен, скажу, что вот такие видео в контексте js порождают только больше непонимания относительно паттернов проектирования. В js нет никакого смысла создавать синглтон описанным в видео способом. ES-модули являются синглтонами сами по себе. Достаточно создать файл и экспортировать желаемый объект или методы. Вот и весь "синглтон". А трюки со статическим полем instance нужны только в джаве.
И тем не менее, Singleton- это отдельный паттерн, который вполне резонно попал в этот курс. Но на счёт модулей, абсолютно согласен
@@YauhenKavalchuk Я согласен, что паттерн не стоит обходить стороной. Проблема на мой взгляд в том, что почти во всех материалах, рассказывающих об этих паттернах, за кадром остаётся главное - мотивация. Когда именно они уместны. Но реальные примеры никто не приводит. Лишь абстрактные советы (бездушное "адаптер полезен, когда вам нужно использовать имеющийся класс для клиента, ожидающего другой интерфейс") или с головой погружения в "заводы для бмв".
Блин, точняк
проблема такой реализации, что свойство instance никак не защищено, и если после создания первого экземпляра класса и перед созданием второго присвоить null свойству Counter.instance, то второй экземпляр получит ссылку на пустой объект.
а можно и другой объект туда передать, со своими свойствами и переписанными методами, которые при вызове будут делать уже совсем не то, что мы планировали.
🤔
Я понял, сначала нужно ставить дизлайк, па потом лайк
🤔🙂👍
Спасибо за видео, к 3:30 вопрос каждый новый вызов конструктора будет сбрасывать счётчик, думаю инициализация count должна быть в ветке if и отрабатывать ровно один раз если !instance?
В целом да, можно и так сделать.
По сути в JS если просто объявить переменную в глобальной области, скажем: let counter = 0; и просто обращаться к ней из разных точек программы то это уже singleton ? Зачем мы вообще используем класс Counter для реализации этого паттерна ? Что бы снабдить доступ к переменной instance внешним удобным интерфейсом ? Есть ли еще причины, по которым мы используем класс ?
Это для примера. Не обязательно же там одна переменная. Воспринимай переменную как массив нужного и полезного кода, инкапсулированного в отдельный класс.
А если понадобится создать ограничение, допустим счётчик не должен быть меньше нуля? Если переменная будет глобальная, её можно вертеть как хотеть. Работая через методы можно сделать проверку, а в случае неверных значений предпринять какие-то действия (вернуть ошибку, предупреждение, поставить дэфолтное значение)
спасибо за видео, но я так и не понял ( + запутался ) примерное назначение етого паттерна... если объекты разные (false) то ето потому что они совсем 2 чужих объекта, если надо сделать (true) то надо сохранить силку объекта в другой переменой и сравнивать, и не надо юзать конструкцию класса и методов
Говорить научись, потом вопросы формулировать, а потом вопрошай.
Не понял с каких пор и по каким причинам синглтон стал антипаттерном? Вот это интересно было бы послушать
код в RunJS вроде написан, класная вещь если хочется что-то затестить в js а разворачивать среду лень, туда даже либы можно импортить, Крч всем советую поставить и попробовать
спасибо за комментарий, дельная штука!
Тогда непонятно зачем создавать новый экземпляр класса к каждой переменной. Ну был бы это export default обычный и все.
🤔
Спасибо за курс, но мне как новичку не понятно)))
Вам, как новичку, я бы не рекомендовал лезть в этот курс. И для начала подтянуть основы
я с небольшим опытом , и то думаю применить не применить и забываю что то и лезу тоже в такие курсы и мне тоже не сразу понятно =))))) один раз пока я применял синглтон
Насколько я понял это похоже на vuex, pinia, redux и тд
Да, это плюс обзёрвер
Я все же пришел к проверке такого типа:
if (Singleton.instance instanceof Singleton) return Singleton.instance;
Более правильная, на мой взгляд)
Если короче, то вообще if (Singleton.instance ) return Singleton.instance;
Make sense
вопрос такой, если мы используем require или import модули из ES6, вот этот самый require читает файл один раз во время жизни программы, тоесть если мы делаем export default new Singleton() в файле модуля, всегда когда я буду делать import в коде я получу один и тот же обьект, что по сути и есть синглтон благодаря модулям, есть ли какие то минусы у такого подхода?
Нет. Просто это не нативная реализация, а реализация с помощью сборщика
Мне кажется это лучше реализация. Описанная в видео плоха тем что пользователь может создавать "разные" экземпляры не понимая что они есть один и тот же обьект. Это может легко привести к багам.
Разница в том, что require или import это инструкции сборщика, а не часть языка JS (пока, во всяком случае). А он читает один раз, но НЕ ГАРАНТИРУЕТ, что чтение будет произведено ровно один раз. В принципе, из каких-либо своих соображений, он может прочитать пару раз. Или завтра реализацию переделают. Ставить свой код в зависимость от таких факторов - не лучшая идея.
Про синглтон: а как это будет реализваться в объекте, на основе созданного класса? То есть в новом объект будет свойство instance (или что это будет?)? И каким образом нам ссылаться из нового объекта на функцию-конструктор? И зачем нам может понадобиться ссылаться на функцию -конструктор?
Никто не ответит, потому что привыкли все только общие случаи объяснять. Как дело доходит до тонкостей, то в лучшем случае на стаковерфлоу найдешь ответ. И то даже там поглумятся над вопросом, потому что не хотят показывать, что сами ничего не знают
Явно ошибка в конструкторе, если инстанс не существует, то необходимо в условие взять все условия, а не только присвоение текущего контекста.
🤔
WTF вопросов больше, чем полученного материала!
"обьект, которий есть в одном єкземпляре" - обьект, как обьект ООП, не JS object literal, потому как последний по определению в одном єкземпляре, потому как реферальний тип. И об єтом в видео сказано. Зачем переписивать { obj } в Class что би потом через instance получить доступ к свойствам и методам, которие уже есть в obj _proto_ ? Зачем создавать два "разних" instance, чтоби потом обратиться к тому же свойству того же обьекта?
"и к которому может бить доступ из разних частей программи". вот тут вобще не понял! если ми создаем глобальную переменную, то как к ней не может бить достпа? И отсюда опять вопрос: "Глобальная переменная єто хорошо нам, или плохо?" Модульная система предотвращает доступ к глобальним переменним извне приложения, а необходимость глобальних переменних диктуется архитектурой приложения. Значит ли єто, что Синглтон вместе с ООП идут лесом?
в ES модульной системе нельзя импортировать и мутировать переменую саму по себе:
// ES module Singlton.js
export let Global = 0;
// app.js
import { Global } from 'Singlton.js' ;
console.log(Global); // 0
Global = Global++; // TypeError: can't assign to 'Global' because it's not a variable or Assignment to constant variable.
потому как модуль/файл заворачивается в IIFE (тоже упомянуто в видео), и потому: Синглтон от ООП - снова в сад! А по природе JS на сегодня - "синглтон"для глобальной переменной не минуем на корню:
// ES module Singlton.js
let Global = 0;
export const increaseGlobal = () => {
Global++;
}
export const getGlobal = () => {
return Global;
}
// app.js
import { getGlobal, increaseGlobal } from 'Singlton.js';
let Global = getGlobal();
console.log(Global); // 0
increaseGlobal();
Global = getGlobal();
console.log(Global); // 1
increaseGlobal();
Global = getGlobal();
console.log(Global); // 2
...
подскажите, так как мы используем new для получения синглтона, то по идее каждый раз, когда мы ходим взять созданный инстанс, мы все равно выделяем память для создания пустого Counter (хоть и в конструкторе подменяем на инстанс). ок ли это?
в теории можно создать статичный метод у класса (или функцию с замыканием), тогда такой проблемы не будет
Можно просто создать класс со static-методами и static-переменными. Тоже синглтон. :)
👍
Голова бобо с такого =)
$counter1 = new Counter();
$counter2 = new Counter();
$counter1 === $counter2 // true
Кроме JS где-то еще можно так? Как-то больше через статику видел. Может это нюанс JS и не стоит на нем пример паттерна показывать? или это общая практика в JS? Тут я больше про реализацию паттерна в конструкторе.
🤷♂️ как есть
зачем глобальная переменная и при чем здесь синтаксис ES6? Это же статическое свойство вы добавляете (без ключевого слова). И зачем проверка на object?
Вот такая конструкция решает проблему во всех синтаксисах:
if (!Count.instance) Count.instance = this;
else return Count.instance;
подход тот же - если объект уже существует, то не создавай новый, а верни старый.
(И возвращать this не нужно, конструкторы это неявно делают.)
я не придираюсь :) Просто не вижу слабые места в указанной мной конструкции
Else не нужен. Так как ты написала, мы проверяем, существует ли instance, и если да, то возвращаем его. Если же нет, то создаём его и полагаемся на возврат конструктором this.
Если же написать всё то же самое, но без else, то мы будем проверять, есть ли instance, если нет, то создавать его, и всегда возвращать instance. Разницы, вроде, нет особо. Что приходит на ум, а может ли this при каких нибудь условиях != instance? Понятно, что не в данном примере.
4:53 "Мы сохранили ссылку на экземпляр в статическом свойстве конструктора" - не правильнее было сказать "Сохранили ссылку на экземпляр в статическом свойстве нашего класса-функции", т.к. по тексту возникает двусмысленность из-за наличии в классе функции "constructor"?
Спасибо за уточнение!
почему ты говоришь что instance - глобальная переменная, если в джаваскрипте глобальные переменные это свойства глобального объекта?
Прочитайте по терминологии JS и всё поймёте
А через сингл паттерн можно реализовать функцию, чтоб на одной странице проигрывалось только одно видео с ютуба из всех остальных?
возможно можно
@webDev , спасибо за курс, все понятно. Попробовал реализовать этот паттерн на примере корзины, и как-то запутался..., реализовал подключение корзины в родительском компоненте, и в дочерних обращался к нему, но все же если импортировать этот же класс в дочернем и вызвать его конструктор, то он перестает быть синглтоном
import Basket from './basket'
class Parent {
static basket = null
prepare() {
this.basket = new Basket()
}
}
class Child extends Parent{
prepare() {
super.prepare()
this.basket.addProduct('item')
this.basket.getProducts()
let newInstance = new Basket()
newInstance.getProducts() //Новый экземпляр
}
}
Код корявые наброски(прошу не критиковать)), но все же, можно как-то сделать чтоб даже при импорте класс в проекте оставался синглтоном, без создания прослойки?
Так у класса или у объекта - в определении этого паттерна именно объект? Потому что начал ты с объекта, а закончил классом. Решил посмотреть еще какую-нибудь статью по теме, открыл Медиум, там тоже начинается с объекта, потом, внезапно, идёт про класс. WTF?
Так что тебе мешает переписать это под объект ?
Ну тип function Singleton() {..}
Почти ничего не меняется. Когда вызываешь конструктор через new, то тип можно просто прописать в начале условие, что если объект существует, ссылаться на него. Изи
let instance;
-class Singleton{
- constructor(){
- if(!instance) instance = this;
+function Singleton(){
+
+ if(!instance) instance = this;
+ else{
instance.count = 0;
return instance;
}
-
- showCount(){
+
+ this.showCount = function(){
console.log(instance.count);
}
- increaseCount(){
+ this.increaseCount = function(){
return instance.count++;
}
}
+
let car1 = new Singleton;
let car2 = new Singleton;
git push youtube Лови )))
Под капотом, классы в JS являются объектами. Классов в «классическом» смысле не существует.
Спасибо, объяснили четко и ясно, я правильно понял, синглтон - это что-то в единственном экземпляре, я люблю аналогию, и можно ли синглотн сравнить с рулем от автомобиля? Руль - является синглотоном, но при этом на него могут влиять другие части, допустим: рулевая рейка, поворот колес, без использования руля и т.п. Т.е синглтон это что-то единственное в своем роде. Допустим есть список песен, я делаю запрос, получаю музыку, слушаю ее, переключаю и тд - это всё объекты и данные, а вот плеер, который есть на моей странице - это синглтон, правильно ли рассуждаю?
Вроде как рассуждения верны
Спасибо за видео! Подскажите пожалуйста, подобную реализацию модуля можно назвать singleton? Ведь при подключении мы получаем один и тот же instance.
class Counter {
constructor () {
this.count = 0
}
...
}
export default new Counter()
Иными словами Store == Singleton
Да
Да супер ваще )))
Спасибо за отзыв!
@@YauhenKavalchuk К сожалению пока нет денег, с удовольствием заплатил бы вам за уроки.
Спасибо, понял только на практике)
👍
них...я не понял но очень интересно!
👍
Когда на собеседовании спрашиваю про паттерны лучше начинать с чего то другого)
Почему?)
@@YauhenKavalchuk Начинающие часто учат этот паттерн первым и на собеседовании сразу выдают его т.к. он один из самых простых. Сам так делал и более опытные коллеги говорят, что если человек начинает с одиночки, велика вероятность, что он только его и знает и надо уделить этой теме больше внимания.
Красава! Спасибо!
Пожалуйста
Автор у меня вопрос, стоит ли лезть во фронтенд без знания математики?
Я правда залез немножко уже, и теперь возник этот вопрос, и не знаю, тратить ли свое время дальше.
Стоит, всё нормально
за несколько лет работы, только месяца 2 назад, первый раз понадобилась математика и геометрия
Благодарю за ответы
Программирование намного больше похоже на физику чем на математику
Спасибо. Не задумывался что можно так сделать хоть и знал про такое поведение конструктора существует. Всегда удивлялся зачем такое правило)))
Пожалуйста
Красавелла!
Благодарю)
Не совсем понял зачем он нужен, если корзину можно просто запихнуть в localstorage и иметь доступ кней с любой страницы?
А если мы пишем на Node? ))
1. LocalStorage не для этого придуман
2. В некоторых браузерах в режиме инкогнито недоступен localStorage соответственно апка будет не рабочая
3. А если у тебя будет 10 -15 синглтонов ты будешь их все на старте через JSON.stringify() перегонять и писать в localStorage а затем через JSON.parse() доставать. Звучит тупо согласись.
Это хороший вариант, а если такую имплементацию делать не получается. Паттерны - это просто шаблоны. Если вы не понимаете зачем тот, или иной паттерн, то скорее всего он вам просто не нужен, пока...)
@@andriidou8023 Искренне надеюсь ты нескем не общаешься с людьми кроме как со мной! Манера общения у тебя как высокомерного дегенерата, тебя бы закрыть в комнате со своими фотками и своим кодом...
@@YauhenKavalchuk Да я уверен просто не сталкивался еще с таким родом проблем, поэтому и спросил когда и зачем он нужен!)
Да уж, сейчас бы паттерны на js учить в 2к19....
Это никогда не поздно
Спасибо, всё наглядно и понятно
Пожалуйста
доходчиво, спасибо за видео
Пожалуйста
градиент привлекает внимание больше чем код на темном фоне. Глаз начинает дергаться от напряжения, ну, это у меня так, по крайней мере.
Такой стиль будет во всех уроках, заделать ничего не могу, т.к. весь материал уже отснят
@@YauhenKavalchuk, ладно, будем монитор скотчем обклеивать тогда по периметру🤣