JavaScript设计模式(十三):超值午餐-组合模式

简介: 超值午餐-组合模式

组合模式(Composite)

又称部分-整体模式,将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

需求:新闻列表组件

  • 新闻列表组件要求:
    • 文字新闻,例:80岁老翁一夜暴富
    • 图片文字新闻,例:(img)90后父母给儿子取名为“张总”
    • 图标文字新闻-类似图片文字新闻,例:(icon)100岁老头儿半夜上吊
    • 图标新闻 和 文字新闻 在一行中,例:(icon)已逝之人30年后再现 | 面对黑暗得用火
    • 分类文字新闻,例:[type]他也曾拿着手术刀在夜半凝视着我的脖颈

实现:

image.png

  • 定义虚拟类,用于标记开发者需要创建怎样的类作为树上的节点(节点模板
      /**
       * 新闻虚拟类
       */
      const News = function () {
         
         
          // 子组件容器
          this.children = [];
          // 当前组件元素
          this.element = null;
      };
      News.prototype = {
         
         
          // 初始化DOM
          init: function () {
         
         
              throw new Error("请重写你的方法");
          },
          // 添加子DOM
          add: function () {
         
         
              throw new Error("请重写你的方法");
          },
          // 获取当前DOM
          getElement: function () {
         
         
              throw new Error("请重写你的方法");
          }
      };
    
  • 定义继承模式(这里使用的是寄生组合设计模式

      /**
       * 寄生组合式继承 - 寄生式继承,继承原型
       */
    
      // 原型式继承
      function inheritObject(o) {
         
         
          // 声明一个 过渡函数对象
          function F() {
         
          }
          // 过渡对象 的 原型 继承 父对象
          F.prototype = o;
          // 返回过度对象的一个实例,该实例的原型继承了父对象
          return new F();
      }
    
      /**
       * 寄生组合式继承 - 寄生式继承,继承原型
       * @param {class} subClass 子类
       * @param {class} superClass 父类
       */
      function inheritPrototype(subClass, superClass) {
         
         
          // 赋值一份父类的原型副本保存在变量中
          let p = inheritObject(superClass.prototype);
          // 修正因为重写子类原型导致子类的constructor属性被修改
          p.constructor = subClass;
          // 设置子类的原型
          subClass.prototype = p;
      }
    
  • 定义一个新闻容器(ul)存在新闻列表(继承自虚拟类
      /**
       * 创建一个新闻列表组件容器(ul)
       * @param {string} id           DOM id
       * @param {HTMLElement} parent  父容器(要把ul放到哪)
       */
      const Container = function (id, parent) {
         
         
          // 构造函数继承父类
          News.call(this);
          // 模块id
          this.id = id;
          // 模块的父容器
          this.parent = parent;
          // 构建方法
          this.init();
      };
      // 寄生式继承父类原型方法
      inheritPrototype(Container, News);
      // 构建方法
      Container.prototype.init = function () {
         
         
          this.element = document.createElement('ul');
          this.element.id = this.id;
          this.element.className = 'news-container';
      };
      // 添加子元素方法(li)
      Container.prototype.add = function (child) {
         
         
          // 在子元素容器中插入子元素
          this.children.push(child);
          // 插入当前组件元素树中
          this.element.appendChild(child.getElement());
          return this;
      };
      // 获取当前元素方法
      Container.prototype.getElement = function () {
         
         
          return this.element;
      };
      // 显示方法
      Container.prototype.show = function () {
         
         
          this.parent.appendChild(this.element);
          return this;
      };
    
      let newsContainer = new Container('news-id', document.body);
      newsContainer.show();  // <ul id="news-id" class="news-container"></ul>
    
  • 定义新闻内容的父容器(li
      /**
       * 创建新闻条目(li)
       * @param {string} className class样式选择器 
       */
      const NewsRow = function (className) {
         
         
          News.call(this);
          this.className = className;
          this.init();
      };
      inheritPrototype(NewsRow, News);
      // 初始化条目DOM
      NewsRow.prototype.init = function () {
         
         
          this.element = document.createElement('li');
          this.element.className = this.className;
      }
      NewsRow.prototype.add = function (child) {
         
         
          // 在子元素容器中插入子元素
          this.children.push(child);
          // 插入当前组件元素树中
          this.element.appendChild(child.getElement());
          return this;
      }
      NewsRow.prototype.getElement = function () {
         
         
          return this.element;
      }
    
  • 定义各类型的新闻
    • 文字新闻
        /**
         * 文字新闻
         * @param {string} text 新闻标题 
         * @param {string} href 新闻链接
         */
        const TextNews = function (text, href) {
             
             
            News.call(this);
            this.text = text || '';
            this.href = href || '';
            this.init();
        };
        inheritPrototype(TextNews, News);
        TextNews.prototype.init = function () {
             
             
            this.element = document.createElement('a');
            this.element.innerText = this.text;
            this.element.href = this.href;
        };
        TextNews.prototype.add = function (child) {
             
             
            // 在子元素容器中插入子元素
            this.children.push(child);
            // 插入当前组件元素树中
            this.element.appendChild(child.getElement());
            return this;
        };
        TextNews.prototype.getElement = function () {
             
             
            return this.element;
        };
      
    • 图片/图标 文字新闻
        /**
         * 图片/图标 文字新闻
         * @param {string} url 图片地址 
         * @param {string} text 新闻标题
         * @param {string} href 新闻链接
         */
        const ImageNews = function (url, text, href) {
             
             
            News.call(this);
            this.url = url || '默认图片地址';
            this.text = text || '';
            this.href = href || '';
            this.init();
        };
        inheritPrototype(ImageNews, News);
        ImageNews.prototype.init = function () {
             
             
            // 创建a标签
            this.element = document.createElement('a');
            this.element.href = this.href;
            // 创建图片标签
            let img = new Image();
            img.src = this.url;
            this.element.appendChild(img);
            // 创建文字标签
            let span = document.createElement('span');
            span.innerText = this.text;
            this.element.appendChild(span);
        };
        ImageNews.prototype.add = function (child) {
             
             
            // 在子元素容器中插入子元素
            this.children.push(child);
            // 插入当前组件元素树中
            this.element.appendChild(child.getElement());
            return this;
        };
        ImageNews.prototype.getElement = function () {
             
             
            return this.element;
        };
      
    • 分类文字新闻
        /**
         * 分类文字新闻
         * @param {string} type 新闻分类 
         * @param {string} text 新闻标题
         * @param {string} href 新闻链接
         */
        const TypeNews = function (type, text, href) {
             
             
            News.call(this);
            this.type = type || '[未分类] ';
            this.text = text || '';
            this.href = href || '';
            this.init();
        };
        inheritPrototype(TypeNews, News);
        TypeNews.prototype.init = function () {
             
             
            this.element = document.createElement('a');
            this.element.href = this.href;
            let iEle = document.createElement('i');
            iEle.innerText = `[${
               
               this.type}] `;
            this.element.appendChild(iEle);
            let span = document.createElement('span');
            span.innerText = this.text;
            this.element.appendChild(span);
        };
        TypeNews.prototype.add = function (child) {
             
             
            // 在子元素容器中插入子元素
            this.children.push(child);
            // 插入当前组件元素树中
            this.element.appendChild(child.getElement());
            return this;
        };
        TypeNews.prototype.getElement = function () {
             
             
            return this.element;
        };
      
  • 实现新闻列表组件:

      let newsContainer = new Container('news-id', document.body);
      newsContainer.add(
          new NewsRow('news-text').add(
              // 文字新闻,例:`80岁老翁一夜暴富`
              new TextNews('80岁老翁一夜暴富', '#')
          )
      ).add(
          new NewsRow('news-img').add(
              // 图片文字新闻,例:`(img)90后父母给儿子取名为“张总”`
              new ImageNews('https://dummyimage.com/200x100/000/fff', '90后父母给儿子取名为“张总”', '#')
          )
      ).add(
          new NewsRow('news-icon').add(
              // 图标文字新闻,例:`(icon)100岁老头儿半夜上吊`
              new ImageNews('https://dummyimage.com/16x16/00f/fff', '100岁老头儿半夜上吊', '#')
          )
      ).add(
          new NewsRow('news-text').add(
              // 图标新闻和文字新闻在一行中,例:`(icon)已逝之人30年后再现 | 面对黑暗得用火`
              new ImageNews('https://dummyimage.com/16x16/00f/fff', '已逝之人30年后再现 | ', '#')
          ).add(
              // 图标新闻和文字新闻在一行中,例:`(icon)已逝之人30年后再现 | 面对黑暗得用火`
              new TextNews('面对黑暗得用火', '#')
          )
      ).add(
          new NewsRow('news-type').add(
              // 带分类新闻,例:`[type]他也曾拿着手术刀在夜半凝视着我的脖颈`
              new TypeNews('深夜惊悚', '他也曾拿着手术刀在夜半凝视着我的脖颈', '#')
          )
      ).show();
    
      console.log(newsContainer);
    

最终的树型数据结构

image.png

特点:

组合模式能够给我们提供一个清晰的组成结构(整体-部分,树型-节点)。

目录
相关文章
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
30 3
|
2月前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
2月前
|
设计模式 存储 安全
Java设计模式-组合模式(13)
Java设计模式-组合模式(13)
|
3月前
|
设计模式 JavaScript 前端开发
从工厂到单例再到策略:Vue.js高效应用JavaScript设计模式
【8月更文挑战第30天】在现代Web开发中,结合使用JavaScript设计模式与框架如Vue.js能显著提升代码质量和项目的可维护性。本文探讨了常见JavaScript设计模式及其在Vue.js中的应用。通过具体示例介绍了工厂模式、单例模式和策略模式的应用场景及其实现方法。例如,工厂模式通过`NavFactory`根据用户角色动态创建不同的导航栏组件;单例模式则通过全局事件总线`eventBus`实现跨组件通信;策略模式用于处理不同的表单验证规则。这些设计模式的应用不仅提高了代码的复用性和灵活性,还增强了Vue应用的整体质量。
50 1
|
3月前
|
设计模式 JavaScript 前端开发
小白请看 JS大项目宝典:设计模式 教你如何追到心仪的女神
小白请看 JS大项目宝典:设计模式 教你如何追到心仪的女神
|
4月前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
79 7
|
4月前
|
设计模式 JavaScript
js设计模式【详解】—— 桥接模式
js设计模式【详解】—— 桥接模式
67 6
|
4月前
|
设计模式 JavaScript
js设计模式【详解】—— 原型模式
js设计模式【详解】—— 原型模式
49 6
|
4月前
|
设计模式 JavaScript 算法
js设计模式【详解】—— 模板方法模式
js设计模式【详解】—— 模板方法模式
47 6
|
4月前
|
设计模式 JavaScript 前端开发
JavaScript进阶 - JavaScript设计模式
【7月更文挑战第7天】在软件工程中,设计模式是解决常见问题的标准解决方案。JavaScript中的工厂模式用于对象创建,但过度使用可能导致抽象过度和缺乏灵活性。单例模式确保唯一实例,但应注意避免全局状态和过度使用。观察者模式实现了一对多依赖,需警惕性能影响和循环依赖。通过理解模式的优缺点,能提升代码质量。例如,工厂模式通过`createShape`函数动态创建对象;单例模式用闭包保证唯一实例;观察者模式让主题对象通知多个观察者。设计模式的恰当运用能增强代码可维护性。
81 0