精华内容
下载资源
问答
  • 在jstl中如何获取list的长度

    千次阅读 2013-04-06 17:08:01
    jsp中使用${list.size }会编译成list.getSize()方法,并不能获取list的长度,因为程序回去找List对象中的getSize()方法,所以只能想别的 办法,一种方法是在后台程序Action中声明一个变量存储list的长度,并对这个变量...
    jsp中使用${list.size }会编译成list.getSize()方法,并不能获取list的长度,因为程序回去找List对象中的getSize()方法,所以只能想别的
    

    办法,一种方法是在后台程序Action中声明一个变量存储list的长度,并对这个变量设置get set方法,但是这个方法有点麻烦,还多了一个变量,

    另外一种方法可以使用jstl中的functions函数实现,具体步骤如下:

    1.引入jstl和jstl函数包

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="
    http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    2.按照以下的方法调用jstl函数就可以获取list的长度
    ${fn:length(list) }

    函数名 函数说明 使用举例
    fn:contains 判断字符串是否包含另外一个字符串 <c:if test="${fn:contains(name, searchString)}">
    fn:containsIgnoreCase 判断字符串是否包含另外一个字符串(大小写无关) <c:if test="${fn:containsIgnoreCase(name, searchString)}">
    fn:endsWith 判断字符串是否以另外字符串结束 <c:if test="${fn:endsWith(filename, ".txt")}">
    fn:escapeXml 把一些字符转成XML表示,例如 <字符应该转为&lt; ${fn:escapeXml(param:info)}
    fn:indexOf 子字符串在母字符串中出现的位置 ${fn:indexOf(name, "-")}
    fn:join 将数组中的数据联合成一个新字符串,并使用指定字符格开 ${fn:join(array, ";")}
    fn:length 获取字符串的长度,或者数组的大小 ${fn:length(shoppingCart.products)}
    fn:replace 替换字符串中指定的字符 ${fn:replace(text, "-", "&#149;")}
    fn:split 把字符串按照指定字符切分 ${fn:split(customerNames, ";")}
    fn:startsWith 判断字符串是否以某个子串开始 <c:if test="${fn:startsWith(product.id, "100-")}">
    fn:substring 获取子串 ${fn:substring(zip, 6, -1)}
    fn:substringAfter 获取从某个字符所在位置开始的子串 ${fn:substringAfter(zip, "-")}
    fn:substringBefore 获取从开始到某个字符所在位置的子串 ${fn:substringBefore(zip, "-")}
    fn:toLowerCase 转为小写 ${fn.toLowerCase(product.name)}
    fn:toUpperCase 转为大写字符 ${fn.UpperCase(product.name)}
    fn:trim 去除字符串前后的空格 ${fn.trim(name)}

    函数描述

    fn:contains(string, substring) 如果参数string中包含参数substring,返回true
    fn:containsIgnoreCase(string, substring) 如果参数string中包含参数substring(忽略大小写),返回true
    fn:endsWith(string, suffix) 如果参数 string 以参数suffix结尾,返回true
    fn:escapeXml(string) 将有特殊意义的XML (和HTML)转换为对应的XML character entity code,并返回
    fn:indexOf(string, substring) 返回参数substring在参数string中第一次出现的位置
    fn:join(array, separator) 将一个给定的数组array用给定的间隔符separator串在一起,组成一个新的字符串并返回。
    fn:length(item) 返回参数item中包含元素的数量。参数Item类型是数组、collection或者String。如果是String类型,返回值是String中的字

    符数。
    fn:replace(string, before, after) 返回一个String对象。用参数after字符串替换参数string中所有出现参数before字符串的地方,并返回

    替换后的结果
    fn:split(string, separator) 返回一个数组,以参数separator 为分割符分割参数string,分割后的每一部分就是数组的一个元素
    fn:startsWith(string, prefix) 如果参数string以参数prefix开头,返回true
    fn:substring(string, begin, end) 返回参数string部分字符串, 从参数begin开始到参数end位置,包括end位置的字符
    fn:substringAfter(string, substring) 返回参数substring在参数string中后面的那一部分字符串
    fn:substringBefore(string, substring) 返回参数substring在参数string中前面的那一部分字符串
    fn:toLowerCase(string) 将参数string所有的字符变为小写,并将其返回
    fn:toUpperCase(string) 将参数string所有的字符变为大写,并将其返回
    fn:trim(string) 去除参数string 首尾的空格,并将其返回

    展开全文
  • JS application原文作者:Manu Ustenko几天前,我朋友菲利普(Philipp)决定创业(一家面包店,用来烹制很棒饼干),并请我帮助他创建一个待办事项列表应用程序,以便他可以记录他工作。他决定用Vue.JS作为...

    f52f22e1de5fa85cf04ba94e50079660.png

    封面图:黄梦莹

    前言

    本文为转载外加意译,并重新排版。

    • 原文地址:How to avoid SOLID principles violations in Vue. JS application
    • 原文作者:Manu Ustenko

    几天前,我的朋友菲利普(Philipp)决定创业(一家面包店,用来烹制很棒的饼干),并请我帮助他创建一个待办事项列表应用程序,以便他可以记录他的工作。

    他决定用Vue.JS作为前端框架,但实际上不知道将来如何使他的应用易于扩展和维护。 他听到了一些关于SOLID (SOLID 是啥???)原理和清晰结构体系的神话,但不知道如何在Vue中使用它们。

    在本文中,我想讨论如何在Vue.JS项目中避免违反SOLID原则。

    啥是SOLID?

    SOLID是Michael Feathers提出的面向对象编程的五大软件设计原则首字母缩写 。

    Robert Cecil Martin (又叫“鲍勃大爷”)在他的《设计原则与设计模式》一书中进行了推广。(《Design Principles and Design Patterns》)

    这五个原则是面向对象编程范式中非常重要的一部分,旨在使我们的程序更灵活、更可读、更易于维护,以便下一步开发。

    SOLID原则包括如下概念:

    • 单一责任原则
    • 开闭原则
    • 里氏替换原则
    • 接口隔离原则
    • 依赖倒置原则

    让我们看看实际的Vue.JS项目中如何应用这些原则,以及如何避免违反原则。我们将创建简单的待办事项列表应用程序。让我们在实际的Vue.JS项目中了解所有这些原则,以及如何避免违反原则。我们将创建简单的待办事项列表应用程序 。

    先决条件

    让我们使用vue cli(https://cli.vuejs.org/)创建一个新的Vue.JS应用。

    vue create todo-app

    在我们的应用中,我将使用vue 2.6.10 + typescript 3.4.3,因此,如果您还不熟悉 typescript ,可以在此处找到文档(https://www.typescriptlang.org/docs/home.html )。

    安装后,我们必须清理一下并删除所有演示组件。 我们的src目录结构看起来像这样:

    src
    ----views
    -------Home.vue
    ----App.vue
    ----main.ts
    ----shims-tsx.d.ts
    ----shims-vue.d.ts
    ----types.ts
    <!-- app.vue -->
    <template>
      <div id="app">
        <router-view />
      </div>
    </template>
    <!-- Home.vue -->
    <template>
      <div>
        Content
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    @Component
    export default class Home extends Vue {}
    </script>

    我们准备出发了……

    单一责任原则(SRP)

    假设我们更改了 **views/Home.vue **组件以获取任务列表并将其显示给用户。 它可能看起来像这样:

    <!--Home.vue-->
    <template>
      <div>
        <header class="header">
          <nav class="header-nav" />
          <div class="container">
            <h1>My todo list</h1>
          </div>
        </header>
        <main>
          <div class="container">
            <div class="todo-list">
              <div
                v-for="{ id, title, completed } in todos"
                :key="id"
                class="todo-list__task"
              >
                <span :class="{ 'todo-list__task--completed': completed }">
                  {{ title }}
                </span>
              </div>
            </div>
          </div>
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    @Component
    export default class Home extends Vue {
      todos: ITodo[] = []
      mounted() {
        this.fetchTodos()
      }
      fetchTodos(): void {
        fetch('https://jsonplaceholder.typicode.com/todos/')
          .then(response => response.json())
          .then((todos: ITodo[]) => (this.todos = todos))
      }
    }
    </script>
    <style lang="scss">
    .header {
      width: 100%;
      &-nav {
        background: teal;
        width: 100%;
        height: 56px;
      }
    }
    .container {
      padding: 1.5rem;
    }
    .todo-list {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: stretch;
      &__task {
        width: 24%;
        padding: 1.5rem;
        margin: 0.5%;
        text-align: left;
        color: #4169e1;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        &:hover {
          box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
            0 10px 10px rgba(0, 0, 0, 0.22);
        }
        &--completed {
          color: #2e8b57;
          text-decoration: line-through;
        }
      }
    }
    </style>
    //type.ts
    
    export interface ITodo {
      id: number
      userId: number
      title: string
      completed: boolean
    }

    基本上,我们在一个 **views/Home.vue **组件中创建了整个应用程序。在这里,我们可以看到违反SRP的说法:“每个组件都只有一个更改的理由”。但是,我们有多少原因需要更改此组件?

    1. 我们要更改 **fetchTodos() **方法来获取待办事项。可能有任何更改的原因:获取到axios或任何其他库,api,方法本身等。
    2. 我们想在此组件中添加更多元素:边栏,菜单,页脚等。
    3. 我们要更改现有元素:标题或待办事项列表。

    我们至少找到了三个要更改 **views/Home.vue **的原因。

    真正的问题始于应用程序的成长和变化。它变得太大了,我们不再记得我们的代码并失去控制。通过将每个原因提取到其自己的组件,类或函数中,可以避免违反SRP。

    让我们做一些重构

    首先,我们可以通过创建一个新的Api类提取提取todos方法并将其保存到 **api.ts **文件中。

    //api.ts
    export class Api {
      private baseUrl: string = 'https://jsonplaceholder.typicode.com/'
      constructor(private url: string) {}
      async fetch() {
        const response = await fetch(`${this.baseUrl}${this.url}`)
        return await response.json()
      }
    }

    接下来,让我们将header提取到 components/Header.vue 中作为函数式组件

    // Header.vue
    <template functional>
      <header class="header">
        <nav class="header-nav" />
        <div class="container">
          <h1>{{ props.listName }}</h1>
        </div>
      </header>
    </template>
    <style lang="scss" scoped>
    .header {
      width: 100%;
      &-nav {
        background: teal;
        width: 100%;
        height: 56px;
      }
    }
    </style>

    我们的最后一步是提取待办事项列表。

    //Home.vue
    
    <template>
      <div>
        <Header listName="My new todo list" />
        <main>
          <TodoList :todos="todos" />
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    import { Api } from '@/api'
    import Header from '@/components/Header.vue'
    import TodoList from '@/components/TodoList.vue'
    @Component({
      components: { Header, TodoList }
    })
    export default class Home extends Vue {
      todos: ITodo[] = []
      async mounted() {
        this.todos = await this.fetchTodos()
      }
      async fetchTodos(): Promise<ITodo[]> {
        const api = new Api('todos')
        return await api.fetch()
      }
    }
    </script>
    <style lang="scss">
    .container {
      padding: 1.5rem;
    }
    </style>
    //TodoList.vue
    <template>
      <div class="container">
        <div class="todo-list">
          <div v-for="todo in todos" :key="todo.id" class="todo-list__task">
            <span :class="{ 'todo-list__task--completed': todo.completed }">
              {{ todo.title }}
            </span>
          </div>
        </div>
      </div>
    </template>
    <script lang="ts">
    import { Component, Vue, Prop } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    @Component
    export default class TodoList extends Vue {
      @Prop({ required: true, default: () => [] }) todos!: ITodo[]
    }
    </script>
    <style lang="scss" scoped>
    .todo-list {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: stretch;
      &__task {
        width: 24%;
        padding: 1.5rem;
        margin: 0.5%;
        text-align: left;
        color: #4169e1;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        &:hover {
          box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
            0 10px 10px rgba(0, 0, 0, 0.22);
        }
        &--completed {
          color: #2e8b57;
          text-decoration: line-through;
        }
      }
    }
    </style>

    现在,views/Home.vue中的代码看起来更加清晰易读。

    开闭原则(OCP)

    让我们更仔细地看一下我们的 **components/TodoList.vue **。我们可以看到,该组件获取待办事项列表并创建了一堆卡片来代表我们的任务。但是,如果我们要更换这些卡片,甚至将任务显示为表格而不是卡片,会发生什么?我们必须更改(修改)我们的组件。现在看起来有点不灵活。

    OCP告诉我们:“组件应该是对于扩展开放,但是对于修改封闭 ”。

    让我们解决此冲突

    我们可以使用 vue slots 来使 **components/TodoList.vue **提高灵活性。

    //TodoList.vue
    <template functional>
      <div class="container">
        <div class="todo-list">
          <slot />
        </div>
      </div>
    </template>
    <style lang="scss" scoped>
    .todo-list {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: stretch;
    }
    </style>

    并将我们的卡片移动到单独的组件 components/TodoCard.vue

    //TodoCard.vue
    <template>
      <div class="todo-list__task">
        <span :class="{ 'todo-list__task--completed': todo.completed }">
          {{ todo.title }}
        </span>
      </div>
    </template>
    <script lang="ts">
    import { Component, Vue, Prop } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    @Component
    export default class TodoCard extends Vue {
      @Prop({ required: true }) todo!: ITodo
    }
    </script>
    <style lang="scss" scoped>
    .todo-list {
      &__task {
        width: 24%;
        padding: 1.5rem;
        margin: 0.5%;
        text-align: left;
        color: #4169e1;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        &:hover {
          box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
            0 10px 10px rgba(0, 0, 0, 0.22);
        }
        &--completed {
          color: #2e8b57;
          text-decoration: line-through;
        }
      }
    }
    </style>

    并更新 views/Home.vue

    //Home.vue
    <template>
      <div>
        <Header listName="My new todo list" />
        <main>
          <TodoList>
            <TodoCard v-for="todo in todos" :key="todo.id" :todo="todo" />
          </TodoList>
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    import { Api } from '@/api'
    import Header from '@/components/Header.vue'
    import TodoList from '@/components/TodoList.vue'
    import TodoCard from '@/components/TodoCard.vue'
    
    @Component({
      components: { Header, TodoList, TodoCard }
    })
    export default class Home extends Vue {
      todos: ITodo[] = []
      async mounted() {
        this.todos = await this.fetchTodos()
      }
      async fetchTodos(): Promise<ITodo[]> {
        const api = new Api('todos')
        return await api.fetch()
      }
    }
    </script>
    <style lang="scss">
    .container {
      padding: 1.5rem;
    }
    </style>

    现在,我们可以轻松地用另一个组件替换我们的任务可视化。

    里氏替换原则(LSP)

    开始我们的Api类

    首先,我们将Api类重命名并重构为 **BaseApi **类,然后将其移动到单独的目录中 api/BaseApi.ts

    //BaseApi.ts
    export class BaseApi {
        protected baseUrl: string = 'https://jsonplaceholder.typicode.com/'
        async fetch(url: string): Promise<any> {
            const response = await fetch(`${this.baseUrl}${url}`)
            return await response.json()
        }
    }

    如您所见,BaseApi类具有 **fetch() **方法,该方法带有一个参数"**url **"。

    由于某些原因,我们决定将axios库添加到我们的应用程序中。

    npm install --save axios

    并创建一个新类AxiosApi,它是 **BaseApi **的子类, api/AxiosApi.ts

    //AxiosApi.ts
    import axios from 'axios';
    import { BaseApi } from '@/api/baseApi';
    
    export class AxiosApi extends BaseApi {
        constructor() {
            super()
        }
        async fetch({ url }): Promise<any> {
            const { data } = await axios.get(`${this.baseUrl}${url}`)
            return data
        }
    }

    如果我们在 **fetchTodos() **方法中使用新的 AxiosApi(子类)替换 BaseApi(父类) views/Home.vue

    //Home.vue
    import { AxiosApi } from '@/api/AxiosApi'
    ...
    async fetchTodos(): Promise<ITodo[]> {
      const api = new AxiosApi()
      return await api.fetch('todos')
    }

    它会破坏我们的应用程序,因为我们没有遵循LSP:“When extending a class, remember that you should be able to pass objects of the subclass in place of objects of the parent class without breaking the client code ”。(我觉得这句更好理解:某个对象实例的子类实例应当可以在不影响程序正确性的基础上替换它们 )

    如您所见,我们将对象作为参数传递给 **AxiosApi **类的 fetch() 方法,但是BaseApi类改为使用字符串。在这种情况下,我们不能轻易地用父类替换子类。

    让我们快速修复它

    //AxiosApi.ts
    import axios from 'axios'
    import { BaseApi } from '@/api/baseApi'
    
    export class AxiosApi extends BaseApi {
      constructor() {
        super()
      }
      async fetch(url: string): Promise<any> {
        const { data } = await axios.get(`${this.baseUrl}${url}`)
        return data
      }
    }

    现在我们可以同时使用 **BaseApi **和 **AxiosApi **。而且,我们甚至可以通过创建 **Api **类来更深入地研究和改进代码,**api/api.ts **类中扩展了 **BaseClass **并具有私有属性。

    //api.ts
    
    import { BaseApi } from '@/api/baseApi'
    import { FetchApi } from '@/api/fetchApi'
    import { AxiosApi } from '@/api/axiosApi'
    
    export class Api extends BaseApi {
      private provider: any = new AxiosApi()
      async fetch(url: string): Promise<any> {
        return await this.provider.fetch(url)
      }
    }

    现在,views/Home.vue中的 fetchTodos() 方法不需要知道我们使用的是哪个库,我们可以在 **Api **类中轻松地切换 fetch 功能提供者。

    //Home.vue
    
    import { Api } from '@/api/api'
    ...
    async fetchTodos(): Promise<ITodo[]> {
      const api = new Api()
      return await api.fetch('todos')
    }

    接口隔离原理(ISP)

    我们现在将任务可视化为卡片。我们还要在 **components/TodoRow.vue **中添加简单的任务列表。

    //TodoRow.vue
    <template>
      <div class="todo-list__row">
        <span>{{ todo.id }}: </span>
        <span :class="{ 'todo-list__row--completed': todo.completed }">{{
          todo.title
        }}</span>
      </div>
    </template>
    <script lang="ts">
    import { Component, Vue, Prop } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    @Component
    export default class Home extends Vue {
      @Prop({ required: true }) todo!: ITodo
    }
    </script>
    <style lang="scss">
    .todo-list {
      &__row {
        width: 100%;
        text-align: left;
        color: #4169e1;
        &--completed {
          text-decoration: line-through;
          color: #2e8b57;
        }
      }
    }
    </style>

    并用列表可视化替换卡片可视化

    //Home.vue
    <template>
      <div>
        <Header listName="My new todo list" />
        <main>
          <TodoList>
            <TodoRow v-for="todo in todos" :key="todo.id" :todo="todo" />
          </TodoList>
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    import { Api } from '@/api/api'
    import Header from '@/components/Header.vue'
    import TodoList from '@/components/TodoList.vue'
    import TodoCard from '@/components/TodoCard.vue'
    import TodoRow from '@/components/TodoRow.vue'
    
    @Component({
      components: { Header, TodoList, TodoCard, TodoRow }
    })
    export default class Home extends Vue {
      todos: ITodo[] = []
      async mounted() {
        this.todos = await this.fetchTodos()
      }
      async fetchTodos(): Promise<ITodo[]> {
        const api = new Api()
        return await api.fetch('todos')
      }
    }
    </script>
    <style lang="scss">
    .container {
      padding: 1.5rem;
    }
    </style>

    如您所见,我们在 **TodoCard.vue **和 **TodoRow.vue **中将整个 **todo **对象作为 **prop **发送,但是我们仅使用此对象的一部分。我们在两个组件中都没有使用 **userId **和 id 属性。我们违反了ISP:“不应强迫组件依赖其不使用的属性和方法”。

    有很多方法可以解决此问题:

    • 将Todo接口裁切为几个较小的接口
    • 仅将使用的属性传递给组件

    让我们重构代码并使用函数式(无状态)组件。

    //Home.vue
    <template>
      <div>
        <Header listName="My new todo list" />
        <main>
          <TodoList>
            <!--<TodoCard-->
            <!--v-for="{ id, title, completed } in todos"-->
            <!--:key="id"-->
            <!--:title="title"-->
            <!--:completed="completed"-->
            <!--/>-->
            <TodoRow
              v-for="{ id, title, completed } in todos"
              :key="id"
              :id="id"
              :title="title"
              :completed="completed"
            />
          </TodoList>
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo } from '@/types'
    import { Api } from '@/api/api'
    import Header from '@/components/Header.vue'
    import TodoList from '@/components/TodoList.vue'
    import TodoCard from '@/components/TodoCard.vue'
    import TodoRow from '@/components/TodoRow.vue'
    
    @Component({
      components: { Header, TodoList, TodoCard, TodoRow }
    })
    export default class Home extends Vue {
      todos: ITodo[] = []
      async mounted() {
        this.todos = await this.fetch()
      }
      async fetch(): Promise<ITodo[]> {
        const api = new Api()
        return await api.fetch('todos')
      }
    }
    </script>
    <style lang="scss">
    .container {
      padding: 1.5rem;
    }
    </style>
    //TodoCard.vue
    <template functional>
      <div class="todo-list__task">
        <span :class="{ 'todo-list__task--completed': props.completed }">
          {{ props.title }}
        </span>
      </div>
    </template>
    <style lang="scss" scoped>
    .todo-list {
      &__task {
        width: 24%;
        padding: 1.5rem;
        margin: 0.5%;
        text-align: left;
        color: #4169e1;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    
        &:hover {
          box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
            0 10px 10px rgba(0, 0, 0, 0.22);
        }
    
        &--completed {
          color: #2e8b57;
          text-decoration: line-through;
        }
      }
    }
    </style>
    //TodoRow.vue
    <template functional>
      <div class="todo-list__row">
        <span>{{ props.id }}: </span>
        <span :class="{ 'todo-list__row--completed': props.completed }">{{
          props.title
        }}</span>
      </div>
    </template>
    <style lang="scss">
    .todo-list {
      &__row {
        width: 100%;
        text-align: left;
        color: #4169e1;
    
        &--completed {
          text-decoration: line-through;
          color: #2e8b57;
        }
      }
    }
    </style>

    现在看起来好多了。

    依赖倒置原则(DIP)

    DIP原则:“高级类(组件)不应依赖于低级类(组件)。两者都应依赖抽象。抽象不应该依赖细节。细节应取决于抽象。

    什么是高级和低级别的类。

    • 低级类实现基本操作,例如使用API​​。
    • 高级类包含复杂的业务逻辑,这些逻辑指导低级类做某事。

    让我们回到 **Api **类,并在 **types.ts **中为 **Api **类创建一个新接口 。

    //type.ts
    
    export interface ITodo {
        id: number
        userId: number
        title: string
        completed: boolean
    }
    
    //new add
    export interface IApi {
        fetch(url: string): Promise<any>
    }

    并更新 **api **目录和 **views/Home.vue **中的所有 **api **类。

    //api.ts
    
    import { BaseApi } from '@/api/baseApi'
    import { FetchApi } from '@/api/fetchApi'
    import { AxiosApi } from '@/api/axiosApi'
    import { IApi } from '@/types'
    
    export class Api extends BaseApi implements IApi {
      private provider: any = new AxiosApi()
      async fetch(url: string): Promise<any> {
        return await this.provider.fetch(url)
    }
    //AxiosApi.ts
    import axios from 'axios'
    import { BaseApi } from '@/api/baseApi'
    import { IApi } from '@/types'
    
    export class AxiosApi extends BaseApi implements IApi {
      constructor() {
        super()
      }
      async fetch(url: string): Promise<any> {
        const { data } = await axios.get(`${this.baseUrl}${url}`)
        return data
    }
    //BaseApi.ts
    import { IApi } from '@/types'
    
    export class BaseApi implements IApi {
      protected baseUrl: string = 'https://jsonplaceholder.typicode.com/'
      async fetch(url: string): Promise<any> {}
    }
    // FetchApi.ts
    
    import { BaseApi } from '@/api/baseApi'
    import { IApi } from '@/types'
    
    export class FetchApi extends BaseApi implements IApi {
      constructor() {
        super()
      }
      async fetch(url: string): Promise<any> {
        const response = await fetch(`${this.baseUrl}${url}`)
        return await response.json()
      }
    }
    //Home.vue
    <template>
      <div>
        <Header listName="My new todo list" />
        <main>
          <TodoList>
            <!--<TodoCard-->
            <!--v-for="{ id, title, completed } in todos"-->
            <!--:key="id"-->
            <!--:title="title"-->
            <!--:completed="completed"-->
            <!--/>-->
            <TodoRow
              v-for="{ id, title, completed } in todos"
              :key="id"
              :id="id"
              :title="title"
              :completed="completed"
            />
          </TodoList>
        </main>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { ITodo, IApi } from '@/types'
    import { Api } from '@/api/api'
    import Header from '@/components/Header.vue'
    import TodoList from '@/components/TodoList.vue'
    import TodoCard from '@/components/TodoCard.vue'
    import TodoRow from '@/components/TodoRow.vue'
    
    @Component({
      components: { Header, TodoList, TodoCard, TodoRow }
    })
    export default class Home extends Vue implements IApi {
      todos: ITodo[] = []
      async mounted() {
        this.todos = await this.fetch()
      }
      async fetch(): Promise<ITodo[]> {
        const api = new Api()
        return await api.fetch('todos')
      }
    }
    </script>
    <style lang="scss">
    .container {
      padding: 1.5rem;
    }
    </style>

    现在,我们的低级(api类)和高级(views/Home.vue)类取决于一个接口。原始依赖关系的方向已经颠倒了:低级api类现在依赖于高级抽象。

    结论

    在本文中,我们研究了小型Vue.JS项目中的所有SOLID原理。我希望它可以帮助您避免项目中的一些架构错误并增进您对SOLID原理的理解。

    可以在这里找到代码https://github.com/NovoManu/SOLID-vue

    祝好运!

    帮助理解的扩展阅读

    • S.O.L.I.D 面向对象设计和编程(简单明了) https://learnku.com/articles/4160/solid-notes-on-object-oriented-design-and-programming-oodoop
    • TypeScript+Vue 插件vue-property-decorator的使用总结 https://my.oschina.net/lpcysz/blog/2980469
    • vue-property-decorator使用手册 https://segmentfault.com/a/1190000019906321
    • 函数式组件 https://cn.vuejs.org/v2/guide/render-function.html#函数式组件
    • 基于类的 Vue 组件 https://cn.vuejs.org/v2/guide/typescript.html#基于类的-Vue-组件
    展开全文
  • 方法1:循环+计数器在这个方法中,只需运行一个循环并增加计数器,直到遍历到列表的最后一个元素时,获取它的计数,就是列表的长度。该方法是最基本的获取列表长度的方法。代码示例:使用循环遍历+计数器来获取列表...

    列表是Python日常编程的一个组成部分,了解列表的相关操作是是必不可少的。下面本篇文章就来带大家了解一下在Python中获取列表长度的方法,希望对大家有所帮助。

    方法1:循环+计数器

    在这个方法中,只需运行一个循环并增加计数器,直到遍历到列表的最后一个元素时,获取它的计数,就是列表的长度。该方法是最基本的获取列表长度的方法。

    代码示例:使用循环遍历+计数器来获取列表长度# 初始化列表

    List = [ 1, 4, 5, 7, 8 ]

    #输出列表

    print ("列表为: " + str(List))

    # 初始化计数器,使用循环查找列表长度

    counter = 0

    for i in List:

    #递增计数器

    counter = counter + 1

    # 输出列表长度

    print ("列表长度为: " + str(counter))

    输出:

    方法2:使用 len()

    Python len()方法返回对象(字符、列表、元组等)长度或项目个数。它提供了最常用和最简单的方法来查找任何列表的长度;这也是当今采用的最常规技术。

    代码示例:使用len()方法来获取列表长度

    示例1:# 初始化列表

    List = [ "php", "python", 3, 7,"html" ]

    List.append(6)

    List.append("Hello")

    #输出列表

    print ("列表为: " + str(List))

    # 输出列表长度

    print ("列表长度为: " ,len(List))

    输出:

    示例1:n = len([10, 20, 30])

    #输出列表长度

    print ("列表长度为:" ,n)

    输出:列表长度为:3

    方法3:使用 length_hint()

    在Python中还可以length_hint()方法来查找列表长度。该方法是查找列表长度的一种鲜为人知的技术;它是在operator类中定义的,它会返回列表中存在的元素的数量。

    代码示例:使用length_hint()方法来获取列表长度from operator import length_hint

    # 初始化列表

    List = [ 1, 4, 5, 7, 8 ]

    #输出列表

    print ("列表为: " + str(List))

    list_len_hint =length_hint(List)

    # 输出列表长度

    print ("列表长度为: " + str(list_len_hint))

    输出:

    相关视频教程推荐:《Python教程》

    以上就是本篇文章的全部内容,希望能对大家的学习有所帮助。更多精彩内容大家可以关注Gxl网相关教程栏目!!!

    展开全文
  • print len(array)6同样,要获取一字符串的长度,也是用这个len函数,包括其他跟长度有关的,都是用这个函数。以上这篇Python返回数组/List长度的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望...

    Python返回数组/List长度的实例

    其实很简单,用len函数:

    >>> array = [0,1,2,3,4,5]

    >>> print len(array)

    6

    同样,要获取一字符串的长度,也是用这个len函数,包括其他跟长度有关的,都是用这个函数。

    以上这篇Python返回数组/List长度的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

    时间: 2018-06-22

    本文实例讲述了Python list操作用法.分享给大家供大家参考,具体如下: List是python中的基本数据结构之一,和Java中的ArrayList有些类似,支持动态的元素的增加.list还支持不同类型的元素在一个列表中,List is an Object. 最基本的创建一个列表的方法 复制代码 代码如下: myList = ['a','b','c'] Python list常见操作如下: 创建列表 复制代码 代码如下: sample_list = ['a',1,('a','b')] P

    1. 给定初值v,和长度l,定义list s 或者: 2. 产生一个数值递增list 2.1 从0开始以1递增 2.2 在[a,b)区间上以1递增 2.3 在[a,b)区间上以c递增 3. list的基本操作 L.append(var) #追加元素 L.insert(index,var) L.pop(var) #返回最后一个元素,并从list中删除之 L.remove(var) #删除第一次出现的该元素 L.count(var) #该元素在列表中出现的个数 L.index(var) #该元素的位

    本文实例讲述了Python中list以及list与array的相互转换实现方法.分享给大家供大家参考,具体如下: python中的list是一种有序集合,可以随时增删元素: # -*- coding: utf-8 -*- frameID = 1 frameID_list = [] frameID_list.append(frameID) print (frameID_list) frameID = 2 frameID_list.append(frameID) print (frameID_lis

    本文实例讲述了python简单获取数组元素个数的方法.分享给大家供大家参考.具体如下: 复制代码 代码如下: mySeq = [1,2,3,4,5]  print len(mySeq) 运行结果如下: 5 希望本文所述对大家的Python程序设计有所帮助.

    本文以实例形式详细讲述了Python列表list数组array用法.分享给大家供大家参考.具体如下: Python中的列表(list)类似于C#中的可变数组(ArrayList),用于顺序存储结构.   创建列表 复制代码 代码如下: sample_list = ['a',1,('a','b')] Python 列表操作 复制代码 代码如下: sample_list = ['a','b',0,1,3] 得到列表中的某一个值 复制代码 代码如下: value_start = sample_list

    通常来说Python中任何值都是一个对象,因此任何类型(int.str.list-)都是一个类.而类就必然有它的方法或属性,我们要记下这么多类的所有方法显然是不可能的,对此本文介绍两个小技巧: dir() :内置函数,用来查询一个类或者对象所有属性,比如>>> dir(list). help() :内置函数,用来查询具体的说明文档,比如>>> help(int). 在上一篇的Python3的基本数据类型中,我们初步了解了list列表,也介绍了列表是Python 中使用最

    Google Maps 基础 创建一个简单的 Google 地图 现在让我们创建一个简单的 Google 地图. 以下是显示了英国伦敦的 Google 地图: 实例

    今天小编和大家一起通过几个实例学习C++基础知识,下面进行实例解析: [1-1]编写一个程序,实现一个整数.长整数.浮点数和双精度数除以2的计算. [分析]这是一个典型的函数重载的程序.声明函数div()为除法函数,每个函数的功能基本都是一致的,不同的只是形式参数的类型不同而已.程序代码如下: #include using namespace std; int division(int x){ return x/2; } long division(long x){ re

    java中建立线程可以有两种方式,分别是继承Thread类和实现Runnable接口. 继承Thread public class MyThread extends Thread{ public MyThread(String name){ super(name); } int i; public void run(){ for(i=0;i<5;i++){ System.out.println(getName()+"--"+i); } } public static void m

    这篇文章主要介绍了Python列表切片常用操作实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 最近在爬一个网站的文档的时候,老师要求把一段文字切割开来,根据中间的文本分成两段 故学习了一段时间的切片操作,现把学习成果po上来与大家分享 1.何为切片? 列表的切片就是处理列表中的部分元素,是把整个列表切开的方法. 切片可以说是整个列表中的重点内容,相信你在以后的Python项目中会经常使用到. 它的语法是: 2.简单介绍切片的几个常见操作

    这篇文章主要介绍了python列表生成器迭代器实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 列表生成式 例如 有一个列表 a =[2,3,4,5] 需要将列表都加上1 #第一种方法 for i in map(lambda i:i+1,a) #第二种方法 for index,i in enumerate(a): a[index] +=1 print(a) #第三种,简单列表生成式 a = [i+1 for i in a] print(a

    这篇文章主要介绍了Python3打包exe代码2种方法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 cx_Freeze(不推荐) 以前只用 cx_Freeze 支持将 python3 打包成 exe ,示例如下: 在你要打包的 python 文件下新建这个 setup.py 文件: #!/usr/bin/env python # -*- coding: utf-8 -*- from cx_Freeze import setup, Ex

    imghdr模块 功能描述:imghdr模块用于识别图片的格式.它通过检测文件的前几个字节,从而判断图片的格式. 唯一一个API imghdr.what(file, h=None) 第一个参数file可以是用rb模式打开的file对象或者表示路径的字符串和PathLike对象.h参数是一段字节串.函数返回表示图片格式的字符串. >>> import imghdr >>> imghdr.what('test.jpg') 'jpeg' 具体的返回值和描述如下: 返回值 描述

    本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库). 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式.例如,当使用一个vector这样的泛型类型或者find这样的泛型函数

    本文研究的是re模块findall()函数的相关内容,首先看看实例代码: >>> import re >>> s = "adfad asdfasdf asdfas asdfawef asd adsfas " >>> reObj1 = re.compile('((\w+)\s+\w+)') >>> reObj1.findall(s) [('adfad asdfasdf', 'adfad'), ('asdfas asd

    展开全文
  • 令人头疼毕设。。。 Stackflow上解答: 实际就是Thymeleaf内置方法#lists.size(这里传入数列名)
  • 参考文章:Python返回数组(List长度的方法
  • Python is a very expressive language that provides different structures to easy developers ... The list is one of the most popular data structures provided by the python. In a regular workflow, we a...
  • 展开全部参考代码:list1 = ['physics', 'chemistry', 1997, 2000];list2 = [1, 2, 3, 4, ...len(list1)len(list2)len(list3)Python支持列表切割(list slices),可以取得完整列表32313133353236313431303231363533...
  • <p>how can i get the post titles list where post content less than 300 words .. <p>1) posts title(List of posts) where post content less than 300 words in wordpress <p>2) posts title(List of posts) ...
  • 如何获取Set的长度。 如何判定Set中是否包含指定的字符串。自己做一个类。包含2个属性学生号,与学生姓名。并在创建一个HashSet只包含这个类的元素。这时候如何判定这个list中是否包含某一个元素?(类equals ...
  • el表达式中获取数组长度的方法

    千次阅读 2020-02-27 11:36:44
    在jsp页面中如何获得el表达式中List类型的长度 ${breakfastTime.size()}
  • 如何在外部方法(比如自定义方法)中获取ListViewitemview private void getItemView(){ for(int i=0;i();i++){ View view = listview.getChildAt(i);// } } getChildAt(position);//这个方法不能用,因为...
  • 上一篇我们说了,等额切割List 等额拆分List 这次我们说一下如何随机获取一个list... * @desc 随机获取List数据 * @date 2020-05-25 */ public class SplitArrayUtils { /** * 随机获取长度为sizelist<br>
  • 最近看到有同事在使用for循环的时候首先会将数组或者字符串的长度赋值给一个变量;在网上查了一下说是这样可以节约资源的消耗,真实的情况又是如何?让我们看下他们的源码来分析。 1.将数组的长度赋值给变量len ...
  • How do I get the number of elements in a list in jinja2 template? 如何获得jinja2模板中列表中元素数? For examp
  • 首先说明,将长list切割成短list可以有效地减轻内存压力,在写入数据库时候,能够缓解数据库压力 json_list = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] # json_list列表是1-20,长度是20,但是...
  • 某些情况下,一个表单中提交内容数量是不一定。比如Facebook编辑照片界面,可以把整个相册...这种情况下,如何用ActionForm来获取这些数据呢? 这个ActionForm(假设名为PhotoForm)应该有一个字段: List
  • 最近看到有同事在使用for循环的时候首先会将数组或者字符串的长度赋值给一个变量;在网上查了一下说是这样可以节约资源的消耗,真实的情况又是如何? 1.数组.length String[] s = {"qw","as","a"}; for(int i=0; i...
  • 1.作为练习,编写一个循环遍历上一个列表并打印每个元素的长度的循环。 2.如果将整数发送给len会怎样? 问题2的答案 如果将整数发送给len(包含混合元素),并且尝试获取其len,则会出现错误。 但是,您将获得仅包含...
  • 来源:https://www.cnblogs.com/chenchen0618/p/13041715.html前言Redis是使用C写,而C中根本不存在string,list,hash,set和zset这些数据类型,那么C是如何将这些数据类型实现出来呢?我们从该篇开始,就要开始...
  • 目录 引言 String 有哪些有用方法? 如何拼接字符串? ... 如何获取字符串长度 如何将 list 拼接成字符串? 如何替换字符串? 如何去除字符串中空格? 如何子字符串是否包含在父字符...
  • jsp中使用${list.size }会编译成list.getSize()方法,并不能获取list的长度,因为程序回去找List对象中的getSize()方法,所以只能想别的办法, 一种方法是在后台程序Action中声明一个变量存储list的长度,并对这个变量...
  • 然后根据获取list的长度和其中对应的id添加相应gridview,所有的信息都是从服务器获取的,不是固定的。有做过这样布局的demo可以分享一下吗 ![图片说明]...
  • 给定一个字符串,如何得到其中重复模式最高子字符串,我采用方法是使用滑窗机制,对给定字符串切分,窗口大小从1增加到字符串长度减1,将所有得到切片统计结果,在这里不考虑单个字符重复模式,好了,...
  • 今天学习了刘金玉老师零基础VB教程第40期,学习...2.如何输入数据到下拉框中,使用combobox控件的list属性,在输入时候,当输入完第一个数据后,按住ctrl+enter键连续输入其他数据,当所有内容输入完成后,按enter...
  • 今天我在写代码的时候,发现Java一个很实际的功能,例如java定义一... 我们可以方便的读取list的长度,但是C++里面,你定义一个数据,实际上就是分配了一块内存,根本不能从数组上获取任何信息。每次遍历数组数据,...
  • 代码是这个逻辑,传输内容的前8位代表长度,获取长度后,在收到信息的长度等于获取到的内容长度时。将信息添加处理。但是如果客户端调用时计算长度错误,就会在这里阻塞。这个情景使用IdleStateHandler 时无法进行...
  • 主要是对sscanf函数使用应用,知道固定长度如何提取?知道固定符号截至如何提取? #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #define TMP_FILE ...
  • 获取我们所需要定义数组或者列表长度,然后再进一步对数组和列表进行初始化该怎么实现呢?这里我们讨论如何对数组和列表进行动态初始化。难点:二维列表动态初始化 数组动态初始化(一维): import numpy as ...
  • 首先实现这个需求我想到的方案有两个:第一:做一个适配器,这个适配器的主要方法是通过获取存入图片的list的长度,接着通过除余的方法循环的返回相应位置的View再添加上去。 缺点:当图片的多的时候,并且没有通

空空如也

空空如也

1 2 3 4 5 6
收藏数 115
精华内容 46
关键字:

如何获取list的长度