# 避免过度设计(Overdesign)

# 过度设计

过度设计(Overdesign)指的是在软件设计中过度注重细节、功能和灵活性,导致设计过于复杂和冗余。

这种现象在软件开发中非常常见,因为很多开发者可能会过于热衷于创建功能完备的程序,而忽略了实际需求和使用场景。

过度设计就像一个人穿着过多的衣服,虽然看上去很厚实,但实际上可能会让他感到过度拘束和不适。

同样地,程序中的过度设计虽然可能看上去很完备和强大,但实际上可能会让程序变得更加复杂和难以维护,同时也会增加开发和维护的成本。

就像一个人应该根据天气和自己的需求来穿衣服一样,软件开发者也应该根据实际需求和使用场景来设计程序,避免过度设计。

# 过度设计的表现

以下是过度设计的一些可能的表现和影响:

  • 过度抽象:开发者可能会过于注重代码的可扩展性和灵活性,将很多代码分成模块或类,造成代码冗余、维护困难,而这些模块或类在实际使用中未必有太多作用。

      ```js 
      // 比如没必要将数组封装成一个类,并提供了一些额外的接口
      class MyArray {
      constructor() {
          this.data = [];
      }
      // 添加一个元素到数组的最后
      push(item) {
          this.data[this.data.length] = item;
      }
      // 从数组的最后删除一个元素
      pop() {
          const item = this.data[this.data.length - 1];
          this.data.length = this.data.length - 1;
          return item;
      }
      // 返回数组的长度
      length() {
          return this.data.length;
      }
      // 获取数组中指定位置的元素
      get(index) {
          return this.data[index];
      }
      // 删除数组中指定位置的元素
      delete(index) {
          const item = this.data[index];
          this.shiftItems(index);
          return item;
      }
      // 在数组中指定位置插入一个元素
      insert(index, item) {
          this.shiftItems(index);
          this.data[index] = item;
      }
      // 将数组中指定位置之后的元素向右移动一个位置
      shiftItems(index) {
          for (let i = index; i < this.data.length - 1; i++) {
          this.data[i] = this.data[i + 1];
          }
          this.data.length = this.data.length - 1;
      }
      }
      ```
    

    虽然这个设计确实增加了一些额外的功能和限制,但是它过度抽象了数组的概念,增加了系统的复杂度和维护成本,而且也没有考虑到未来可能出现的需求变化和扩展。相比之下,一个更简单和直接的设计可能是直接使用 JavaScript 数组提供的原生方法,例如 push、pop、slice、splice 等,这样的设计更加直观

  • 过多的配置:程序可能有大量的配置选项,给用户带来困惑,且程序可能会变得更加难以理解和维护。

    用过antd-pro+umi的小伙伴可能深有体会

  • 不必要的优化:开发者可能会过度优化程序的性能,但实际上这些优化对程序的运行并没有多大的影响。这些优化不仅增加了开发和维护的成本,也可能导致代码更加复杂和难以理解。

    例如:使用虚拟滚动的方式来展示列表,这种优化确实可以提高页面的性能,但是它也增加了代码的复杂度和维护成本,需要使用一些复杂的算法和技术来实现。如果列表中的数据量不是很大,或者用户的设备性能比较高,这种优化可能是不必要的,会浪费开发人员的时间和精力。

  • 过于泛化:指的是在设计和实现中过度关注通用性,而不是专注于解决当前的问题。在编写代码时,开发人员可能会尝试编写通用的、可复用的代码,以便在多个场景中使用。然而,如果过度泛化,代码可能会变得过于抽象和复杂,从而降低代码的可读性和可维护性。

    
    // 假设需要写一个函数来计算两个数字的和,并且需要处理负数。
    
    //如果你过于泛化,你可能会编写一个非常通用的函数,它可以处理任意数量的数字,并且可以处理任何类型的数字(整数、小数等)。这个函数可能看起来像这样:
    function addNumbers() {
        let total = 0;
        for (let i = 0; i < arguments.length; i++) {
            const arg = arguments[i];
            if (typeof arg === 'number') {
            total += arg;
            } else if (Array.isArray(arg)) {
            total += addNumbers(...arg);
            } else {
            throw new Error('Invalid argument type');
            }
        }
        return total;
    }
    // 上面这个函数会让人感到困惑和不必要地复杂,因为它试图处理过多的情况。
    function add(a, b) {
        if (typeof a !== 'number' || typeof b !== 'number') {
            throw new Error('Invalid argument type');
        }
        return a + b;
    }
    
    
  • 模块化不当:模块化是一个很好的设计原则,将代码分成独立的模块,以便于管理、测试和复用的一种方式。但是如果模块化不当,过于细分模块或者完全没有模块化可能会导致代码冗余和维护难度增加。

    比如说现在这个时候偶我还是可以看到很多公司把所有的api接口放在一个文件。一个文件里面有几百个接口,找起来想死

  • 过多的层次:过多的层次指的是软件系统中的层次结构过于复杂,嵌套过深,层次过多,导致代码难以理解、维护和扩展。而这些层次可能并不是必要的。

    ** 一般来说前端只需要这些层次就完全够了**

    • 数据访问层:负责与数据库交互,提供数据访问接口
    • 服务层:负责业务逻辑处理,调用数据访问层提供的接口
    • 控制器层:负责处理用户请求,调用服务层提供的接口,返回响应结果
    • 视图层:负责展示用户界面,调用控制器层提供的接口,显示数据

# 总结

过度设计会对软件开发带来负面影响,包括增加开发和维护的成本降低代码的可读性和可维护性,以及增加出错的可能性

因此,软件开发者应该在设计时注重实际需求,避免过度设计。更需要注意上面提到的几点

  1. 过度抽象
  2. 过多的配置
  3. 不必要的优化
  4. 过于泛化
  5. 模块化不当
  6. 过多的层次