重构:各司其职,让你的世界秩序凛然

662 阅读6分钟

很多人在初入行业的时候,喜欢走到哪写到哪,随手一时爽,一直随手一直爽,重构直接火葬场。很多人会搬出来单一职责或者其他设计原理或者设计模式来给你解释,当然,本期不讲这些。我们来聊一聊:展示组件和容器组件。

又是一个晴朗的周末,爬起来已经中午了,突然发现身边乱糟糟的,书啊,衣服啊,电脑啊丢的到处都是。今天还有人来家里进行友好访问(俗称过来白嫖一顿饭),这么乱可不行,那么稍微起来收拾收拾吧。

// example .01 code
const clean = good => {
  if (good === 'clothes') {
    return 'My clothes are in the closet';
  } else if (good === 'book') {
    return 'My book is in the bookcase';
  } else if (good === 'laptop') {
    return 'My laptop is on the desk';
  } else {
    return false;
  }
};

const goodsInRoom = ['clothes', 'book', 'laptop'];

(() => {
  goodsInRoom.forEach(good => {
    const result = clean(good);
    console.log(result ? result : `${good} is not clean`);
  });
  console.log('My room is clean');
})();

看起来这样就可以轻松的收拾房间了呢,但是,好像地面需要打扫,我还需要扫地、拖地。

突然,我放佛觉得哪里不对劲,什么都要我干,老子那个破扫地机器人,买了当大爷吗?不行,我需要让他也干活。

class SweepingRobot {
    #range = [];

    constructor(name) {
        this.name = name;
    }

    setRange(range) {
        this.#range = range;
    }

    clean() {
        this.#range.forEach(range => {
            console.log(`${this.name} -> ${range}: start to clean`);
        );
    }
}

const robotClean = range => {
    const sweepingRobot = new SweepingRobot('斯沃特机器人');
    sweepingRobot.setRange(range);
    sweepingRobot.clean();
};

// ... example .01 code here ...

(() => {
    goodsInRoom.forEach(good => {
        const result = clean(good);
        console.log(result ? result : `${good} is not clean`);
    });

    robotClean(['厨房', '卧室', '洗手间', '阳台']);
    console.log('My room is clean');
})();

看起来真不错,回过头,我们看看整个过程,我们在主入口首先把物品整理了,然后调用robotClean方法清理了厨房、卧室、洗手间、阳台四个地方,我的房间终于干净了。

看起来,这很符合逻辑,先收拾房间嘛,收拾完之后,发现房间太脏了,要扫地拖地,我就创建一个扫地机器人,替我干活就好了。

回归引言:随手一时爽,一直随手一直爽,重构直接火葬场。

很奇怪,我们按照正常的逻辑,哪里不对呢?不对,不对大发了。

看似都狠符合逻辑,但是这是人的逻辑,不是机器的逻辑,在机器的世界里,他需要一个抽象的,秩序凌然的世界。房间就是用来展示,房间不会自己打扫自己,打扫的只能是工具人。工具人要分开,比如我就可以收拾衣服、收拾书、收拾电脑,而我的扫地机器人就会打扫指定区域。

那么这样做还有其他好处吗?有,当有人接手你的代码的时候,你希望别人上来就是一句“f**k”吗?混乱的代码,会意味着你的逻辑也是混乱的,也就是你在写这块的时候,并没有想好要怎么做要做什么。通往高级的一个重要点就是需要有代码设计能力,这个设计可以通俗的理解成抽象能力。

那么让我们,提升抽象能力,开启新的篇章,向高级迈进!

按例:(先来介绍展示组件和容器组件)

Presentational components are concerned with how things look.展示组件关注事物的外观

Container components are concerned with how things work.容器组件关注事物如何工作

很多人会好奇,这不是 React 老生常谈的,拆分组件要注意展示组件和容器组件嘛?关我们写函数有啥关系。其实不然,展示和容器其实追溯应该回归到mvc体系,我们应该活用这样的思想去整理自己的代码,分析一下我们打扫房间这个过程。

  1. 房间仅做展示效果,无论是脏乱,还是干净整洁。
  2. 我可以收拾衣服、书、电脑,讲他们放到原位。
  3. 斯沃特机器人可以帮我打扫房间。

抽象出三个模块,我、机器人、房间,我和机器人注重的是工作,房间注重的展示,毕竟她还不是一个成熟的房间,不会自己打扫。

首先创建一个PersonClass这个类是抽象类,包含基本功能,clean方法,用来收拾指定的事件。

// PersonClass
class PersonClass {
  #name = '';

  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name;
  }

  clean(event) {
    console.log(`${this.#name} packed up ${event}`);
  }
}

在确认了本宝宝虽然是个宝宝但是还是个人这个事实,我决定,需要实现一个PersonClass类,然后我去继承一下,但是我这个人比较厉害,我不是只能干一件事,我可以按照队列执行任务。

// IhapMrFatClass
class IhapMrFatClass extends PersonClass {
  #room = '';
  #packedEventQueue = [];

  constructor(name, room) {
    super(name);
    this.#room = room;
    this.#packedEventQueue = room.getPackedEventQueue();
  }

  clean() {
    let event = '';
    while ((event = this.#packedEventQueue.shift())) {
      super.clean(event);
    }
    this.#room.setPackedEventQueue(this.#packedEventQueue);
  }
}

继续我们搞一个机器人类,机器人类也是一个抽象类,当然和我们人类也差不多,也有一个clean方法。

// RobotClass
class RobotClass {
  #name = '机器人';

  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name;
  }

  clean(range) {
    console.log(`${this.#name} -> ${range}: cleaned`);
  }
}

接下来创建一个扫地机器人类来继承机器人类,扫地机器人就是需要一个区域范围,然后在区域范围内打扫。

// SweepRobotClass
class SweepRobotClass extends RobotClass {
  #room = '';
  #cleanedRange = [];

  constructor(name, room) {
    super(name);
    this.#room = room;
    this.#cleanedRange = room.getCleanedRange();
  }

  clean() {
    let range = '';
    while ((range = this.#cleanedRange.shift())) {
      super.clean(range);
    }
    this.#room.setCleanedRange(this.#cleanedRange);
  }
}

好了,既然两个“工具人”都搞定了,我们就来搞一搞我们的展示组件,展示组件需要的就是进行房间初始化,当房间在创建的时候,就有一个待打扫队列,比如packedEventQueue待收拾事件队列以及cleanedRange待清洁区域,在渲染render的时候,首先检查一下页面清理状态,然后根据状态告诉大家,是否干净。本身是不会与外界有任何交互,仅会提供一些方法出去,交给外部去处理。换句话就是,作为一个不成熟的RoomClass,她是不会自己打扫自己的,她也不管到底是谁打扫,总之,她里面就是包含待收拾事件队列以及待清洁区域,只要打扫干净了,她就认为干净了。

// RoomClass
class RoomClass {
  #packedEventQueue = [];
  #cleanedRange = [];
  #clearStatus = false;

  constructor(initStatus) {
    const { packedEventQueue, cleanedRange } = initStatus;
    this.#packedEventQueue = packedEventQueue;
    this.#cleanedRange = cleanedRange;
  }

  setPackedEventQueue(packedEventQueue) {
    this.#packedEventQueue = packedEventQueue;
  }
  getPackedEventQueue() {
    return this.#packedEventQueue;
  }

  setCleanedRange(cleanedRange) {
    this.#cleanedRange = cleanedRange;
  }
  getCleanedRange() {
    return this.#cleanedRange;
  }

  _checkClearStatus() {
    this.#clearStatus = !(this.#packedEventQueue.length || this.#cleanedRange.length);
  }

  render() {
    this._checkClearStatus();
    console.log(this.#clearStatus ? '干净的房间' : '脏乱的房间');
  }
}

好了,让我们用起来吧

const packedEventQueue = ['clothes', 'book', 'laptop'];
const cleanedRange = ['厨房', '洗手间', '客厅', '阳台'];

const ihapMrFatRoom = new RoomClass({ packedEventQueue, cleanedRange });
ihapMrFatRoom.render();
// 脏乱的房间

const ihapMrFat = new IhapMrFatClass('ihap 肥少', ihapMrFatRoom);
ihapMrFat.clean();

const sweepRobot = new SweepRobotClass('扫地机器人', ihapMrFatRoom);
sweepRobot.clean();

ihapMrFatRoom.render();
// 干净的房间

这样,主函数是不是很清晰明了了呢,不光我们的主函数清晰明了,我们的各大工具人也分工明确,做着他们工具人的本分,而我们的展示组件,也在尽心尽力的展示着我房间从脏乱到干净的过程。

请注意,不是任何情况都要有展示组件,展示组件也不是一定不能做任何的逻辑,只是展示组件不对外做任何处理。

打扫完了房间,朋友们也差不多到了,我们 high 了一晚,结果,第二天我醒来之后,又是脏乱的房间。

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

我是 ihap 肥少,喜欢我的文章,请关注哟。我们是 ihap 技术黑洞,更多咨询、撸猫一手资料,快关注吧!

参考文献: