zoukankan      html  css  js  c++  java
  • 外观模式-designer-facade-ts

    外观模式

    环境搭建

    npm init -y
    npm i vite -D

    外观模式简介:

    是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

    优点:

    1.降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。 2.对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。 3.降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象

    缺点:

    1.不能很好地限制客户使用子系统类,很容易带来未知风险。 2.增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

    外观模式的结构

    外观(Facade)角色:为多个子系统对外提供一个共同的接口。
    子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
    客户(Client)角色:通过一个外观角色访问各个子系统的功能。

    使用场景:
    对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
    当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
    当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

    案例设计

    实现 todoList 组件

    具体操作流程

    1.设计角色:

    客户角色: app.ts 通过访问外观角色的功能
    外观角色: 组件的 index.ts 为子系统的各个方法提供方法和接口
    子系统角色: 外观角色调用的具体方法实现

    客户角色:
    客户角色中外观组件提供了类,通过传递容器和数据集合,外观组件的类去创建一个实例,实例执行初始化函数。

    ;((doc) => {
      //1.dom操作,获取容器
      const oApp: HTMLElement = document.querySelector('#app')
    
      //2.定义数据
      const todoData: ITodoData[] = []
    
      //3.定义模块入口程序
      const init = (): void => {
        //4.创建具体类操作的实例
        const todoList: TodoList = new TodoList(oApp, todoData)
        todoList.init()
      }
    
      init()
    })(document)
    

    外观角色:

    1.外观角色给客户角色提供接口,实力化以及执行的入口函数。 2.外观角色要调用子系统的各个方法实现子系统的功能:
    主要有组件创建,组件渲染,组件事件绑定及处理

    class TodoList {
      private el: HTMLElement
      private todoData: ITodoData[]
      private todoWrapper: HTMLElement
    
      private input: Input
      private list: List
    
      constructor(el: HTMLElement, todoData: ITodoData[]) {
        this.el = el
        this.todoData = todoData
        this.todoWrapper = document.createElement('div')
      }
    
      // 类的init函数:
      public init() {
        this.createComponents()
        this.render()
        this.bindEvent()
      }
    
      private createComponents() {
        this.input = new Input(<IInputOptions>{
          wrapperEl: this.todoWrapper,
          placeholderText: '请输入todo',
          buttonText: '新增',
        })
    
        this.list = new List(<IListOptions>{
          todoData: this.todoData,
          wrapperEl: this.todoWrapper,
        })
      }
    
      private render() {
        this.input.render()
        this.list.render()
        this.el.appendChild(this.todoWrapper)
      }
    
      private bindEvent() {
        this.input.bindEvent()
        this.list.bindEvent()
      }
    }
    

    子系统角色的设计:
    子系统有三个部分[类]构成:
    Component:组件的视图部分,本类的设计有三个受保护的方法,给其他类的继承提供基本的视图部分。

    class Component {
      /**
       * 输入区域的组件
       * @param placeholderText
       * @param buttonText
       * @returns
       */
      protected static inputView(
        placeholderText: string,
        buttonText: string
      ): string {
        return `
        <div>
          <input type="text" class="todo-input" placeholder="${placeholderText}" />
          <button class="add-btn">${buttonText}</button>
        </div>
        `
      }
    
      /**
       * listView组件视图,根据数据调用视图组件
       * @param data
       * @returns
       */
      protected static listView(data: ITodoData[]): string {
        return `
          <div class="todo-list">
            ${
              data.length
                ? data.map((todo: ITodoData) => {
                    return Component.todoView(todo)
                  })
                : '当前没有数据'
            }
          </div>
        `
          .split(',')
          .join('')
      }
    
      /**
       * todoView视图组件,list的item组件
       * @param todo
       * @returns
       */
      protected static todoView(todo: ITodoData): string {
        const { id, content, completed } = todo
        return `
          <div class="todo-item">
            <input type="checkbox" data-id="${id}" ${completed ? 'checked' : ''} />
            <span style="text-decoration: ${
              completed ? 'line-through' : ''
            }">${content}</span>
            <button data-id="${id}">删除</button>
          </div>
        `
      }
    }
    

    Input 类自系统的实现:继承 Component 类,调用 Component 的方法创建 Input 组件的视图部分,子系统主要实现:

    1.创建视图 2.事件绑定 3.事件处理函数

    class Input extends Component {
      private options: IInputOptions
    
      constructor(options: IInputOptions) {
        super()
        this.options = options
      }
    
      /**
       * 输入视图渲染
       */
      public render() {
        const { placeholderText, buttonText } = this.options
        this.options.wrapperEl.innerHTML += Component.inputView(
          placeholderText,
          buttonText
        )
      }
    
      /**
       * 事件绑定处理:
       */
      public bindEvent() {
        const oAddBtn: HTMLElement = document.querySelector('.add-btn')
        const oInput: HTMLElement = document.querySelector('.todo-input')
        oAddBtn.addEventListener(
          'click',
          this.handlerBtnClick.bind(this, oInput),
          false
        )
      }
    
      /**
       * 具体事件处理:handleBtnClick
       */
      public handlerBtnClick(inputDom) {
        const val: string = inputDom.value.trim()
        if (val.length) {
          List.addItem(val)
          inputDom.value = ''
        }
      }
    }
    

    List 类的实现:
    具体实现和 Input 组件相似,具体处理: 1.视图创建 2.事件绑定 3.事件处理 4.要处理 Input 的增加事件,因为数据的处理统一在 List 组件中实现

    class List extends Component {
      private wrapperEl: HTMLElement
      private static todoData: ITodoData[]
    
      constructor(options: IListOptions) {
        super()
        this.wrapperEl = options.wrapperEl
        List.todoData = options.todoData
      }
    
      /**
       * 组件渲染函数render
       */
      public render() {
        this.wrapperEl.innerHTML += Component.listView(List.todoData)
      }
    
      /**
       * 事件绑定函数bindEvent
       */
      public bindEvent() {
        const oTodoList: HTMLElement = document.querySelector('.todo-list')
        oTodoList.addEventListener('click', this.handleListClick.bind(this), false)
      }
    
      /**
       *
       * @param e 监听那一个事件触发了
       */
      private handleListClick(e: MouseEvent) {
        const tar = e.target as HTMLElement
        const tagName = tar.tagName.toLowerCase()
        const oTodoItems: HTMLCollection = document.getElementsByClassName(
          'todo-item'
        )
    
        if ('input' === tagName || 'button' === tagName) {
          const _id: number = parseInt(tar.dataset.id)
          switch (tagName) {
            case 'input':
              this._handleCheckedBoxClick(_id, oTodoItems)
              break
            case 'button':
              this._handleBtnClick(_id, oTodoItems)
              break
            default:
              break
          }
        }
      }
    
      /**
       * 复选框组件的点击事件
       * @param id
       * @param oTodoItems
       */
      private _handleCheckedBoxClick(id: number, oTodoItems: HTMLCollection) {
        List.todoData = List.todoData.map((todo: ITodoData, index: number) => {
          if (todo.id === id) {
            todo.completed = !todo.completed
            oTodoItems[index].querySelector(
              'span'
            ).style.textDecoration = todo.completed ? 'line-through' : ''
          }
          return todo
        })
      }
    
      /**
       * 删除按钮事件的处理
       * @param id
       * @param oTodoItems
       */
      private _handleBtnClick(id: number, oTodoItems: HTMLCollection) {
        List.todoData = List.todoData.filter((todo: ITodoData, index: number) => {
          if (todo.id !== id) {
            return todo
          } else {
            oTodoItems[index].remove()
          }
        })
      }
    
      /**
       * 组件项添加:本应该是在input组件中,但是数据处理在List组件中
       */
      public static addItem(val: string) {
        const oTodoList: HTMLElement = document.querySelector('.todo-list')
        const _item = {
          id: new Date().getTime(),
          content: val,
          completed: false,
        }
        const _todo = List.todoData.find((td) => td.content === val)
        if (_todo) {
          alert('已添加...')
          return
        }
        List.todoData.push(_item)
        if (List.todoData.length === 1) {
          oTodoList.innerHTML = ''
        }
        oTodoList.innerHTML += Component.todoView(_item)
      }
    }
    
  • 相关阅读:
    Hadoop
    Mapreduce
    ssh原理
    HDFS
    Centos
    创建jira插件
    新型的领导者是一名推动者,而不是一名发号施令者
    上善若水,虚怀若谷
    GoAhead 嵌入式web
    Eclipse基金会
  • 原文地址:https://www.cnblogs.com/intelwisd/p/14684084.html
Copyright © 2011-2022 走看看