Последнее упрямство Четузая: Шаблоны проектирования не входят в обучение - структурный тип

Шаблоны проектирования JavaScript
Последнее упрямство Четузая: Шаблоны проектирования не входят в обучение - структурный тип

1. Что такое структурный паттерн

Структурные паттерны в основном используются для работы с комбинацией классов и объектов, соответствующих интеллект-картам:

2. Режим внешнего вида:Facade Pattern

Скройте сложность вторичной инкапсуляции интерфейса и упростите его использование. Режим внешнего вида включает в себя следующие роли:

  • Facade: Внешний вид роль
  • SubSystem: Роль подсистемы

Когда использовать

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

1. Следите за событиями в разных браузерах

Чтобы убедиться, что код, обрабатывающий событие, работает последовательно в большинстве браузеров, вам нужно обратить внимание на фазу всплытия.

При создании кроссбраузерного веб-сайтаВы непреднамеренно использовали режим внешнего вида:

var addMyEvent = function( el,ev,fn ){
  if( el.addEventListener ){//存在DOM2级方法,则使用并传入事件类型、事件处理程序函数和第3个参数false(表示冒泡阶段)
        el.addEventListener( ev,fn, false );
  }else if(el.attachEvent){ // 为兼容IE8及更早浏览器,注意事件类型必须加上"on"前缀
        el.attachEvent( "on" + ev, fn );
  }else{
       el["on" + ev] = fn;//其他方法都无效,默认采用DOM0级方法,使用方括号语法将属性名指定为事件处理程序
    }
};

2. jQuery $(document).ready(..)

мы все знакомы$(document).ready(..). В исходном коде это фактически обеспечивается вызываемым методомbindReady():

События загрузки используют два метода:window.onload()а также$(document).ready()

bindReady: function() {
    ...
    if ( document.addEventListener ) {
      // Use the handy event callback
      document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

      // A fallback to window.onload, that will always work
      window.addEventListener( "load", jQuery.ready, false );

    // If IE event model is used
    } else if ( document.attachEvent ) {

      document.attachEvent( "onreadystatechange", DOMContentLoaded );

      // A fallback to window.onload, that will always work
      window.attachEvent( "onload", jQuery.ready );

FacadeВнешний вид широко используется вjQueryбиблиотека для удобства использования. Например, мы используемjQueryиз$(el).css()или$(el).animate()и т.п. метод.

чтобы нам не приходилось вручнуюjQueryВ ядре вызываются многие внутренние методы для реализации определенного поведения и в то же время избегания ручного иDOM APIвзаимодействовать.

Похожие такжеD3.js

3. Режим адаптера:Adapter Pattern

  • Традиционный: Адаптация к проблеме несовместимости между двумя или более интерфейсами классов.
  • JS: он может дополнительно адаптироваться к двум или более базам кода, внешним и внутренним данным и т. д.

Когда использовать Часто используемые адаптеры:

  • Новые компоненты должны быть интегрированы и работать с существующими компонентами в приложении.
  • Рефакторинг, при котором части программы переписываются с улучшенным интерфейсом, но старому коду по-прежнему нужен исходный интерфейс.

1. jQuery.fn.css()нормализованное отображение

// Cross browser opacity:
// opacity: 0.9;  Chrome 4+, FF2+, Saf3.1+, Opera 9+, IE9, iOS 3.2+, Android 2.1+ 
// filter: alpha(opacity=90);  IE6-IE8 
   
// Setting opacity
$( ".container" ).css( { opacity: .5 } );

// Getting opacity
var currentOpacity = $( ".container" ).css('opacity');

Внутренне реализовано как:

get: function( elem, computed ) {
  return ropacity.test( (
        computed && elem.currentStyle ? 
            elem.currentStyle.filter : elem.style.filter) || "" ) ?
    ( parseFloat( RegExp.$1 ) / 100 ) + "" :
    computed ? "1" : "";
},

set: function( elem, value ) {
  var style = elem.style,
    currentStyle = elem.currentStyle,
    opacity = jQuery.isNumeric( value ) ? 
          "alpha(opacity=" + value * 100 + ")" : "",
    filter = currentStyle && currentStyle.filter || style.filter || "";

  style.zoom = 1;

  // 如果将不透明度设置为1,则移除其他過濾器
  //exist - attempt to remove filter attribute #6652
  if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
    style.removeAttribute( "filter" );
    if ( currentStyle && !currentStyle.filter ) {
      return;
    }
  }

  // otherwise, set new filter values
  style.filter = ralpha.test( filter ) ?
    filter.replace( ralpha, opacity ) :
    filter + " " + opacity;
}
};

2. Vueсерединаcomputed

yck - "Путь фронтенд-интервью"

существуетVueНа самом деле, мы часто используем шаблон адаптера.

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

4. Режим прокси:Proxy Pattern

Предоставляет прокси для других объектов для управления доступом к этому объекту.

Вы можете детально контролировать методы доступа к классу (объекту),Предварительная обработка перед вызовом этого метода (унифицированный код процесса помещается в агент для обработки). Выполните постобработку после вызова этого метода.

Например: агент звезды, агент арендного дома и т. д. — все агенты.

В чем смысл использования режима прокси?

  • «Принцип единой ответственности»: объектно-ориентированный дизайн поощряет распределение различных обязанностей по мелкозернистым объектам. Прокси получает функции на основе исходного объекта, не затрагивая исходный объект, что соответствует концепции проектирования слабой связи и высокой степени связности. .

  • Соблюдайте принцип «открыто-закрыто»: прокси можно удалить из программы в любой момент без модификации других частей кода.В реальных сценариях могут быть разные причины того, что прокси больше не нужен с итерацией версии , то вы можете легко заменить прокси-объект вызовом исходного объекта.

Функции:

  • Устранение связи между системами и высокой стоимостью системных ресурсов
  • Через прокси-объект можно защитить прокси-объект, чтобы на его расширяемость не влиял внешний мир.
  • В js его выполнение часто зависит от браузера
  • Прокси события использует шаблон прокси.

Классификация:

  1. удаленный прокси (Remote Proxy): предоставляет локальный прокси-объект для объекта, расположенного в другом адресном пространстве.
  2. виртуальный агент (Virtual Proxy): Если вам нужно создать объект с большим потреблением ресурсов, сначала создайте объект с относительно небольшим потреблением для представления, а реальный объект будет создаваться только при необходимости.
  3. средство защиты (Protect Proxy): Управляет доступом к объекту и может предоставлять разные уровни прав использования разным пользователям.
  4. буферный прокси (Cache Proxy): Предоставляет место для временного хранения результатов целевой операции, чтобы несколько клиентов могли совместно использовать эти результаты.
  5. Умный прокси-сервер котировок (Smart Reference Proxy): при ссылке на объект предоставьте некоторые дополнительные операции, такие как запись количества вызовов объекта.

недостаток::

  1. Из-за добавления прокси-объектов между клиентом и реальной темой некоторые типы прокси-шаблонов могут замедлять обработку запросов, например прокси-серверы защиты.

  2. Реализация шаблонов прокси требует дополнительной работы, а некоторые шаблоны прокси сложны в реализации, например удаленные прокси.

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

1. ES6серединаProxy

ES6При условииProxyКонструкторы позволяют нам легко использовать шаблон прокси:

// target: 表示所要代理的对象,handler: 用来设置对所代理的对象的行为。
let proxy = new Proxy(target, handler);

2. Предварительная загрузка изображения

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

const img = new Image();
img.src = '/some/big/size/image.jpg';
document.body.appendChild(img);

Создайте узел виртуального образаvirtualImgИ построить для создания прокси-функции:

// 图片懒加载: 虚拟代理
const createImgProxy = (img, loadingImg, realImg) => {
  let hasLoaded = false;
  const virtualImg = new Image();
  virtualImg.src = realImg;
  virtualImg.onload = () => {
    Reflect.set(img, 'src', realImg);
    hasLoaded = true;
  }
  return new Proxy(img, {
    get(obj, prop) {
      if (prop === 'src' && !hasLoaded) {
        return loadingImg;
      }
      return obj[prop];
    }
  });

Наконец, замените исходный узел изображения прокси-изображением для вызова:

const img = new Image();
const imgProxy = createImgProxy(img, '/loading.gif', '/some/big/size/img.jpg');
document.body.appendChild(imgProxy);

3. Пейджинговые данные: кеширующие прокси

Например, интерфейс и сервер разделены. При запросе данных с разбивкой на страницы из сервера, данные сервера должны запрашиваться повторно каждый раз, когда изменяется номер страницы. Мы можем кэшировать страницу и соответствующий Результаты. Когда та же страница запрашивается, она больше не будет использоваться. Вместо этого интерфейс бэкенда запроса извлекает данные из кеша.

const getFib = (number) => {
  if (number <= 2) {
    return 1;
  } else {
    return getFib(number - 1) + getFib(number - 2);
  }
}

const getCacheProxy = (fn, cache = new Map()) => {
  return new Proxy(fn, {
    apply(target, context, args) {
      const argsString = args.join(' ');
      if (cache.has(argsString)) {
        // 如果有缓存,直接返回缓存数据        console.log(`输出${args}的缓存结果: ${cache.get(argsString)}`);
        
        return cache.get(argsString);
      }
      const result = fn(...args);
      cache.set(argsString, result);

      return result;
    }
  })
}
const getFibProxy = getCacheProxy(getFib);
getFibProxy(40); // 102334155getFibProxy(40); // 输出40的缓存结果: 102334155

4. Брокер событий

Прокси события использует шаблон прокси.

<ul id="ul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
<script>
    let ul = document.querySelector('#ul')
    ul.addEventListener('click', (event) => {
        console.log(event.target);
    })
</script>

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

5. Шаблон декоратора:Decorator Pattern

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

Декораторы аналогичны концепции функций высшего порядка. Декоратор принимает базовую форму в качестве параметра, добавляет к ней обработку и возвращает ее. преимущество:

  • Преимущество в том, что основная ответственность класса (функция) отделена от функции оформления.

вопрос:

  • Цепочка оформления перекрывает область действия функции, и если она слишком длинная, это также вызовет проблемы с производительностью.

существуетJavaScriptсередина:

  • Шаблон декоратора представляет собой более гибкую альтернативу наследованию.
  • Декоратор используется для обертывания объектов с тем же интерфейсом и для добавления новых функций в виде перегруженных методов.Этот режим может добавлять собственное поведение перед декорированным объектом или за ним для достижения определенной цели.

Ядро состоит в том, чтобы кэшировать последнюю функцию

1. Простой пример

Возьмем простой пример:


var xiaoming = function () {
  this.run = function () {
    return '跑步'
  },
  this.eat = function () {
    return: '吃饭'
  }
}
// 小明可以跑步,也可以吃饭
// 下面是一个装饰类,给小明进行装饰
var decor = function (xiaoming) {
  this.run = function () {
    return xiaoming.run + '很快'
  }
  this.eat = function () {
    return xiaoming.eat + '很多'
  }
}

Через класс украшения реализуется украшение класса Сяомин.

2. TypeScriptмодификатор функции:@

«@» — это не столько модифицированная функция, сколько ссылка и вызов функции, которую она модифицирует.

Или опишите его на просторечии: @: «Меня окружает следующее».

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

test(f){
    console.log("before ...");
    f()
		console.log("after ...");
 }

@test
func(){
	console.log("func was called");
}

Запустите его напрямую и выведите результат:

before ...
func was called
after ...

3. ReactШаблон декоратора в

существуетReact, шаблон декоратора везде:


import React, { Component } from 'react';
import {connect} from 'react-redux';
class App extends Component {
 render() {
  //...
 }
}
// const mapStateToProps
// const actionCreators
export default connect(mapStateToProps,actionCreators)(App);

Ant DesignПоследний шаг в создании формы — это шаблон декоратора.

class CustomizedForm extends React.Component {}

CustomizedForm = Form.create({})(CustomizedForm);

6. Режим моста:Bridge Pattern

Шаблон моста отделяет уровень реализации от подуровня абстракции, так что две части могут быть изменены независимо. Этот режим включает в себя следующие роли:

  • Abstraction(абстрактный класс)
  • RefinedAbstraction(Расширить абстрактный класс)
  • Implementor(реализация интерфейса класса)
  • ConcreteImplementor(конкретный класс реализации)

Обычно используется в приложениях (клиентах) и драйверах баз данных (сервисах):

Приложение записывает в определенный API базы данных, например.ODBC, но после этого API каждая реализация драйвера находится для каждого поставщика базы данных (SQL Server,MySQL,Oracleд.), совершенно разные.

  • Чаще встречается при разработке драйверов, вJavaScriptредко в.
  • Кроссплатформенный дизайн некоторых программ иногда также применяет режим моста.

1. Замена темы сайта

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

Создание нескольких копий каждой страницы для каждой темы явно нецелесообразно, и режим моста — лучший выбор:

javascript-design-patterns-for-human

Различные модули:

class About{ 
    constructor(theme) {
        this.theme = theme
    }
    
    getContent() {
        return "About page in " + this.theme.getColor()
    }
}

class Careers{
   constructor(theme) {
       this.theme = theme
   }
   
   getContent() {
       return "Careers page in " + this.theme.getColor()
   } 
}

и разные темы:


class DarkTheme{
    getColor() {
        return 'Dark Black'
    }
}
class LightTheme{
    getColor() {
        return 'Off white'
    }
}
class AquaTheme{
    getColor() {
        return 'Light blue'
    }
}

Создайте тему:

const darkTheme = new DarkTheme()

const about = new About(darkTheme)
const careers = new Careers(darkTheme)

console.log(about.getContent() )// "About page in Dark Black"
console.log(careers.getContent() )// "Careers page in Dark Black"

7. Комбинированный режим:Composite Pattern

  • Также известный как шаблон «часть-целое», объекты объединяются в древовидную структуру для представления иерархии «часть-целое».
  • Сделайте так, чтобы пользователи согласовывали использование отдельных объектов и составных объектов. (См. состав карточек и форм)

Этот режим включает в себя следующие роли:

  1. Component- Объявить интерфейс объектов в композиции и реализовать поведение по умолчанию (на основеComposite)
  2. Leaf- представляет исходный объект в композиции
  3. Composite- существуетComponentРеализовать дочерние операции в интерфейсе и хранитьLeaf(primitive)объект.

1. Структура каталогов файлов в операционной системе

Файловая структура компьютера является примером шаблона композиции.

Если вы удалите папку, все содержимое этой папки также будет удалено, верно? По сути, так работает комбинированный режим. ты

Вы можете вызывать составные объекты выше в дереве структуры, и сообщения будут перемещаться вниз по этой иерархии.

2. Пакетные операцииDOM

Теория и практика шаблонов проектирования Javascript: шаблон композиции

HTMLДокументацияDOMСтруктура представляет собой естественную древовидную структуру, и самые основные элементы впиваются в дерево DOM, которое в конечном итоге формируется.DOMДокументация, очень полезная для комбинирования шаблонов.

наш обычныйjQueryБиблиотеки классов, в которых чаще применяется комбинированный паттерн, например, часто реализуется следующий код:

$(".test").addClass("noTest").removeClass("test");

независимо от $(“.test”)Будь то один элемент или несколько элементов, в конечном итоге это достигается за счет унифицированногоaddClassа такжеremoveClassинтерфейс для вызова.

Давайте просто смоделируемaddClassРеализация:

var addClass = function (eles, className) {
    if (eles instanceof NodeList) {
        for (var i = 0, length = eles.length; i < length; i++) {
            eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' '));
        }
    }
    else if (eles instanceof Node) {
        eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
    }
    else {
        throw "eles is not a html node";
    }
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");

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

8. Режим наилегчайшего веса:Flyweight Pattern

Наилегчайший вес (flyweight) режим — это режим, используемый для оптимизации производительности, "fly«Вот значение слова «муха», что означает «легковес».

  • В основном используется для уменьшения количества создаваемых объектов, чтобы уменьшить объем памяти и повысить производительность.
  • Используйте методы совместного использования для эффективной поддержки большого количества мелких объектов.

В основе шаблона Flyweight лежит использование методов совместного использования для эффективной поддержки большого количества мелких объектов.

Шаблон Flyweight очень полезен, если использование памяти слишком велико из-за создания большого количества однотипных объектов в системе. существуетJavaScript, браузеры, особенно мобильные браузеры, не выделяют много памяти, поэтому то, как экономить память, стало очень значимой вещью.

Шаблон Flyweight выполняет следующие функции:

  • Клиент: класс, используемый для вызова Flyweight Factory для получения внутренних данных, обычно объектов, требуемых приложением.
  • Фабрика наилегчайшего веса: класс для хранения данных наилегчайшего веса
  • Легковесные классы: классы, содержащие внутренние данные

1. Простой пример

В следующем примере мы создаем класс «Книга» для работы с конкретными книгами, а затем создаем класс «Книга».BookFactory", чтобы управлять тем, как создаются эти объекты Book.

Для повышения производительности памяти объекты используются повторно, если экземпляр одного и того же объекта создается дважды.

class Book {
  constructor(title, isbn, author, ratings) {
    this.title = title;
    this.isbn = isbn;
    this.author = author;
    this.ratings = ratings;
  }

  getAverageReview() {
    let averageReview =  (this.ratings.reduce((a,b) => a+b)) / this.ratings.length
    return averageReview;
  }
}

class BookFactory {
  constructor() {
    this._books = [];
  }

  createBook(title, isbn, author, ratings) {
    let book = this.getBookBy(isbn);
    if (book) { //重用对象
      return book;
    } else {
      const newBook = new Book(title, isbn, author, ratings);
      this._books.push(newBook);
      return newBook;
    }
  }

  getBookBy(attr) {
    return this._books.find(book => book.attr === attr);
  }
}

2. Реализация идей онлайн-форм

Откройте Google Таблицы, извлеките и распечатайте элемент узла.

Вы можете видеть, что даже если вы прокрутите до тысячи строк, они просто разделяют два представления.

Режим облегченного веса используется для предотвращения задержек при бесконечной прокрутке.

Вот фиктивная реализация:

Сначала HTML

<section id="app">
  <table id="table"></table>
  <div class="controls">
    <input type="range" name="scroll" id="scroll" value="0">
  </div>
</section>

стиль:

#app {
  position: relative;
  padding: 30px 0 30px 10px;
  
  #table {
    padding: 20px;
    border-radius: 10px;
    min-width: 450px;
    transition: background 0.5s;
    background: rgba(73, 224, 56, 0.1);
    
    &.low-range {
      background: rgba(73, 224, 56, 0.47);
      td {
        border-bottom: 1px solid rgba(73, 224, 56, 0.9)
      }
    }
    &.mid-range {
      background: rgba(224, 196, 56, 0.47);
      td {
        border-bottom: 1px solid rgba(224, 196, 56, 0.9)
      }
    }
    &.high-range {
      background: rgba(224, 56, 56, 0.47);
      td {
        border-bottom: 1px solid rgba(224, 56, 56, 0.9)
      }
    }
    &.ultra-high-range {
      background: rgba(224, 56, 56, 0.9);
      td {
        border-bottom: 1px solid black
      }
    }
    td {
      border-bottom: 1px solid black;
      padding: 10px;
      font-weight: bold;
    }
  }
  .controls {
    padding-top: 20px;
    
    #scroll {
      width: 450px;
      box-sizing: border-box;
    }
  }
}

Реализация логики, пожалуйста, съешьте с комментариями:

// 生成单元格实例
const makeRowCells = data => data.map(value => new Cell(value));

// 定义常量
const scrollViewport = 10; // 当前表格视图大小
const tableSize = 2000; // 行数
let scrollIndex = 0; // 初始滚动索引

let DATA = []; // 初始数据集
while (DATA.length < scrollViewport) {
  const unit = DATA.length * 10;
  DATA.push('12345678'.split('').map(() => unit));
}

/**
* cell类 - 列
*/
class Cell {
  constructor(content) {
    this.content = content;
  }
  // 更新列
  updateContent(content) {
    this.content = content;
    this.cell.innerText = content;
  }
  
  // 渲染列
  render() {
    const cell = document.createElement('td');
    this.cell = cell;
    cell.innerText = this.content;
    
    return cell;
    
  }
}

/**
* row类 - 行
*/
class Row {
  constructor(cellItems) {
    this.cellItems = cellItems;
  }
  // 更新行
  updateRowData(newData) {
    this.cellItems.forEach((item, idx) => {
      item.updateContent(newData[idx]);
    });
  }
  
  // 渲染行
  render() {
    const row = document.createElement('tr');
    this.cellItems.forEach(item => row.appendChild(item.render()));
    
    return row;
  }
}

/**
* 表格类
*/
class Table {
  constructor(selector) {
    this.$table = document.querySelector(selector);
  }
  // 添加行
  addRows(rows) {
    this.rows = rows;
    this.rows.forEach(row => this.$table.appendChild(row.render()));
  }
  
  // 更新table数据
  updateTableData(data) {
    this.rows.forEach((row, idx) => row.updateRowData(data[idx]));
  }
}

// 实例化新表
const table = new Table('#table');
// 匹配滚动条的DOM
const scrollControl = document.querySelector('#scroll');
// 在table下添加单元格行
table.addRows(
  DATA.map(dataItem => new Row(makeRowCells(dataItem))));

const onScrollChange = event => {
  // 为视图准备新数据
  DATA = DATA.map((item, idx) => item.map(cell => parseInt(event.target.value, 10)*10 + idx*10));
  // 更新当前table的数据
  table.updateTableData(DATA);
  // 添加颜色区别样式
  scrollIndex = event.target.value;
  if (event.target.value >= 0) {
    table.$table.classList = 'low-range';
  }
  if (event.target.value > tableSize * 0.4) {
    table.$table.classList = 'mid-range';
  }
  if (event.target.value > tableSize * 0.7) {
    table.$table.classList = 'high-range';
  }
  if (event.target.value > tableSize * 0.9) {
    table.$table.classList = 'ultra-high-range';
  }
};
// 设置滚动条最小和最大范围
scrollControl.setAttribute('min', 0);
scrollControl.setAttribute('max', tableSize);
// 添加滚动事件
scrollControl.addEventListener('input', onScrollChange);

// 初始化事件
const event = {target: {value: 0}};
onScrollChange(event);

9. Заключение и ссылки

До сих пор говорилось о паттерне структурного проектирования (вода), а о паттерне Flyweight стоит написать в блог отдельно.

Справочная статья

❤️ После прочтения трех вещей

Если вы найдете этот контент вдохновляющим, я хотел бы пригласить вас сделать мне три небольших одолжения:

  1. Ставьте лайк, чтобы больше людей увидело этот контент
  2. Обратите внимание на паблик «Учитель фронтенд-убеждения», и время от времени делитесь оригинальными знаниями.
  3. Также смотрите другие статьи

Закулисный ответ публичного аккаунта "Шаблоны проектирования” Получите авторский старательно самодельныйкарта разума.