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

特点:

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

目录
相关文章
|
16天前
|
设计模式 存储 安全
「全网最细 + 实战源码案例」设计模式——组合模式
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它允许客户端以一致的方式对待单个对象和对象集合,简化了复杂结构的处理。组合模式包含三个主要组件:抽象组件(Component)、叶子节点(Leaf)和组合节点(Composite)。通过这种模式,客户端可以统一处理简单元素和复杂元素,而无需关心其内部结构。适用于需要实现树状对象结构或希望以相同方式处理简单和复杂元素的场景。优点包括支持树形结构、透明性和遵循开闭原则;缺点是可能引入不必要的复杂性和过度抽象。
72 22
|
1月前
|
设计模式 数据安全/隐私保护
Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式
这篇文章介绍了在Next.js框架下,如何处理中后台管理系统中特殊页面(如登录页)不包裹根布局(RootLayout)的问题。作者指出Next.js的设计理念是通过布局的嵌套来创建复杂的页面结构,这虽然保持了代码的整洁和可维护性,但对于特殊页面来说,却造成了不必要的布局包裹。文章提出了一个解决方案,即通过判断页面的skipGlobalLayout属性来决定是否包含RootLayout,从而实现特殊页面不包裹根布局的目标。
90 33
|
3月前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
57 2
|
5月前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
4月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
49 3
|
5月前
|
设计模式 存储 安全
Java设计模式-组合模式(13)
Java设计模式-组合模式(13)
|
6月前
|
设计模式 JavaScript 前端开发
从工厂到单例再到策略:Vue.js高效应用JavaScript设计模式
【8月更文挑战第30天】在现代Web开发中,结合使用JavaScript设计模式与框架如Vue.js能显著提升代码质量和项目的可维护性。本文探讨了常见JavaScript设计模式及其在Vue.js中的应用。通过具体示例介绍了工厂模式、单例模式和策略模式的应用场景及其实现方法。例如,工厂模式通过`NavFactory`根据用户角色动态创建不同的导航栏组件;单例模式则通过全局事件总线`eventBus`实现跨组件通信;策略模式用于处理不同的表单验证规则。这些设计模式的应用不仅提高了代码的复用性和灵活性,还增强了Vue应用的整体质量。
92 1
|
6月前
|
设计模式 JavaScript 前端开发
小白请看 JS大项目宝典:设计模式 教你如何追到心仪的女神
小白请看 JS大项目宝典:设计模式 教你如何追到心仪的女神
|
7月前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
112 7
|
7月前
|
设计模式 JavaScript
js设计模式【详解】—— 桥接模式
js设计模式【详解】—— 桥接模式
95 6

热门文章

最新文章

  • 1
    当面试官再问我JS闭包时,我能答出来的都在这里了。
    49
  • 2
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 3
    Node.js 中实现多任务下载的并发控制策略
    34
  • 4
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    26
  • 5
    【JavaScript】深入理解 let、var 和 const
    49
  • 6
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    47
  • 7
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    57
  • 8
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    57
  • 9
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    72
  • 10
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    57