Что такое шаблоны проектирования?
Шаблоны проектирования: Процедуры создания различных типов объектов в соответствии с различными сценариями называются шаблонами проектирования.
Основная причина использования шаблонов проектирования?
① Сопровождаемость: шаблоны проектирования помогают уменьшить степень связанности между модулями, что упрощает рефакторинг кода и переключение на разные модули, а также упрощает работу программистов в больших командах и упрощает сотрудничество с другими программистами.
②Общение: Шаблоны проектирования предоставляют набор общих терминов для работы с различными типами объектов. Программисты могут более кратко описать, как работают их системы. Вам не нужно делать длинных объяснений. Часто в одном предложении, какой дизайн я использовал? Шаблоны, каждый со своим названием, что означает, что вы можете обсуждать на высоком уровне, не вдаваясь в подробности.
③Производительность: некоторые режимы являются оптимизированными режимами, которые могут значительно повысить скорость работы программы и уменьшить объем кода, который необходимо передать клиенту.
Шаблоны проектирования
Легче реализовать шаблоны проектирования, и трудно понять, когда какие шаблоны использовать.Слепое применение шаблонов проектирования без понимания их назначения является небезопасной практикой.Вы должны убедиться, что выбранный шаблон является наиболее подходящим. И не жертвуйте производительностью слишком сильно.
1. Одноэлементный режим
确保单体对象只存在一个实例。
Бизнес-сценарий: когда мы используем узел для запуска службы для подключения к базе данных, мы обычно создаем экземпляр, который подключается к базе данных (этот экземпляр является одноэлементным). Каждый запрос данных идет через этот синглтон, и для каждого запроса не будет создаваться отдельный инстанс, синглтон удобен для унифицированного управления.
var Single = (function () {
var instance
var createSingle = function (name) {
if (instance) {
return instance
}
this.name = name
instance = this
return instance
}
return createSingle
})();
var a = new Single('123')
var b = new Single('456')
console.log(a === b) // true
Во-вторых, заводской режим
工厂模式使用一个方法来决定究竟要实例化哪个具体的类。
-
Простой заводской шаблон
Используйте общую фабрику, чтобы выполнить распределение для всех классов.情景再现:Один宠物店Внутри много домашних животных, гости могут зайти в зоомагазин传递消息Так что на следующий день мы можем пойти в зоомагазин и купить кошку.猫.
①Фабрика (зоомагазин) ②Параметры передачи (сообщение) ③Создание экземпляра соответствующего класса (кошка)
class Cat{
constructor() {
this.name = '猫'
}
}
class Dog{
constructor() {
this.name = '狗'
}
}
class Factory {
constructor(role){
return this.switchRole(role)
}
switchRole(role){
switch(role){
case '猫':
return new Cat()
case '狗':
return new Dog()
default:
return {}
}
}
}
var dog = new Factory('狗') // {name:'猫'}
var cat = new Factory('猫') // {name:'狗'}
Мы уже реализовали простую фабричную модель.На данный момент нам нужно раскинуть наши маленькие мозги,чтобы разобраться в этой модели.Мы обнаружили,что каждый раз,когда в зоомагазине появляются новые питомцы на продажу,например,сегодня зоомагазин представил черепашек, Домашняя свинья, то мы должны не только сначала реализовать соответствующий класс, но иFactoryсерединаswitchRole()Дополнительные условия. Если логика метода, создающего экземпляр, изменится, класс фабрики будет изменен несколько раз.
-
сложный заводской узор
Поскольку простая фабричная модель не может удовлетворить все потребности нашего бизнеса, она может только развиваться.《javascript设计模式》Дается определение: Разница между настоящим фабричным шаблоном и простым фабричным шаблоном заключается в том, что он не использует другой класс или объект для создания экземпляра, а использует子类.Фабрика — это класс, который помещает экземпляры своих объектов-членов в подклассы.То есть мы определяем реальный шаблон фабрики, который мы видим, который должен предоставить фабрику父类抽象类, для процесса инстанцирования по переданным параметрам реализован в подклассе.
Переосмысление проблемы: кошки, собаки, черепахи, домашние свиньи, можно ли разделить эти классы, появились подклассы Гарфилда, перса, сиба-ину, аляски. При покупке питомцев нужны особые условия подкласса вышеназванных растений, способных решить эту проблему: появление专卖店Эти специализированные магазины, специализирующиеся на продаже кошек, собак и домашних свиней (工厂子类) каждый поддерживает свой собственный класс, когда вам нужен новый магазин, вы можете повторно реализовать его工厂子类
class Cat{
constructor() {
this.name = '猫'
}
}
class Garfield {
constructor() {
this.name = '加菲猫'
}
}
class Persian {
constructor() {
this.name = '波斯猫'
}
}
class Dog{
constructor() {
this.name = '狗'
}
}
// 定义成为抽象类,工厂的父类,不接受任何修改
class Factory {
constructor(role){
return this.createModule(role)
}
createModule(role){
return new Error('我是抽象类不要改我,也不要是实例化我')
}
}
// 猫的专卖工厂,需要重写父类那里继承来的返回实例的方法。购买猫的逻辑可以放在找个类中实现。
class CatFactory extends Factory{
constructor(role){
super(role)
}
// 重写createFactory的方法
createModule(role){
switch(role){
case '加菲猫':
return new Garfield()
case '波斯猫':
return new Persian()
default:
return {}
}
}
}
.... 狗、宠物猪、乌龟的都可以重新继承父类Factory。
var catFac = new CatFactory('波斯猫')
console.log(catFac)
Суммировать:复杂工厂模式Измените фабричный класс в исходном простом фабричном режиме на抽象类Судя по выходу实例строить разные工厂子类. Таким образом, он не будет изменен на абстрактный класс фабрики, который соответствует принципу проектирования и обеспечивает расширяемость.
3. Режим моста
将抽象与其实现隔离开来,以便二者独立变化。
Когда вы увидите приведенное выше предложение, вы почувствуете себя немного сбитым с толку.Взгляните на следующий код:
//一个事件的监听,点击元素获得id,根据获得的id我们发送请求查询对应id的猫
element.addEventListener('click',getCatById)
var getCatById = function(e){
var id = this.id
asyncRequst('Get',`cat.url?id=${id}`,function(resp){
console.log('我已经获取了信息')
})
}
Давайте взглянем на функцию API getCatById, мы можем понять ее как抽象И процесс клика实现эффект, но мы обнаружили, что getCatById и логика реализации не полностью разделены,getCatByIdЭто API, который может работать только в браузере.Согласно механизму работы функции прослушивания событий, объект события естественно будет передан этой функции в качестве первого параметра.В данном примере он не используется, но мы видимvar id = this.id, если вы хотите юнит-тестировать этот API, или выполнять его в командной среде, то вам можно только пожелать удачи, любой API не должен把它与任何特定环境搅在一起
// 改写getCatById 将抽象与现实完全隔离,抽象完全依赖传参,同时我们在别的地方也可以引用,不受制与业务
var getCatById = function(id,callback){
asyncRequst('Get',`cat.url?id=${id}`,function(resp){
console.log('我已经获取了信息')
})
}
В это время мы обнаружили, что абстрактный getCatById больше не может напрямую использоваться в качестве обратного вызова функции события, В это время мы должны торжественно пригласить нас桥接模式Здесь должны быть брызги.
element.addEventListener('click',getCatByIdBridge)
var getCatByIdBridge(e){ // getCatByIdBridge 桥接元素
getCatById(this.id,function(cat){
console.log('request cat')
})
}
Мы видим, чтоgetCatByIdBridgeЭто продукт режима моста. Общение абстрактно и реалистично桥梁. С этим уровнем соединения область использования API getCatById значительно расширяется и не привязана к объекту события.
Резюме: Шаблон моста очень полезен при реализации API, мы используем этот шаблон для弱化Связь между API и классами и объектами, которые его используют, этот шаблон распространен в js.事件驱动большой пользы.
В-четвертых, режим стратегии
定义一系列的规则,根据环境的不同我们执行不同的规则,来避免大量重复的工作。
В соответствии с приведенным выше утверждением можно смутно угадать режим стратегии.Для реализации режима стратегии нам нужны два класса: во-первых, нам нужен класс, который определяет ряд правил.策略类Это краеугольный камень всей модели стратегии. Затем есть метод, который выставляет внешний метод环境类,通过传递不同的参数给环境类,环境类从策类中选取不同的方法执行,最终返回结果
Бизнес-сценарий: в качестве внешнего интерфейса неизбежно выполнять проверку формы. Когда вы не используете библиотеку, вам неизбежно потребуется написать некоторые методы, такие как регулярная проверка, пустое суждение и суждение о типе. На данный момент ваш код выглядит следующим образом:
// vue下的表单校验
checkForm () {
if (this.form.realName === '') {
Toast.fail('真实姓名不能为空')
return false
}
if (!/^[\u4e00-\u9fa5]{0,}$/.test(this.form.realName)) {
Toast.fail('请输入中文')
return false
}
if (!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(this.form.idCardNum)) {
Toast.fail('请输入正确的身份证格式')
return false
}
return true
},
Примечание: в написании таким образом нет проблем, но если мы внимательно посмотрим на этот код, мы обнаружим, что этот код почти непригоден для использования.Даже если нам нужно оценить китайский язык для полей в других местах, нам нужно снова использовать обычное суждение. , а то когда в форме много внутренних полей проверки, так что вышеописанный способ будет слишком избыточен
Идея модификации: мы поместили конкретную реализацию проверки в класс стратегии, чтобы использовать единый метод для единообразной поддержки многократно используемых правил проверки, а затем использовать открытый класс среды для каждого бизнес-сценария и передать требуемое имя метода. , Проверьте значение, а затем вызовите стратегию, чтобы вернуть результат.
// 策略类为表单校验正则及及自定义的规则
var rules = {
// 是否中文
isChinese: function (value) {
if (/^[\u4e00-\u9fa5]{0,}$/.test(value)) {
return true
} else {
return false
}
},
// 是否不为空
notNull: function (value) {
if (value !== '') {
return true
} else {
return false
}
},
.... // 不同策略
}
// 环境类
var validate = function(rule,value) {
return rules[rule](value);
};
//业务执行
const isChinese = validate('isChinese',value)
const notNull = validate('notNull',value)
const checkResult = isChinese||notNull
if(checkResult){
.....
}
Резюме: Реализован простой паттерн стратегии: абстрактный метод проверки помещается в класс стратегии, после чего вызываются разные стратегии по разным параметрам, что реализует повторное использование стратегий и упрощает код. Но является ли такая стратегия настоящей любовью в твоем сердце, братан?
недостаток: Вышеупомянутый код уже удовлетворяет режиму стратегии, но поддержка определенных сервисов кажется немного ошибочной Прежде всего, когда вышеупомянутые сервисы выполняются, вы обнаружите, что мы возвращаем логическое значение для одной проверки, если нам нужна функция обратного вызова, которая дает сбой Если вы хотите вызвать, то вам все равно нужно добавить оператор суждения.На самом деле невозможно иметь оба, то есть приведенный выше код может поддерживать только обратный вызов с полным отказом после всех Обнаружены элементы формы, и при сбое одного элемента формы Неподдерживаемые пользователи часто получают уведомление об ошибке в форме после ввода всех элементов формы, и они не знают, какой именно.
оптимизация: изменить класс среды, передать массив, сформированный объектом параметра, или одиночный объект параметра, при передаче объекта параметра предоставляется функция отказа, а метод тестирования предоставляется извне, обходя и проверяя объект параметра. массив, все успешные возвращают истину, а функция обратного вызова при ошибке выполнения возвращает в то же время ложь.
class Validate {
constructor () {
this.cache = []
if (Array.isArray(arguments[0])) {
// 数组的单个元素{rule:string[规则的名称],value:any[校验的值],efn:fn[失败的回调]}
this.cache = arguments[0]
}
// 传入参数为对象时
this.cache.push(arguments[0])
}
// 执行校验,失败的话执行失败的回调,成功静默,所有的参数符合规则则返回true
valid () {
let i = 0
for (const value of this.cache) {
if (rules[value.rule] && rules[value.rule](value.value)) {
i++
} else {
if (value.efn) value.efn()
return false
}
}
return i === this.cache.length
}
}
Суммировать: режим стратегии можно применять, когда имеется много утверждений суждения, а содержание суждения является одним и тем же режимом. Классы стратегий в режиме стратегии можно использовать повторно, чтобы избежать копирования и вставки во многих местах, а независимые классы стратегий легко понять, переключать и расширять.
Промежуточная модель
中介者模式的作用就是解除对象与对象之间的紧耦合关系。对象与对象中的通信以中介者为媒介触发。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
Анализ сценария: Настройте две роли: арендодатель и арендатор. Арендодатели и арендаторы образуют сетку отношений «многие ко многим».
Предварительные условия Мы создаем две роли, арендодателя и арендатора, и используем простой фабричный шаблон.
// 房东类
class Owener{
constructor(name){
this.name=name
}
// 想要出租
sell(renters=[]){
renters.map(renter=>{
renter.onMessage(`${this.name}想要出租房子`)
})
}
// 收到消息
onMessage(msg){
console.log(`${this.name}知道了${msg}`)
}
}
// 租客类
class Renter{
constructor(name){
this.name=name
}
// 收到消息
onMessage(msg){
console.log(`${this.name}知道了${msg}`)
}
}
class Factory{
constructor(role,name){
this.name = name
return this.switchRole(role,name)
}
switchRole(role,name){
switch (role) {
case 'owner':
return new Owener(name)
case 'renter':
return new Renter(name)
default:
return new Error('无当前角色')
}
}
}
var owner1 = new Factory('owner','房东一')
var owner2 = new Factory('owner','房东二')
var renter1 = new Factory('renter','租客一')
var renter2 = new Factory('renter','租客二')
var renter3 = new Factory('renter','租客三')
owner.sell([renter1,renter2,renter3])
Приведенный выше код завершает волю домовладельца освободить сдаваемый в аренду дом, и трое арендаторов получают новости. Можно добиться и обратного. Арендатор публикует сообщение, арендодатель получает его, но мы обнаруживаем связь между арендодателем и арендатором. По-прежнему существует тесная связь, и разные отношения между арендодателями и арендаторами требуют разных подходов, и весь класс арендодателей станет чрезвычайно раздутым, и наоборот. Итак, мы вводим модель посредника
В приведенном выше примере посредническая модель понимается как посредническая компания, которая действует как мост для поддержания отношений между арендодателями и арендаторами Арендаторы и арендодатели должны учитывать только свое собственное поведение и не должны учитывать влияние поведения на этих объектах отношений. Эти задачи возложены на нас посредник
оптимизация: создание посредника для обработки взаимных вызовов между арендодателем и арендатором и определения взаимных отношений. Посредник обеспечивает двусторонний метод вызова для арендодателя и арендатора, а затем реализует распределение связанных объектов.
class Owener{
constructor(name){
this.name=name
}
// 想要出租
sell(mediator){
mediator.sendMessage('owner',`${this.name}想要出租房子`)
}
// 收到消息
onMessage(msg){
console.log(`${this.name}知道了${msg}`)
}
}
class Renter{
constructor(name){
this.name=name
}
// 想要租房
rent(mediator){
mediator.sendMessage('renter',`${this.name}想要租房子`)
}
// 收到消息
onMessage(msg){
console.log(`${this.name}知道了${msg}`)
}
}
class Factory{
constructor(role,name){
this.name = name
return this.switchRole(role,name)
}
switchRole(role,name){
switch (role) {
case 'owner':
return new Owener(name)
break;
case 'renter':
return new Renter(name)
break;
default:
return new Error('无当前角色')
break;
}
}
}
class Mediator{
constructor(owner,renter){
// 房东集合
this.owner = owner
// 房客集合
this.renter = renter
}
sendMessage(role,msg){
if(role === 'owner'){
for(const value of this.renter){
value.onMessage(msg)
}
}
if(role === 'renter'){
for(const value of this.owner){
value.onMessage(msg)
}
}
}
}
var owner1 = new Factory('owner','房东一')
var owner2 = new Factory('owner','房东二')
var renter1 = new Factory('renter','租客一')
var renter2 = new Factory('renter','租客二')
var renter3 = new Factory('renter','租客三')
var mediator = new Mediator([owner1,owner2],[renter1,renter2,renter3])
owner1.sell(mediator)
租客一知道了房东一想要出租房子
租客二知道了房东一想要出租房子
租客三知道了房东一想要出租房子
renter1.rent(mediator)
房东一知道了租客一想要租房子
房东二知道了租客一想要租房子
Суммировать: Арендодатель и арендатор сохраняют свое поведение и уведомляют посредника. Посредник поддерживает объектные отношения и вызовы объектных методов. Преимущества: Отношения объектов могут быть свободно определены и ясны в посреднике. Уменьшено раздувание для обоих классов. Жесткая связь между двумя объектами удаляется. Недостаток: отсутствие раздувания двух классов отношений приводит к раздуванию класса-посредника.
6. Режим декоратора
为对象增添特性的技术,它并不使用创建新子类这种手段
Анализ сценария: прочитав другие статьи, я тоже зайду сюда, хи-хи~, магазин велосипедов: есть четыре марки велосипедов: A, B, C и D. В это время вы можете выбрать аксессуары, фары, переднюю корзину. . Если мы будем следовать обычному методу создания подклассов, мы сначала определим родительские классы велосипедов четырех марок A, B, C и D, а затем реализуем подкласс, наследуя родительский класс в соответствии с аксессуарами.
// A自行车父类
class Bicycle{
constructor(){
this.type = 'A'
}
getBicycle(){
console.log('A自行车的售价100')
}
}
// 拥有车灯的A类自行车类
class BicycleDeng extends Bicycle{
constructor(){
super()
}
setDeng(){
console.log('我安装上了大灯')
}
}
Из приведенного выше кода мы можем обнаружить, что: когда нам нужно исчерпать возможные велосипеды в сцене: у класса A есть фары, у класса A есть передние рули, у класса B есть фары... всего 4 * 2, всего восемь классов. В настоящее время мы используем метод создания нового подкласса, чтобы исчерпывающе перечислить все ситуации в классе сущностей.Если вы хотите создать экземпляр объекта, вам нужно только найти соответствующий класс сущностей.
оптимизация: Если будет больше типов велосипедов и больше аксессуаров, геометрия класса сущностей увеличится. Вы не должны тратить остаток своей жизни на обслуживание класса сущностей. Давай, выходи из нашего режима декоратора. Сначала вернемся к бизнес-сценарию: A, B, C и D можно временно назвать компонентами. А наши аксессуары - это декораторы. Главная мысль:Вместо создания нового подкласса каждый декоратор поддерживает один класс декоратора, и передача компонента в качестве экземпляра в класс декоратора для «украшения» может переписать исходный метод или добавить новый метод.Таким образом, нам нужно только поддерживать класс компонента плюс класс декоратора 4+2 класса 6.
class Bicycle{
constructor(){
this.type = 'A'
// 自行车售价
this.price = 5000
}
getBicycle(){
console.log(`A自行车的售价${this.price}`)
}
}
class BicycleDecorator{
constructor(bicycle){
// 传入的组件
this.bicycle = bicycle
// 大灯售价
this.dengPrice = 200
}
// 新增方法
openDeng(){
console.log('我打开了灯')
}
// 重写方法
getBicycle(){
console.log(`A自行车的售价${this.bicycle.price + this.dengPrice}`)
}
}
// 先创建类型自行车
var a = new Bicycle()
// 装饰后替换原有
a = new BicycleDecorator(a)
a.getBicycle() // A自行车的售价5200
Резюме: Шаблон декоратора предлагает новую идею добавления функций к объектам. Он устраняет необходимость создания нового подкласса, соответствующего классу сущностей с наименьшими единицами. Метод добавления новых функций в родительский класс путем передачи родительского класса состоит в том, чтобы сохранить исходный родительский класс.Есть методы, которые также являются гибкими. очень ароматный~~
заключительные замечания
В этой статье представлены некоторые шаблоны проектирования и их собственное понимание. Могут быть отклонения в понимании и приветствуются для обсуждения. Эта статья относится к «шаблонам проектирования JavaScript». После этого, если будут добавлены шаблоны проектирования, они будут продолжать обновляться. благодарныйТеперь есть выражение, верное 囧rzуказать на ошибки