Шаблоны проектирования JavaScript: написание сложных компонентов с помощью шаблонов композиции

Шаблоны проектирования JavaScript
Шаблоны проектирования JavaScript: написание сложных компонентов с помощью шаблонов композиции

Комбинированный режим

  • Что такое комбинированный режим
  • Комбинации паттернов в жизни
  • Практическое применение комбинированной модели
  • Зачем использовать наследование js

js литература по наследству

официальный:组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象" 的含义。

  • Ну, вы можете игнорировать ту чушь, которую я сказал выше, и слушать мой ББ ниже.

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

Комбинации паттернов в жизни

В жизни еще много комбинаций:麦当劳套餐,饭店美团X人套餐,联通流量包Подождите, они сочетают в себе несколько «отдельных людей» в целом «, это пример от жизни, позвольте мне поговорить с недавним практическим небольшим проектом.

Возьмите каштан:

Недавно в компании появился небольшой модуль под названием «Диаграмма Ганта»,githubВ основном это так:

Но как может компания хотеть такую ​​традиционную вещь, она должна быть инновационной! ! ! Что именно есть, традиционные только一条线, босс: добавь меня两条, а预期, а实际, иметьmileStone(веха), добавить总进度, Добавить...组合模式Бар 😆.

Во-первых, мы убедимся, что имя компонента называетсяGantt(KFC) Ну и что же включают в себя компоненты: разделить на эти мелкие составляющие (пакеты):画图Пакет (включает: индикатор выполнения, фоновую сетку, дополнение контента),格式化Пакет, а также несколько простых инструментов: получить самое длинное время, форматирование даты, просто чтобы дать вам каштан.

Это будет выглядеть так, когда это будет сделано:

адрес с открытым исходным кодом на github

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

  • Сначала создайте колесо наследника↓
 //建立一个属于我们自己的Jquery,里面只有inheritObject、inheritPrototype两个方法,
    (function () {
    var util = {
        inheritObject: function (o) {//对象继承封装
            var F = function () {
            };
            F.prototype = o;
            return new F();
        },
        inheritPrototype: function (subclass, supperclass) {//原型继承封装
            var obj = this.inheritObject(supperclass.prototype);
            obj.constructor = subclass;
            subclass.prototype = obj;
        }
    };
    window.$ = window.util = util;
})(window);//把闭包变量弄到全局

var Gantt = function (data) {
    this.ganttData = data;
    this.children = [];
    this.element = null;
}

Gantt.prototype = {
    init: function () {
        throw new Error('此方法必须子类重写')
    },
    build: function () {
        throw new Error('此方法必须子类重写')
    },

}

/**
 * 创建 Gantt外层容器
 * @param name
 * @param parent
 * @constructor
 */
var Container = function (name, parent) {
    Gantt.call(this);
    this.name = name;
    this.parent = parent;
    this.init();//构建子容器的基本点(id,dom,name)
}
$.inheritPrototype(Container, Gantt); //


Container.prototype = {
    /**
     *重写父类init
     */
    init: function () {
        this.element = document.createElement('div');//创建一个div元素
        this.element.name = this.name;
        this.element.id = 'ganttView';
        this.parent.append(this.element);
    },
    /**
     *重写父类build
     */
    build: function (child, text) {
        child.append(document.createTextNode(text)); //添加测试描述
        this.children.push(child);
        this.element.appendChild(child);
        return this;
    },
    getElement: function () {
        return this.element;
    },
    draw: function () {
        this.parent.appendChild(this.element)
    }
}
//调用方法
var ganttView = new Container('GanttView', document.body);
ganttView.build(document.createElement("div"), '左侧详情项目1')
    .build(document.createElement("div"), '左侧详情项目2').build(document.createElement("div"), '右侧画图')
      
   
    ```
    
  
节约时间css就不写了,
float:left;
height,width,
color,backbround,
text:center.....脑补中...
```

Затем мы замечаем,Containerявляется вторичным контейнером на основеparentТо есть самый примитивный элемент 'тело' постепенно прорисовывается внутрь, а есть более сложное наполнение (вычисление максимального временного диапазона, отрисовка фона, добавление календаря и т.д.)

Также должно бытьitemвнутри увеличение描述,详情,画图Ждать Конечно, описания, детали и чертежи — это всего лишь «пакеты», а «члены» нижнего уровня по-прежнему необходимы в качестве основы. Члены — это самое основное, у них больше нет подклассов, но они наследуют родительский класс.

Итак, давайте изменим код в целом:

  var Gantt = function (data) {
        this.ganttData = data;
        this.children = [];
        this.element = null;
    }

    Gantt.prototype = {
        init: function () {
            throw new Error('此方法必须子类重写')
        },
        build: function () {
            throw new Error('此方法必须子类重写')
        },

    }

    /**
     * 创建 Gantt外层容器
     * @param name
     * @param parent
     * @constructor
     */
    var Container = function (name, parent) {
        Gantt.call(this);
        this.name = name;
        this.parent = parent;
        this.init();//构建子容器的基本点(id,dom,name)
    }
    $.inheritPrototype(Container, Gantt); //


    Container.prototype = {
        /**
         *重写父类init
         */
        init: function () {
            this.element = document.createElement('div');//创建一个div元素
            this.element.id = 'ganttView';
            this.element.textContent= this.name;
            this.parent.append(this.element);
        },
        /**
         *重写父类build
         */
        build: function (child) {
//            child.append(document.createTextNode(text)); //添加测试描述
            this.children.push(child);
            this.element.appendChild(child.element);
            return this;
        },
        getElement: function () {
            return this.element;
        },
        draw: function () {
            this.parent.appendChild(this.element)
        }
    }

    /**
     * 创建 Gantt基础成员
     * @param name
     * @param parent
     * @constructor
     */
    var Item = function (name, parent) {
        Gantt.call(this);
        this.name = name;
        this.parent = parent;
        this.init();//构建子容器的基本点(id,dom,name)
    }
    $.inheritPrototype(Item, Container); //

    Item.prototype = {
        /**
         *重写父类init
         */
        init: function () {
            this.element = document.createElement('div');//创建一个div元素
            this.element.id = 'ganttItem';
            this.element.textContent = this.name;
//            this.parent.append(this.element);
//            return this.element;
        },
        /**
         *重写父类build
         */
        build: function (text) {
            //可以再画进度条,以及为进度条绑定点击事件
        },
        getElement: function () {
            return this.element;
        },
        draw: function () {
            this.parent.appendChild(this.element)
        }
    }

    //调用方法
    var container = new Container('GanttView', document.body);
        container.build(new Item('左侧详情项目1'))
                .build(new Item('左侧详情项目2'))
                
                ```
 
 ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/10/18/997cfd2b701a539045c4dad52aa460f3~tplv-t2oaga2asx-image.image)
    
- 为什么要使用`继承`,贼麻烦.我直接`new`不行么?

 “行,但是记住一点,我们使用了组合模式就要保障接口的统一,这也是[面向对象编程](https://juejin.cn/post/6844903475843694600)的思想,类似`java`的`interface`接口,这样我们处理回调、异常更加方便,简化了复杂的整体,又通过子类丰富了整体。“
    
我原来写的甘特图,是一次性生成的,如果产品提出,若使用`长轮循`监听到服务器,如果服务器增加了一个任务,以动画的形式动态添加一条进度条,这对于我以前的组件来说改动比较大,但是这个就很简单了,可能我们加一个add原型方法继承就好了。
    
- 那么组合模式有啥好处?
    “使得项目更加模块化,一个组件可以无限向下分成各个套餐,直到到达最底层的成员,以原子粒度书写代码,对于代码维护十分有利。”
    
    
开源的[Gantt插件](https://github.com/pkwenda/gantt.js)是基于`Jquery`的扩展插件,没有用到svg等笨重的组件,只依赖jquery,只有14k,还有一些亮点没有提交,当时写的比较匆忙,现在最近想想也许当时考虑用组合模式更加适合于以后需求的变更。极大节约开发时间。
    
### 最后希望大家能写出更加风骚的代码