存放资料的 state、module

在 JavaScript 中,储存资料的方式,长这样。

{
  name: 'Chris',
  age: 18
}

抽象资料型别 ADT

强型别语言的话,要先宣告「抽象资料型别」

struct User {
  char *name;
  int age;
};

然後宣告一个实体

struct User chris;

chris.name = "Chris";
chris.age = 18;

这样要变更资料,是直接修改变数本身。像是 JavaScript 操作物件一样的做法。

const chris = {
  name: '',
  age: 0
}

chris.name = "Chris";
chris.age = 18;

但是,这样会遇到一些问题

const chris = {
  first_name: '',
  last_name: '',
}

chris.first_name = "Chris";
chris.last_name = "Wang";

如果想要 full name 就要另外写一个逻辑,把它们组合起来。或者写一个 chris.full_name 储存 "Chris Wang"

那麽,会不会遇到资料不同步的问题?
如果只是单纯改变 last_name 需要同步改变 full_name 的内容吗?

抽象介面

抽象资料型别,在物件的体现,就是相关的资料与行为,要放在一起。通常强型别语言,都是放在 class 上面。

class User {
  string first_name;
  string last_name;
  string full_name;
  
  string setFirstName(const string& name) {
    first_name = name;
    full_name = name + " " + last_name
  }
  
  string setLastName(const string& name) {
    last_name = name;
    full_name = first_name + " " + name
  }
};

一样将 full_name 视为资料,并且在设定 first_namelast_name 时,同步资料。

另一种做法

class User {
  string first_name;
  string last_name;
  
  string getFullName() const {
    return first_name + " " + last_name;
  }
};

取得全名,视为的行为,里面的实作就是将 first_namelast_name 组合起来。

这两种做法都是透过 User 的抽象行为,操作资料。

当然也有针对各别资料设定 getter/setter 的做法。起初这样写会觉得很多此一举,但是後来如果要在 setter 加入资料的定义域,或者要在增加一个 getter 代表资料的另一种表现型式。都是很方便的事情。

资料的另一种表现型式

後面我就用 javascript 实作

class User {
  constructor() {
    this.birthday = new Date();
  }

  setBirthday(year, month, day) {
    this.birthday.setYear(year);
    this.birthday.setMonth(month -1);
    this.birthday.setDate(day);
  }
    
  getBirthday () {
    return this.birthday;
  }
  
  getAge() {
    return new Date().getFullYear() - this.birthday.getFullYear();
  }
};

有了抽象行为可以将生日与年龄的关系建立起来,并且让资料只有一笔,不会有资料同步的问题。

定义域

如果你想要在输入月份超过 12 月,就要抛出例外的话。
就可以随时在 setter 的 method 里面加上限制条件。

class User {
  setBirthday(year, month, day) {
    // ...
    if ((month -1) > 12) throw Error('month > 12');
    this.birthday.setMonth(month -1);
    // ...
  }
};

如果没有先写好 setter ,在想加上条件时,就会比较辛苦的再写一个 function 并且birthday.setMonth(month -1); 搬进去,再将所有原本写 birthday.setMonth(month -1); 替换成 setBirthday()

今天不是要讲 vuex 的 state

State | Vuex

在 vuex 中,如果只有 state 直接存取资料。就像是没有抽象介面的抽象资料型别。

所以我自己的用法,是坚持绝对不要直接存取 state,并且一定要用 mutationgetters

其实 state 就是这样短短的。没有什麽特别的

几乎等同於抽像资料型别的 Module

Modules | Vuex

vuex 里面还有一个东西叫 module,我自己就它当作是「系统的抽象资料型别」在使用,像是 User 就是使用者的资料型别,在後端会建立资料表,在前端用 Vue.js 的话,我就会选择在 vuex 建一个 user 的 module。也就是说,将系统要 CRUD 的「名词」找出来,并建立成各种的 module 里面的 state 则是存放资料的地方。

vuex 的里的 module 是对 state 做分群,并且限制 mutation 和 getters 可以取得的 state 的范围,但是mutation 可以透过 commit、getters 可以再呼叫 getters 的方式跨 module 取得其它 module 的 state。

通常系统上需要储存的方式和後端资料表不一样。以 user 为例,要嘛就是显示一个 user 的「列表」要嘛就是显示「一笔」 user 资料。

所以,我有一个极致的用法(但不见得每一次都是这样使用)

在每一个 module 里的 state 都这样规画,其实就可以符合每一个画面的需要了。

export default {
  state: {
    one: {},
    list: []
  }
}

这只是一种极致的做法
极致的地方在於,state 里的命名,都只有 one 和 list
当如果有其它的需求,就会另外命名
而且它只适用於 CRUD 的资料。
登入的 token 不适用於这种做法。


<<:  D18 - TiDB备份还原

>>:  【Day11】特殊性营运流程篇-CRM

Day-17 中位数与顺序统计

最大值与最小值 在一个有n个元素的,未经排序的阵列中,如果我们要找到最小值,我们可以将一个阵列进行排...

CMoney第八届菁英软件工程师战斗营_Week 2

安安 过了一周我又来了 首先需要先为自己与同学鼓掌撑过第一周✌️ 第二周开始就是介面地狱 每周第一天...

从零开始学游戏开发:入门程序实作 Part.7 重新计分

这是 Roblox 从零开始系列,入门章节的第十三个单元,我们的游戏出现Bug了,那就是死亡後分数还...

Golang-Channel & Goroutine-进阶篇

基础篇简单了介绍Channel&Goroutine的基本使用方法 接下来就是实际应用的问题了...

最短路径问题 (8)

10.10 Thorup’s 无向非负整数权重 SSSP 演算法 今天来介绍 Thorup 在 19...