整洁代码之道 6 对象和数据结构

对象和数据结构在使用场景上有不同的侧重点,要做到代码整洁,需要明确区分两者,并灵活运用

6.1 数据抽象

  1. 我们习惯性熟知的数据封装方式:变量都是私有,通过 Getter / Setter 对变量进行取值赋值
  2. 但其实这么做之后,变量依旧是暴露的,隐藏实现并非只是在变量之前放上一个函数层这么简单
  3. 隐藏的关键点是 抽象 ,类并不是简单的通过 Getter / Setter 将变量推向外层
  4. 而是曝露抽象接口,以方便用户无需了解数据的实现就能操作数据本体

6.2 数据、对象的反对称性

  1. 对象把数据隐藏在抽象之后,曝露操作数据的函数
  2. 数据结构曝露自己的数据,没有提供有意义的函数
  3. 在如今面向对象盛行的时期,其实我们在决定具体时候什么方式实现代码时,并不是只有单一的选择
  4. 有时候根据场景需要,使用数据结构做一些过程式的操作也是可行的

6.2.1 过程式代码

  1. 就是使用数据结构的代码,可以再不改动既有数据结构的前提下添加新函数
  2. 难以添加新数据结构,因为每次添加都需要所有所有调用的函数结构
public class Square {
  public Point topLeft;
  public double side;
}

public class Rectangle {
  public Point topLeft;
  public double height;
  public double width;
}

public class Circle {
  public Point center;
  public double radius;
}

public class Geometry {
  public final double PI = 3.1415926;

  public double area(Object shape) throws NoSuchShapeException {
    if (shape instanceOf Square) {
      Square square = (Square) shape;
      return square.side * square.side;
    } else if (shape instanceof Rectangle) {
      Rectangle rectangle = (Rectangle) shape;
      return rectangle.height * rectangle.width;
    } else if (shape instanceof Circle) {
      Circle circle = (Circle) shape;
      return PI * cicle.radius * circle.radius;
    }

    throw new NoSuchShapeException();
  }
}

6.2.2 面向对象代码

  1. 可以在不改动既有函数的前提下添加新类
  2. 难以添加新函数,因为每次添加都需要修改所有实现的类
public class Square implements Shape {
  private Point topLeft;
  private double side;

  public double area() {
    return side * side;
  }
}

public class Rectangle implements Shape {
  private Point topLeft;
  private double height;
  private double width;

  public double area() {
    return height * width;
  }
}

public class Circle implements Shape {
  private Point center;
  private double radius;
  private final double PI = 3.1415926;

  public double area() {
    return PI * raduis * raduis;
  }
}

6.3 得墨忒耳率( The Law of Demeter )

  1. 核心观念:模块不应该了解它所操作对象的内部情形

6.3.1 火车失事

  1. ctxt.getOptions().getScratchDir().getAbsolutePath() 这种链式语句就像是一列火车
  2. 这句代码显然是违反了得墨忒耳率,但事实告诉我们,由于使用场景的限制,没有绝对的规则
  3. 例如这串代码优化成以下格式,依旧违反了得墨忒耳率,但在语义理解上要直观的多
Options options = ctxt.getOptions();
File scratchDir = options.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();

6.3.2 混杂

  1. 在一个类中,一半是对象,一半是数据结构,导致这个类即拥有 执行操作的函数 ,也拥有 公共变量或 Getter / Setter 函数

6.3.3 隐藏结构

  1. 对上述那串代码追根溯源找到最终使用的位置,并将整个逻辑进行封装,直接返回最后结果
  2. 例如 BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName) ,就比较好的隐藏了内部实现

6.4 数据传送对象

  1. 最为精炼的数据结构,是一个只有公共变量、没有函数的类
  2. 这种类通常被称为 数据传送对象 DTO( Data Transfer Objects )
  3. 但现在 DTO 的使用场景更像是一个 Bean ,拥有私有变量的同时,也拥有共有的 Getter / Setter 函数

6.5 小结

  1. 对象曝露行为,隐藏数据,便于添加新对象类型而不需要修改既有行为
  2. 但对象难以在既有对象中添加新行为
  3. 数据结构曝露数据,没有明显的行为,便于向既有数据结构添加新行为
  4. 但数据结构难以向既有函数添加新数据结构
发布了418 篇原创文章 · 获赞 47 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/asing1elife/article/details/102874063